diff --git a/DEPS b/DEPS
index 5ffe680..e5a9c15 100644
--- a/DEPS
+++ b/DEPS
@@ -195,11 +195,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': '578215be44d00af16d431bc277b0d0d429fbf53d',
+  'skia_revision': '8025507f746b2b95a840684ce6746aae55cecbfd',
   # 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': 'bccf9d557196688f8cc91c00db6526f8f493124c',
+  'v8_revision': '70f0b007689faeba4c5edb01018c40b2f203f2f9',
   # 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.
@@ -266,7 +266,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': '5a8fc1fe06a281f2ece82031d9e91281a71c9805',
+  'devtools_frontend_revision': 'b8ae5a73f8754a47acc50f4c1d7461e8a6fd5e26',
   # 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.
@@ -875,7 +875,7 @@
 
   # Build tools for Chrome OS.
   'src/third_party/chromite': {
-      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + 'ac74b9c703be054e6f7ce1360ef02dbcd3e8b1a4',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '7f786caa5d60e1feec3af7913f47e632440b07da',
       'condition': 'checkout_chromeos',
   },
 
@@ -1248,7 +1248,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' + 'cd8de1d295b4bedf5b0b00a6655575881df8696a',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' + '41db2fb64914bb8c4cd2261f8ecedf1ef3b28942',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '6f3e5028eb65d0b4c5fdd792106ac4c84eee1eb3',
@@ -1470,7 +1470,7 @@
   },
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + 'd37b0ec2bb0c4cbf8e9b196f75795d7106b57ad3',
+    Var('webrtc_git') + '/src.git' + '@' + '59230836579a7ce0386faebb07c73ac23f31a2b4',
 
   'src/third_party/libgifcodec':
      Var('skia_git') + '/libgifcodec' + '@'+  Var('libgifcodec_revision'),
@@ -1542,7 +1542,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@25e9f126d8c331c5a72893d07c11f95a23acae3b',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@65f9045b37a9f02731008b8f71d86bb9db4f0444',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/android_webview/BUILD.gn b/android_webview/BUILD.gn
index a06c44f..932d731 100644
--- a/android_webview/BUILD.gn
+++ b/android_webview/BUILD.gn
@@ -53,6 +53,19 @@
 standalone_system_webview_apk_tmpl("system_webview_no_weblayer_apk") {
   exclude_weblayer_java = true
   apk_name = "SystemWebViewNoWebLayer"
+
+  # Adding deps on recycler view in the base WebView APK will end up keeping the
+  # Java in the base APK instead of the WebLayer DFM, even though it is not
+  # needed in the base APK.
+  #
+  # If you hit this check and are adding a dep to //ui/android:ui_java, use
+  # //ui/android:ui_no_recycler_view instead. If you hit this check because you
+  # are adding //third_party/android_deps:android_support_*, use the androidx
+  # version of the dep instead.
+  # TODO(b/165810905): Use per-feature -keep rules in R8 once supported, then
+  # this can be removed.
+  assert_no_deps =
+      [ "//third_party/android_deps:androidx_recyclerview_recyclerview_java" ]
 }
 
 if (enable_webview_bundles) {
@@ -571,7 +584,7 @@
     "//third_party/android_deps:androidx_annotation_annotation_java",
     "//third_party/android_deps:protobuf_lite_runtime_java",
     "//third_party/blink/public:blink_headers_java",
-    "//ui/android:ui_java",
+    "//ui/android:ui_no_recycler_view_java",
     "//url:gurl_java",
   ]
   annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ]
diff --git a/android_webview/glue/BUILD.gn b/android_webview/glue/BUILD.gn
index 10bf86b2..3508bce4 100644
--- a/android_webview/glue/BUILD.gn
+++ b/android_webview/glue/BUILD.gn
@@ -24,7 +24,7 @@
     "//content/public/android:content_java",
     "//net/android:net_java",
     "//third_party/android_deps:androidx_annotation_annotation_java",
-    "//ui/android:ui_java",
+    "//ui/android:ui_no_recycler_view_java",
     "//url:gurl_java",
   ]
   annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ]
diff --git a/android_webview/java/src/org/chromium/android_webview/AwContents.java b/android_webview/java/src/org/chromium/android_webview/AwContents.java
index 3dd9051..53f5dc3 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwContents.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwContents.java
@@ -1716,6 +1716,8 @@
         mContentsClient.getVisitedHistory(callback);
     }
 
+    private static final Pattern BAD_HEADER_CHAR = Pattern.compile("[\u0000\r\n]");
+
     /**
      * WebView.loadUrl.
      */
@@ -1740,6 +1742,15 @@
 
         LoadUrlParams params = new LoadUrlParams(url, PageTransition.TYPED);
         if (additionalHttpHeaders != null) {
+            boolean valid = true;
+            for (Map.Entry<String, String> header : additionalHttpHeaders.entrySet()) {
+                if (BAD_HEADER_CHAR.matcher(header.getKey()).find()
+                        || BAD_HEADER_CHAR.matcher(header.getValue()).find()) {
+                    valid = false;
+                    break;
+                }
+            }
+            RecordHistogram.recordBooleanHistogram("Android.WebView.ExtraHeaders.Valid", valid);
             params.setExtraHeaders(new HashMap<String, String>(additionalHttpHeaders));
         }
 
diff --git a/android_webview/java/src/org/chromium/android_webview/AwSettings.java b/android_webview/java/src/org/chromium/android_webview/AwSettings.java
index 8b2219ef..107ee18 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwSettings.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwSettings.java
@@ -26,6 +26,7 @@
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.base.annotations.JNINamespace;
 import org.chromium.base.annotations.NativeMethods;
+import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.content_public.browser.WebContents;
 
 import java.lang.annotation.Retention;
@@ -642,6 +643,66 @@
                 < Build.VERSION_CODES.P;
     }
 
+    // Used to record the UMA histogram Android.WebView.UserAgent.Valid. Since these values
+    // are persisted to logs, they should never be renumbered or reused.
+    @IntDef({UserAgentType.VALID, UserAgentType.HAS_NULL, UserAgentType.EXTRA_HEADERS,
+            UserAgentType.EXTRA_HEADERS_SLOPPY_LINEEND, UserAgentType.HEADER_TERMINATION,
+            UserAgentType.UNKNOWN_INVALID})
+    @interface UserAgentType {
+        int VALID = 0;
+        int HAS_NULL = 1;
+        int EXTRA_HEADERS = 2;
+        int EXTRA_HEADERS_SLOPPY_LINEEND = 3;
+        int HEADER_TERMINATION = 4;
+        int UNKNOWN_INVALID = 5;
+        int COUNT = 6;
+    }
+
+    // Regex fragments used in checkUserAgentValueValidity.
+    private static final String HEADER_NAME = "[^\r\n:]+";
+    private static final String HEADER_VALUE = "[^\r\n]+";
+    private static final String HEADER = HEADER_NAME + ":" + HEADER_VALUE;
+    private static final String STRICT_LINEEND = "\r\n";
+    private static final String SLOPPY_LINEEND = "(?:\r\n|\r|\n)";
+
+    private static @UserAgentType int checkUserAgentValueValidity(String ua) {
+        boolean hasLineEnds = false;
+        for (int i = 0; i < ua.length(); ++i) {
+            char c = ua.charAt(i);
+            if (c == '\u0000') {
+                // An embedded null is never going to be valid.
+                return UserAgentType.HAS_NULL;
+            }
+            if (c == '\r' || c == '\n') {
+                hasLineEnds = true;
+                break;
+            }
+        }
+
+        if (!hasLineEnds) {
+            // If we had no nulls and no CR or LF, it's good enough to pass
+            // net::HttpUtil::IsValidHeaderValue().
+            return UserAgentType.VALID;
+        }
+
+        // If it has CR/LFs in it, it might be trying to insert extra headers or other "creative"
+        // uses; check if there's a plausible interpretation. We already established there are no
+        // nulls above.
+        if (ua.matches(HEADER_VALUE + "(?:" + STRICT_LINEEND + HEADER + ")+")) {
+            // Looks like a working attempt to insert additional headers, using CRLF as per spec.
+            return UserAgentType.EXTRA_HEADERS;
+        } else if (ua.matches(HEADER_VALUE + "(?:" + SLOPPY_LINEEND + HEADER + ")+")) {
+            // Looks like an attempt to insert additional headers, but wrong line endings.
+            return UserAgentType.EXTRA_HEADERS_SLOPPY_LINEEND;
+        } else if (ua.matches(".*" + SLOPPY_LINEEND + SLOPPY_LINEEND + ".*")) {
+            // Possibly an attempt to terminate headers and push the rest into the request body?
+            return UserAgentType.HEADER_TERMINATION;
+        } else {
+            // Maybe just random garbage, or some more weird/subtle usage.
+            return UserAgentType.UNKNOWN_INVALID;
+        }
+    }
+
     /**
      * See {@link android.webkit.WebSettings#setUserAgentString}.
      */
@@ -655,6 +716,13 @@
                 mUserAgent = ua;
             }
             if (!oldUserAgent.equals(mUserAgent)) {
+                if (ua != null && ua.length() > 0) {
+                    // If we're using the passed-in string (not the default), and we've actually
+                    // changed the UA since the last call, then check whether it's a valid header
+                    // value so we can log metrics.
+                    RecordHistogram.recordEnumeratedHistogram("Android.WebView.UserAgent.Valid",
+                            checkUserAgentValueValidity(ua), UserAgentType.COUNT);
+                }
                 mEventHandler.runOnUiThreadBlockingAndLocked(() -> {
                     if (mNativeAwSettings != 0) {
                         AwSettingsJni.get().updateUserAgentLocked(
diff --git a/android_webview/lib/aw_main_delegate.cc b/android_webview/lib/aw_main_delegate.cc
index fd25a45..6007355 100644
--- a/android_webview/lib/aw_main_delegate.cc
+++ b/android_webview/lib/aw_main_delegate.cc
@@ -248,6 +248,8 @@
 
     features.EnableIfNotSet(
         metrics::UnsentLogStoreMetrics::kRecordLastUnsentLogMetadataMetrics);
+
+    features.DisableIfNotSet(::features::kPeriodicBackgroundSync);
   }
 
   android_webview::RegisterPathProvider();
diff --git a/android_webview/nonembedded/BUILD.gn b/android_webview/nonembedded/BUILD.gn
index 618fb9f..e9792e0d 100644
--- a/android_webview/nonembedded/BUILD.gn
+++ b/android_webview/nonembedded/BUILD.gn
@@ -33,7 +33,7 @@
     "//third_party/android_deps:androidx_annotation_annotation_java",
     "//third_party/android_deps:com_google_code_findbugs_jsr305_java",
     "//third_party/android_deps:protobuf_lite_runtime_java",
-    "//ui/android:ui_java",
+    "//ui/android:ui_no_recycler_view_java",
   ]
   annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ]
 }
@@ -72,7 +72,7 @@
     "//third_party/android_deps:androidx_lifecycle_lifecycle_common_java",
     "//third_party/android_deps:androidx_lifecycle_lifecycle_viewmodel_java",
     "//third_party/android_deps:androidx_savedstate_savedstate_java",
-    "//ui/android:ui_java",
+    "//ui/android:ui_no_recycler_view_java",
   ]
   resources_package = "org.chromium.android_webview.devui"
 }
diff --git a/ash/accelerators/accelerator_confirmation_dialog.cc b/ash/accelerators/accelerator_confirmation_dialog.cc
index dfbaf82..9e6aa14 100644
--- a/ash/accelerators/accelerator_confirmation_dialog.cc
+++ b/ash/accelerators/accelerator_confirmation_dialog.cc
@@ -27,6 +27,7 @@
     int dialog_text_id,
     base::OnceClosure on_accept_callback,
     base::OnceClosure on_cancel_callback) {
+  SetModalType(ui::MODAL_TYPE_SYSTEM);
   SetTitle(l10n_util::GetStringUTF16(window_title_text_id));
   SetButtonLabel(ui::DIALOG_BUTTON_OK,
                  l10n_util::GetStringUTF16(IDS_ASH_CONTINUE_BUTTON));
@@ -59,10 +60,6 @@
 
 AcceleratorConfirmationDialog::~AcceleratorConfirmationDialog() = default;
 
-ui::ModalType AcceleratorConfirmationDialog::GetModalType() const {
-  return ui::MODAL_TYPE_SYSTEM;
-}
-
 base::WeakPtr<AcceleratorConfirmationDialog>
 AcceleratorConfirmationDialog::GetWeakPtr() {
   return weak_ptr_factory_.GetWeakPtr();
diff --git a/ash/accelerators/accelerator_confirmation_dialog.h b/ash/accelerators/accelerator_confirmation_dialog.h
index 51770af..e378b7a 100644
--- a/ash/accelerators/accelerator_confirmation_dialog.h
+++ b/ash/accelerators/accelerator_confirmation_dialog.h
@@ -23,9 +23,6 @@
                                 base::OnceClosure on_cancel_callback);
   ~AcceleratorConfirmationDialog() override;
 
-  // views::DialogDelegateView:
-  ui::ModalType GetModalType() const override;
-
   base::WeakPtr<AcceleratorConfirmationDialog> GetWeakPtr();
 
  private:
diff --git a/ash/app_list/views/privacy_info_view.cc b/ash/app_list/views/privacy_info_view.cc
index 44547d1..ca7133d 100644
--- a/ash/app_list/views/privacy_info_view.cc
+++ b/ash/app_list/views/privacy_info_view.cc
@@ -245,11 +245,12 @@
   size_t offset;
   const base::string16 text =
       l10n_util::GetStringFUTF16(info_string_id_, link, &offset);
-  auto text_view = std::make_unique<views::StyledLabel>(text, this);
+  text_view_ = AddChildView(std::make_unique<views::StyledLabel>(this));
+  text_view_->SetText(text);
 
   views::StyledLabel::RangeStyleInfo style;
   style.override_color = gfx::kGoogleGrey900;
-  text_view->AddStyleRange(gfx::Range(0, offset), style);
+  text_view_->AddStyleRange(gfx::Range(0, offset), style);
 
   // TODO(crbug.com/1114628): Remove the custom view once RangeStyleInfo
   // supports selected links.
@@ -259,13 +260,12 @@
   custom_view->SetEnabledColor(gfx::kGoogleBlue700);
   link_style.custom_view = custom_view.get();
   link_view_ = custom_view.get();
-  text_view->AddCustomView(std::move(custom_view));
-  text_view->AddStyleRange(gfx::Range(offset, offset + link.length()),
-                           link_style);
+  text_view_->AddCustomView(std::move(custom_view));
+  text_view_->AddStyleRange(gfx::Range(offset, offset + link.length()),
+                            link_style);
 
-  text_view->SetFocusBehavior(FocusBehavior::ALWAYS);
-  text_view->SetAutoColorReadabilityEnabled(false);
-  text_view_ = AddChildView(std::move(text_view));
+  text_view_->SetFocusBehavior(FocusBehavior::ALWAYS);
+  text_view_->SetAutoColorReadabilityEnabled(false);
 }
 
 void PrivacyInfoView::InitCloseButton() {
diff --git a/ash/app_list/views/remove_query_confirmation_dialog.cc b/ash/app_list/views/remove_query_confirmation_dialog.cc
index 8a2d3558..a23f7258 100644
--- a/ash/app_list/views/remove_query_confirmation_dialog.cc
+++ b/ash/app_list/views/remove_query_confirmation_dialog.cc
@@ -27,6 +27,7 @@
     const base::string16& query,
     RemovalConfirmationCallback confirm_callback)
     : confirm_callback_(std::move(confirm_callback)) {
+  SetModalType(ui::MODAL_TYPE_WINDOW);
   SetTitle(l10n_util::GetStringUTF16(IDS_REMOVE_ZERO_STATE_SUGGESTION_TITLE));
   SetShowCloseButton(false);
 
@@ -67,8 +68,4 @@
   return gfx::Size(default_width, GetHeightForWidth(default_width));
 }
 
-ui::ModalType RemoveQueryConfirmationDialog::GetModalType() const {
-  return ui::MODAL_TYPE_WINDOW;
-}
-
 }  // namespace ash
diff --git a/ash/app_list/views/remove_query_confirmation_dialog.h b/ash/app_list/views/remove_query_confirmation_dialog.h
index 21c156a7..ce623c5 100644
--- a/ash/app_list/views/remove_query_confirmation_dialog.h
+++ b/ash/app_list/views/remove_query_confirmation_dialog.h
@@ -29,9 +29,6 @@
   const char* GetClassName() const override;
   gfx::Size CalculatePreferredSize() const override;
 
-  // views::WidgetDelegate:
-  ui::ModalType GetModalType() const override;
-
  private:
   RemovalConfirmationCallback confirm_callback_;
 
diff --git a/ash/assistant/ui/main_stage/assistant_opt_in_view.cc b/ash/assistant/ui/main_stage/assistant_opt_in_view.cc
index 889bb4b3..e1619ed 100644
--- a/ash/assistant/ui/main_stage/assistant_opt_in_view.cc
+++ b/ash/assistant/ui/main_stage/assistant_opt_in_view.cc
@@ -143,8 +143,7 @@
       views::BoxLayout::CrossAxisAlignment::kCenter);
 
   // Label.
-  label_ = container_->AddChildView(std::make_unique<views::StyledLabel>(
-      base::string16(), /*listener=*/nullptr));
+  label_ = container_->AddChildView(std::make_unique<views::StyledLabel>());
   label_->SetAutoColorReadabilityEnabled(false);
   label_->SetHorizontalAlignment(gfx::HorizontalAlignment::ALIGN_CENTER);
 
diff --git a/ash/assistant/ui/main_stage/assistant_query_view.cc b/ash/assistant/ui/main_stage/assistant_query_view.cc
index a272abd..9ba8bdc 100644
--- a/ash/assistant/ui/main_stage/assistant_query_view.cc
+++ b/ash/assistant/ui/main_stage/assistant_query_view.cc
@@ -80,8 +80,7 @@
       views::BoxLayout::CrossAxisAlignment::kStretch);
 
   // Label.
-  label_ = AddChildView(std::make_unique<views::StyledLabel>(
-      base::string16(), /*listener=*/nullptr));
+  label_ = AddChildView(std::make_unique<views::StyledLabel>());
   label_->SetAutoColorReadabilityEnabled(false);
   label_->SetHorizontalAlignment(gfx::HorizontalAlignment::ALIGN_CENTER);
   label_->SetLineHeight(kLineHeightDip);
diff --git a/ash/display/display_change_dialog.cc b/ash/display/display_change_dialog.cc
index b21fc439..a4e907dd 100644
--- a/ash/display/display_change_dialog.cc
+++ b/ash/display/display_change_dialog.cc
@@ -48,6 +48,7 @@
                                    base::Unretained(this)));
   SetCancelCallback(base::BindOnce(&DisplayChangeDialog::OnCancelButtonClicked,
                                    base::Unretained(this)));
+  SetModalType(ui::MODAL_TYPE_SYSTEM);
 
   SetLayoutManager(std::make_unique<views::FillLayout>());
   SetBorder(views::CreateEmptyBorder(
@@ -82,10 +83,6 @@
   RecordDisplayChangeDialogHistogram(/*accepted=*/false);
 }
 
-ui::ModalType DisplayChangeDialog::GetModalType() const {
-  return ui::MODAL_TYPE_SYSTEM;
-}
-
 gfx::Size DisplayChangeDialog::CalculatePreferredSize() const {
   return gfx::Size(350, 100);
 }
diff --git a/ash/display/display_change_dialog.h b/ash/display/display_change_dialog.h
index 1ebb17c9..c2ac34b 100644
--- a/ash/display/display_change_dialog.h
+++ b/ash/display/display_change_dialog.h
@@ -33,8 +33,6 @@
   DisplayChangeDialog(const DisplayChangeDialog&) = delete;
   DisplayChangeDialog& operator=(const DisplayChangeDialog&) = delete;
 
-  ui::ModalType GetModalType() const override;
-
   // views::View:
   gfx::Size CalculatePreferredSize() const override;
 
diff --git a/ash/extended_desktop_unittest.cc b/ash/extended_desktop_unittest.cc
index 35023dccb..ef34d3a 100644
--- a/ash/extended_desktop_unittest.cc
+++ b/ash/extended_desktop_unittest.cc
@@ -47,18 +47,6 @@
       std::move(layout));
 }
 
-class ModalWidgetDelegate : public views::WidgetDelegateView {
- public:
-  ModalWidgetDelegate() = default;
-  ~ModalWidgetDelegate() override = default;
-
-  // Overridden from views::WidgetDelegate:
-  ui::ModalType GetModalType() const override { return ui::MODAL_TYPE_SYSTEM; }
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(ModalWidgetDelegate);
-};
-
 // An event handler which moves the target window to the secondary root window
 // at pre-handle phase of a mouse release event.
 class MoveWindowByClickEventHandler : public ui::EventHandler {
@@ -234,8 +222,10 @@
   EXPECT_EQ(root_windows[0], Shell::GetRootWindowForNewWindows());
 
   // Open system modal. Make sure it's on 2nd root window and active.
+  auto delegate = std::make_unique<views::WidgetDelegateView>();
+  delegate->SetModalType(ui::MODAL_TYPE_SYSTEM);
   views::Widget* modal_widget = views::Widget::CreateWindowWithContext(
-      new ModalWidgetDelegate(), GetContext(), gfx::Rect(1200, 100, 100, 100));
+      delegate.release(), GetContext(), gfx::Rect(1200, 100, 100, 100));
   modal_widget->Show();
   EXPECT_TRUE(wm::IsActiveWindow(modal_widget->GetNativeView()));
   EXPECT_EQ(root_windows[1], modal_widget->GetNativeView()->GetRootWindow());
diff --git a/ash/login/ui/lock_contents_view.cc b/ash/login/ui/lock_contents_view.cc
index f86e3ed1..6b62e88 100644
--- a/ash/login/ui/lock_contents_view.cc
+++ b/ash/login/ui/lock_contents_view.cc
@@ -1979,7 +1979,8 @@
     *bold_start += shortcut_offset_in_string;
   }
 
-  auto label = std::make_unique<views::StyledLabel>(error_text, this);
+  auto label = std::make_unique<views::StyledLabel>(this);
+  label->SetText(error_text);
   MakeSectionBold(label.get(), error_text, bold_start, bold_length);
   label->SetAutoColorReadabilityEnabled(false);
 
diff --git a/ash/login/ui/login_expanded_public_account_view.cc b/ash/login/ui/login_expanded_public_account_view.cc
index 95c95668..8a191e0f 100644
--- a/ash/login/ui/login_expanded_public_account_view.cc
+++ b/ash/login/ui/login_expanded_public_account_view.cc
@@ -320,7 +320,9 @@
     size_t offset;
     const base::string16 text = l10n_util::GetStringFUTF16(
         IDS_ASH_LOGIN_PUBLIC_ACCOUNT_SIGNOUT_REMINDER, link, &offset);
-    learn_more_label_ = new views::StyledLabel(text, this);
+    learn_more_label_ =
+        labels_view_->AddChildView(std::make_unique<views::StyledLabel>(this));
+    learn_more_label_->SetText(text);
 
     views::StyledLabel::RangeStyleInfo style;
     style.custom_font = learn_more_label_->GetFontList().Derive(
@@ -335,8 +337,6 @@
                                      link_style);
     learn_more_label_->SetAutoColorReadabilityEnabled(false);
 
-    labels_view_->AddChildView(learn_more_label_);
-
     // Create button to show/hide advanced view.
     advanced_view_button_ = new SelectionButtonView(
         l10n_util::GetStringUTF16(
diff --git a/ash/login/ui/pin_request_view.cc b/ash/login/ui/pin_request_view.cc
index 0ec91e3..fde7b78 100644
--- a/ash/login/ui/pin_request_view.cc
+++ b/ash/login/ui/pin_request_view.cc
@@ -182,6 +182,12 @@
       default_accessible_title_(request.accessible_title.empty()
                                     ? request.title
                                     : request.accessible_title) {
+  // MODAL_TYPE_SYSTEM is used to get a semi-transparent background behind the
+  // pin request view, when it is used directly on a widget. The overlay
+  // consumes all the inputs from the user, so that they can only interact with
+  // the pin request view while it is visible.
+  SetModalType(ui::MODAL_TYPE_SYSTEM);
+
   // Main view contains all other views aligned vertically and centered.
   auto layout = std::make_unique<views::BoxLayout>(
       views::BoxLayout::Orientation::kVertical,
@@ -407,14 +413,6 @@
   return GetPinRequestViewSize();
 }
 
-ui::ModalType PinRequestView::GetModalType() const {
-  // MODAL_TYPE_SYSTEM is used to get a semi-transparent background behind the
-  // pin request view, when it is used directly on a widget. The overlay
-  // consumes all the inputs from the user, so that they can only interact with
-  // the pin request view while it is visible.
-  return ui::MODAL_TYPE_SYSTEM;
-}
-
 views::View* PinRequestView::GetInitiallyFocusedView() {
   return access_code_view_;
 }
diff --git a/ash/login/ui/pin_request_view.h b/ash/login/ui/pin_request_view.h
index fd3fbc2..f2adefe 100644
--- a/ash/login/ui/pin_request_view.h
+++ b/ash/login/ui/pin_request_view.h
@@ -130,7 +130,6 @@
   void GetAccessibleNodeData(ui::AXNodeData* node_data) override;
 
   // views::DialogDelegateView:
-  ui::ModalType GetModalType() const override;
   views::View* GetInitiallyFocusedView() override;
   base::string16 GetAccessibleWindowTitle() const override;
 
diff --git a/ash/login/ui/public_account_warning_dialog.cc b/ash/login/ui/public_account_warning_dialog.cc
index cd91cd0..34f5c4c 100644
--- a/ash/login/ui/public_account_warning_dialog.cc
+++ b/ash/login/ui/public_account_warning_dialog.cc
@@ -62,6 +62,7 @@
 PublicAccountWarningDialog::PublicAccountWarningDialog(
     base::WeakPtr<LoginExpandedPublicAccountView> controller)
     : controller_(controller) {
+  SetModalType(ui::MODAL_TYPE_SYSTEM);
   SetButtons(ui::DIALOG_BUTTON_NONE);
   SetLayoutManager(std::make_unique<views::BoxLayout>(
       views::BoxLayout::Orientation::kVertical, gfx::Insets(),
@@ -130,10 +131,6 @@
   frame_view->SetTitleView(std::move(title_label));
 }
 
-ui::ModalType PublicAccountWarningDialog::GetModalType() const {
-  return ui::MODAL_TYPE_SYSTEM;
-}
-
 gfx::Size PublicAccountWarningDialog::CalculatePreferredSize() const {
   return gfx::Size(kDialogWidthDp, kDialogHeightDp);
 }
diff --git a/ash/login/ui/public_account_warning_dialog.h b/ash/login/ui/public_account_warning_dialog.h
index c6721679..189d3461 100644
--- a/ash/login/ui/public_account_warning_dialog.h
+++ b/ash/login/ui/public_account_warning_dialog.h
@@ -27,9 +27,6 @@
   // views::DialogDelegate:
   void AddedToWidget() override;
 
-  // views::WidgetDelegate:
-  ui::ModalType GetModalType() const override;
-
   // views::View:
   gfx::Size CalculatePreferredSize() const override;
 
diff --git a/ash/public/cpp/presentation_time_recorder.cc b/ash/public/cpp/presentation_time_recorder.cc
index f59f13f..f55459c 100644
--- a/ash/public/cpp/presentation_time_recorder.cc
+++ b/ash/public/cpp/presentation_time_recorder.cc
@@ -143,6 +143,7 @@
   if (report_immediately_for_test) {
     state_ = COMMITTED;
     gfx::PresentationFeedback feedback;
+    feedback.timestamp = now;
     OnPresented(request_count_++, now, feedback);
     return true;
   }
@@ -170,7 +171,20 @@
                  << ", flags=" << ToFlagString(feedback.flags);
     return;
   }
+  if (feedback.timestamp.is_null()) {
+    // TODO(b/165951963): ideally feedback.timestamp should not be null.
+    // Consider replacing this by DCHECK or CHECK.
+    LOG(ERROR) << "Invalid feedback timestamp (" << count << "):"
+               << " timestamp is not set";
+    return;
+  }
   const base::TimeDelta delta = feedback.timestamp - requested_time;
+  if (delta.InMilliseconds() < 0) {
+    LOG(ERROR) << "Invalid timestamp for presentation feedback (" << count
+               << "): requested_time=" << requested_time
+               << " feedback.timestamp=" << feedback.timestamp;
+    return;
+  }
   if (delta.InMilliseconds() > max_latency_ms_)
     max_latency_ms_ = delta.InMilliseconds();
 
diff --git a/ash/root_window_controller_unittest.cc b/ash/root_window_controller_unittest.cc
index 8953c2d0..39c59e4d 100644
--- a/ash/root_window_controller_unittest.cc
+++ b/ash/root_window_controller_unittest.cc
@@ -51,22 +51,6 @@
 namespace ash {
 namespace {
 
-class TestDelegate : public views::WidgetDelegateView {
- public:
-  explicit TestDelegate(bool system_modal) : system_modal_(system_modal) {}
-  ~TestDelegate() override = default;
-
-  // Overridden from views::WidgetDelegate:
-  ui::ModalType GetModalType() const override {
-    return system_modal_ ? ui::MODAL_TYPE_SYSTEM : ui::MODAL_TYPE_NONE;
-  }
-
- private:
-  bool system_modal_;
-
-  DISALLOW_COPY_AND_ASSIGN(TestDelegate);
-};
-
 class DeleteOnBlurDelegate : public aura::test::TestWindowDelegate,
                              public aura::client::FocusChangeObserver {
  public:
@@ -115,9 +99,15 @@
     return widget;
   }
 
+  views::WidgetDelegate* CreateModalWidgetDelegate() {
+    auto delegate = std::make_unique<views::WidgetDelegateView>();
+    delegate->SetModalType(ui::MODAL_TYPE_SYSTEM);
+    return delegate.release();
+  }
+
   views::Widget* CreateModalWidget(const gfx::Rect& bounds) {
     views::Widget* widget = views::Widget::CreateWindowWithContext(
-        new TestDelegate(true), GetContext());
+        CreateModalWidgetDelegate(), GetContext());
     // See the above comment.
     widget->SetBounds(bounds);
     widget->Show();
@@ -126,8 +116,8 @@
 
   views::Widget* CreateModalWidgetWithParent(const gfx::Rect& bounds,
                                              aura::Window* parent) {
-    views::Widget* widget =
-        views::Widget::CreateWindowWithParent(new TestDelegate(true), parent);
+    views::Widget* widget = views::Widget::CreateWindowWithParent(
+        CreateModalWidgetDelegate(), parent);
     // See the above comment.
     widget->SetBounds(bounds);
     widget->Show();
diff --git a/ash/session/multiprofiles_intro_dialog.cc b/ash/session/multiprofiles_intro_dialog.cc
index 16479e9..7aede6eb 100644
--- a/ash/session/multiprofiles_intro_dialog.cc
+++ b/ash/session/multiprofiles_intro_dialog.cc
@@ -37,10 +37,6 @@
   widget->Show();
 }
 
-ui::ModalType MultiprofilesIntroDialog::GetModalType() const {
-  return ui::MODAL_TYPE_SYSTEM;
-}
-
 gfx::Size MultiprofilesIntroDialog::CalculatePreferredSize() const {
   return gfx::Size(
       kDefaultWidth,
@@ -52,6 +48,7 @@
           l10n_util::GetStringUTF16(IDS_ASH_DIALOG_DONT_SHOW_AGAIN))),
       on_accept_(std::move(on_accept)) {
   never_show_again_checkbox_->SetChecked(true);
+  SetModalType(ui::MODAL_TYPE_SYSTEM);
   SetTitle(l10n_util::GetStringUTF16(IDS_ASH_MULTIPROFILES_INTRO_HEADLINE));
   SetShowCloseButton(false);
   SetAcceptCallback(base::BindOnce(
diff --git a/ash/session/multiprofiles_intro_dialog.h b/ash/session/multiprofiles_intro_dialog.h
index 8a6f7a77..bb5af9fc 100644
--- a/ash/session/multiprofiles_intro_dialog.h
+++ b/ash/session/multiprofiles_intro_dialog.h
@@ -23,9 +23,6 @@
 
   static void Show(OnAcceptCallback on_accept);
 
-  // views::WidgetDelegate overrides.
-  ui::ModalType GetModalType() const override;
-
   // views::View overrides.
   gfx::Size CalculatePreferredSize() const override;
 
diff --git a/ash/session/session_aborted_dialog.cc b/ash/session/session_aborted_dialog.cc
index 405a6ef7..bff4fe2 100644
--- a/ash/session/session_aborted_dialog.cc
+++ b/ash/session/session_aborted_dialog.cc
@@ -49,10 +49,6 @@
   }
 }
 
-ui::ModalType SessionAbortedDialog::GetModalType() const {
-  return ui::MODAL_TYPE_SYSTEM;
-}
-
 gfx::Size SessionAbortedDialog::CalculatePreferredSize() const {
   return gfx::Size(
       kDefaultWidth,
@@ -60,6 +56,7 @@
 }
 
 SessionAbortedDialog::SessionAbortedDialog() {
+  SetModalType(ui::MODAL_TYPE_SYSTEM);
   SetTitle(
       l10n_util::GetStringUTF16(IDS_ASH_MULTIPROFILES_SESSION_ABORT_HEADLINE));
   SetShowCloseButton(false);
diff --git a/ash/session/session_aborted_dialog.h b/ash/session/session_aborted_dialog.h
index be2173fb..db74931 100644
--- a/ash/session/session_aborted_dialog.h
+++ b/ash/session/session_aborted_dialog.h
@@ -18,9 +18,6 @@
  public:
   static void Show(const std::string& user_email);
 
-  // views::WidgetDelegate overrides.
-  ui::ModalType GetModalType() const override;
-
   // views::View overrides.
   gfx::Size CalculatePreferredSize() const override;
 
diff --git a/ash/session/teleport_warning_dialog.cc b/ash/session/teleport_warning_dialog.cc
index 48cec03..a9476e2 100644
--- a/ash/session/teleport_warning_dialog.cc
+++ b/ash/session/teleport_warning_dialog.cc
@@ -31,6 +31,7 @@
       on_accept_(std::move(callback)) {
   never_show_again_checkbox_->SetChecked(true);
   SetShowCloseButton(false);
+  SetModalType(ui::MODAL_TYPE_SYSTEM);
   SetTitle(l10n_util::GetStringUTF16(IDS_ASH_TELEPORT_WARNING_TITLE));
   SetAcceptCallback(base::BindOnce(
       [](TeleportWarningDialog* dialog) {
@@ -59,10 +60,6 @@
   widget->Show();
 }
 
-ui::ModalType TeleportWarningDialog::GetModalType() const {
-  return ui::MODAL_TYPE_SYSTEM;
-}
-
 gfx::Size TeleportWarningDialog::CalculatePreferredSize() const {
   return gfx::Size(
       kDefaultWidth,
diff --git a/ash/session/teleport_warning_dialog.h b/ash/session/teleport_warning_dialog.h
index 18b5459..f1c5cb7 100644
--- a/ash/session/teleport_warning_dialog.h
+++ b/ash/session/teleport_warning_dialog.h
@@ -27,9 +27,6 @@
 
   static void Show(OnAcceptCallback callback);
 
-  // views::WidgetDelegate overrides.
-  ui::ModalType GetModalType() const override;
-
   // views::View overrides.
   gfx::Size CalculatePreferredSize() const override;
 
diff --git a/ash/shell_unittest.cc b/ash/shell_unittest.cc
index 8d9671b5..7990c46 100644
--- a/ash/shell_unittest.cc
+++ b/ash/shell_unittest.cc
@@ -130,32 +130,13 @@
   EXPECT_FALSE(Shell::GetContainer(root_window, kShellWindowId_PhantomWindow));
 }
 
-class ModalWindow : public views::WidgetDelegateView {
- public:
-  ModalWindow() { SetTitle(base::ASCIIToUTF16("Modal Window")); }
-  ~ModalWindow() override = default;
-
-  // Overridden from views::WidgetDelegate:
-  bool CanResize() const override { return true; }
-  ui::ModalType GetModalType() const override { return ui::MODAL_TYPE_SYSTEM; }
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(ModalWindow);
-};
-
-class WindowWithPreferredSize : public views::WidgetDelegateView {
- public:
-  WindowWithPreferredSize() = default;
-  ~WindowWithPreferredSize() override = default;
-
-  // views::WidgetDelegate:
-  gfx::Size CalculatePreferredSize() const override {
-    return gfx::Size(400, 300);
-  }
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(WindowWithPreferredSize);
-};
+views::WidgetDelegateView* CreateModalWidgetDelegate() {
+  auto delegate = std::make_unique<views::WidgetDelegateView>();
+  delegate->SetCanResize(true);
+  delegate->SetModalType(ui::MODAL_TYPE_SYSTEM);
+  delegate->SetTitle(base::ASCIIToUTF16("Modal Window"));
+  return delegate.release();
+}
 
 class SimpleMenuDelegate : public ui::SimpleMenuModel::Delegate {
  public:
@@ -264,7 +245,11 @@
   views::Widget::InitParams params;
   params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
   // Don't specify bounds, parent or context.
-  params.delegate = new WindowWithPreferredSize;
+  {
+    auto delegate = std::make_unique<views::WidgetDelegateView>();
+    delegate->SetPreferredSize(gfx::Size(400, 300));
+    params.delegate = delegate.release();
+  }
   views::Widget widget;
   params.context = GetContext();
   widget.Init(std::move(params));
@@ -321,7 +306,7 @@
 
   // Create a modal window.
   views::Widget* modal_widget = views::Widget::CreateWindowWithParent(
-      new ModalWindow(), widget->GetNativeView());
+      CreateModalWidgetDelegate(), widget->GetNativeView());
   modal_widget->Show();
 
   // It should be in modal container.
@@ -370,7 +355,7 @@
 
   // Create a modal window with a lock window as parent.
   views::Widget* lock_modal_widget = views::Widget::CreateWindowWithParent(
-      new ModalWindow(), lock_widget->GetNativeView());
+      CreateModalWidgetDelegate(), lock_widget->GetNativeView());
   lock_modal_widget->Show();
   EXPECT_TRUE(lock_modal_widget->GetNativeView()->HasFocus());
 
@@ -383,7 +368,7 @@
 
   // Create a modal window with a normal window as parent.
   views::Widget* modal_widget = views::Widget::CreateWindowWithParent(
-      new ModalWindow(), widget->GetNativeView());
+      CreateModalWidgetDelegate(), widget->GetNativeView());
   modal_widget->Show();
   // Window on lock screen shouldn't lost focus.
   EXPECT_FALSE(modal_widget->GetNativeView()->HasFocus());
diff --git a/ash/shortcut_viewer/views/keyboard_shortcut_item_view.cc b/ash/shortcut_viewer/views/keyboard_shortcut_item_view.cc
index 758d81a7..8421d6f 100644
--- a/ash/shortcut_viewer/views/keyboard_shortcut_item_view.cc
+++ b/ash/shortcut_viewer/views/keyboard_shortcut_item_view.cc
@@ -58,8 +58,10 @@
     const KeyboardShortcutItem& item,
     ShortcutCategory category)
     : shortcut_item_(&item), category_(category) {
-  description_label_view_ = AddChildView(std::make_unique<views::StyledLabel>(
-      l10n_util::GetStringUTF16(item.description_message_id), nullptr));
+  description_label_view_ =
+      AddChildView(std::make_unique<views::StyledLabel>());
+  description_label_view_->SetText(
+      l10n_util::GetStringUTF16(item.description_message_id));
   // StyledLabel will flip the alignment if UI layout is right-to-left.
   // Flip the alignment here in order to make |description_label_view_| always
   // align to left.
@@ -111,8 +113,8 @@
     accessible_string = l10n_util::GetStringFUTF16(
         item.shortcut_message_id, accessible_names, /*offsets=*/nullptr);
   }
-  shortcut_label_view_ = AddChildView(
-      std::make_unique<views::StyledLabel>(shortcut_string, nullptr));
+  shortcut_label_view_ = AddChildView(std::make_unique<views::StyledLabel>());
+  shortcut_label_view_->SetText(shortcut_string);
   // StyledLabel will flip the alignment if UI layout is right-to-left.
   // Flip the alignment here in order to make |shortcut_label_view_| always
   // align to right.
diff --git a/ash/system/accessibility/accessibility_feature_disable_dialog.cc b/ash/system/accessibility/accessibility_feature_disable_dialog.cc
index 9a695f8b..7b9b58d8 100644
--- a/ash/system/accessibility/accessibility_feature_disable_dialog.cc
+++ b/ash/system/accessibility/accessibility_feature_disable_dialog.cc
@@ -27,6 +27,7 @@
     base::OnceClosure on_accept_callback,
     base::OnceClosure on_cancel_callback)
     : on_cancel_callback_(std::move(on_cancel_callback)) {
+  SetModalType(ui::MODAL_TYPE_SYSTEM);
   SetTitle(l10n_util::GetStringUTF16(window_title_text_id));
   SetButtonLabel(ui::DIALOG_BUTTON_OK,
                  l10n_util::GetStringUTF16(IDS_ASH_YES_BUTTON));
@@ -67,10 +68,6 @@
 AccessibilityFeatureDisableDialog::~AccessibilityFeatureDisableDialog() =
     default;
 
-ui::ModalType AccessibilityFeatureDisableDialog::GetModalType() const {
-  return ui::MODAL_TYPE_SYSTEM;
-}
-
 base::WeakPtr<AccessibilityFeatureDisableDialog>
 AccessibilityFeatureDisableDialog::GetWeakPtr() {
   return weak_ptr_factory_.GetWeakPtr();
diff --git a/ash/system/accessibility/accessibility_feature_disable_dialog.h b/ash/system/accessibility/accessibility_feature_disable_dialog.h
index bb630f8..442b4a5 100644
--- a/ash/system/accessibility/accessibility_feature_disable_dialog.h
+++ b/ash/system/accessibility/accessibility_feature_disable_dialog.h
@@ -25,9 +25,6 @@
                                     base::OnceClosure on_cancel_callback);
   ~AccessibilityFeatureDisableDialog() override;
 
-  // views::DialogDelegateView:
-  ui::ModalType GetModalType() const override;
-
   base::WeakPtr<AccessibilityFeatureDisableDialog> GetWeakPtr();
 
   // views::View:
diff --git a/ash/system/message_center/stacked_notification_bar.cc b/ash/system/message_center/stacked_notification_bar.cc
index e92d217..68f0703 100644
--- a/ash/system/message_center/stacked_notification_bar.cc
+++ b/ash/system/message_center/stacked_notification_bar.cc
@@ -284,6 +284,8 @@
 
   expand_all_button_->SetVisible(false);
   AddChildView(expand_all_button_);
+
+  SetPaintToLayer();
 }
 
 StackedNotificationBar::~StackedNotificationBar() {
diff --git a/ash/system/session/logout_confirmation_dialog.cc b/ash/system/session/logout_confirmation_dialog.cc
index c74dd894..e3d711b4 100644
--- a/ash/system/session/logout_confirmation_dialog.cc
+++ b/ash/system/session/logout_confirmation_dialog.cc
@@ -37,6 +37,7 @@
     LogoutConfirmationController* controller,
     base::TimeTicks logout_time)
     : controller_(controller), logout_time_(logout_time) {
+  SetModalType(ui::MODAL_TYPE_SYSTEM);
   SetTitle(l10n_util::GetStringUTF16(IDS_ASH_LOGOUT_CONFIRMATION_TITLE));
   SetShowCloseButton(false);
 
@@ -82,10 +83,6 @@
   GetWidget()->Close();
 }
 
-ui::ModalType LogoutConfirmationDialog::GetModalType() const {
-  return ui::MODAL_TYPE_SYSTEM;
-}
-
 void LogoutConfirmationDialog::WindowClosing() {
   update_timer_.Stop();
   if (controller_)
diff --git a/ash/system/session/logout_confirmation_dialog.h b/ash/system/session/logout_confirmation_dialog.h
index d117ca1..ada175a 100644
--- a/ash/system/session/logout_confirmation_dialog.h
+++ b/ash/system/session/logout_confirmation_dialog.h
@@ -33,7 +33,6 @@
   void ControllerGone();
 
   // views::WidgetDelegate:
-  ui::ModalType GetModalType() const override;
   void WindowClosing() override;
 
   // views::View:
diff --git a/ash/system/session/shutdown_confirmation_dialog.cc b/ash/system/session/shutdown_confirmation_dialog.cc
index e1ec587..b76586c 100644
--- a/ash/system/session/shutdown_confirmation_dialog.cc
+++ b/ash/system/session/shutdown_confirmation_dialog.cc
@@ -35,6 +35,7 @@
     base::OnceClosure on_cancel_callback) {
   SetTitle(l10n_util::GetStringUTF16(window_title_text_id));
   SetShowCloseButton(false);
+  SetModalType(ui::MODAL_TYPE_SYSTEM);
 
   SetButtonLabel(
       ui::DIALOG_BUTTON_OK,
@@ -66,10 +67,6 @@
 
 ShutdownConfirmationDialog::~ShutdownConfirmationDialog() = default;
 
-ui::ModalType ShutdownConfirmationDialog::GetModalType() const {
-  return ui::MODAL_TYPE_SYSTEM;
-}
-
 gfx::Size ShutdownConfirmationDialog::CalculatePreferredSize() const {
   return gfx::Size(
       kDefaultWidth,
diff --git a/ash/system/session/shutdown_confirmation_dialog.h b/ash/system/session/shutdown_confirmation_dialog.h
index 6d2aa1c..acda4b18 100644
--- a/ash/system/session/shutdown_confirmation_dialog.h
+++ b/ash/system/session/shutdown_confirmation_dialog.h
@@ -26,9 +26,6 @@
                              base::OnceClosure on_cancel_callback);
   ~ShutdownConfirmationDialog() override;
 
-  // views::WidgetDelegate:
-  ui::ModalType GetModalType() const override;
-
   // views::View:
   gfx::Size CalculatePreferredSize() const override;
 
diff --git a/ash/wm/drag_window_controller.cc b/ash/wm/drag_window_controller.cc
index 7d5dc01..ce46aac 100644
--- a/ash/wm/drag_window_controller.cc
+++ b/ash/wm/drag_window_controller.cc
@@ -96,10 +96,6 @@
                                       window->parent(), &bounds);
     window->SetBounds(bounds);
     window->SetTransform(original_window->transform());
-    window->layer()->SetClipRect(original_window->layer()->clip_rect());
-    window->layer()->SetRoundedCornerRadius(
-        original_window->layer()->rounded_corner_radii());
-    window->layer()->SetIsFastRoundedCorner(true);
     widget_->SetOpacity(opacity);
   }
 
diff --git a/ash/wm/mru_window_tracker_unittest.cc b/ash/wm/mru_window_tracker_unittest.cc
index 1077ebe..4bed2d8 100644
--- a/ash/wm/mru_window_tracker_unittest.cc
+++ b/ash/wm/mru_window_tracker_unittest.cc
@@ -81,19 +81,6 @@
   }
 };
 
-namespace {
-
-class TestDelegate : public views::WidgetDelegateView {
- public:
-  TestDelegate() = default;
-  ~TestDelegate() override = default;
-
-  // views::WidgetDelegateView:
-  ui::ModalType GetModalType() const override { return ui::MODAL_TYPE_SYSTEM; }
-};
-
-}  // namespace
-
 // Test basic functionalities of MruWindowTracker.
 TEST_P(MruWindowTrackerOrderTest, Basic) {
   std::unique_ptr<aura::Window> w1(CreateTestWindow());
@@ -142,8 +129,10 @@
   EXPECT_EQ(w5.get(), window_list[3]);
   EXPECT_EQ(w6.get(), window_list[4]);
 
+  auto delegate = std::make_unique<views::WidgetDelegateView>();
+  delegate->SetModalType(ui::MODAL_TYPE_SYSTEM);
   std::unique_ptr<views::Widget> modal =
-      CreateTestWidget(new TestDelegate(), kShellWindowId_Invalid);
+      CreateTestWidget(delegate.release(), kShellWindowId_Invalid);
   EXPECT_EQ(modal.get()->GetNativeView()->parent()->id(),
             kShellWindowId_SystemModalContainer);
 
diff --git a/ash/wm/system_modal_container_layout_manager_unittest.cc b/ash/wm/system_modal_container_layout_manager_unittest.cc
index fb21b5f..9c9b7cc 100644
--- a/ash/wm/system_modal_container_layout_manager_unittest.cc
+++ b/ash/wm/system_modal_container_layout_manager_unittest.cc
@@ -68,7 +68,10 @@
 
 class TestWindow : public views::WidgetDelegateView {
  public:
-  explicit TestWindow(bool modal) : modal_(modal) {}
+  explicit TestWindow(bool modal) {
+    SetModalType(modal ? ui::MODAL_TYPE_SYSTEM : ui::MODAL_TYPE_NONE);
+    SetPreferredSize(gfx::Size(50, 50));
+  }
   ~TestWindow() override = default;
 
   // The window needs be closed from widget in order for
@@ -77,19 +80,7 @@
     views::Widget::GetWidgetForNativeWindow(window)->Close();
   }
 
-  // Overridden from views::View:
-  gfx::Size CalculatePreferredSize() const override {
-    return gfx::Size(50, 50);
-  }
-
-  // Overridden from views::WidgetDelegate:
-  ui::ModalType GetModalType() const override {
-    return modal_ ? ui::MODAL_TYPE_SYSTEM : ui::MODAL_TYPE_NONE;
-  }
-
  private:
-  bool modal_;
-
   DISALLOW_COPY_AND_ASSIGN(TestWindow);
 };
 
diff --git a/ash/wm/test_child_modal_parent.cc b/ash/wm/test_child_modal_parent.cc
index 13101c1..c364ac0 100644
--- a/ash/wm/test_child_modal_parent.cc
+++ b/ash/wm/test_child_modal_parent.cc
@@ -43,34 +43,23 @@
 const SkColor kModalParentColor = SK_ColorBLUE;
 const SkColor kChildColor = SK_ColorWHITE;
 
+views::WidgetDelegateView* CreateChildModalWindow() {
+  auto child = std::make_unique<views::WidgetDelegateView>();
+  child->SetModalType(ui::MODAL_TYPE_CHILD);
+  child->SetTitle(base::ASCIIToUTF16("Examples: Child Modal Window"));
+  child->SetBackground(views::CreateSolidBackground(kChildColor));
+  child->SetPreferredSize(gfx::Size(kChildWindowWidth, kChildWindowHeight));
+
+  auto textfield = std::make_unique<views::Textfield>();
+  textfield->SetBounds(kTextfieldLeft, kTextfieldTop, kTextfieldWidth,
+                       kTextfieldHeight);
+  textfield->SetPlaceholderText(base::ASCIIToUTF16("modal child window"));
+  child->AddChildView(std::move(textfield));
+  return child.release();
+}
+
 }  // namespace
 
-class ChildModalWindow : public views::WidgetDelegateView {
- public:
-  ChildModalWindow() {
-    SetTitle(base::ASCIIToUTF16("Examples: Child Modal Window"));
-    SetBackground(views::CreateSolidBackground(kChildColor));
-    views::Textfield* modal_child_textfield = new views::Textfield;
-    AddChildView(modal_child_textfield);
-    modal_child_textfield->SetBounds(kTextfieldLeft, kTextfieldTop,
-                                     kTextfieldWidth, kTextfieldHeight);
-    modal_child_textfield->SetPlaceholderText(
-        base::ASCIIToUTF16("modal child window"));
-  }
-  ~ChildModalWindow() override = default;
-
- private:
-  // Overridden from View:
-  gfx::Size CalculatePreferredSize() const override {
-    return gfx::Size(kChildWindowWidth, kChildWindowHeight);
-  }
-
-  // Overridden from WidgetDelegate:
-  ui::ModalType GetModalType() const override { return ui::MODAL_TYPE_CHILD; }
-
-  DISALLOW_COPY_AND_ASSIGN(ChildModalWindow);
-};
-
 // static
 TestChildModalParent* TestChildModalParent::Show(aura::Window* context) {
   auto* test_child_modal_parent = new TestChildModalParent(context);
@@ -119,7 +108,7 @@
 
 aura::Window* TestChildModalParent::ShowModalChild() {
   DCHECK(!modal_child_);
-  modal_child_ = Widget::CreateWindowWithParent(new ChildModalWindow(),
+  modal_child_ = Widget::CreateWindowWithParent(CreateChildModalWindow(),
                                                 GetWidget()->GetNativeView());
   wm::SetModalParent(modal_child_->GetNativeView(),
                      modal_parent_->GetNativeView());
diff --git a/base/BUILD.gn b/base/BUILD.gn
index 19928b7..293a4a0 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -3483,7 +3483,6 @@
 
     deps = [
       ":jni_java",
-      "//third_party/android_deps:android_support_v4_java",
       "//third_party/android_deps:androidx_annotation_annotation_java",
       "//third_party/android_deps:androidx_collection_collection_java",
       "//third_party/android_deps:androidx_core_core_java",
diff --git a/base/allocator/partition_allocator/page_allocator_internals_posix.h b/base/allocator/partition_allocator/page_allocator_internals_posix.h
index d796bf5..d8a21bc 100644
--- a/base/allocator/partition_allocator/page_allocator_internals_posix.h
+++ b/base/allocator/partition_allocator/page_allocator_internals_posix.h
@@ -24,7 +24,7 @@
 #if defined(OS_ANDROID)
 #include <sys/prctl.h>
 #endif
-#if defined(OS_LINUX)
+#if defined(OS_LINUX) || defined(OS_CHROMEOS)
 #include <sys/resource.h>
 
 #include <algorithm>
diff --git a/base/allocator/partition_allocator/partition_alloc_unittest.cc b/base/allocator/partition_allocator/partition_alloc_unittest.cc
index 56c094c..a83950e 100644
--- a/base/allocator/partition_allocator/partition_alloc_unittest.cc
+++ b/base/allocator/partition_allocator/partition_alloc_unittest.cc
@@ -330,7 +330,7 @@
   }
 }
 
-#if defined(OS_LINUX)
+#if defined(OS_LINUX) || defined(OS_CHROMEOS)
 bool CheckPageInCore(void* ptr, bool in_core) {
   unsigned char ret = 0;
   EXPECT_EQ(0, mincore(ptr, kSystemPageSize, &ret));
@@ -341,7 +341,7 @@
   EXPECT_TRUE(CheckPageInCore(ptr, in_core))
 #else
 #define CHECK_PAGE_IN_CORE(ptr, in_core) (void)(0)
-#endif  // defined(OS_LINUX)
+#endif  // defined(OS_LINUX) || defined(OS_CHROMEOS)
 
 class MockPartitionStatsDumper : public PartitionStatsDumper {
  public:
diff --git a/build/android/gyp/lint.py b/build/android/gyp/lint.py
index 78628c6..1b10be0 100755
--- a/build/android/gyp/lint.py
+++ b/build/android/gyp/lint.py
@@ -25,6 +25,7 @@
 
 # These checks are not useful for chromium.
 _DISABLED_ALWAYS = [
+    "AppCompatResource",  # Lint does not correctly detect our appcompat lib.
     "Assert",  # R8 --force-enable-assertions is used to enable java asserts.
     "LintBaseline",  # Don't warn about using baseline.xml files.
     "MissingApplicationIcon",  # False positive for non-production targets.
diff --git a/build/android/gyp/proguard.py b/build/android/gyp/proguard.py
index 3ba4f819..3199073 100755
--- a/build/android/gyp/proguard.py
+++ b/build/android/gyp/proguard.py
@@ -253,6 +253,7 @@
 
     cmd = build_utils.JavaCmd(options.warnings_as_errors) + [
         '-Dcom.android.tools.r8.allowTestProguardOptions=1',
+        '-Dcom.android.tools.r8.enableVerticalClassMerging=1',
     ]
     if options.disable_outlining:
       cmd += ['-Dcom.android.tools.r8.disableOutlining=1']
diff --git a/build/config/linux/pangocairo/pangocairo.gni b/build/config/linux/pangocairo/pangocairo.gni
index 0449105..395a1c0 100644
--- a/build/config/linux/pangocairo/pangocairo.gni
+++ b/build/config/linux/pangocairo/pangocairo.gni
@@ -2,8 +2,10 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import("//build/config/chromeos/ui_mode.gni")
 import("//build/config/ui.gni")
 
 declare_args() {
-  use_pangocairo = is_linux && !is_chromeos && !is_chromecast
+  use_pangocairo =
+      is_linux && !is_chromeos && !is_chromecast && !chromeos_is_browser_only
 }
diff --git a/build/fuchsia/linux.sdk.sha1 b/build/fuchsia/linux.sdk.sha1
index 9fcd918..bff53f91 100644
--- a/build/fuchsia/linux.sdk.sha1
+++ b/build/fuchsia/linux.sdk.sha1
@@ -1 +1 @@
-0.20200825.0.1
+0.20200825.1.1
diff --git a/build/fuchsia/mac.sdk.sha1 b/build/fuchsia/mac.sdk.sha1
index 9fcd918..bff53f91 100644
--- a/build/fuchsia/mac.sdk.sha1
+++ b/build/fuchsia/mac.sdk.sha1
@@ -1 +1 @@
-0.20200825.0.1
+0.20200825.1.1
diff --git a/build/install-build-deps.sh b/build/install-build-deps.sh
index 6310137..b437309d 100755
--- a/build/install-build-deps.sh
+++ b/build/install-build-deps.sh
@@ -225,6 +225,15 @@
 # 64-bit systems need a minimum set of 32-bit compat packages for the pre-built
 # NaCl binaries.
 if file -L /sbin/init | grep -q 'ELF 64-bit'; then
+  dev_list="${dev_list} libc6-i386 lib32stdc++6"
+
+  # lib32gcc-s1 used to be called lib32gcc1 in older distros.
+  if package_exists lib32gcc-s1; then
+    dev_list="${dev_list} lib32gcc-s1"
+  elif package_exists lib32gcc1; then
+    dev_list="${dev_list} lib32gcc1"
+  fi
+
   dev_list="${dev_list} libc6-i386 lib32gcc-s1 lib32stdc++6"
 fi
 
diff --git a/cc/mojo_embedder/async_layer_tree_frame_sink.cc b/cc/mojo_embedder/async_layer_tree_frame_sink.cc
index b37945a..bc859573 100644
--- a/cc/mojo_embedder/async_layer_tree_frame_sink.cc
+++ b/cc/mojo_embedder/async_layer_tree_frame_sink.cc
@@ -356,8 +356,9 @@
 void AsyncLayerTreeFrameSink::OnMojoConnectionError(
     uint32_t custom_reason,
     const std::string& description) {
+  // TODO(sgilhuly): Use DLOG(FATAL) once crbug.com/1043899 is resolved.
   if (custom_reason)
-    DLOG(FATAL) << description;
+    DLOG(ERROR) << description;
   if (client_)
     client_->DidLoseLayerTreeFrameSink();
 }
diff --git a/chrome/android/expectations/lint-baseline.xml b/chrome/android/expectations/lint-baseline.xml
index 8d93a63..421c798 100644
--- a/chrome/android/expectations/lint-baseline.xml
+++ b/chrome/android/expectations/lint-baseline.xml
@@ -255,380 +255,6 @@
     </issue>
 
     <issue
-        id="AppCompatResource"
-        message="Should use `android:showAsAction` when not using the appcompat library"
-        errorLine1="            app:showAsAction=&quot;ifRoom&quot;"
-        errorLine2="            ~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="../../chrome/android/java/res/menu/bookmark_action_bar_menu.xml"
-            line="15"
-            column="13"/>
-    </issue>
-
-    <issue
-        id="AppCompatResource"
-        message="Should use `android:showAsAction` when not using the appcompat library"
-        errorLine1="            app:showAsAction=&quot;ifRoom&quot;"
-        errorLine2="            ~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="../../chrome/android/java/res/menu/bookmark_action_bar_menu.xml"
-            line="22"
-            column="13"/>
-    </issue>
-
-    <issue
-        id="AppCompatResource"
-        message="Should use `android:showAsAction` when not using the appcompat library"
-        errorLine1="            app:showAsAction=&quot;ifRoom&quot;"
-        errorLine2="            ~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="../../chrome/android/java/res/menu/bookmark_action_bar_menu.xml"
-            line="28"
-            column="13"/>
-    </issue>
-
-    <issue
-        id="AppCompatResource"
-        message="Should use `android:showAsAction` when not using the appcompat library"
-        errorLine1="            app:showAsAction=&quot;ifRoom&quot;"
-        errorLine2="            ~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="../../chrome/android/java/res/menu/bookmark_action_bar_menu.xml"
-            line="38"
-            column="13"/>
-    </issue>
-
-    <issue
-        id="AppCompatResource"
-        message="Should use `android:showAsAction` when not using the appcompat library"
-        errorLine1="            app:showAsAction=&quot;ifRoom&quot;"
-        errorLine2="            ~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="../../chrome/android/java/res/menu/bookmark_action_bar_menu.xml"
-            line="44"
-            column="13"/>
-    </issue>
-
-    <issue
-        id="AppCompatResource"
-        message="Should use `android:showAsAction` when not using the appcompat library"
-        errorLine1="            app:showAsAction=&quot;ifRoom&quot;"
-        errorLine2="            ~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="../../chrome/android/java/res/menu/bookmark_action_bar_menu.xml"
-            line="50"
-            column="13"/>
-    </issue>
-
-    <issue
-        id="AppCompatResource"
-        message="Should use `android:showAsAction` when not using the appcompat library"
-        errorLine1="            app:showAsAction=&quot;never&quot; />"
-        errorLine2="            ~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="../../chrome/android/java/res/menu/bookmark_action_bar_menu.xml"
-            line="55"
-            column="13"/>
-    </issue>
-
-    <issue
-        id="AppCompatResource"
-        message="Should use `android:showAsAction` when not using the appcompat library"
-        errorLine1="            app:showAsAction=&quot;never&quot; />"
-        errorLine2="            ~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="../../chrome/android/java/res/menu/bookmark_action_bar_menu.xml"
-            line="59"
-            column="13"/>
-    </issue>
-
-    <issue
-        id="AppCompatResource"
-        message="Should use `android:showAsAction` when not using the appcompat library"
-        errorLine1="            app:showAsAction=&quot;ifRoom&quot;"
-        errorLine2="            ~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="../../chrome/browser/download/android/java/res/menu/download_manager_menu.xml"
-            line="16"
-            column="13"/>
-    </issue>
-
-    <issue
-        id="AppCompatResource"
-        message="Should use `android:showAsAction` when not using the appcompat library"
-        errorLine1="            app:showAsAction=&quot;ifRoom&quot;"
-        errorLine2="            ~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="../../chrome/browser/download/android/java/res/menu/download_manager_menu.xml"
-            line="23"
-            column="13"/>
-    </issue>
-
-    <issue
-        id="AppCompatResource"
-        message="Should use `android:showAsAction` when not using the appcompat library"
-        errorLine1="            app:showAsAction=&quot;ifRoom&quot;"
-        errorLine2="            ~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="../../chrome/browser/download/android/java/res/menu/download_manager_menu.xml"
-            line="29"
-            column="13"/>
-    </issue>
-
-    <issue
-        id="AppCompatResource"
-        message="Should use `android:showAsAction` when not using the appcompat library"
-        errorLine1="            app:showAsAction=&quot;ifRoom&quot;"
-        errorLine2="            ~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="../../chrome/browser/download/android/java/res/menu/download_manager_menu.xml"
-            line="39"
-            column="13"/>
-    </issue>
-
-    <issue
-        id="AppCompatResource"
-        message="Should use `android:showAsAction` when not using the appcompat library"
-        errorLine1="            app:showAsAction=&quot;ifRoom&quot;"
-        errorLine2="            ~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="../../chrome/browser/download/android/java/res/menu/download_manager_menu.xml"
-            line="45"
-            column="13"/>
-    </issue>
-
-    <issue
-        id="AppCompatResource"
-        message="Should use `android:showAsAction` when not using the appcompat library"
-        errorLine1="            app:showAsAction=&quot;ifRoom&quot; />"
-        errorLine2="            ~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="../../chrome/android/java/res/menu/history_manager_menu.xml"
-            line="16"
-            column="13"/>
-    </issue>
-
-    <issue
-        id="AppCompatResource"
-        message="Should use `android:showAsAction` when not using the appcompat library"
-        errorLine1="            app:showAsAction=&quot;ifRoom&quot;"
-        errorLine2="            ~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="../../chrome/android/java/res/menu/history_manager_menu.xml"
-            line="22"
-            column="13"/>
-    </issue>
-
-    <issue
-        id="AppCompatResource"
-        message="Should use `android:showAsAction` when not using the appcompat library"
-        errorLine1="            app:showAsAction=&quot;ifRoom&quot;"
-        errorLine2="            ~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="../../chrome/android/java/res/menu/history_manager_menu.xml"
-            line="28"
-            column="13"/>
-    </issue>
-
-    <issue
-        id="AppCompatResource"
-        message="Should use `android:showAsAction` when not using the appcompat library"
-        errorLine1="            app:showAsAction=&quot;ifRoom&quot;"
-        errorLine2="            ~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="../../chrome/android/java/res/menu/history_manager_menu.xml"
-            line="38"
-            column="13"/>
-    </issue>
-
-    <issue
-        id="AppCompatResource"
-        message="Should use `android:showAsAction` when not using the appcompat library"
-        errorLine1="            app:showAsAction=&quot;never&quot; />"
-        errorLine2="            ~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="../../chrome/android/java/res/menu/history_manager_menu.xml"
-            line="43"
-            column="13"/>
-    </issue>
-
-    <issue
-        id="AppCompatResource"
-        message="Should use `android:showAsAction` when not using the appcompat library"
-        errorLine1="            app:showAsAction=&quot;never&quot; />"
-        errorLine2="            ~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="../../chrome/android/java/res/menu/history_manager_menu.xml"
-            line="47"
-            column="13"/>
-    </issue>
-
-    <issue
-        id="AppCompatResource"
-        message="Should use `android:showAsAction` when not using the appcompat library"
-        errorLine1="            app:showAsAction=&quot;never&quot; />"
-        errorLine2="            ~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="../../chrome/android/java/res/menu/history_manager_menu.xml"
-            line="51"
-            column="13"/>
-    </issue>
-
-    <issue
-        id="AppCompatResource"
-        message="Should use `android:showAsAction` when not using the appcompat library"
-        errorLine1="        app:showAsAction=&quot;always&quot;"
-        errorLine2="        ~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="../../chrome/android/java/res/menu/languages_action_bar_menu.xml"
-            line="12"
-            column="9"/>
-    </issue>
-
-    <issue
-        id="AppCompatResource"
-        message="Should use `android:actionViewClass` when not using the appcompat library"
-        errorLine1="        app:actionViewClass=&quot;androidx.appcompat.widget.SearchView&quot;/>"
-        errorLine2="        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="../../chrome/android/java/res/menu/languages_action_bar_menu.xml"
-            line="13"
-            column="9"/>
-    </issue>
-
-    <issue
-        id="AppCompatResource"
-        message="Should use `android:showAsAction` when not using the appcompat library"
-        errorLine1="        app:showAsAction=&quot;always&quot;/>"
-        errorLine2="        ~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="../../chrome/browser/password_check/android/java/res/menu/password_check_editor_action_bar_menu.xml"
-            line="12"
-            column="9"/>
-    </issue>
-
-    <issue
-        id="AppCompatResource"
-        message="Should use `android:showAsAction` when not using the appcompat library"
-        errorLine1="        app:showAsAction=&quot;always&quot;/>"
-        errorLine2="        ~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="../../chrome/android/java/res/menu/password_entry_editor_action_bar_menu.xml"
-            line="12"
-            column="9"/>
-    </issue>
-
-    <issue
-        id="AppCompatResource"
-        message="Should use `android:showAsAction` when not using the appcompat library"
-        errorLine1="        app:showAsAction=&quot;ifRoom&quot;/>"
-        errorLine2="        ~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="../../chrome/android/java/res/menu/password_entry_viewer_action_bar_menu.xml"
-            line="15"
-            column="9"/>
-    </issue>
-
-    <issue
-        id="AppCompatResource"
-        message="Should use `android:showAsAction` when not using the appcompat library"
-        errorLine1="        app:showAsAction=&quot;ifRoom&quot;/>"
-        errorLine2="        ~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="../../chrome/android/java/res/menu/password_entry_viewer_action_bar_menu.xml"
-            line="23"
-            column="9"/>
-    </issue>
-
-    <issue
-        id="AppCompatResource"
-        message="Should use `android:showAsAction` when not using the appcompat library"
-        errorLine1="        app:showAsAction=&quot;ifRoom&quot;"
-        errorLine2="        ~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="../../chrome/android/java/res/menu/prefeditor_editor_menu.xml"
-            line="13"
-            column="9"/>
-    </issue>
-
-    <issue
-        id="AppCompatResource"
-        message="Should use `android:showAsAction` when not using the appcompat library"
-        errorLine1="        app:showAsAction=&quot;ifRoom&quot; />"
-        errorLine2="        ~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="../../chrome/android/java/res/menu/prefeditor_editor_menu.xml"
-            line="20"
-            column="9"/>
-    </issue>
-
-    <issue
-        id="AppCompatResource"
-        message="Should use `android:showAsAction` when not using the appcompat library"
-        errorLine1="        app:showAsAction=&quot;always&quot;"
-        errorLine2="        ~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="../../chrome/android/java/res/menu/save_password_preferences_action_bar_menu.xml"
-            line="13"
-            column="9"/>
-    </issue>
-
-    <issue
-        id="AppCompatResource"
-        message="Should use `android:actionViewClass` when not using the appcompat library"
-        errorLine1="        app:actionViewClass=&quot;androidx.appcompat.widget.SearchView&quot; />"
-        errorLine2="        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="../../chrome/android/java/res/menu/save_password_preferences_action_bar_menu.xml"
-            line="14"
-            column="9"/>
-    </issue>
-
-    <issue
-        id="AppCompatResource"
-        message="Should use `android:showAsAction` when not using the appcompat library"
-        errorLine1="        app:showAsAction=&quot;ifRoom&quot;/>"
-        errorLine2="        ~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="../../chrome/android/java/res/menu/save_password_preferences_action_bar_menu.xml"
-            line="20"
-            column="9"/>
-    </issue>
-
-    <issue
-        id="AppCompatResource"
-        message="Should use `android:showAsAction` when not using the appcompat library"
-        errorLine1="        app:showAsAction=&quot;never&quot;/>"
-        errorLine2="        ~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="../../chrome/android/java/res/menu/save_password_preferences_action_bar_menu.xml"
-            line="26"
-            column="9"/>
-    </issue>
-
-    <issue
-        id="AppCompatResource"
-        message="Should use `android:showAsAction` when not using the appcompat library"
-        errorLine1="          app:showAsAction=&quot;always&quot;"
-        errorLine2="          ~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="../../components/browser_ui/site_settings/android/java/res/menu/website_preferences_menu.xml"
-            line="10"
-            column="11"/>
-    </issue>
-
-    <issue
-        id="AppCompatResource"
-        message="Should use `android:actionViewClass` when not using the appcompat library"
-        errorLine1="          app:actionViewClass=&quot;androidx.appcompat.widget.SearchView&quot; />"
-        errorLine2="          ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="../../components/browser_ui/site_settings/android/java/res/menu/website_preferences_menu.xml"
-            line="11"
-            column="11"/>
-    </issue>
-
-    <issue
         id="VisibleForTests"
         message="This method should only be accessed from tests or within private scope"
         errorLine1="            if (AccountUtils.GOOGLE_ACCOUNT_TYPE.equals(desc.type)) return true;"
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantClient.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantClient.java
index ca92f80..d41e826a 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantClient.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantClient.java
@@ -191,7 +191,7 @@
      *
      * @param actionId id of the action
      * @param experimentIds comma-separated set of experiments to use while running the flow
-     * @param arguments report these as autobot parameters while performing this specific action
+     * @param arguments report these as script parameters while performing this specific action
      * @param onboardingCoordinator if non-null, reuse existing UI elements, usually created to show
      *         onboarding.
      * @return true if the action was found started, false otherwise. The action can still fail
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/DetachedResourceRequestTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/DetachedResourceRequestTest.java
index 120ff7c..17019095 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/DetachedResourceRequestTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/DetachedResourceRequestTest.java
@@ -4,7 +4,7 @@
 
 package org.chromium.chrome.browser.customtabs;
 
-import static org.chromium.components.content_settings.PrefNames.BLOCK_THIRD_PARTY_COOKIES;
+import static org.chromium.components.content_settings.PrefNames.COOKIE_CONTROLS_MODE;
 
 import android.content.ComponentName;
 import android.content.Context;
@@ -40,6 +40,7 @@
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.chrome.test.util.browser.Features.DisableFeatures;
 import org.chromium.chrome.test.util.browser.Features.EnableFeatures;
+import org.chromium.components.content_settings.CookieControlsMode;
 import org.chromium.components.embedder_support.util.Origin;
 import org.chromium.components.prefs.PrefService;
 import org.chromium.components.safe_browsing.SafeBrowsingApiBridge;
@@ -371,8 +372,9 @@
         mServer = EmbeddedTestServer.createAndStartHTTPSServer(mContext, ServerCertificate.CERT_OK);
         TestThreadUtils.runOnUiThreadBlocking(() -> {
             PrefService prefs = UserPrefs.get(Profile.getLastUsedRegularProfile());
-            Assert.assertFalse(prefs.getBoolean(BLOCK_THIRD_PARTY_COOKIES));
-            prefs.setBoolean(BLOCK_THIRD_PARTY_COOKIES, true);
+            Assert.assertEquals(
+                    prefs.getInteger(COOKIE_CONTROLS_MODE), CookieControlsMode.INCOGNITO_ONLY);
+            prefs.setInteger(COOKIE_CONTROLS_MODE, CookieControlsMode.BLOCK_THIRD_PARTY);
         });
         final Uri url = Uri.parse(mServer.getURL("/set-cookie?acookie;SameSite=none;Secure"));
         TestThreadUtils.runOnUiThreadBlocking(() -> {
@@ -405,7 +407,8 @@
         // This isn't blocking third-party cookies by preferences.
         TestThreadUtils.runOnUiThreadBlocking(() -> {
             PrefService prefs = UserPrefs.get(Profile.getLastUsedRegularProfile());
-            Assert.assertFalse(prefs.getBoolean(BLOCK_THIRD_PARTY_COOKIES));
+            Assert.assertEquals(
+                    prefs.getInteger(COOKIE_CONTROLS_MODE), CookieControlsMode.INCOGNITO_ONLY);
         });
 
         // Of the three cookies, only one that's both SameSite=None and Secure
@@ -435,8 +438,9 @@
         mServer = EmbeddedTestServer.createAndStartServer(mContext);
         TestThreadUtils.runOnUiThreadBlocking(() -> {
             PrefService prefs = UserPrefs.get(Profile.getLastUsedRegularProfile());
-            Assert.assertFalse(prefs.getBoolean(BLOCK_THIRD_PARTY_COOKIES));
-            prefs.setBoolean(BLOCK_THIRD_PARTY_COOKIES, true);
+            Assert.assertEquals(
+                    prefs.getInteger(COOKIE_CONTROLS_MODE), CookieControlsMode.INCOGNITO_ONLY);
+            prefs.setInteger(COOKIE_CONTROLS_MODE, CookieControlsMode.BLOCK_THIRD_PARTY);
         });
         final Uri url = Uri.parse(mServer.getURL("/set-cookie?acookie"));
         final Uri origin = Uri.parse(Origin.create(url).toString());
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/dom_distiller/ReaderModeTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/dom_distiller/ReaderModeTest.java
index bb7c26b..cd29d51 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/dom_distiller/ReaderModeTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/dom_distiller/ReaderModeTest.java
@@ -26,6 +26,7 @@
 import static org.chromium.chrome.browser.dom_distiller.ReaderModeManager.DOM_DISTILLER_SCHEME;
 
 import android.app.Activity;
+import android.os.Build.VERSION_CODES;
 import android.support.test.InstrumentationRegistry;
 
 import androidx.annotation.NonNull;
@@ -46,6 +47,7 @@
 
 import org.chromium.base.ApplicationStatus;
 import org.chromium.base.test.util.CommandLineFlags;
+import org.chromium.base.test.util.DisableIf;
 import org.chromium.base.test.util.Restriction;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ChromeTabbedActivity;
@@ -256,6 +258,8 @@
     @Test
     @MediumTest
     @DisableFeatures(ChromeFeatureList.READER_MODE_IN_CCT)
+    @DisableIf.Build(sdk_is_less_than = VERSION_CODES.M,
+            message = "Failing on Lollipop Phone Tester. https://crbug.com/1120830")
     public void testPreferenceInTab() throws TimeoutException {
         mDownloadTestRule.loadUrl(
                 DomDistillerUrlUtils.getDistillerViewUrlFromUrl(DOM_DISTILLER_SCHEME, mURL, TITLE));
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/modaldialog/ModalDialogViewRenderTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/modaldialog/ModalDialogViewRenderTest.java
index 958b16f..cce7b63 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/modaldialog/ModalDialogViewRenderTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/modaldialog/ModalDialogViewRenderTest.java
@@ -110,7 +110,7 @@
     public void testRender_TitleAndTitleIcon() throws IOException {
         setUpViews(R.style.Theme_Chromium_ModalDialog_TextPrimaryButton);
         final Drawable icon = UiUtils.getTintedDrawable(
-                getActivity(), R.drawable.ic_add, R.color.default_icon_color);
+                getActivity(), org.chromium.chrome.R.drawable.ic_add, R.color.default_icon_color);
         createModel(mModelBuilder.with(ModalDialogProperties.TITLE, mResources, R.string.title)
                             .with(ModalDialogProperties.TITLE_ICON, icon));
         mRenderTestRule.render(mModalDialogView, "title_and_title_icon");
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/page_info/PageInfoViewTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/page_info/PageInfoViewTest.java
index a9e850d6..c9b4524 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/page_info/PageInfoViewTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/page_info/PageInfoViewTest.java
@@ -10,7 +10,7 @@
 
 import static org.junit.Assert.assertNotNull;
 
-import static org.chromium.components.content_settings.PrefNames.BLOCK_THIRD_PARTY_COOKIES;
+import static org.chromium.components.content_settings.PrefNames.COOKIE_CONTROLS_MODE;
 
 import android.os.Build;
 import android.view.View;
@@ -37,6 +37,7 @@
 import org.chromium.components.browser_ui.site_settings.WebsitePreferenceBridge;
 import org.chromium.components.content_settings.ContentSettingValues;
 import org.chromium.components.content_settings.ContentSettingsType;
+import org.chromium.components.content_settings.CookieControlsMode;
 import org.chromium.components.location.LocationUtils;
 import org.chromium.components.page_info.PageInfoController;
 import org.chromium.components.page_info.PageInfoFeatureList;
@@ -97,10 +98,10 @@
         return view;
     }
 
-    private void setThirdPartyCookieBlocking(boolean value) {
+    private void setThirdPartyCookieBlocking(@CookieControlsMode int value) {
         TestThreadUtils.runOnUiThreadBlocking(() -> {
             UserPrefs.get(Profile.getLastUsedRegularProfile())
-                    .setBoolean(BLOCK_THIRD_PARTY_COOKIES, value);
+                    .setInteger(COOKIE_CONTROLS_MODE, value);
         });
     }
 
@@ -214,7 +215,7 @@
     @MediumTest
     @Feature({"RenderTest"})
     public void testShowWithCookieBlocking() throws IOException {
-        setThirdPartyCookieBlocking(true);
+        setThirdPartyCookieBlocking(CookieControlsMode.BLOCK_THIRD_PARTY);
         loadUrlAndOpenPageInfo(mTestServerRule.getServer().getURL(mPath));
         mRenderTestRule.render(getPageInfoView(), "PageInfo_CookieBlocking");
     }
@@ -227,7 +228,7 @@
     @Feature({"RenderTest"})
     public void testShowWithPermissionsAndCookieBlocking() throws IOException {
         addSomePermissions(mTestServerRule.getServer().getURL("/"));
-        setThirdPartyCookieBlocking(true);
+        setThirdPartyCookieBlocking(CookieControlsMode.BLOCK_THIRD_PARTY);
         loadUrlAndOpenPageInfo(mTestServerRule.getServer().getURL(mPath));
         mRenderTestRule.render(getPageInfoView(), "PageInfo_PermissionsAndCookieBlocking");
     }
@@ -292,7 +293,7 @@
     @Feature({"RenderTest"})
     @Features.EnableFeatures(PageInfoFeatureList.PAGE_INFO_V2)
     public void testShowCookiesSubpage() throws IOException {
-        setThirdPartyCookieBlocking(true);
+        setThirdPartyCookieBlocking(CookieControlsMode.BLOCK_THIRD_PARTY);
         loadUrlAndOpenPageInfo(mTestServerRule.getServer().getURL(mPath));
         View dialog = (View) getPageInfoView().getParent();
         onView(withId(R.id.page_info_cookies_row)).perform(click());
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/site_settings/SiteSettingsTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/site_settings/SiteSettingsTest.java
index 55bfd6a..795cc977 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/site_settings/SiteSettingsTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/site_settings/SiteSettingsTest.java
@@ -5,7 +5,7 @@
 package org.chromium.chrome.browser.site_settings;
 
 import static org.chromium.components.browser_ui.site_settings.WebsitePreferenceBridge.SITE_WILDCARD;
-import static org.chromium.components.content_settings.PrefNames.BLOCK_THIRD_PARTY_COOKIES;
+import static org.chromium.components.content_settings.PrefNames.COOKIE_CONTROLS_MODE;
 
 import android.content.Context;
 import android.content.Intent;
@@ -252,7 +252,7 @@
             Assert.assertEquals(
                     "Third Party Cookie Blocking should be " + (expected ? "managed" : "unmanaged"),
                     UserPrefs.get(Profile.getLastUsedRegularProfile())
-                            .isManagedPreference(BLOCK_THIRD_PARTY_COOKIES),
+                            .isManagedPreference(COOKIE_CONTROLS_MODE),
                     expected);
         });
     }
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index d585800..3963363 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -5652,6 +5652,20 @@
         </message>
       </if>
 
+      <!-- Star View menu -->
+      <message name="IDS_STAR_VIEW_MENU_ADD_BOOKMARK" desc="The item label of the menu triggered from the star icon in the location bar for adding a bookmark.">
+        Add Bookmark
+      </message>
+      <message name="IDS_STAR_VIEW_MENU_EDIT_BOOKMARK" desc="The item label of the menu triggered from the star icon in the location bar for editing a bookmark.">
+        Edit Bookmark
+      </message>
+      <message name="IDS_STAR_VIEW_MENU_MOVE_TO_READ_LATER" desc="The item label of the menu triggered from the star icon in the location bar for moving the current tab to read later.">
+        Add to Read later
+      </message>
+      <message name="IDS_STAR_VIEW_MENU_MARK_AS_READ" desc="The item label of the menu triggered from the star icon in the location bar for marking the current tab's read later entry as read.">
+        Mark as Read
+      </message>
+
       <!--Tooltip strings-->
       <message name="IDS_TOOLTIP_BACK" desc="The tooltip for back button">
         Click to go back, hold to see history
diff --git a/chrome/app/generated_resources_grd/IDS_STAR_VIEW_MENU_ADD_BOOKMARK.png.sha1 b/chrome/app/generated_resources_grd/IDS_STAR_VIEW_MENU_ADD_BOOKMARK.png.sha1
new file mode 100644
index 0000000..7d33700f
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_STAR_VIEW_MENU_ADD_BOOKMARK.png.sha1
@@ -0,0 +1 @@
+cd19bd38c1c505428e1b952dff29ea2dcc23a749
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_STAR_VIEW_MENU_EDIT_BOOKMARK.png.sha1 b/chrome/app/generated_resources_grd/IDS_STAR_VIEW_MENU_EDIT_BOOKMARK.png.sha1
new file mode 100644
index 0000000..5140577
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_STAR_VIEW_MENU_EDIT_BOOKMARK.png.sha1
@@ -0,0 +1 @@
+ca95daa02a7ba6b6dcc742fa2359f58058ac8025
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_STAR_VIEW_MENU_MARK_AS_READ.png.sha1 b/chrome/app/generated_resources_grd/IDS_STAR_VIEW_MENU_MARK_AS_READ.png.sha1
new file mode 100644
index 0000000..5140577
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_STAR_VIEW_MENU_MARK_AS_READ.png.sha1
@@ -0,0 +1 @@
+ca95daa02a7ba6b6dcc742fa2359f58058ac8025
\ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_STAR_VIEW_MENU_MOVE_TO_READ_LATER.png.sha1 b/chrome/app/generated_resources_grd/IDS_STAR_VIEW_MENU_MOVE_TO_READ_LATER.png.sha1
new file mode 100644
index 0000000..7d33700f
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_STAR_VIEW_MENU_MOVE_TO_READ_LATER.png.sha1
@@ -0,0 +1 @@
+cd19bd38c1c505428e1b952dff29ea2dcc23a749
\ No newline at end of file
diff --git a/chrome/app/os_settings_strings.grdp b/chrome/app/os_settings_strings.grdp
index 8f4fdf5..b0b97aef 100644
--- a/chrome/app/os_settings_strings.grdp
+++ b/chrome/app/os_settings_strings.grdp
@@ -2197,6 +2197,21 @@
   <message name="IDS_SETTINGS_MULTIDEVICE_PHONE_HUB_TASK_CONTINUATION_SECTION_TITLE" translateable="false" desc="The title of the Phone Hub Task Continuation section on the settings page.">
     Task Continuation
   </message>
+  <message name="IDS_SETTINGS_MULTIDEVICE_NOTIFICATION_ACCESS_SETUP_DIALOG_ACK_TITLE" translateable="false" desc="The title of the dialog containing the Phone Hub notification opt-in flow shown when the Phone Hub 'Notifications' toggle is switched on.">
+    Turn on notifications on Android phone
+  </message>
+  <message name="IDS_SETTINGS_MULTIDEVICE_NOTIFICATION_ACCESS_SETUP_DIALOG_CONNECTING_TITLE" translateable="false" desc="The title of the dialog containing the Phone Hub notification opt-in flow shown when the user has confirmed to enable the feature that will mirror phone notifications to their Chromebook.">
+    Connecting to your Phone
+  </message>
+  <message name="IDS_SETTINGS_MULTIDEVICE_NOTIFICATION_ACCESS_SETUP_DIALOG_INSTRUCTIONS" translateable="false" desc="The body text of the dialog containing the Phone Hub notification opt-in flow when the Phone Hub 'Notifications' toggle is switched on, and when the user has confirmed to turn on the feature.">
+    Make sure your phone is nearby, unlocked and has Bluetooth and Wi-Fi turned on. Turn on notifications toggle under Google Play Services&gt;Chromebook Phone Hub from your phone.
+  </message>
+  <message name="IDS_SETTINGS_MULTIDEVICE_NOTIFICATION_ACCESS_SETUP_DIALOG_COMPLETED_TITLE" translateable="false" desc="The title of the dialog containing the Phone Hub notification opt-in flow when the feature is successfully turned on and notifications on their phone will now be mirrored to their Chromebook.">
+    Notifications turned on
+  </message>
+  <message name="IDS_SETTINGS_MULTIDEVICE_NOTIFICATION_ACCESS_SETUP_DIALOG_COMPLETED_SUMMARY" translateable="false" desc="The body text of the dialog containing the Phone Hub notification opt-in flow when the feature is successfully turned on and notifications on their phone will now be mirrored to their Chromebook.">
+    You will receive your phone notifications on your Chromebook
+  </message>
 
   <!-- Lock Screen Page (OS settings) -->
   <message name="IDS_ASH_SETTINGS_LOCK_SCREEN_NOTIFICATION_TITLE" desc="The title of options to change the behavior of notifications on the lock screen.">
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 2c2d794..8b278d6 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -776,14 +776,14 @@
     "metrics/chrome_browser_main_extra_parts_metrics.h",
     "metrics/chrome_feature_list_creator.cc",
     "metrics/chrome_feature_list_creator.h",
+    "metrics/chrome_metrics_extensions_helper.cc",
+    "metrics/chrome_metrics_extensions_helper.h",
     "metrics/chrome_metrics_service_accessor.cc",
     "metrics/chrome_metrics_service_accessor.h",
     "metrics/chrome_metrics_service_client.cc",
     "metrics/chrome_metrics_service_client.h",
     "metrics/chrome_metrics_services_manager_client.cc",
     "metrics/chrome_metrics_services_manager_client.h",
-    "metrics/chrome_stability_metrics_provider.cc",
-    "metrics/chrome_stability_metrics_provider.h",
     "metrics/https_engagement_metrics_provider.cc",
     "metrics/https_engagement_metrics_provider.h",
     "metrics/incognito_observer.cc",
@@ -1415,6 +1415,8 @@
     "push_messaging/push_messaging_service_factory.h",
     "push_messaging/push_messaging_service_impl.cc",
     "push_messaging/push_messaging_service_impl.h",
+    "push_messaging/push_messaging_utils.cc",
+    "push_messaging/push_messaging_utils.h",
     "query_tiles/tile_background_task.cc",
     "query_tiles/tile_background_task.h",
     "query_tiles/tile_service_factory.cc",
diff --git a/chrome/browser/chrome_browser_interface_binders.cc b/chrome/browser/chrome_browser_interface_binders.cc
index 91edd4c..f9f4832 100644
--- a/chrome/browser/chrome_browser_interface_binders.cc
+++ b/chrome/browser/chrome_browser_interface_binders.cc
@@ -126,6 +126,7 @@
 #include "chrome/browser/ui/webui/tab_search/tab_search_ui.h"
 #include "chrome/common/caption.mojom.h"
 #include "chrome/common/webui_url_constants.h"
+#include "media/base/media_switches.h"
 #include "media/mojo/mojom/speech_recognition_service.mojom.h"
 #endif
 
@@ -372,7 +373,8 @@
   Profile* profile = Profile::FromBrowserContext(
       frame_host->GetProcess()->GetBrowserContext());
   PrefService* profile_prefs = profile->GetPrefs();
-  if (profile_prefs->GetBoolean(prefs::kLiveCaptionEnabled)) {
+  if (profile_prefs->GetBoolean(prefs::kLiveCaptionEnabled) &&
+      base::FeatureList::IsEnabled(media::kLiveCaption)) {
     SpeechRecognitionServiceFactory::GetForProfile(profile)->Create(
         std::move(receiver));
   }
@@ -384,7 +386,8 @@
   Profile* profile = Profile::FromBrowserContext(
       frame_host->GetProcess()->GetBrowserContext());
   PrefService* profile_prefs = profile->GetPrefs();
-  if (profile_prefs->GetBoolean(prefs::kLiveCaptionEnabled)) {
+  if (profile_prefs->GetBoolean(prefs::kLiveCaptionEnabled) &&
+      base::FeatureList::IsEnabled(media::kLiveCaption)) {
     captions::CaptionHostImpl::Create(frame_host, std::move(receiver));
   }
 }
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index 814294f..c481dd9 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -353,6 +353,7 @@
 #include "storage/browser/file_system/external_mount_points.h"
 #include "third_party/blink/public/common/associated_interfaces/associated_interface_registry.h"
 #include "third_party/blink/public/common/features.h"
+#include "third_party/blink/public/common/loader/referrer_utils.h"
 #include "third_party/blink/public/common/loader/url_loader_throttle.h"
 #include "third_party/blink/public/common/switches.h"
 #include "third_party/blink/public/mojom/renderer_preferences.mojom.h"
@@ -2168,7 +2169,7 @@
                                  kDinosaurEasterEggSwitches,
                                  base::size(kDinosaurEasterEggSwitches));
 
-  if (content::Referrer::ShouldForceLegacyDefaultReferrerPolicy())
+  if (blink::ReferrerUtils::ShouldForceLegacyDefaultReferrerPolicy())
     command_line->AppendSwitch(
         blink::switches::kForceLegacyDefaultReferrerPolicy);
 
diff --git a/chrome/browser/chrome_service_worker_browsertest.cc b/chrome/browser/chrome_service_worker_browsertest.cc
index 2399b571..0bc4890d 100644
--- a/chrome/browser/chrome_service_worker_browsertest.cc
+++ b/chrome/browser/chrome_service_worker_browsertest.cc
@@ -28,6 +28,7 @@
 #include "chrome/test/base/test_chrome_web_ui_controller_factory.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "components/content_settings/browser/page_specific_content_settings.h"
+#include "components/content_settings/core/browser/cookie_settings.h"
 #include "components/content_settings/core/browser/host_content_settings_map.h"
 #include "components/content_settings/core/common/pref_names.h"
 #include "components/favicon/content/content_favicon_driver.h"
@@ -875,8 +876,9 @@
 IN_PROC_BROWSER_TEST_F(ChromeServiceWorkerNavigationPreloadTest,
                        TopFrameWithThirdPartyBlocking) {
   // Enable third-party cookie blocking.
-  browser()->profile()->GetPrefs()->SetBoolean(prefs::kBlockThirdPartyCookies,
-                                               true);
+  browser()->profile()->GetPrefs()->SetInteger(
+      prefs::kCookieControlsMode,
+      static_cast<int>(content_settings::CookieControlsMode::kBlockThirdParty));
 
   // Load a page that registers a service worker.
   ui_test_utils::NavigateToURL(
@@ -908,8 +910,9 @@
 IN_PROC_BROWSER_TEST_F(ChromeServiceWorkerNavigationPreloadTest,
                        SubFrameWithThirdPartyBlocking) {
   // Enable third-party cookie blocking.
-  browser()->profile()->GetPrefs()->SetBoolean(prefs::kBlockThirdPartyCookies,
-                                               true);
+  browser()->profile()->GetPrefs()->SetInteger(
+      prefs::kCookieControlsMode,
+      static_cast<int>(content_settings::CookieControlsMode::kBlockThirdParty));
 
   // Load a page that registers a service worker.
   ui_test_utils::NavigateToURL(
diff --git a/chrome/browser/chrome_worker_browsertest.cc b/chrome/browser/chrome_worker_browsertest.cc
index b00e12ce..3123805 100644
--- a/chrome/browser/chrome_worker_browsertest.cc
+++ b/chrome/browser/chrome_worker_browsertest.cc
@@ -11,6 +11,7 @@
 #include "chrome/browser/ui/browser.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chrome/test/base/ui_test_utils.h"
+#include "components/content_settings/core/browser/cookie_settings.h"
 #include "components/content_settings/core/common/pref_names.h"
 #include "components/prefs/pref_service.h"
 #include "content/public/browser/browser_context.h"
@@ -20,8 +21,6 @@
 #include "net/test/embedded_test_server/http_request.h"
 #include "net/test/embedded_test_server/http_response.h"
 
-enum class EnableThirdPartyCookieBlocking { kEnable, kDisable };
-
 // A simple fixture used for testing dedicated workers and shared workers. The
 // fixture stashes the HTTP request to the worker script for inspecting the
 // headers.
@@ -47,15 +46,13 @@
   // third-party cookie blocking configuration.
   // This is the regression test for https://crbug.com/933287.
   void TestWorkerScriptFetchWithThirdPartyCookieBlocking(
-      EnableThirdPartyCookieBlocking enable_third_party_cookie_blocking,
+      content_settings::CookieControlsMode cookie_controls_mode,
       const std::string& test_url) {
     const std::string kCookie = "foo=bar";
 
     // Set up third-party cookie blocking.
-    browser()->profile()->GetPrefs()->SetBoolean(
-        prefs::kBlockThirdPartyCookies,
-        enable_third_party_cookie_blocking ==
-            EnableThirdPartyCookieBlocking::kEnable);
+    browser()->profile()->GetPrefs()->SetInteger(
+        prefs::kCookieControlsMode, static_cast<int>(cookie_controls_mode));
 
     // Make sure cookies are not set.
     ASSERT_TRUE(
@@ -112,27 +109,27 @@
 IN_PROC_BROWSER_TEST_F(ChromeWorkerBrowserTest,
                        DedicatedWorkerScriptFetchWithThirdPartyBlocking) {
   TestWorkerScriptFetchWithThirdPartyCookieBlocking(
-      EnableThirdPartyCookieBlocking::kEnable,
+      content_settings::CookieControlsMode::kBlockThirdParty,
       "/workers/create_dedicated_worker.html?worker_url=/capture");
 }
 
 IN_PROC_BROWSER_TEST_F(ChromeWorkerBrowserTest,
                        DedicatedWorkerScriptFetchWithoutThirdPartyBlocking) {
   TestWorkerScriptFetchWithThirdPartyCookieBlocking(
-      EnableThirdPartyCookieBlocking::kDisable,
+      content_settings::CookieControlsMode::kOff,
       "/workers/create_dedicated_worker.html?worker_url=/capture");
 }
 
 IN_PROC_BROWSER_TEST_F(ChromeWorkerBrowserTest,
                        SharedWorkerScriptFetchWithThirdPartyBlocking) {
   TestWorkerScriptFetchWithThirdPartyCookieBlocking(
-      EnableThirdPartyCookieBlocking::kEnable,
+      content_settings::CookieControlsMode::kBlockThirdParty,
       "/workers/create_shared_worker.html?worker_url=/capture");
 }
 
 IN_PROC_BROWSER_TEST_F(ChromeWorkerBrowserTest,
                        SharedWorkerScriptFetchWithoutThirdPartyBlocking) {
   TestWorkerScriptFetchWithThirdPartyCookieBlocking(
-      EnableThirdPartyCookieBlocking::kDisable,
+      content_settings::CookieControlsMode::kOff,
       "/workers/create_shared_worker.html?worker_url=/capture");
 }
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn
index a245944..bac93a6 100644
--- a/chrome/browser/chromeos/BUILD.gn
+++ b/chrome/browser/chromeos/BUILD.gn
@@ -122,6 +122,7 @@
     "//chromeos/components/quick_answers/public/cpp:prefs",
     "//chromeos/components/remote_apps/mojom",
     "//chromeos/components/scanning",
+    "//chromeos/components/scanning/mojom",
     "//chromeos/components/smbfs",
     "//chromeos/components/smbfs/mojom",
     "//chromeos/components/string_matching",
@@ -1103,6 +1104,7 @@
     "extensions/extensions_permissions_tracker.h",
     "extensions/external_cache.cc",
     "extensions/external_cache.h",
+    "extensions/external_cache_delegate.cc",
     "extensions/external_cache_delegate.h",
     "extensions/external_cache_impl.cc",
     "extensions/external_cache_impl.h",
@@ -2439,6 +2441,10 @@
     "scanning/lorgnette_scanner_manager_factory.h",
     "scanning/lorgnette_scanner_manager_util.cc",
     "scanning/lorgnette_scanner_manager_util.h",
+    "scanning/scan_service.cc",
+    "scanning/scan_service.h",
+    "scanning/scan_service_factory.cc",
+    "scanning/scan_service_factory.h",
     "scanning/scanner_detector.h",
     "scanning/zeroconf_scanner_detector.cc",
     "scanning/zeroconf_scanner_detector.h",
@@ -3497,6 +3503,7 @@
     "scanning/fake_lorgnette_scanner_manager.h",
     "scanning/lorgnette_scanner_manager_unittest.cc",
     "scanning/lorgnette_scanner_manager_util_unittest.cc",
+    "scanning/scan_service_unittest.cc",
     "scanning/zeroconf_scanner_detector_unittest.cc",
     "scheduler_configuration_manager_unittest.cc",
     "session_length_limiter_unittest.cc",
diff --git a/chrome/browser/chromeos/app_mode/kiosk_app_manager.cc b/chrome/browser/chromeos/app_mode/kiosk_app_manager.cc
index b9a4af6..b86055b3 100644
--- a/chrome/browser/chromeos/app_mode/kiosk_app_manager.cc
+++ b/chrome/browser/chromeos/app_mode/kiosk_app_manager.cc
@@ -809,11 +809,8 @@
   external_cache_->UpdateExtensionsList(std::move(prefs));
 }
 
-void KioskAppManager::OnExtensionListsUpdated(
-    const base::DictionaryValue* prefs) {
-}
-
-void KioskAppManager::OnExtensionLoadedInCache(const std::string& id) {
+void KioskAppManager::OnExtensionLoadedInCache(
+    const extensions::ExtensionId& id) {
   KioskAppData* app_data = GetAppDataMutable(id);
   if (!app_data)
     return;
@@ -827,7 +824,8 @@
     observer.OnKioskExtensionLoadedInCache(id);
 }
 
-void KioskAppManager::OnExtensionDownloadFailed(const std::string& id) {
+void KioskAppManager::OnExtensionDownloadFailed(
+    const extensions::ExtensionId& id) {
   KioskAppData* app_data = GetAppDataMutable(id);
   if (!app_data)
     return;
@@ -835,11 +833,6 @@
     observer.OnKioskExtensionDownloadFailed(id);
 }
 
-std::string KioskAppManager::GetInstalledExtensionVersion(
-    const std::string& id) {
-  return std::string();
-}
-
 KioskAppManager::AutoLoginState KioskAppManager::GetAutoLoginState() const {
   PrefService* prefs = g_browser_process->local_state();
   const base::DictionaryValue* dict =
diff --git a/chrome/browser/chromeos/app_mode/kiosk_app_manager.h b/chrome/browser/chromeos/app_mode/kiosk_app_manager.h
index 3e89ba8b..fc181dd 100644
--- a/chrome/browser/chromeos/app_mode/kiosk_app_manager.h
+++ b/chrome/browser/chromeos/app_mode/kiosk_app_manager.h
@@ -21,6 +21,7 @@
 #include "chrome/browser/chromeos/settings/cros_settings.h"
 #include "chromeos/tpm/install_attributes.h"
 #include "components/account_id/account_id.h"
+#include "extensions/common/extension_id.h"
 
 class GURL;
 class PrefRegistrySimple;
@@ -278,10 +279,8 @@
   void UpdateExternalCachePrefs();
 
   // ExternalCacheDelegate:
-  void OnExtensionListsUpdated(const base::DictionaryValue* prefs) override;
-  void OnExtensionLoadedInCache(const std::string& id) override;
-  void OnExtensionDownloadFailed(const std::string& id) override;
-  std::string GetInstalledExtensionVersion(const std::string& id) override;
+  void OnExtensionLoadedInCache(const extensions::ExtensionId& id) override;
+  void OnExtensionDownloadFailed(const extensions::ExtensionId& id) override;
 
   // Callback for InstallAttributes::LockDevice() during
   // EnableConsumerModeKiosk() call.
diff --git a/chrome/browser/chromeos/arc/intent_helper/open_with_menu.cc b/chrome/browser/chromeos/arc/intent_helper/open_with_menu.cc
index 36fc749..0375bd3 100644
--- a/chrome/browser/chromeos/arc/intent_helper/open_with_menu.cc
+++ b/chrome/browser/chromeos/arc/intent_helper/open_with_menu.cc
@@ -13,6 +13,7 @@
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/context_menu_params.h"
 #include "ui/base/l10n/l10n_util.h"
+#include "ui/base/models/image_model.h"
 
 namespace arc {
 
@@ -101,7 +102,8 @@
           IDS_CONTENT_CONTEXT_OPEN_WITH_APP, it->second.name);
       proxy_->UpdateMenuItem(command_id, true, false, label);
       if (!it->second.icon.IsEmpty())
-        proxy_->UpdateMenuIcon(command_id, it->second.icon);
+        proxy_->UpdateMenuIcon(command_id,
+                               ui::ImageModel::FromImage(it->second.icon));
     }
   }
 }
diff --git a/chrome/browser/chromeos/arc/intent_helper/start_smart_selection_action_menu.cc b/chrome/browser/chromeos/arc/intent_helper/start_smart_selection_action_menu.cc
index df24ce1..cf2282c 100644
--- a/chrome/browser/chromeos/arc/intent_helper/start_smart_selection_action_menu.cc
+++ b/chrome/browser/chromeos/arc/intent_helper/start_smart_selection_action_menu.cc
@@ -25,6 +25,7 @@
 #include "components/arc/session/arc_bridge_service.h"
 #include "components/renderer_context_menu/render_view_context_menu_proxy.h"
 #include "content/public/browser/context_menu_params.h"
+#include "ui/base/models/image_model.h"
 #include "ui/gfx/image/image_skia_operations.h"
 
 namespace arc {
@@ -179,7 +180,7 @@
 
 void StartSmartSelectionActionMenu::SetMenuIcon(int command_id,
                                                 const gfx::ImageSkia& image) {
-  proxy_->UpdateMenuIcon(command_id, gfx::Image(image));
+  proxy_->UpdateMenuIcon(command_id, ui::ImageModel::FromImageSkia(image));
 }
 
 }  // namespace arc
diff --git a/chrome/browser/chromeos/crosapi/browser_manager.cc b/chrome/browser/chromeos/crosapi/browser_manager.cc
index dff0173..5422ea5 100644
--- a/chrome/browser/chromeos/crosapi/browser_manager.cc
+++ b/chrome/browser/chromeos/crosapi/browser_manager.cc
@@ -196,6 +196,10 @@
                                    "--enable-crashpad",
                                    "--breakpad-dump-location=" + crash_dir};
 
+  // CrAS is the default audio server in Chrome OS.
+  if (base::SysInfo::IsRunningOnChromeOS())
+    argv.push_back("--use-cras");
+
   std::string additional_flags =
       base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
           chromeos::switches::kLacrosChromeAdditionalArgs);
@@ -206,14 +210,9 @@
     argv.push_back(flag);
   }
 
-  // We assume that if there's a custom chrome path, that this is a developer
-  // and they want to enable logging.
-  bool custom_chrome_path = base::CommandLine::ForCurrentProcess()->HasSwitch(
-      chromeos::switches::kLacrosChromePath);
-  if (custom_chrome_path) {
-    argv.push_back("--enable-logging");
-    argv.push_back("--log-file=" + LacrosLogPath().value());
-  }
+  // Always enable logging.
+  argv.push_back("--enable-logging");
+  argv.push_back("--log-file=" + LacrosLogPath().value());
 
   // Set up Mojo channel.
   base::CommandLine command_line(argv);
diff --git a/chrome/browser/chromeos/extensions/device_local_account_external_policy_loader.cc b/chrome/browser/chromeos/extensions/device_local_account_external_policy_loader.cc
index 0075cccc..2c98156 100644
--- a/chrome/browser/chromeos/extensions/device_local_account_external_policy_loader.cc
+++ b/chrome/browser/chromeos/extensions/device_local_account_external_policy_loader.cc
@@ -91,18 +91,6 @@
     LoadFinished(std::move(prefs_));
 }
 
-void DeviceLocalAccountExternalPolicyLoader::OnExtensionLoadedInCache(
-    const std::string& id) {}
-
-void DeviceLocalAccountExternalPolicyLoader::OnExtensionDownloadFailed(
-    const std::string& id) {}
-
-std::string
-DeviceLocalAccountExternalPolicyLoader::GetInstalledExtensionVersion(
-    const std::string& id) {
-  return std::string();
-}
-
 ExternalCache*
 DeviceLocalAccountExternalPolicyLoader::GetExternalCacheForTesting() {
   return external_cache_.get();
diff --git a/chrome/browser/chromeos/extensions/device_local_account_external_policy_loader.h b/chrome/browser/chromeos/extensions/device_local_account_external_policy_loader.h
index 23ff108..df8831d 100644
--- a/chrome/browser/chromeos/extensions/device_local_account_external_policy_loader.h
+++ b/chrome/browser/chromeos/extensions/device_local_account_external_policy_loader.h
@@ -60,9 +60,6 @@
 
   // ExternalCacheDelegate:
   void OnExtensionListsUpdated(const base::DictionaryValue* prefs) override;
-  void OnExtensionLoadedInCache(const std::string& id) override;
-  void OnExtensionDownloadFailed(const std::string& id) override;
-  std::string GetInstalledExtensionVersion(const std::string& id) override;
 
   ExternalCache* GetExternalCacheForTesting();
 
diff --git a/chrome/browser/chromeos/extensions/external_cache_delegate.cc b/chrome/browser/chromeos/extensions/external_cache_delegate.cc
new file mode 100644
index 0000000..0ac759a
--- /dev/null
+++ b/chrome/browser/chromeos/extensions/external_cache_delegate.cc
@@ -0,0 +1,23 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/chromeos/extensions/external_cache_delegate.h"
+
+namespace chromeos {
+
+void ExternalCacheDelegate::OnExtensionListsUpdated(
+    const base::DictionaryValue* prefs) {}
+
+void ExternalCacheDelegate::OnExtensionLoadedInCache(
+    const extensions::ExtensionId& id) {}
+
+void ExternalCacheDelegate::OnExtensionDownloadFailed(
+    const extensions::ExtensionId& id) {}
+
+std::string ExternalCacheDelegate::GetInstalledExtensionVersion(
+    const extensions::ExtensionId& id) {
+  return std::string();
+}
+
+}  // namespace chromeos
diff --git a/chrome/browser/chromeos/extensions/external_cache_delegate.h b/chrome/browser/chromeos/extensions/external_cache_delegate.h
index fcd983f..c1d88084 100644
--- a/chrome/browser/chromeos/extensions/external_cache_delegate.h
+++ b/chrome/browser/chromeos/extensions/external_cache_delegate.h
@@ -7,6 +7,8 @@
 
 #include <string>
 
+#include "extensions/common/extension_id.h"
+
 namespace base {
 class DictionaryValue;
 }
@@ -18,18 +20,19 @@
   virtual ~ExternalCacheDelegate() = default;
 
   // Caller owns |prefs|.
-  virtual void OnExtensionListsUpdated(const base::DictionaryValue* prefs) = 0;
+  virtual void OnExtensionListsUpdated(const base::DictionaryValue* prefs);
 
   // Called after extension with |id| is loaded in cache.
-  virtual void OnExtensionLoadedInCache(const std::string& id) = 0;
+  virtual void OnExtensionLoadedInCache(const extensions::ExtensionId& id);
 
   // Called when extension with |id| fails to load due to a download error.
-  virtual void OnExtensionDownloadFailed(const std::string& id) = 0;
+  virtual void OnExtensionDownloadFailed(const extensions::ExtensionId& id);
 
   // Cache needs to provide already installed extensions otherwise they
   // will be removed. Cache calls this function to get version of installed
   // extension or empty string if not installed.
-  virtual std::string GetInstalledExtensionVersion(const std::string& id) = 0;
+  virtual std::string GetInstalledExtensionVersion(
+      const extensions::ExtensionId& id);
 };
 
 }  // namespace chromeos
diff --git a/chrome/browser/chromeos/extensions/external_cache_impl_unittest.cc b/chrome/browser/chromeos/extensions/external_cache_impl_unittest.cc
index 35b2966..9def4250 100644
--- a/chrome/browser/chromeos/extensions/external_cache_impl_unittest.cc
+++ b/chrome/browser/chromeos/extensions/external_cache_impl_unittest.cc
@@ -63,9 +63,8 @@
   void OnExtensionListsUpdated(const base::DictionaryValue* prefs) override {
     prefs_.reset(prefs->DeepCopy());
   }
-  void OnExtensionLoadedInCache(const std::string& id) override {}
-  void OnExtensionDownloadFailed(const std::string& id) override {}
-  std::string GetInstalledExtensionVersion(const std::string& id) override {
+  std::string GetInstalledExtensionVersion(
+      const extensions::ExtensionId& id) override {
     std::map<std::string, std::string>::iterator it =
         installed_extensions_.find(id);
     return it != installed_extensions_.end() ? it->second : std::string();
diff --git a/chrome/browser/chromeos/extensions/signin_screen_extensions_external_loader.cc b/chrome/browser/chromeos/extensions/signin_screen_extensions_external_loader.cc
index 1801c937..c60d1df 100644
--- a/chrome/browser/chromeos/extensions/signin_screen_extensions_external_loader.cc
+++ b/chrome/browser/chromeos/extensions/signin_screen_extensions_external_loader.cc
@@ -85,17 +85,6 @@
   LoadFinished(prefs->CreateDeepCopy());
 }
 
-void SigninScreenExtensionsExternalLoader::OnExtensionLoadedInCache(
-    const std::string& id) {}
-
-void SigninScreenExtensionsExternalLoader::OnExtensionDownloadFailed(
-    const std::string& id) {}
-
-std::string SigninScreenExtensionsExternalLoader::GetInstalledExtensionVersion(
-    const std::string& id) {
-  return std::string();
-}
-
 SigninScreenExtensionsExternalLoader::~SigninScreenExtensionsExternalLoader() =
     default;
 
diff --git a/chrome/browser/chromeos/extensions/signin_screen_extensions_external_loader.h b/chrome/browser/chromeos/extensions/signin_screen_extensions_external_loader.h
index f2502f9..cdf66e9 100644
--- a/chrome/browser/chromeos/extensions/signin_screen_extensions_external_loader.h
+++ b/chrome/browser/chromeos/extensions/signin_screen_extensions_external_loader.h
@@ -39,9 +39,6 @@
 
   // ExternalCacheDelegate:
   void OnExtensionListsUpdated(const base::DictionaryValue* prefs) override;
-  void OnExtensionLoadedInCache(const std::string& id) override;
-  void OnExtensionDownloadFailed(const std::string& id) override;
-  std::string GetInstalledExtensionVersion(const std::string& id) override;
 
  private:
   friend class base::RefCounted<SigninScreenExtensionsExternalLoader>;
diff --git a/chrome/browser/chromeos/input_method/ui/suggestion_view.cc b/chrome/browser/chromeos/input_method/ui/suggestion_view.cc
index 91eebd5..1b37247 100644
--- a/chrome/browser/chromeos/input_method/ui/suggestion_view.cc
+++ b/chrome/browser/chromeos/input_method/ui/suggestion_view.cc
@@ -47,9 +47,7 @@
 // Creates the suggestion label, and returns it (never returns nullptr).
 // The label text is not set in this function.
 std::unique_ptr<views::StyledLabel> CreateSuggestionLabel() {
-  std::unique_ptr<views::StyledLabel> suggestion_label =
-      std::make_unique<views::StyledLabel>(base::EmptyString16(),
-                                           /*listener=*/nullptr);
+  auto suggestion_label = std::make_unique<views::StyledLabel>();
   suggestion_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
   suggestion_label->SetBorder(
       views::CreateEmptyBorder(gfx::Insets(kPadding / 2, 0)));
diff --git a/chrome/browser/chromeos/login/demo_mode/demo_extensions_external_loader.cc b/chrome/browser/chromeos/login/demo_mode/demo_extensions_external_loader.cc
index de9078b..05030c5 100644
--- a/chrome/browser/chromeos/login/demo_mode/demo_extensions_external_loader.cc
+++ b/chrome/browser/chromeos/login/demo_mode/demo_extensions_external_loader.cc
@@ -121,17 +121,6 @@
   LoadFinished(prefs->CreateDeepCopy());
 }
 
-void DemoExtensionsExternalLoader::OnExtensionLoadedInCache(
-    const std::string& id) {}
-
-void DemoExtensionsExternalLoader::OnExtensionDownloadFailed(
-    const std::string& id) {}
-
-std::string DemoExtensionsExternalLoader::GetInstalledExtensionVersion(
-    const std::string& id) {
-  return std::string();
-}
-
 void DemoExtensionsExternalLoader::StartLoadingFromOfflineDemoResources() {
   DemoSession* demo_session = DemoSession::Get();
   DCHECK(demo_session->resources()->loaded());
diff --git a/chrome/browser/chromeos/login/demo_mode/demo_extensions_external_loader.h b/chrome/browser/chromeos/login/demo_mode/demo_extensions_external_loader.h
index df48cef..dd7a489b 100644
--- a/chrome/browser/chromeos/login/demo_mode/demo_extensions_external_loader.h
+++ b/chrome/browser/chromeos/login/demo_mode/demo_extensions_external_loader.h
@@ -50,9 +50,6 @@
 
   // ExternalCacheDelegate:
   void OnExtensionListsUpdated(const base::DictionaryValue* prefs) override;
-  void OnExtensionLoadedInCache(const std::string& id) override;
-  void OnExtensionDownloadFailed(const std::string& id) override;
-  std::string GetInstalledExtensionVersion(const std::string& id) override;
 
  protected:
   ~DemoExtensionsExternalLoader() override;
diff --git a/chrome/browser/chromeos/scanning/scan_service.cc b/chrome/browser/chromeos/scanning/scan_service.cc
new file mode 100644
index 0000000..dd2b5fd
--- /dev/null
+++ b/chrome/browser/chromeos/scanning/scan_service.cc
@@ -0,0 +1,59 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/chromeos/scanning/scan_service.h"
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/check.h"
+#include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/chromeos/scanning/lorgnette_scanner_manager.h"
+
+namespace chromeos {
+
+namespace {
+
+namespace mojo_ipc = scanning::mojom;
+
+}  // namespace
+
+ScanService::ScanService(LorgnetteScannerManager* lorgnette_scanner_manager)
+    : lorgnette_scanner_manager_(lorgnette_scanner_manager) {
+  DCHECK(lorgnette_scanner_manager_);
+}
+
+ScanService::~ScanService() = default;
+
+void ScanService::GetScanners(GetScannersCallback callback) {
+  lorgnette_scanner_manager_->GetScannerNames(
+      base::BindOnce(&ScanService::OnScannerNamesReceived,
+                     weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+}
+
+void ScanService::BindInterface(
+    mojo::PendingReceiver<mojo_ipc::ScanService> pending_receiver) {
+  receiver_.Bind(std::move(pending_receiver));
+}
+
+void ScanService::Shutdown() {
+  lorgnette_scanner_manager_ = nullptr;
+  receiver_.reset();
+  weak_ptr_factory_.InvalidateWeakPtrs();
+}
+
+void ScanService::OnScannerNamesReceived(
+    GetScannersCallback callback,
+    std::vector<std::string> scanner_names) {
+  std::vector<mojo_ipc::ScannerPtr> scanners;
+  scanners.reserve(scanner_names.size());
+  for (const auto& name : scanner_names) {
+    base::UnguessableToken id = base::UnguessableToken::Create();
+    scanners.push_back(mojo_ipc::Scanner::New(id, base::UTF8ToUTF16(name)));
+  }
+
+  std::move(callback).Run(std::move(scanners));
+}
+
+}  // namespace chromeos
diff --git a/chrome/browser/chromeos/scanning/scan_service.h b/chrome/browser/chromeos/scanning/scan_service.h
new file mode 100644
index 0000000..17477fc
--- /dev/null
+++ b/chrome/browser/chromeos/scanning/scan_service.h
@@ -0,0 +1,60 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_CHROMEOS_SCANNING_SCAN_SERVICE_H_
+#define CHROME_BROWSER_CHROMEOS_SCANNING_SCAN_SERVICE_H_
+
+#include <string>
+#include <vector>
+
+#include "base/memory/weak_ptr.h"
+#include "base/unguessable_token.h"
+#include "chromeos/components/scanning/mojom/scanning.mojom.h"
+#include "components/keyed_service/core/keyed_service.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/receiver.h"
+
+namespace chromeos {
+
+class LorgnetteScannerManager;
+
+// Implementation of the chromeos::scanning::mojom::ScanService interface. Used
+// by the scanning WebUI (chrome://scanning) to get connected scanners, obtain
+// scanner capabilities, and perform scans.
+class ScanService : public scanning::mojom::ScanService, public KeyedService {
+ public:
+  explicit ScanService(LorgnetteScannerManager* lorgnette_scanner_manager);
+  ~ScanService() override;
+
+  ScanService(const ScanService&) = delete;
+  ScanService& operator=(const ScanService&) = delete;
+
+  // scanning::mojom::ScanService:
+  void GetScanners(GetScannersCallback callback) override;
+
+  // Binds receiver_ by consuming |pending_receiver|.
+  void BindInterface(
+      mojo::PendingReceiver<scanning::mojom::ScanService> pending_receiver);
+
+ private:
+  // KeyedService:
+  void Shutdown() override;
+
+  // Processes the result of calling LorgnetteScannerManager::GetScannerNames().
+  void OnScannerNamesReceived(GetScannersCallback callback,
+                              std::vector<std::string> scanner_names);
+
+  // Receives and dispatches method calls to this implementation of the
+  // chromeos::scanning::mojom::ScanService interface.
+  mojo::Receiver<scanning::mojom::ScanService> receiver_{this};
+
+  // Unowned. Used to get scanner information and perform scans.
+  LorgnetteScannerManager* lorgnette_scanner_manager_;
+
+  base::WeakPtrFactory<ScanService> weak_ptr_factory_{this};
+};
+
+}  // namespace chromeos
+
+#endif  // CHROME_BROWSER_CHROMEOS_SCANNING_SCAN_SERVICE_H_
diff --git a/chrome/browser/chromeos/scanning/scan_service_factory.cc b/chrome/browser/chromeos/scanning/scan_service_factory.cc
new file mode 100644
index 0000000..f1ad3185
--- /dev/null
+++ b/chrome/browser/chromeos/scanning/scan_service_factory.cc
@@ -0,0 +1,60 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/chromeos/scanning/scan_service_factory.h"
+
+#include "base/memory/singleton.h"
+#include "chrome/browser/chromeos/profiles/profile_helper.h"
+#include "chrome/browser/chromeos/scanning/lorgnette_scanner_manager_factory.h"
+#include "chrome/browser/chromeos/scanning/scan_service.h"
+#include "chrome/browser/profiles/profile.h"
+#include "components/keyed_service/content/browser_context_dependency_manager.h"
+#include "content/public/browser/browser_context.h"
+
+namespace chromeos {
+
+// static
+ScanService* ScanServiceFactory::GetForBrowserContext(
+    content::BrowserContext* context) {
+  return static_cast<ScanService*>(
+      ScanServiceFactory::GetInstance()->GetServiceForBrowserContext(
+          context, /*create=*/true));
+}
+
+// static
+ScanServiceFactory* ScanServiceFactory::GetInstance() {
+  return base::Singleton<ScanServiceFactory>::get();
+}
+
+ScanServiceFactory::ScanServiceFactory()
+    : BrowserContextKeyedServiceFactory(
+          "ScanService",
+          BrowserContextDependencyManager::GetInstance()) {
+  DependsOn(LorgnetteScannerManagerFactory::GetInstance());
+}
+
+ScanServiceFactory::~ScanServiceFactory() = default;
+
+KeyedService* ScanServiceFactory::BuildServiceInstanceFor(
+    content::BrowserContext* context) const {
+  // Prevent an instance of ScanService from being created on the lock screen.
+  Profile* profile = Profile::FromBrowserContext(context);
+  if (ProfileHelper::IsLockScreenAppProfile(profile) ||
+      ProfileHelper::IsSigninProfile(profile)) {
+    return nullptr;
+  }
+
+  return new ScanService(
+      LorgnetteScannerManagerFactory::GetForBrowserContext(context));
+}
+
+bool ScanServiceFactory::ServiceIsCreatedWithBrowserContext() const {
+  return true;
+}
+
+bool ScanServiceFactory::ServiceIsNULLWhileTesting() const {
+  return true;
+}
+
+}  // namespace chromeos
diff --git a/chrome/browser/chromeos/scanning/scan_service_factory.h b/chrome/browser/chromeos/scanning/scan_service_factory.h
new file mode 100644
index 0000000..b36c953
--- /dev/null
+++ b/chrome/browser/chromeos/scanning/scan_service_factory.h
@@ -0,0 +1,47 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_CHROMEOS_SCANNING_SCAN_SERVICE_FACTORY_H_
+#define CHROME_BROWSER_CHROMEOS_SCANNING_SCAN_SERVICE_FACTORY_H_
+
+#include "components/keyed_service/content/browser_context_keyed_service_factory.h"
+
+namespace base {
+template <typename T>
+struct DefaultSingletonTraits;
+}  // namespace base
+
+namespace content {
+class BrowserContext;
+}  // namespace content
+
+namespace chromeos {
+
+class ScanService;
+
+// Factory for ScanService.
+class ScanServiceFactory : public BrowserContextKeyedServiceFactory {
+ public:
+  static ScanService* GetForBrowserContext(content::BrowserContext* context);
+  static ScanServiceFactory* GetInstance();
+
+ private:
+  friend struct base::DefaultSingletonTraits<ScanServiceFactory>;
+
+  ScanServiceFactory();
+  ~ScanServiceFactory() override;
+
+  ScanServiceFactory(const ScanServiceFactory&) = delete;
+  ScanServiceFactory& operator=(const ScanServiceFactory&) = delete;
+
+  // BrowserContextKeyedServiceFactory:
+  KeyedService* BuildServiceInstanceFor(
+      content::BrowserContext* context) const override;
+  bool ServiceIsCreatedWithBrowserContext() const override;
+  bool ServiceIsNULLWhileTesting() const override;
+};
+
+}  // namespace chromeos
+
+#endif  // CHROME_BROWSER_CHROMEOS_SCANNING_SCAN_SERVICE_FACTORY_H_
diff --git a/chrome/browser/chromeos/scanning/scan_service_unittest.cc b/chrome/browser/chromeos/scanning/scan_service_unittest.cc
new file mode 100644
index 0000000..db2d99a
--- /dev/null
+++ b/chrome/browser/chromeos/scanning/scan_service_unittest.cc
@@ -0,0 +1,85 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/chromeos/scanning/scan_service.h"
+
+#include <vector>
+
+#include "base/strings/utf_string_conversions.h"
+#include "base/test/task_environment.h"
+#include "chrome/browser/chromeos/scanning/fake_lorgnette_scanner_manager.h"
+#include "chromeos/components/scanning/mojom/scanning.mojom-test-utils.h"
+#include "chromeos/components/scanning/mojom/scanning.mojom.h"
+#include "mojo/public/cpp/bindings/remote.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace chromeos {
+
+namespace {
+
+namespace mojo_ipc = scanning::mojom;
+
+// Scanner names used for tests.
+constexpr char kFirstTestScannerName[] = "Test Scanner 1";
+constexpr char kSecondTestScannerName[] = "Test Scanner 2";
+
+}  // namespace
+
+class ScanServiceTest : public testing::Test {
+ public:
+  ScanServiceTest() = default;
+
+  void SetUp() override {
+    scan_service_.BindInterface(
+        scan_service_remote_.BindNewPipeAndPassReceiver());
+  }
+
+  // Gets scanners by calling ScanService::GetScanners() via the mojo::Remote.
+  std::vector<mojo_ipc::ScannerPtr> GetScanners() {
+    std::vector<mojo_ipc::ScannerPtr> scanners;
+    mojo_ipc::ScanServiceAsyncWaiter(scan_service_remote_.get())
+        .GetScanners(&scanners);
+    return scanners;
+  }
+
+ protected:
+  FakeLorgnetteScannerManager fake_lorgnette_scanner_manager_;
+
+ private:
+  base::test::TaskEnvironment task_environment_;
+  ScanService scan_service_{&fake_lorgnette_scanner_manager_};
+  mojo::Remote<mojo_ipc::ScanService> scan_service_remote_;
+};
+
+// Test that no scanners are returned when there are no scanner names.
+TEST_F(ScanServiceTest, NoScannerNames) {
+  fake_lorgnette_scanner_manager_.SetGetScannerNamesResponse({});
+  auto scanners = GetScanners();
+  EXPECT_TRUE(scanners.empty());
+}
+
+// Test that a scanner is returned with the correct display name.
+TEST_F(ScanServiceTest, GetScanners) {
+  fake_lorgnette_scanner_manager_.SetGetScannerNamesResponse(
+      {kFirstTestScannerName});
+  auto scanners = GetScanners();
+  ASSERT_EQ(scanners.size(), 1u);
+  EXPECT_EQ(scanners[0]->display_name,
+            base::UTF8ToUTF16(kFirstTestScannerName));
+}
+
+// Test that two returned scanners have unique IDs.
+TEST_F(ScanServiceTest, UniqueScannerIds) {
+  fake_lorgnette_scanner_manager_.SetGetScannerNamesResponse(
+      {kFirstTestScannerName, kSecondTestScannerName});
+  auto scanners = GetScanners();
+  ASSERT_EQ(scanners.size(), 2u);
+  EXPECT_EQ(scanners[0]->display_name,
+            base::UTF8ToUTF16(kFirstTestScannerName));
+  EXPECT_EQ(scanners[1]->display_name,
+            base::UTF8ToUTF16(kSecondTestScannerName));
+  EXPECT_NE(scanners[0]->id, scanners[1]->id);
+}
+
+}  // namespace chromeos
diff --git a/chrome/browser/chromeos/ui/echo_dialog_view.cc b/chrome/browser/chromeos/ui/echo_dialog_view.cc
index e57488b5..17cccf1 100644
--- a/chrome/browser/chromeos/ui/echo_dialog_view.cc
+++ b/chrome/browser/chromeos/ui/echo_dialog_view.cc
@@ -83,7 +83,8 @@
   base::string16 text = l10n_util::GetStringFUTF16(IDS_ECHO_CONSENT_DIALOG_TEXT,
                                                    service_name, &offset);
 
-  auto label = std::make_unique<views::StyledLabel>(text, nullptr);
+  auto label = std::make_unique<views::StyledLabel>();
+  label->SetText(text);
 
   views::StyledLabel::RangeStyleInfo service_name_style;
   gfx::FontList font_list = label->GetFontList();
diff --git a/chrome/browser/component_updater/soda_component_installer.cc b/chrome/browser/component_updater/soda_component_installer.cc
index 7c0a4e68..59f7835 100644
--- a/chrome/browser/component_updater/soda_component_installer.cc
+++ b/chrome/browser/component_updater/soda_component_installer.cc
@@ -19,6 +19,7 @@
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
 #include "crypto/sha2.h"
+#include "media/base/media_switches.h"
 
 using content::BrowserThread;
 
@@ -133,17 +134,18 @@
                            PrefService* prefs,
                            base::OnceClosure callback) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  if (!base::FeatureList::IsEnabled(media::kLiveCaption))
+    return;
+
 #if BUILDFLAG(ENABLE_SODA)
   auto installer = base::MakeRefCounted<ComponentInstaller>(
       std::make_unique<SODAComponentInstallerPolicy>(base::BindRepeating(
           [](ComponentUpdateService* cus, PrefService* prefs,
              const base::FilePath& install_dir) {
-            if (prefs->GetBoolean(prefs::kLiveCaptionEnabled)) {
               content::GetUIThreadTaskRunner({base::TaskPriority::BEST_EFFORT})
                   ->PostTask(FROM_HERE,
                              base::BindOnce(&UpdateSODAInstallDirPref, prefs,
                                             install_dir));
-            }
           },
           cus, prefs)));
 
diff --git a/chrome/browser/component_updater/soda_en_us_component_installer.cc b/chrome/browser/component_updater/soda_en_us_component_installer.cc
index b54f8628..b4950cb 100644
--- a/chrome/browser/component_updater/soda_en_us_component_installer.cc
+++ b/chrome/browser/component_updater/soda_en_us_component_installer.cc
@@ -17,6 +17,7 @@
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
 #include "crypto/sha2.h"
+#include "media/base/media_switches.h"
 
 using content::BrowserThread;
 
@@ -137,19 +138,19 @@
                                base::OnceClosure callback) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 #if BUILDFLAG(ENABLE_SODA)
-  if (!prefs->GetBoolean(prefs::kLiveCaptionEnabled))
+  if (!prefs->GetBoolean(prefs::kLiveCaptionEnabled) ||
+      !base::FeatureList::IsEnabled(media::kLiveCaption)) {
     return;
+  }
 
   auto installer = base::MakeRefCounted<ComponentInstaller>(
       std::make_unique<SodaEnUsComponentInstallerPolicy>(base::BindRepeating(
           [](ComponentUpdateService* cus, PrefService* prefs,
              const base::FilePath& install_dir) {
-            if (prefs->GetBoolean(prefs::kLiveCaptionEnabled)) {
               content::GetUIThreadTaskRunner({base::TaskPriority::BEST_EFFORT})
                   ->PostTask(FROM_HERE,
                              base::BindOnce(&UpdateSodaEnUsInstallDirPref,
                                             prefs, install_dir));
-            }
           },
           cus, prefs)));
 
diff --git a/chrome/browser/component_updater/soda_ja_jp_component_installer.cc b/chrome/browser/component_updater/soda_ja_jp_component_installer.cc
index 4096ea92..c819e181 100644
--- a/chrome/browser/component_updater/soda_ja_jp_component_installer.cc
+++ b/chrome/browser/component_updater/soda_ja_jp_component_installer.cc
@@ -17,6 +17,7 @@
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
 #include "crypto/sha2.h"
+#include "media/base/media_switches.h"
 
 using content::BrowserThread;
 
@@ -137,19 +138,19 @@
                                base::OnceClosure callback) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 #if BUILDFLAG(ENABLE_SODA)
-  if (!prefs->GetBoolean(prefs::kLiveCaptionEnabled))
+  if (!prefs->GetBoolean(prefs::kLiveCaptionEnabled) ||
+      !base::FeatureList::IsEnabled(media::kLiveCaption)) {
     return;
+  }
 
   auto installer = base::MakeRefCounted<ComponentInstaller>(
       std::make_unique<SodaJaJpComponentInstallerPolicy>(base::BindRepeating(
           [](ComponentUpdateService* cus, PrefService* prefs,
              const base::FilePath& install_dir) {
-            if (prefs->GetBoolean(prefs::kLiveCaptionEnabled)) {
               content::GetUIThreadTaskRunner({base::TaskPriority::BEST_EFFORT})
                   ->PostTask(FROM_HERE,
                              base::BindOnce(&UpdateSodaJaJpInstallDirPref,
                                             prefs, install_dir));
-            }
           },
           cus, prefs)));
 
diff --git a/chrome/browser/content_settings/cookie_settings_factory.cc b/chrome/browser/content_settings/cookie_settings_factory.cc
index 19238e6c..cc9cfc12 100644
--- a/chrome/browser/content_settings/cookie_settings_factory.cc
+++ b/chrome/browser/content_settings/cookie_settings_factory.cc
@@ -60,22 +60,14 @@
   Profile* profile = Profile::FromBrowserContext(context);
   PrefService* prefs = profile->GetPrefs();
 
-  // Migrate BlockThirdPartyCookies to CookieControlsMode pref.
-  if (prefs->IsUserModifiablePreference(prefs::kBlockThirdPartyCookies) &&
-      prefs->GetBoolean(prefs::kBlockThirdPartyCookies) &&
-      prefs->GetInteger(prefs::kCookieControlsMode) !=
-          static_cast<int>(CookieControlsMode::kBlockThirdParty)) {
-    prefs->SetInteger(prefs::kCookieControlsMode,
-                      static_cast<int>(CookieControlsMode::kBlockThirdParty));
-  }
-
   // Record cookie setting histograms.
-  base::UmaHistogramBoolean("Privacy.ThirdPartyCookieBlockingSetting",
-                            prefs->GetBoolean(prefs::kBlockThirdPartyCookies));
-  base::UmaHistogramEnumeration(
-      "Privacy.CookieControlsSetting",
-      static_cast<CookieControlsMode>(
-          prefs->GetInteger(prefs::kCookieControlsMode)));
+  auto cookie_controls_mode = static_cast<CookieControlsMode>(
+      prefs->GetInteger(prefs::kCookieControlsMode));
+  base::UmaHistogramBoolean(
+      "Privacy.ThirdPartyCookieBlockingSetting",
+      cookie_controls_mode == CookieControlsMode::kBlockThirdParty);
+  base::UmaHistogramEnumeration("Privacy.CookieControlsSetting",
+                                cookie_controls_mode);
   // The DNT setting is only vaguely cookie-related. However, there is currently
   // no DNT-related code that is executed once per Profile lifetime, and
   // creating a new BrowserContextKeyedService to record this metric would be
diff --git a/chrome/browser/content_settings/cookie_settings_factory_unittest.cc b/chrome/browser/content_settings/cookie_settings_factory_unittest.cc
index e7373726..60aa78a 100644
--- a/chrome/browser/content_settings/cookie_settings_factory_unittest.cc
+++ b/chrome/browser/content_settings/cookie_settings_factory_unittest.cc
@@ -86,8 +86,7 @@
 
 // Android does not have guest profiles.
 #if !defined(OS_ANDROID)
-// Tests that improved cookie controls are not available by default for guest
-// profiles.
+// Tests that cookie blocking is not enabled by default for guest profiles.
 TEST_F(CookieSettingsFactoryTest, GuestProfile) {
   TestingProfile::Builder guest_profile_builder;
   guest_profile_builder.SetGuestSession();
@@ -96,12 +95,12 @@
   EXPECT_TRUE(otr_guest_profile->IsOffTheRecord());
   scoped_refptr<content_settings::CookieSettings> guest_settings =
       CookieSettingsFactory::GetForProfile(otr_guest_profile);
-  EXPECT_FALSE(guest_settings->IsCookieControlsEnabled());
+  EXPECT_FALSE(guest_settings->ShouldBlockThirdPartyCookies());
 
-  // OTOH, improved controls are available by default for an incognito profile.
+  // OTOH, cookie blocking is default for an incognito profile.
   EXPECT_TRUE(
       CookieSettingsFactory::GetForProfile(profile_.GetPrimaryOTRProfile())
-          ->IsCookieControlsEnabled());
+          ->ShouldBlockThirdPartyCookies());
 }
 #endif
 
diff --git a/chrome/browser/download/download_shelf_context_menu.cc b/chrome/browser/download/download_shelf_context_menu.cc
index 62d24dd..8ef7463 100644
--- a/chrome/browser/download/download_shelf_context_menu.cc
+++ b/chrome/browser/download/download_shelf_context_menu.cc
@@ -413,7 +413,8 @@
         DownloadCommands::ALWAYS_OPEN_TYPE,
         GetLabelForCommandId(DownloadCommands::ALWAYS_OPEN_TYPE),
         ui::ImageModel::FromVectorIcon(vector_icons::kBusinessIcon,
-                                       gfx::kChromeIconGrey, 16));
+                                       gfx::kChromeIconGrey,
+                                       ui::SimpleMenuModel::kDefaultIconSize));
   } else {
     menu->AddCheckItem(
         DownloadCommands::ALWAYS_OPEN_TYPE,
diff --git a/chrome/browser/extensions/BUILD.gn b/chrome/browser/extensions/BUILD.gn
index af74217c..e0cc4ef 100644
--- a/chrome/browser/extensions/BUILD.gn
+++ b/chrome/browser/extensions/BUILD.gn
@@ -690,10 +690,6 @@
     "updater/extension_updater.h",
     "updater/extension_updater_switches.cc",
     "updater/extension_updater_switches.h",
-    "updater/fetched_crx_file.cc",
-    "updater/fetched_crx_file.h",
-    "updater/parallel_unpacker.cc",
-    "updater/parallel_unpacker.h",
     "user_script_listener.cc",
     "user_script_listener.h",
     "warning_badge_service.cc",
diff --git a/chrome/browser/extensions/api/passwords_private/password_check_delegate.cc b/chrome/browser/extensions/api/passwords_private/password_check_delegate.cc
index 825d3bc1..9f1ff907 100644
--- a/chrome/browser/extensions/api/passwords_private/password_check_delegate.cc
+++ b/chrome/browser/extensions/api/passwords_private/password_check_delegate.cc
@@ -224,8 +224,8 @@
           profile,
           ServiceAccessType::EXPLICIT_ACCESS)),
       saved_passwords_presenter_(password_store_),
-      compromised_credentials_manager_(password_store_,
-                                       &saved_passwords_presenter_),
+      compromised_credentials_manager_(&saved_passwords_presenter_,
+                                       password_store_),
       bulk_leak_check_service_adapter_(
           &saved_passwords_presenter_,
           BulkLeakCheckServiceFactory::GetForProfile(profile_),
diff --git a/chrome/browser/extensions/updater/extension_updater.cc b/chrome/browser/extensions/updater/extension_updater.cc
index 248c0f9..f85479b 100644
--- a/chrome/browser/extensions/updater/extension_updater.cc
+++ b/chrome/browser/extensions/updater/extension_updater.cc
@@ -106,6 +106,27 @@
 ExtensionUpdater::CheckParams& ExtensionUpdater::CheckParams::operator=(
     ExtensionUpdater::CheckParams&& other) = default;
 
+ExtensionUpdater::FetchedCRXFile::FetchedCRXFile(
+    const CRXFileInfo& file,
+    bool file_ownership_passed,
+    const std::set<int>& request_ids,
+    InstallCallback callback)
+    : info(file),
+      file_ownership_passed(file_ownership_passed),
+      request_ids(request_ids),
+      callback(std::move(callback)) {}
+
+ExtensionUpdater::FetchedCRXFile::FetchedCRXFile()
+    : file_ownership_passed(true) {}
+
+ExtensionUpdater::FetchedCRXFile::FetchedCRXFile(FetchedCRXFile&& other) =
+    default;
+
+ExtensionUpdater::FetchedCRXFile& ExtensionUpdater::FetchedCRXFile::operator=(
+    FetchedCRXFile&& other) = default;
+
+ExtensionUpdater::FetchedCRXFile::~FetchedCRXFile() = default;
+
 ExtensionUpdater::InProgressCheck::InProgressCheck() = default;
 
 ExtensionUpdater::InProgressCheck::~InProgressCheck() = default;
diff --git a/chrome/browser/extensions/updater/extension_updater.h b/chrome/browser/extensions/updater/extension_updater.h
index a65bb43..a4b125e5 100644
--- a/chrome/browser/extensions/updater/extension_updater.h
+++ b/chrome/browser/extensions/updater/extension_updater.h
@@ -16,7 +16,6 @@
 #include "base/files/file_path.h"
 #include "base/memory/weak_ptr.h"
 #include "base/scoped_observer.h"
-#include "chrome/browser/extensions/updater/fetched_crx_file.h"
 #include "content/public/browser/notification_observer.h"
 #include "content/public/browser/notification_registrar.h"
 #include "extensions/browser/extension_registry_observer.h"
@@ -155,6 +154,25 @@
   friend class ExtensionUpdaterTest;
   friend class ExtensionUpdaterFileHandler;
 
+  // FetchedCRXFile holds information about a CRX file we fetched to disk,
+  // but have not yet installed.
+  struct FetchedCRXFile {
+    FetchedCRXFile();
+    FetchedCRXFile(const CRXFileInfo& file,
+                   bool file_ownership_passed,
+                   const std::set<int>& request_ids,
+                   InstallCallback callback);
+    FetchedCRXFile(FetchedCRXFile&& other);
+    FetchedCRXFile& operator=(FetchedCRXFile&& other);
+    ~FetchedCRXFile();
+
+    CRXFileInfo info;
+    GURL download_url;
+    bool file_ownership_passed;
+    std::set<int> request_ids;
+    InstallCallback callback;
+  };
+
   struct InProgressCheck {
     InProgressCheck();
     InProgressCheck(const InProgressCheck&) = delete;
diff --git a/chrome/browser/extensions/updater/fetched_crx_file.cc b/chrome/browser/extensions/updater/fetched_crx_file.cc
deleted file mode 100644
index f0a0d00..0000000
--- a/chrome/browser/extensions/updater/fetched_crx_file.cc
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/extensions/updater/fetched_crx_file.h"
-
-namespace extensions {
-
-FetchedCRXFile::FetchedCRXFile(
-    const CRXFileInfo& file,
-    bool file_ownership_passed,
-    const std::set<int>& request_ids,
-    ExtensionDownloaderDelegate::InstallCallback callback)
-    : info(file),
-      file_ownership_passed(file_ownership_passed),
-      request_ids(request_ids),
-      callback(std::move(callback)) {}
-
-FetchedCRXFile::FetchedCRXFile() = default;
-
-FetchedCRXFile::FetchedCRXFile(FetchedCRXFile&&) = default;
-FetchedCRXFile& FetchedCRXFile::operator=(FetchedCRXFile&&) = default;
-
-FetchedCRXFile::~FetchedCRXFile() = default;
-
-}  // namespace extensions
diff --git a/chrome/browser/extensions/updater/fetched_crx_file.h b/chrome/browser/extensions/updater/fetched_crx_file.h
deleted file mode 100644
index fc02ad33..0000000
--- a/chrome/browser/extensions/updater/fetched_crx_file.h
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_EXTENSIONS_UPDATER_FETCHED_CRX_FILE_H_
-#define CHROME_BROWSER_EXTENSIONS_UPDATER_FETCHED_CRX_FILE_H_
-
-#include <set>
-
-#include "extensions/browser/crx_file_info.h"
-#include "extensions/browser/updater/extension_downloader_delegate.h"
-
-namespace extensions {
-
-// FetchedCRXFile holds information about a CRX file we fetched to disk,
-// but have not yet unpacked or installed.
-struct FetchedCRXFile {
-  FetchedCRXFile();
-  FetchedCRXFile(const CRXFileInfo& file,
-                 bool file_ownership_passed,
-                 const std::set<int>& request_ids,
-                 ExtensionDownloaderDelegate::InstallCallback callback);
-  FetchedCRXFile(FetchedCRXFile&& other);
-  FetchedCRXFile& operator=(FetchedCRXFile&&);
-  ~FetchedCRXFile();
-
-  FetchedCRXFile(const FetchedCRXFile&) = delete;
-  FetchedCRXFile& operator=(const FetchedCRXFile&) = delete;
-
-  CRXFileInfo info;
-  GURL download_url;
-  bool file_ownership_passed = true;
-  std::set<int> request_ids;
-  ExtensionDownloaderDelegate::InstallCallback callback;
-};
-
-}  // namespace extensions
-
-#endif  // CHROME_BROWSER_EXTENSIONS_UPDATER_FETCHED_CRX_FILE_H_
diff --git a/chrome/browser/extensions/updater/parallel_unpacker.cc b/chrome/browser/extensions/updater/parallel_unpacker.cc
deleted file mode 100644
index cfb0342..0000000
--- a/chrome/browser/extensions/updater/parallel_unpacker.cc
+++ /dev/null
@@ -1,151 +0,0 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/extensions/updater/parallel_unpacker.h"
-
-#include "base/task/post_task.h"
-#include "base/values.h"
-#include "chrome/browser/extensions/pending_extension_info.h"
-#include "chrome/browser/profiles/profile.h"
-#include "content/public/browser/browser_task_traits.h"
-#include "content/public/browser/browser_thread.h"
-#include "extensions/browser/content_verifier.h"
-#include "extensions/browser/extension_file_task_runner.h"
-#include "extensions/browser/extension_system.h"
-#include "extensions/common/extension.h"
-#include "third_party/skia/include/core/SkBitmap.h"
-
-namespace extensions {
-
-ParallelUnpacker::UnpackedExtension::UnpackedExtension() = default;
-ParallelUnpacker::UnpackedExtension::UnpackedExtension(
-    FetchedCRXFile fetch_info,
-    const base::FilePath& temp_dir,
-    const base::FilePath& extension_root,
-    std::unique_ptr<base::DictionaryValue> original_manifest,
-    scoped_refptr<const Extension> extension,
-    const SkBitmap& install_icon,
-    declarative_net_request::RulesetInstallPrefs ruleset_install_prefs)
-    : fetch_info(std::move(fetch_info)),
-      temp_dir(temp_dir),
-      extension_root(extension_root),
-      original_manifest(std::move(original_manifest)),
-      extension(extension),
-      install_icon(install_icon),
-      ruleset_install_prefs(std::move(ruleset_install_prefs)) {}
-
-ParallelUnpacker::UnpackedExtension::UnpackedExtension(UnpackedExtension&&) =
-    default;
-
-ParallelUnpacker::UnpackedExtension&
-ParallelUnpacker::UnpackedExtension::operator=(UnpackedExtension&&) = default;
-
-ParallelUnpacker::UnpackedExtension::~UnpackedExtension() = default;
-
-ParallelUnpacker::ParallelUnpacker(Delegate* delegate, Profile* profile)
-    : delegate_(delegate), profile_(profile) {}
-
-ParallelUnpacker::~ParallelUnpacker() = default;
-
-void ParallelUnpacker::Unpack(
-    FetchedCRXFile fetch_info,
-    const PendingExtensionInfo* pending_extension_info,
-    const Extension* extension,
-    const base::FilePath& install_directory) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  DCHECK(pending_extension_info || extension);
-
-  auto install_source = pending_extension_info
-                            ? pending_extension_info->install_source()
-                            : extension->location();
-  auto creation_flags = pending_extension_info
-                            ? pending_extension_info->creation_flags()
-                            : Extension::NO_FLAGS;
-  auto io_task_runner = GetExtensionFileTaskRunner();
-  auto client = base::MakeRefCounted<Client>(
-      weak_ptr_factory_.GetWeakPtr(), std::move(fetch_info), io_task_runner);
-
-  auto unpacker = base::MakeRefCounted<SandboxedUnpacker>(
-      install_source, creation_flags, install_directory, io_task_runner,
-      client.get());
-  if (!io_task_runner->PostTask(
-          FROM_HERE, base::BindOnce(&SandboxedUnpacker::StartWithCrx, unpacker,
-                                    client->fetch_info().info))) {
-    NOTREACHED();
-  }
-}
-
-ParallelUnpacker::Client::Client(
-    base::WeakPtr<ParallelUnpacker> unpacker,
-    FetchedCRXFile fetch_info,
-    scoped_refptr<base::SequencedTaskRunner> io_task_runner)
-    : unpacker_(unpacker),
-      fetch_info_(std::move(fetch_info)),
-      io_task_runner_(io_task_runner) {}
-ParallelUnpacker::Client::~Client() = default;
-
-void ParallelUnpacker::Client::ShouldComputeHashesForOffWebstoreExtension(
-    scoped_refptr<const Extension> extension,
-    base::OnceCallback<void(bool)> callback) {
-  DCHECK(io_task_runner_->RunsTasksInCurrentSequence());
-  base::PostTask(
-      FROM_HERE, {content::BrowserThread::UI},
-      base::BindOnce(&ParallelUnpacker::Client::ShouldComputeHashesOnUIThread,
-                     this, extension, std::move(callback)));
-}
-
-void ParallelUnpacker::Client::ShouldComputeHashesOnUIThread(
-    scoped_refptr<const Extension> extension,
-    base::OnceCallback<void(bool)> callback) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  if (!unpacker_) {
-    // |ExtensionUpdater| isn't running, e.g. Stop() was called. Drop the refs
-    // in |callback|.
-    return;
-  }
-  auto* content_verifier =
-      ExtensionSystem::Get(unpacker_->profile_)->content_verifier();
-  bool result = content_verifier &&
-                content_verifier->ShouldComputeHashesOnInstall(*extension);
-  io_task_runner_->PostTask(FROM_HERE,
-                            base::BindOnce(std::move(callback), result));
-}
-
-void ParallelUnpacker::Client::OnUnpackSuccess(
-    const base::FilePath& temp_dir,
-    const base::FilePath& extension_root,
-    std::unique_ptr<base::DictionaryValue> original_manifest,
-    const Extension* extension,
-    const SkBitmap& install_icon,
-    declarative_net_request::RulesetInstallPrefs ruleset_install_prefs) {
-  DCHECK(io_task_runner_->RunsTasksInCurrentSequence());
-  UnpackedExtension unpacked_extension(
-      std::move(fetch_info_), temp_dir, extension_root,
-      std::move(original_manifest), extension, install_icon,
-      std::move(ruleset_install_prefs));
-  base::PostTask(FROM_HERE, {content::BrowserThread::UI},
-                 base::BindOnce(&ParallelUnpacker::ReportSuccessOnUIThread,
-                                unpacker_, std::move(unpacked_extension)));
-}
-
-void ParallelUnpacker::Client::OnUnpackFailure(const CrxInstallError& error) {
-  DCHECK(io_task_runner_->RunsTasksInCurrentSequence());
-  base::PostTask(FROM_HERE, {content::BrowserThread::UI},
-                 base::BindOnce(&ParallelUnpacker::ReportFailureOnUIThread,
-                                unpacker_, std::move(fetch_info_), error));
-}
-
-void ParallelUnpacker::ReportSuccessOnUIThread(
-    UnpackedExtension unpacked_extension) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  delegate_->OnParallelUnpackSuccess(std::move(unpacked_extension));
-}
-
-void ParallelUnpacker::ReportFailureOnUIThread(FetchedCRXFile fetch_info,
-                                               CrxInstallError error) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  delegate_->OnParallelUnpackFailure(std::move(fetch_info), std::move(error));
-}
-
-}  // namespace extensions
diff --git a/chrome/browser/extensions/updater/parallel_unpacker.h b/chrome/browser/extensions/updater/parallel_unpacker.h
deleted file mode 100644
index 39f23ffb..0000000
--- a/chrome/browser/extensions/updater/parallel_unpacker.h
+++ /dev/null
@@ -1,153 +0,0 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_EXTENSIONS_UPDATER_PARALLEL_UNPACKER_H_
-#define CHROME_BROWSER_EXTENSIONS_UPDATER_PARALLEL_UNPACKER_H_
-
-#include <memory>
-#include <set>
-
-#include "base/files/file_path.h"
-#include "base/memory/scoped_refptr.h"
-#include "chrome/browser/extensions/updater/fetched_crx_file.h"
-#include "extensions/browser/api/declarative_net_request/ruleset_install_pref.h"
-#include "extensions/browser/crx_file_info.h"
-#include "extensions/browser/install/crx_install_error.h"
-#include "extensions/browser/sandboxed_unpacker.h"
-#include "extensions/browser/updater/extension_downloader_delegate.h"
-
-namespace base {
-class DictionaryValue;
-}  // namespace base
-
-class Profile;
-
-namespace extensions {
-
-class PendingExtensionInfo;
-
-// Unpacks multiple extensions in parallel, and notifies |updater| when an
-// extension has finished unpacking.
-class ParallelUnpacker {
- public:
-  // UnpackedExtension holds information about a CRX file we fetched and
-  // unpacked.
-  struct UnpackedExtension {
-    UnpackedExtension();
-    UnpackedExtension(
-        FetchedCRXFile fetch_info,
-        const base::FilePath& temp_dir,
-        const base::FilePath& extension_root,
-        std::unique_ptr<base::DictionaryValue> original_manifest,
-        scoped_refptr<const Extension> extension,
-        const SkBitmap& install_icon,
-        declarative_net_request::RulesetInstallPrefs ruleset_install_prefs);
-    UnpackedExtension(UnpackedExtension&& other);
-    UnpackedExtension& operator=(UnpackedExtension&&);
-    ~UnpackedExtension();
-
-    UnpackedExtension(const UnpackedExtension&) = delete;
-    UnpackedExtension& operator=(const UnpackedExtension&) = delete;
-
-    // Information about the fetched CRX file, including CRXFileInfo and a
-    // callback.
-    FetchedCRXFile fetch_info;
-
-    // The fields below are the result of
-    // SandboxedUnpackerClient::OnUnpackSuccess().
-
-    // Temporary directory with results of unpacking. It should be deleted once
-    // we don't need it anymore.
-    base::FilePath temp_dir;
-    // The path to the extension root inside of temp_dir.
-    base::FilePath extension_root;
-    // The parsed but unmodified version of the manifest, with no modifications
-    // such as localization, etc.
-    std::unique_ptr<base::DictionaryValue> original_manifest;
-    // The extension that was unpacked.
-    scoped_refptr<const Extension> extension;
-    // The icon we will display in the installation UI, if any.
-    SkBitmap install_icon;
-    // Install prefs needed for the Declarative Net Request API.
-    declarative_net_request::RulesetInstallPrefs ruleset_install_prefs;
-  };
-
-  class Delegate {
-   public:
-    virtual void OnParallelUnpackSuccess(
-        UnpackedExtension unpacked_extension) = 0;
-    virtual void OnParallelUnpackFailure(FetchedCRXFile fetch_info,
-                                         CrxInstallError error) = 0;
-  };
-
-  // |delegate| must outlive this object.
-  ParallelUnpacker(Delegate* delegate, Profile* profile);
-  ~ParallelUnpacker();
-
-  ParallelUnpacker(const ParallelUnpacker&) = delete;
-  ParallelUnpacker& operator=(const ParallelUnpacker&) = delete;
-
-  // Starts unpacking |crx_file|. Either |pending_extension_info| or |extension|
-  // must be non-null. When done unpacking, calls
-  // OnParallelUnpackSuccess/Failure() on this object's delegate.
-  //
-  // May be called multiple times in a row to unpack multiple extensions in
-  // parallel.
-  void Unpack(FetchedCRXFile crx_file,
-              const PendingExtensionInfo* pending_extension_info,
-              const Extension* extension,
-              const base::FilePath& install_directory);
-
- private:
-  // Listens for a single SandboxedUnpacker's events. Routes
-  // OnUnpackSuccess/Failure() back to the ParallelUnpacker via
-  // ReportSuccess/FailureOnUIThread().
-  class Client : public SandboxedUnpackerClient {
-   public:
-    Client(base::WeakPtr<ParallelUnpacker> unpacker,
-           FetchedCRXFile fetch_info,
-           scoped_refptr<base::SequencedTaskRunner> io_task_runner);
-
-    // SandboxedUnpackerClient:
-    void ShouldComputeHashesForOffWebstoreExtension(
-        scoped_refptr<const Extension> extension,
-        base::OnceCallback<void(bool)> callback) override;
-    void OnUnpackSuccess(
-        const base::FilePath& temp_dir,
-        const base::FilePath& extension_root,
-        std::unique_ptr<base::DictionaryValue> original_manifest,
-        const Extension* extension,
-        const SkBitmap& install_icon,
-        declarative_net_request::RulesetInstallPrefs ruleset_install_prefs)
-        override;
-    void OnUnpackFailure(const CrxInstallError& error) override;
-
-    FetchedCRXFile& fetch_info() { return fetch_info_; }
-
-   private:
-    ~Client() override;
-
-    // To check whether we need to compute hashes or not, we have to make a
-    // query to ContentVerifier, and that should be done on the UI thread.
-    void ShouldComputeHashesOnUIThread(scoped_refptr<const Extension> extension,
-                                       base::OnceCallback<void(bool)> callback);
-
-    base::WeakPtr<ParallelUnpacker> unpacker_;
-    FetchedCRXFile fetch_info_;
-    scoped_refptr<base::SequencedTaskRunner> io_task_runner_;
-  };
-
-  void ReportSuccessOnUIThread(UnpackedExtension unpacked_extension);
-  void ReportFailureOnUIThread(FetchedCRXFile fetch_info,
-                               CrxInstallError error);
-
-  Delegate* const delegate_;
-  Profile* const profile_;
-
-  base::WeakPtrFactory<ParallelUnpacker> weak_ptr_factory_{this};
-};
-
-}  // namespace extensions
-
-#endif  // CHROME_BROWSER_EXTENSIONS_UPDATER_PARALLEL_UNPACKER_H_
diff --git a/chrome/browser/extensions/updater/parallel_unpacker_unittest.cc b/chrome/browser/extensions/updater/parallel_unpacker_unittest.cc
deleted file mode 100644
index 9aa028ed..0000000
--- a/chrome/browser/extensions/updater/parallel_unpacker_unittest.cc
+++ /dev/null
@@ -1,126 +0,0 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/extensions/updater/parallel_unpacker.h"
-
-#include "base/files/file_util.h"
-#include "base/path_service.h"
-#include "chrome/browser/extensions/pending_extension_info.h"
-#include "chrome/test/base/testing_profile.h"
-#include "content/public/test/test_utils.h"
-#include "extensions/browser/extensions_test.h"
-#include "extensions/common/extension_paths.h"
-#include "extensions/common/verifier_formats.h"
-#include "services/data_decoder/public/cpp/test_support/in_process_data_decoder.h"
-
-namespace extensions {
-
-class ParallelUnpackerTest : public testing::Test,
-                             public ParallelUnpacker::Delegate {
- public:
-  ParallelUnpackerTest()
-      : task_environment_(content::BrowserTaskEnvironment::IO_MAINLOOP) {}
-
-  void SetUp() override {
-    ASSERT_TRUE(extensions_dir_.CreateUniqueTempDir());
-    in_process_utility_thread_helper_.reset(
-        new content::InProcessUtilityThreadHelper);
-    parallel_unpacker_ = std::make_unique<ParallelUnpacker>(this, &profile_);
-  }
-
-  void TearDown() override {
-    in_process_utility_thread_helper_.reset();
-    parallel_unpacker_.reset();
-  }
-
-  base::FilePath GetCrxFullPath(const std::string& crx_name) {
-    base::FilePath full_path;
-    EXPECT_TRUE(base::PathService::Get(extensions::DIR_TEST_DATA, &full_path));
-    full_path = full_path.AppendASCII("unpacker").AppendASCII(crx_name);
-    EXPECT_TRUE(base::PathExists(full_path)) << full_path.value();
-    return full_path;
-  }
-
-  void Unpack(const std::string& crx_name) {
-    base::FilePath crx_path = GetCrxFullPath(crx_name);
-    extensions::CRXFileInfo crx_info(crx_path, GetTestVerifierFormat());
-    extensions::FetchedCRXFile fetch_info(crx_info, false, std::set<int>(),
-                                          base::BindOnce([](bool) {}));
-    extensions::PendingExtensionInfo pending_extension_info(
-        "", "", GURL(), base::Version(), [](const Extension*) { return true; },
-        false, Manifest::INTERNAL, Extension::NO_FLAGS, true, false);
-
-    parallel_unpacker_->Unpack(std::move(fetch_info), &pending_extension_info,
-                               nullptr, extensions_dir_.GetPath());
-    in_progress_count_++;
-  }
-
-  // ParallelUnpacker::Delegate:
-  void OnParallelUnpackSuccess(
-      ParallelUnpacker::UnpackedExtension unpacked_extension) override {
-    std::string file_name =
-        unpacked_extension.fetch_info.info.path.BaseName().MaybeAsASCII();
-    successful_unpacks_.emplace(file_name, std::move(unpacked_extension));
-    if (--in_progress_count_ == 0)
-      std::move(quit_closure_).Run();
-  }
-  void OnParallelUnpackFailure(FetchedCRXFile fetch_info,
-                               CrxInstallError error) override {
-    std::string file_name = fetch_info.info.path.BaseName().MaybeAsASCII();
-    failed_unpacks_.emplace(file_name, std::move(error));
-    if (--in_progress_count_ == 0)
-      std::move(quit_closure_).Run();
-  }
-
-  void WaitForAllComplete() {
-    base::RunLoop run_loop;
-    quit_closure_ = run_loop.QuitClosure();
-    run_loop.Run();
-  }
-
- protected:
-  std::map<std::string, ParallelUnpacker::UnpackedExtension>
-      successful_unpacks_;
-  std::map<std::string, CrxInstallError> failed_unpacks_;
-
- private:
-  content::BrowserTaskEnvironment task_environment_;
-  TestingProfile profile_;
-  base::ScopedTempDir extensions_dir_;
-
-  std::unique_ptr<content::InProcessUtilityThreadHelper>
-      in_process_utility_thread_helper_;
-  data_decoder::test::InProcessDataDecoder in_process_data_decoder_;
-
-  std::unique_ptr<ParallelUnpacker> parallel_unpacker_;
-  base::OnceClosure quit_closure_;
-  int in_progress_count_ = 0;
-};
-
-TEST_F(ParallelUnpackerTest, OneGood) {
-  Unpack("good_package.crx");
-  WaitForAllComplete();
-  EXPECT_EQ(successful_unpacks_.size(), 1u);
-  EXPECT_EQ(failed_unpacks_.size(), 0u);
-}
-
-TEST_F(ParallelUnpackerTest, TwoGoodInParallel) {
-  Unpack("good_package.crx");
-  Unpack("good_l10n.crx");
-  WaitForAllComplete();
-  EXPECT_EQ(successful_unpacks_.size(), 2u);
-  EXPECT_EQ(failed_unpacks_.size(), 0u);
-}
-
-TEST_F(ParallelUnpackerTest, OneGoodAndOneBadInParallel) {
-  Unpack("good_package.crx");
-  Unpack("missing_default_data.crx");
-  WaitForAllComplete();
-  EXPECT_EQ(successful_unpacks_.size(), 1u);
-  EXPECT_EQ(failed_unpacks_.size(), 1u);
-  EXPECT_EQ(CrxInstallErrorType::SANDBOXED_UNPACKER_FAILURE,
-            failed_unpacks_.find("missing_default_data.crx")->second.type());
-}
-
-}  // namespace extensions
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index 7574e36..d726ec9 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -4235,18 +4235,18 @@
   },
   {
     "name": "tab-groups",
-    "owners": [ "chrome-desktop-ui-seattle@google.com", "bsep" ],
-    "expiry_milestone": 86
+    "owners": [ "chrome-desktop-ui-seattle@google.com", "connily" ],
+    "expiry_milestone": 88
   },
   {
     "name": "tab-groups-collapse",
     "owners": [ "chrome-desktop-ui-seattle@google.com", "xialinyan" ],
-    "expiry_milestone": 86
+    "expiry_milestone": 89
   },
   {
     "name": "tab-groups-collapse-freezing",
     "owners": [ "chrome-desktop-ui-seattle@google.com", "xialinyan" ],
-    "expiry_milestone": 87
+    "expiry_milestone": 89
   },
   {
     "name": "tab-groups-feedback",
diff --git a/chrome/browser/metrics/chrome_metrics_extensions_helper.cc b/chrome/browser/metrics/chrome_metrics_extensions_helper.cc
new file mode 100644
index 0000000..9baeb68
--- /dev/null
+++ b/chrome/browser/metrics/chrome_metrics_extensions_helper.cc
@@ -0,0 +1,25 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/metrics/chrome_metrics_extensions_helper.h"
+
+#include "content/public/browser/render_process_host.h"
+#include "extensions/buildflags/buildflags.h"
+
+#if BUILDFLAG(ENABLE_EXTENSIONS)
+#include "extensions/browser/process_map.h"
+#endif
+
+ChromeMetricsExtensionsHelper::ChromeMetricsExtensionsHelper() = default;
+ChromeMetricsExtensionsHelper::~ChromeMetricsExtensionsHelper() = default;
+
+bool ChromeMetricsExtensionsHelper::IsExtensionProcess(
+    content::RenderProcessHost* render_process_host) {
+#if BUILDFLAG(ENABLE_EXTENSIONS)
+  return extensions::ProcessMap::Get(render_process_host->GetBrowserContext())
+      ->Contains(render_process_host->GetID());
+#else
+  return false;
+#endif
+}
diff --git a/chrome/browser/metrics/chrome_metrics_extensions_helper.h b/chrome/browser/metrics/chrome_metrics_extensions_helper.h
new file mode 100644
index 0000000..a73ad75
--- /dev/null
+++ b/chrome/browser/metrics/chrome_metrics_extensions_helper.h
@@ -0,0 +1,23 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_METRICS_CHROME_METRICS_EXTENSIONS_HELPER_H_
+#define CHROME_BROWSER_METRICS_CHROME_METRICS_EXTENSIONS_HELPER_H_
+
+#include "components/metrics/content/extensions_helper.h"
+
+class ChromeMetricsExtensionsHelper : public metrics::ExtensionsHelper {
+ public:
+  ChromeMetricsExtensionsHelper();
+  ChromeMetricsExtensionsHelper(const ChromeMetricsExtensionsHelper&) = delete;
+  ChromeMetricsExtensionsHelper& operator=(
+      const ChromeMetricsExtensionsHelper&) = delete;
+  ~ChromeMetricsExtensionsHelper() override;
+
+  // metrics::ExtensionsHelper:
+  bool IsExtensionProcess(
+      content::RenderProcessHost* render_process_host) override;
+};
+
+#endif  // CHROME_BROWSER_METRICS_CHROME_METRICS_EXTENSIONS_HELPER_H_
diff --git a/chrome/browser/metrics/chrome_metrics_service_client.cc b/chrome/browser/metrics/chrome_metrics_service_client.cc
index 88df467..867b30a 100644
--- a/chrome/browser/metrics/chrome_metrics_service_client.cc
+++ b/chrome/browser/metrics/chrome_metrics_service_client.cc
@@ -42,8 +42,8 @@
 #include "chrome/browser/google/google_brand.h"
 #include "chrome/browser/history/history_service_factory.h"
 #include "chrome/browser/metrics/cached_metrics_profile.h"
+#include "chrome/browser/metrics/chrome_metrics_extensions_helper.h"
 #include "chrome/browser/metrics/chrome_metrics_service_accessor.h"
-#include "chrome/browser/metrics/chrome_stability_metrics_provider.h"
 #include "chrome/browser/metrics/desktop_platform_features_metrics_provider.h"
 #include "chrome/browser/metrics/desktop_session_duration/desktop_profile_session_durations_service_factory.h"
 #include "chrome/browser/metrics/https_engagement_metrics_provider.h"
@@ -68,6 +68,7 @@
 #include "components/history/core/browser/history_service.h"
 #include "components/metrics/call_stack_profile_metrics_provider.h"
 #include "components/metrics/component_metrics_provider.h"
+#include "components/metrics/content/content_stability_metrics_provider.h"
 #include "components/metrics/content/gpu_metrics_provider.h"
 #include "components/metrics/content/rendering_perf_metrics_provider.h"
 #include "components/metrics/content/subprocess_metrics_provider.h"
@@ -627,7 +628,8 @@
       std::make_unique<OmniboxMetricsProvider>());
 
   metrics_service_->RegisterMetricsProvider(
-      std::make_unique<ChromeStabilityMetricsProvider>(local_state));
+      std::make_unique<metrics::ContentStabilityMetricsProvider>(
+          local_state, std::make_unique<ChromeMetricsExtensionsHelper>()));
 
   metrics_service_->RegisterMetricsProvider(
       std::make_unique<metrics::GPUMetricsProvider>());
diff --git a/chrome/browser/metrics/chrome_stability_metrics_provider.h b/chrome/browser/metrics/chrome_stability_metrics_provider.h
deleted file mode 100644
index ae996e9e..0000000
--- a/chrome/browser/metrics/chrome_stability_metrics_provider.h
+++ /dev/null
@@ -1,84 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_METRICS_CHROME_STABILITY_METRICS_PROVIDER_H_
-#define CHROME_BROWSER_METRICS_CHROME_STABILITY_METRICS_PROVIDER_H_
-
-#include "base/gtest_prod_util.h"
-#include "base/macros.h"
-#include "base/scoped_observer.h"
-#include "build/build_config.h"
-#include "components/metrics/metrics_provider.h"
-#include "components/metrics/stability_metrics_helper.h"
-#include "content/public/browser/browser_child_process_observer.h"
-#include "content/public/browser/notification_observer.h"
-#include "content/public/browser/notification_registrar.h"
-
-#if defined(OS_ANDROID)
-#include "components/crash/content/browser/crash_metrics_reporter_android.h"
-#endif  // defined(OS_ANDROID)
-
-class PrefService;
-
-// ChromeStabilityMetricsProvider gathers and logs Chrome-specific stability-
-// related metrics.
-class ChromeStabilityMetricsProvider
-    : public metrics::MetricsProvider,
-      public content::BrowserChildProcessObserver,
-#if defined(OS_ANDROID)
-      public crash_reporter::CrashMetricsReporter::Observer,
-#endif
-      public content::NotificationObserver {
- public:
-  explicit ChromeStabilityMetricsProvider(PrefService* local_state);
-  ~ChromeStabilityMetricsProvider() override;
-
-  // metrics::MetricsDataProvider:
-  void OnRecordingEnabled() override;
-  void OnRecordingDisabled() override;
-  void ProvideStabilityMetrics(
-      metrics::SystemProfileProto* system_profile_proto) override;
-  void ClearSavedStabilityMetrics() override;
-
- private:
-  FRIEND_TEST_ALL_PREFIXES(ChromeStabilityMetricsProviderTest,
-                           BrowserChildProcessObserverGpu);
-  FRIEND_TEST_ALL_PREFIXES(ChromeStabilityMetricsProviderTest,
-                           BrowserChildProcessObserverUtility);
-  FRIEND_TEST_ALL_PREFIXES(ChromeStabilityMetricsProviderTest,
-                           NotificationObserver);
-
-  // content::NotificationObserver:
-  void Observe(int type,
-               const content::NotificationSource& source,
-               const content::NotificationDetails& details) override;
-
-  // content::BrowserChildProcessObserver:
-  void BrowserChildProcessCrashed(
-      const content::ChildProcessData& data,
-      const content::ChildProcessTerminationInfo& info) override;
-  void BrowserChildProcessLaunchedAndConnected(
-      const content::ChildProcessData& data) override;
-
-#if defined(OS_ANDROID)
-  // crash_reporter::CrashMetricsReporter::Observer:
-  void OnCrashDumpProcessed(
-      int rph_id,
-      const crash_reporter::CrashMetricsReporter::ReportedCrashTypeSet&
-          reported_counts) override;
-
-  ScopedObserver<crash_reporter::CrashMetricsReporter,
-                 crash_reporter::CrashMetricsReporter::Observer>
-      scoped_observer_;
-#endif  // defined(OS_ANDROID)
-
-  metrics::StabilityMetricsHelper helper_;
-
-  // Registrar for receiving stability-related notifications.
-  content::NotificationRegistrar registrar_;
-
-  DISALLOW_COPY_AND_ASSIGN(ChromeStabilityMetricsProvider);
-};
-
-#endif  // CHROME_BROWSER_METRICS_CHROME_STABILITY_METRICS_PROVIDER_H_
diff --git a/chrome/browser/metrics/chrome_stability_metrics_provider_unittest.cc b/chrome/browser/metrics/chrome_stability_metrics_provider_unittest.cc
index 3bb5f9e..f6a423b 100644
--- a/chrome/browser/metrics/chrome_stability_metrics_provider_unittest.cc
+++ b/chrome/browser/metrics/chrome_stability_metrics_provider_unittest.cc
@@ -2,11 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/metrics/chrome_stability_metrics_provider.h"
+#include "components/metrics/content/content_stability_metrics_provider.h"
 
 #include "base/macros.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "build/build_config.h"
+#include "chrome/browser/metrics/chrome_metrics_extensions_helper.h"
 #include "chrome/test/base/testing_browser_process.h"
 #include "chrome/test/base/testing_profile.h"
 #include "chrome/test/base/testing_profile_manager.h"
@@ -38,6 +39,8 @@
 const char kTestGpuProcessName[] = "content_gpu";
 const char kTestUtilityProcessName[] = "test_utility_process";
 
+}  // namespace
+
 class ChromeStabilityMetricsProviderTest : public testing::Test {
  protected:
   ChromeStabilityMetricsProviderTest() : prefs_(new TestingPrefServiceSimple) {
@@ -53,11 +56,9 @@
   DISALLOW_COPY_AND_ASSIGN(ChromeStabilityMetricsProviderTest);
 };
 
-}  // namespace
-
 TEST_F(ChromeStabilityMetricsProviderTest, BrowserChildProcessObserverGpu) {
   base::HistogramTester histogram_tester;
-  ChromeStabilityMetricsProvider provider(prefs());
+  metrics::ContentStabilityMetricsProvider provider(prefs(), nullptr);
 
   content::ChildProcessData child_process_data(content::PROCESS_TYPE_GPU);
   child_process_data.metrics_name = kTestGpuProcessName;
@@ -89,7 +90,7 @@
 
 TEST_F(ChromeStabilityMetricsProviderTest, BrowserChildProcessObserverUtility) {
   base::HistogramTester histogram_tester;
-  ChromeStabilityMetricsProvider provider(prefs());
+  metrics::ContentStabilityMetricsProvider provider(prefs(), nullptr);
 
   content::ChildProcessData child_process_data(content::PROCESS_TYPE_UTILITY);
   child_process_data.metrics_name = kTestUtilityProcessName;
@@ -131,7 +132,8 @@
 
 TEST_F(ChromeStabilityMetricsProviderTest, NotificationObserver) {
   base::HistogramTester histogram_tester;
-  ChromeStabilityMetricsProvider provider(prefs());
+  metrics::ContentStabilityMetricsProvider provider(
+      prefs(), std::make_unique<ChromeMetricsExtensionsHelper>());
   std::unique_ptr<TestingProfileManager> profile_manager(
       new TestingProfileManager(TestingBrowserProcess::GetGlobal()));
   EXPECT_TRUE(profile_manager->SetUp());
diff --git a/chrome/browser/nearby_sharing/constants.h b/chrome/browser/nearby_sharing/constants.h
index 5c4bd38..3606742 100644
--- a/chrome/browser/nearby_sharing/constants.h
+++ b/chrome/browser/nearby_sharing/constants.h
@@ -11,6 +11,10 @@
 constexpr base::TimeDelta kReadResponseFrameTimeout =
     base::TimeDelta::FromSeconds(60);
 
+// Timeout for initiating a connection to a remote device.
+constexpr base::TimeDelta kInitiateNearbyConnectionTimeout =
+    base::TimeDelta::FromSeconds(60);
+
 // The delay before the receiver will disconnect from the sender after rejecting
 // an incoming file. The sender is expected to disconnect immediately after
 // reading the rejection frame.
diff --git a/chrome/browser/nearby_sharing/nearby_connections_manager_impl.cc b/chrome/browser/nearby_sharing/nearby_connections_manager_impl.cc
index 233550c0..ad3437db 100644
--- a/chrome/browser/nearby_sharing/nearby_connections_manager_impl.cc
+++ b/chrome/browser/nearby_sharing/nearby_connections_manager_impl.cc
@@ -8,6 +8,7 @@
 #include "base/strings/string_number_conversions.h"
 #include "base/task/post_task.h"
 #include "base/unguessable_token.h"
+#include "chrome/browser/nearby_sharing/constants.h"
 #include "chrome/browser/nearby_sharing/logging/logging.h"
 #include "chrome/services/sharing/public/mojom/nearby_connections_types.mojom.h"
 #include "crypto/random.h"
@@ -152,7 +153,6 @@
     base::Optional<std::vector<uint8_t>> bluetooth_mac_address,
     DataUsage data_usage,
     NearbyConnectionCallback callback) {
-  // TOOD(crbug/1076008): Implement.
   if (!nearby_connections_) {
     std::move(callback).Run(nullptr);
     return;
@@ -162,38 +162,43 @@
   connection_lifecycle_listeners_.Add(
       this, lifecycle_listener.InitWithNewPipeAndPassReceiver());
 
+  auto result =
+      pending_outgoing_connections_.emplace(endpoint_id, std::move(callback));
+  DCHECK(result.second);
+
+  auto timeout_timer = std::make_unique<base::OneShotTimer>();
+  timeout_timer->Start(
+      FROM_HERE, kInitiateNearbyConnectionTimeout,
+      base::BindOnce(&NearbyConnectionsManagerImpl::OnConnectionTimedOut,
+                     weak_ptr_factory_.GetWeakPtr(), endpoint_id));
+  connect_timeout_timers_.emplace(endpoint_id, std::move(timeout_timer));
+
   // TODO(crbug/10706008): Add MediumSelector and bluetooth_mac_address.
   nearby_connections_->RequestConnection(
       endpoint_info, endpoint_id, std::move(lifecycle_listener),
       base::BindOnce(&NearbyConnectionsManagerImpl::OnConnectionRequested,
-                     weak_ptr_factory_.GetWeakPtr(), endpoint_id,
-                     std::move(callback)));
+                     weak_ptr_factory_.GetWeakPtr(), endpoint_id));
+}
+
+void NearbyConnectionsManagerImpl::OnConnectionTimedOut(
+    const std::string& endpoint_id) {
+  NS_LOG(ERROR) << "Failed to connect to the remote shareTarget: Timed out.";
+  Disconnect(endpoint_id);
 }
 
 void NearbyConnectionsManagerImpl::OnConnectionRequested(
     const std::string& endpoint_id,
-    NearbyConnectionCallback callback,
     ConnectionsStatus status) {
+  auto it = pending_outgoing_connections_.find(endpoint_id);
+  if (it == pending_outgoing_connections_.end())
+    return;
+
   if (status != ConnectionsStatus::kSuccess) {
     NS_LOG(ERROR) << "Failed to connect to the remote shareTarget: " << status;
-    nearby_connections_->DisconnectFromEndpoint(
-        endpoint_id,
-        base::BindOnce(
-            [](const std::string& endpoint_id, ConnectionsStatus status) {
-              NS_LOG(VERBOSE)
-                  << __func__ << ": Disconnecting from endpoint " << endpoint_id
-                  << " attempted over Nearby Connections with result "
-                  << status;
-            },
-            endpoint_id));
-    std::move(callback).Run(nullptr);
+    Disconnect(endpoint_id);
     return;
   }
 
-  auto result =
-      pending_outgoing_connections_.emplace(endpoint_id, std::move(callback));
-  DCHECK(result.second);
-
   // TODO(crbug/1111458): Support TransferManager.
 }
 
@@ -450,6 +455,7 @@
     DCHECK(result.second);
     std::move(it->second).Run(result.first->second.get());
     pending_outgoing_connections_.erase(it);
+    connect_timeout_timers_.erase(endpoint_id);
   }
 }
 
@@ -462,6 +468,7 @@
   if (it != pending_outgoing_connections_.end()) {
     std::move(it->second).Run(nullptr);
     pending_outgoing_connections_.erase(it);
+    connect_timeout_timers_.erase(endpoint_id);
   }
 
   // TODO(crbug/1111458): Support TransferManager.
@@ -475,6 +482,7 @@
   if (it != pending_outgoing_connections_.end()) {
     std::move(it->second).Run(nullptr);
     pending_outgoing_connections_.erase(it);
+    connect_timeout_timers_.erase(endpoint_id);
   }
 
   connections_.erase(endpoint_id);
@@ -562,7 +570,6 @@
   }
   nearby_connections_ = nullptr;
   discovered_endpoints_.clear();
-  pending_outgoing_connections_.clear();
   payload_status_listeners_.clear();
   ClearIncomingPayloads();
   connections_.clear();
@@ -570,4 +577,10 @@
   discovery_listener_ = nullptr;
   incoming_connection_listener_ = nullptr;
   endpoint_discovery_listener_.reset();
+  connect_timeout_timers_.clear();
+
+  for (auto& entry : pending_outgoing_connections_)
+    std::move(entry.second).Run(/*connection=*/nullptr);
+
+  pending_outgoing_connections_.clear();
 }
diff --git a/chrome/browser/nearby_sharing/nearby_connections_manager_impl.h b/chrome/browser/nearby_sharing/nearby_connections_manager_impl.h
index 7ecd1b7..0348d6b 100644
--- a/chrome/browser/nearby_sharing/nearby_connections_manager_impl.h
+++ b/chrome/browser/nearby_sharing/nearby_connections_manager_impl.h
@@ -12,6 +12,7 @@
 #include "base/files/file.h"
 #include "base/gtest_prod_util.h"
 #include "base/memory/weak_ptr.h"
+#include "base/timer/timer.h"
 #include "chrome/browser/nearby_sharing/nearby_connection_impl.h"
 #include "chrome/browser/nearby_sharing/nearby_file_handler.h"
 #include "chrome/browser/nearby_sharing/nearby_process_manager.h"
@@ -122,8 +123,8 @@
   void OnPayloadTransferUpdate(const std::string& endpoint_id,
                                PayloadTransferUpdatePtr update) override;
 
+  void OnConnectionTimedOut(const std::string& endpoint_id);
   void OnConnectionRequested(const std::string& endpoint_id,
-                             NearbyConnectionCallback callback,
                              ConnectionsStatus status);
   bool BindNearbyConnections();
   void Reset();
@@ -146,6 +147,9 @@
   // A map of endpoint_id to NearbyConnection.
   base::flat_map<std::string, std::unique_ptr<NearbyConnectionImpl>>
       connections_;
+  // A map of endpoint_id to timers that timeout a connection request.
+  base::flat_map<std::string, std::unique_ptr<base::OneShotTimer>>
+      connect_timeout_timers_;
   // A map of payload_id to PayloadStatusListener*.
   base::flat_map<int64_t, PayloadStatusListener*> payload_status_listeners_;
   // A map of payload_id to PayloadPtr.
diff --git a/chrome/browser/nearby_sharing/nearby_connections_manager_impl_unittest.cc b/chrome/browser/nearby_sharing/nearby_connections_manager_impl_unittest.cc
index 005898b..95945f8 100644
--- a/chrome/browser/nearby_sharing/nearby_connections_manager_impl_unittest.cc
+++ b/chrome/browser/nearby_sharing/nearby_connections_manager_impl_unittest.cc
@@ -11,6 +11,7 @@
 #include "base/run_loop.h"
 #include "base/test/bind_test_util.h"
 #include "base/test/mock_callback.h"
+#include "chrome/browser/nearby_sharing/constants.h"
 #include "chrome/browser/nearby_sharing/mock_nearby_connections.h"
 #include "chrome/browser/nearby_sharing/mock_nearby_process_manager.h"
 #include "chrome/browser/nearby_sharing/nearby_connection_impl.h"
@@ -339,7 +340,8 @@
     run_loop.Run();
   }
 
-  content::BrowserTaskEnvironment task_environment_;
+  content::BrowserTaskEnvironment task_environment_{
+      base::test::TaskEnvironment::TimeSource::MOCK_TIME};
   TestingProfile profile_;
   std::unique_ptr<net::test::MockNetworkChangeNotifier> network_notifier_ =
       net::test::MockNetworkChangeNotifier::Create();
@@ -803,6 +805,60 @@
   cancel_run_loop.Run();
 }
 
+TEST_F(NearbyConnectionsManagerImplTest, ConnectTimeout) {
+  mojo::Remote<EndpointDiscoveryListener> discovery_listener_remote;
+  testing::NiceMock<MockDiscoveryListener> discovery_listener;
+  StartDiscovery(discovery_listener_remote, discovery_listener);
+
+  // RequestConnection will time out.
+  const std::vector<uint8_t> local_endpoint_info(std::begin(kEndpointInfo),
+                                                 std::end(kEndpointInfo));
+
+  mojo::Remote<ConnectionLifecycleListener> connection_listener_remote;
+  NearbyConnectionsMojom::RequestConnectionCallback connect_callback;
+  EXPECT_CALL(nearby_connections_, RequestConnection)
+      .WillOnce(
+          [&](const std::vector<uint8_t>& endpoint_info,
+              const std::string& endpoint_id,
+              mojo::PendingRemote<ConnectionLifecycleListener> listener,
+              NearbyConnectionsMojom::RequestConnectionCallback callback) {
+            EXPECT_EQ(local_endpoint_info, endpoint_info);
+            EXPECT_EQ(kRemoteEndpointId, endpoint_id);
+
+            connection_listener_remote.Bind(std::move(listener));
+            // Do not call callback until connection timed out.
+            connect_callback = std::move(callback);
+          });
+
+  // Timing out should call disconnect.
+  EXPECT_CALL(nearby_connections_, DisconnectFromEndpoint)
+      .WillOnce(
+          [&](const std::string& endpoint_id,
+              NearbyConnectionsMojom::DisconnectFromEndpointCallback callback) {
+            EXPECT_EQ(kRemoteEndpointId, endpoint_id);
+            std::move(callback).Run(Status::kSuccess);
+          });
+
+  base::RunLoop run_loop;
+  NearbyConnection* nearby_connection = nullptr;
+  nearby_connections_manager_.Connect(
+      local_endpoint_info, kRemoteEndpointId,
+      /*bluetooth_mac_address=*/base::nullopt, DataUsage::kOffline,
+      base::BindLambdaForTesting([&](NearbyConnection* connection) {
+        nearby_connection = connection;
+        run_loop.Quit();
+      }));
+  // Simulate time passing until timeout is reached.
+  task_environment_.FastForwardBy(kInitiateNearbyConnectionTimeout);
+  run_loop.Run();
+
+  // Expect callback to be called with null connection.
+  EXPECT_FALSE(nearby_connection);
+
+  // Resolving the connect callback after timeout should do nothing.
+  std::move(connect_callback).Run(Status::kSuccess);
+}
+
 TEST_F(NearbyConnectionsManagerImplTest, StartAdvertising) {
   mojo::Remote<ConnectionLifecycleListener> connection_listener_remote;
   testing::NiceMock<MockIncomingConnectionListener>
diff --git a/chrome/browser/nearby_sharing/nearby_sharing_service_impl.cc b/chrome/browser/nearby_sharing/nearby_sharing_service_impl.cc
index ea7b9ee..2db699f 100644
--- a/chrome/browser/nearby_sharing/nearby_sharing_service_impl.cc
+++ b/chrome/browser/nearby_sharing/nearby_sharing_service_impl.cc
@@ -767,7 +767,6 @@
     NS_LOG(VERBOSE) << __func__ << ": Nearby sharing disabled!";
     StopAdvertising();
     StopScanning();
-    // TODO(crbug/1085067): Stop discovery.
     nearby_connections_manager_->Shutdown();
   }
   InvalidateSurfaceState();
@@ -1520,7 +1519,6 @@
       settings_.GetDataUsage(), share_target);
 
   // TODO(crbug.com/1111458): Add preferred transfer type.
-  // TODO(crbug.com/1085067): Add timeout
   nearby_connections_manager_->Connect(
       std::move(endpoint_info), *info->endpoint_id(),
       std::move(bluetooth_mac_address), adjusted_data_usage,
diff --git a/chrome/browser/net/cookie_policy_browsertest.cc b/chrome/browser/net/cookie_policy_browsertest.cc
index de6dc9d..f780fba 100644
--- a/chrome/browser/net/cookie_policy_browsertest.cc
+++ b/chrome/browser/net/cookie_policy_browsertest.cc
@@ -62,8 +62,11 @@
   }
 
   void SetBlockThirdPartyCookies(bool value) {
-    browser()->profile()->GetPrefs()->SetBoolean(prefs::kBlockThirdPartyCookies,
-                                                 value);
+    browser()->profile()->GetPrefs()->SetInteger(
+        prefs::kCookieControlsMode,
+        static_cast<int>(
+            value ? content_settings::CookieControlsMode::kBlockThirdParty
+                  : content_settings::CookieControlsMode::kOff));
   }
 
   void NavigateToPageWithFrame(const std::string& host) {
diff --git a/chrome/browser/net/network_context_configuration_browsertest.cc b/chrome/browser/net/network_context_configuration_browsertest.cc
index ef22656..1968782c 100644
--- a/chrome/browser/net/network_context_configuration_browsertest.cc
+++ b/chrome/browser/net/network_context_configuration_browsertest.cc
@@ -795,8 +795,12 @@
   net::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS);
   net::test_server::RegisterDefaultHandlers(&https_server);
   ASSERT_TRUE(https_server.Start());
-  if (GetPrefService()->FindPreference(prefs::kBlockThirdPartyCookies))
-    GetPrefService()->SetBoolean(prefs::kBlockThirdPartyCookies, true);
+  if (GetPrefService()->FindPreference(prefs::kCookieControlsMode)) {
+    GetPrefService()->SetInteger(
+        prefs::kCookieControlsMode,
+        static_cast<int>(
+            content_settings::CookieControlsMode::kBlockThirdParty));
+  }
   SetCookie(CookieType::kFirstParty, CookiePersistenceType::kPersistent,
             embedded_test_server());
 
@@ -849,7 +853,9 @@
   test_server.RegisterRequestHandler(base::BindRepeating(&EchoCookieHeader));
   ASSERT_TRUE(test_server.Start());
 
-  GetPrefService()->SetBoolean(prefs::kBlockThirdPartyCookies, true);
+  GetPrefService()->SetInteger(
+      prefs::kCookieControlsMode,
+      static_cast<int>(content_settings::CookieControlsMode::kBlockThirdParty));
   SetCookie(CookieType::kFirstParty, CookiePersistenceType::kPersistent,
             embedded_test_server());
 
@@ -1565,7 +1571,9 @@
   if (system)
     return;
 
-  GetPrefService()->SetBoolean(prefs::kBlockThirdPartyCookies, true);
+  GetPrefService()->SetInteger(
+      prefs::kCookieControlsMode,
+      static_cast<int>(content_settings::CookieControlsMode::kBlockThirdParty));
   SetCookie(CookieType::kThirdParty, CookiePersistenceType::kSession,
             https_server());
 
@@ -1584,33 +1592,19 @@
   if (system)
     return;
 
-  // The preference is expected to be reset in incognito mode.
-  if (is_incognito()) {
-    EXPECT_FALSE(GetPrefService()->GetBoolean(prefs::kBlockThirdPartyCookies));
-    EXPECT_EQ(
-        static_cast<int>(content_settings::CookieControlsMode::kIncognitoOnly),
-        GetPrefService()->GetInteger(prefs::kCookieControlsMode));
-    return;
-  }
-
-  // For regular sessions, the kBlockThirdpartyCookies preference gets migrated
-  // to kCookieControlsMode. Reset it so it doesn't interfere with the test.
+  // The third-party cookies pref should carry over to the next session.
   EXPECT_EQ(
       static_cast<int>(content_settings::CookieControlsMode::kBlockThirdParty),
       GetPrefService()->GetInteger(prefs::kCookieControlsMode));
-  GetPrefService()->SetInteger(
-      prefs::kCookieControlsMode,
-      static_cast<int>(content_settings::CookieControlsMode::kIncognitoOnly));
-
-  // The kBlockThirdPartyCookies pref should carry over to the next session.
-  EXPECT_TRUE(GetPrefService()->GetBoolean(prefs::kBlockThirdPartyCookies));
   SetCookie(CookieType::kThirdParty, CookiePersistenceType::kSession,
             https_server());
 
   EXPECT_TRUE(GetCookies(https_server()->base_url()).empty());
 
   // Set pref to false, third party cookies should be allowed now.
-  GetPrefService()->SetBoolean(prefs::kBlockThirdPartyCookies, false);
+  GetPrefService()->SetInteger(
+      prefs::kCookieControlsMode,
+      static_cast<int>(content_settings::CookieControlsMode::kOff));
   // Set a third-party cookie. It should actually get set this time.
   SetCookie(CookieType::kThirdParty, CookiePersistenceType::kSession,
             https_server());
diff --git a/chrome/browser/net/referrer_policy_policy_browsertest.cc b/chrome/browser/net/referrer_policy_policy_browsertest.cc
index 1c6a4af..28f4694 100644
--- a/chrome/browser/net/referrer_policy_policy_browsertest.cc
+++ b/chrome/browser/net/referrer_policy_policy_browsertest.cc
@@ -12,10 +12,10 @@
 #include "components/policy/core/common/policy_map.h"
 #include "components/policy/core/common/policy_types.h"
 #include "components/policy/policy_constants.h"
-#include "content/public/common/referrer.h"
 #include "content/public/test/browser_test.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/common/loader/referrer_utils.h"
 
 namespace policy {
 
@@ -37,7 +37,7 @@
 IN_PROC_BROWSER_TEST_F(ForceLegacyDefaultReferrerPolicy, UpdatesDynamically) {
   // When the policy's unset, we shouldn't be forcing the legacy default
   // referrer policy.
-  EXPECT_FALSE(content::Referrer::ShouldForceLegacyDefaultReferrerPolicy());
+  EXPECT_FALSE(blink::ReferrerUtils::ShouldForceLegacyDefaultReferrerPolicy());
 
   policy::PolicyMap values;
   values.Set(key::kForceLegacyDefaultReferrerPolicy, POLICY_LEVEL_RECOMMENDED,
@@ -46,7 +46,7 @@
   policy_provider_.UpdateChromePolicy(values);
   base::RunLoop().RunUntilIdle();
   // When the policy's true, we should have flipped the global to true.
-  EXPECT_TRUE(content::Referrer::ShouldForceLegacyDefaultReferrerPolicy());
+  EXPECT_TRUE(blink::ReferrerUtils::ShouldForceLegacyDefaultReferrerPolicy());
 
   values.Set(key::kForceLegacyDefaultReferrerPolicy, POLICY_LEVEL_RECOMMENDED,
              POLICY_SCOPE_MACHINE, POLICY_SOURCE_CLOUD, base::Value(false),
@@ -54,7 +54,7 @@
   policy_provider_.UpdateChromePolicy(values);
   base::RunLoop().RunUntilIdle();
   // When the policy's false, we should have flipped the global back to false.
-  EXPECT_FALSE(content::Referrer::ShouldForceLegacyDefaultReferrerPolicy());
+  EXPECT_FALSE(blink::ReferrerUtils::ShouldForceLegacyDefaultReferrerPolicy());
 }
 
 }  // namespace policy
diff --git a/chrome/browser/net/referrer_policy_policy_handler.cc b/chrome/browser/net/referrer_policy_policy_handler.cc
index 0ee95754..1ba71d2 100644
--- a/chrome/browser/net/referrer_policy_policy_handler.cc
+++ b/chrome/browser/net/referrer_policy_policy_handler.cc
@@ -8,7 +8,7 @@
 #include "components/policy/core/common/policy_map.h"
 #include "components/policy/policy_constants.h"
 #include "components/prefs/pref_value_map.h"
-#include "content/public/common/referrer.h"
+#include "third_party/blink/public/common/loader/referrer_utils.h"
 
 namespace policy {
 
@@ -24,7 +24,7 @@
       policies.GetValue(key::kForceLegacyDefaultReferrerPolicy);
   if (value) {
     DCHECK(value->is_bool());
-    content::Referrer::SetForceLegacyDefaultReferrerPolicy(value->GetBool());
+    blink::ReferrerUtils::SetForceLegacyDefaultReferrerPolicy(value->GetBool());
   }
 }
 
diff --git a/chrome/browser/net/referrer_policy_policy_handler_unittest.cc b/chrome/browser/net/referrer_policy_policy_handler_unittest.cc
index 6003561..812268e9 100644
--- a/chrome/browser/net/referrer_policy_policy_handler_unittest.cc
+++ b/chrome/browser/net/referrer_policy_policy_handler_unittest.cc
@@ -13,8 +13,8 @@
 #include "components/policy/core/common/policy_types.h"
 #include "components/policy/policy_constants.h"
 #include "components/prefs/pref_value_map.h"
-#include "content/public/common/referrer.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/common/loader/referrer_utils.h"
 
 namespace policy {
 
@@ -69,13 +69,13 @@
   SetPolicyValue(key::kForceLegacyDefaultReferrerPolicy, base::Value(true));
 
   CheckAndApplyPolicySettings();
-  EXPECT_TRUE(content::Referrer::ShouldForceLegacyDefaultReferrerPolicy());
+  EXPECT_TRUE(blink::ReferrerUtils::ShouldForceLegacyDefaultReferrerPolicy());
 }
 
 TEST_F(ReferrerPolicyPolicyHandlerTest, ValueFalse) {
   SetPolicyValue(key::kForceLegacyDefaultReferrerPolicy, base::Value(false));
 
   CheckAndApplyPolicySettings();
-  EXPECT_FALSE(content::Referrer::ShouldForceLegacyDefaultReferrerPolicy());
+  EXPECT_FALSE(blink::ReferrerUtils::ShouldForceLegacyDefaultReferrerPolicy());
 }
 }  // namespace policy
diff --git a/chrome/browser/offline_pages/background_loader_offliner_unittest.cc b/chrome/browser/offline_pages/background_loader_offliner_unittest.cc
index c866fb13..6b3cedbfb 100644
--- a/chrome/browser/offline_pages/background_loader_offliner_unittest.cc
+++ b/chrome/browser/offline_pages/background_loader_offliner_unittest.cc
@@ -19,6 +19,7 @@
 #include "chrome/browser/previews/previews_ui_tab_helper.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/test/base/testing_profile.h"
+#include "components/content_settings/core/browser/cookie_settings.h"
 #include "components/content_settings/core/common/pref_names.h"
 #include "components/offline_pages/content/background_loader/background_loader_contents_stub.h"
 #include "components/offline_pages/core/background/load_termination_listener.h"
@@ -373,7 +374,9 @@
   SavePageRequest request(kRequestId, HttpUrl(), custom_tabs_client_id,
                           creation_time, kUserRequested);
 
-  profile()->GetPrefs()->SetBoolean(prefs::kBlockThirdPartyCookies, true);
+  profile()->GetPrefs()->SetInteger(
+      prefs::kCookieControlsMode,
+      static_cast<int>(content_settings::CookieControlsMode::kBlockThirdParty));
   EXPECT_FALSE(offliner()->LoadAndSave(request, completion_callback(),
                                        progress_callback()));
 }
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 0a9ef7e..ae6a89d2 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
@@ -258,9 +258,11 @@
       navigation_handle->GetReloadType() != content::ReloadType::NONE;
 
   aggregate_frame_data_->UpdateForNavigation(
-      navigation_handle->GetRenderFrameHost(), true /* frame_navigated */);
+      navigation_handle->GetRenderFrameHost(), true /* frame_navigated */,
+      true /* record_frame_metrics */);
   main_frame_data_->UpdateForNavigation(navigation_handle->GetRenderFrameHost(),
-                                        true /* frame_navigated */);
+                                        true /* frame_navigated */,
+                                        true /* record_frame_metrics */);
 
   // The main frame is never considered an ad.
   ad_frames_data_[navigation_handle->GetFrameTreeNodeId()] =
@@ -397,7 +399,8 @@
 
   if (should_create_new_frame_data) {
     if (previous_data) {
-      previous_data->UpdateForNavigation(ad_host, frame_navigated);
+      previous_data->UpdateForNavigation(ad_host, frame_navigated,
+                                         true /* record_frame_metrics */);
       return;
     }
     if (base::FeatureList::IsEnabled(features::kV8PerAdFrameMemoryMonitoring) &&
@@ -418,7 +421,8 @@
         heavy_ad_threshold_noise_provider_->GetNetworkThresholdNoiseForFrame());
     ad_data_iterator = --ad_frames_data_storage_.end();
     ad_data = &*ad_data_iterator;
-    ad_data->UpdateForNavigation(ad_host, frame_navigated);
+    ad_data->UpdateForNavigation(ad_host, frame_navigated,
+                                 true /* record_frame_metrics */);
   }
 
   // Maybe update frame depth based on the new ad frames distance to the ad
@@ -461,6 +465,34 @@
   content::RenderFrameHost* frame_host =
       FindFrameMaybeUnsafe(navigation_handle);
 
+  // We want to reset the FrameData for an ad after heavy ads fires, so that we
+  // can trigger on subsequent navigations if the page tries to serve another ad
+  // in the frame.
+  if (navigation_handle->IsErrorPage() &&
+      navigation_handle->GetNetErrorCode() == net::ERR_BLOCKED_BY_CLIENT &&
+      navigation_handle->HasCommitted()) {
+    const auto& id_and_data = ad_frames_data_.find(frame_tree_node_id);
+    if (id_and_data != ad_frames_data_.end() &&
+        id_and_data->second != ad_frames_data_storage_.end() &&
+        id_and_data->second->heavy_ad_status_with_policy() !=
+            FrameData::HeavyAdStatus::kNone) {
+      RecordPerFrameHistograms(*id_and_data->second);
+      id_and_data->second->RecordAdFrameLoadUkmEvent(
+          GetDelegate().GetPageUkmSourceId());
+      ad_frames_data_storage_.erase(id_and_data->second);
+      ad_frames_data_.erase(id_and_data);
+      ad_frames_data_storage_.emplace_back(
+          frame_tree_node_id, heavy_ad_threshold_noise_provider_
+                                  ->GetNetworkThresholdNoiseForFrame());
+      auto ad_data_iterator = --ad_frames_data_storage_.end();
+      FrameData* ad_data = &*ad_data_iterator;
+      ad_frames_data_[frame_tree_node_id] = ad_data_iterator;
+      ad_data->UpdateForNavigation(frame_host, true /*frame_navigated=*/,
+                                   false /*record_frame_metrics=*/);
+      return;
+    }
+  }
+
   bool is_adframe = client->GetThrottleManager()->IsFrameTaggedAsAd(frame_host);
 
   // TODO(https://crbug.com): The following block is a hack to ignore certain
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 72f4cce..92d0b9e 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
@@ -1672,15 +1672,31 @@
   EXPECT_EQ(4u, console_observer.messages().size());
 }
 
+class NoHostThresholdHeavyAdsBrowserTest
+    : public AdsPageLoadMetricsObserverResourceBrowserTest {
+ public:
+  NoHostThresholdHeavyAdsBrowserTest() {
+    scoped_feature_list_.InitAndEnableFeatureWithParameters(
+        features::kHeavyAdPrivacyMitigations, {{"host-threshold", "100"}});
+  }
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
+
 // Verifies that the frame is navigated to the intervention page when a
 // heavy ad intervention triggers.
-IN_PROC_BROWSER_TEST_F(AdsPageLoadMetricsObserverResourceBrowserTest,
+IN_PROC_BROWSER_TEST_F(NoHostThresholdHeavyAdsBrowserTest,
                        HeavyAdInterventionEnabled_ErrorPageLoaded) {
   base::HistogramTester histogram_tester;
   auto incomplete_resource_response =
       std::make_unique<net::test_server::ControllableHttpResponse>(
           embedded_test_server(), "/ads_observer/incomplete_resource.js",
           true /*relative_url_is_prefix*/);
+  auto incomplete_resource_response2 =
+      std::make_unique<net::test_server::ControllableHttpResponse>(
+          embedded_test_server(), "/ads_observer/incomplete_resource2.js",
+          true /*relative_url_is_prefix*/);
   ASSERT_TRUE(embedded_test_server()->Start());
 
   // Create a navigation observer that will watch for the intervention to
@@ -1690,7 +1706,6 @@
   content::TestNavigationObserver error_observer(web_contents,
                                                  net::ERR_BLOCKED_BY_CLIENT);
 
-  auto waiter = CreateAdsPageLoadMetricsTestWaiter();
   GURL url = embedded_test_server()->GetURL(
       "/ads_observer/ad_with_incomplete_resource.html");
   ui_test_utils::NavigateToURL(browser(), url);
@@ -1712,6 +1727,26 @@
   histogram_tester.ExpectBucketCount(
       "Blink.UseCounter.Features",
       blink::mojom::WebFeature::kHeavyAdIntervention, 1);
+
+  content::TestNavigationObserver error_observer2(web_contents,
+                                                  net::ERR_BLOCKED_BY_CLIENT);
+
+  // Test that subsequent navigations to the error page can still trigger heavy
+  // ads (crbug.com/1099014).
+  EXPECT_TRUE(content::ExecJs(web_contents, "advanceSrcdoc();",
+                              content::EXECUTE_SCRIPT_NO_USER_GESTURE));
+
+  // Load a resource large enough to trigger the intervention.
+  LoadLargeResource(incomplete_resource_response2.get(),
+                    kMaxHeavyAdNetworkSize);
+
+  error_observer2.WaitForNavigationFinished();
+
+  // We can't check whether the navigation didn't occur because the error page
+  // load is not synchronous. Instead check that we didn't log intervention UMA
+  // that is always recorded when the intervention occurs. Both
+  // interventions should have been logged.
+  histogram_tester.ExpectTotalCount(kHeavyAdInterventionTypeHistogramId, 2);
 }
 
 class AdsPageLoadMetricsObserverResourceBrowserTestWithoutHeavyAdIntervention
diff --git a/chrome/browser/page_load_metrics/observers/ad_metrics/frame_data.cc b/chrome/browser/page_load_metrics/observers/ad_metrics/frame_data.cc
index 80c3f35..b1306f22 100644
--- a/chrome/browser/page_load_metrics/observers/ad_metrics/frame_data.cc
+++ b/chrome/browser/page_load_metrics/observers/ad_metrics/frame_data.cc
@@ -84,8 +84,10 @@
 FrameData::~FrameData() = default;
 
 void FrameData::UpdateForNavigation(content::RenderFrameHost* render_frame_host,
-                                    bool frame_navigated) {
+                                    bool frame_navigated,
+                                    bool record_metrics) {
   frame_navigated_ = frame_navigated;
+  record_metrics_ = record_metrics;
   if (!render_frame_host)
     return;
 
@@ -267,7 +269,7 @@
 }
 
 bool FrameData::ShouldRecordFrameForMetrics() const {
-  return bytes() != 0 || !GetTotalCpuUsage().is_zero();
+  return record_metrics_ && (bytes() != 0 || !GetTotalCpuUsage().is_zero());
 }
 
 void FrameData::RecordAdFrameLoadUkmEvent(ukm::SourceId source_id) const {
diff --git a/chrome/browser/page_load_metrics/observers/ad_metrics/frame_data.h b/chrome/browser/page_load_metrics/observers/ad_metrics/frame_data.h
index 9a1cb71..7aea0bb 100644
--- a/chrome/browser/page_load_metrics/observers/ad_metrics/frame_data.h
+++ b/chrome/browser/page_load_metrics/observers/ad_metrics/frame_data.h
@@ -156,7 +156,8 @@
 
   // Update the metadata of this frame if it is being navigated.
   void UpdateForNavigation(content::RenderFrameHost* render_frame_host,
-                           bool frame_navigated);
+                           bool frame_navigated,
+                           bool record_frame_metrics);
 
   // Updates the number of bytes loaded in the frame given a resource load.
   void ProcessResourceLoadInFrame(
@@ -405,6 +406,12 @@
   // Number of bytes of noise that should be added to the network threshold.
   const int heavy_ad_network_threshold_noise_;
 
+  // |record_metrics| indicates whether metrics should be logged for this frame.
+  // This may be false in cases where we are tracking a frame, but do not want
+  // to log metrics until a subsequent navigation, e.g. if a frame is currently
+  // navigated to the heavy ads error page.
+  bool record_metrics_ = true;
+
   DISALLOW_COPY_AND_ASSIGN(FrameData);
 };
 
diff --git a/chrome/browser/paint_preview/android/java/src/org/chromium/chrome/browser/paint_preview/PaintPreviewDemoManager.java b/chrome/browser/paint_preview/android/java/src/org/chromium/chrome/browser/paint_preview/PaintPreviewDemoManager.java
index b365b0dd..989d99b9 100644
--- a/chrome/browser/paint_preview/android/java/src/org/chromium/chrome/browser/paint_preview/PaintPreviewDemoManager.java
+++ b/chrome/browser/paint_preview/android/java/src/org/chromium/chrome/browser/paint_preview/PaintPreviewDemoManager.java
@@ -55,7 +55,7 @@
                     PaintPreviewDemoManager.this::removePaintPreviewDemo,
                     PaintPreviewDemoManager.this::addPlayerView, null, null,
                     ChromeColors.getPrimaryBackgroundColor(mTab.getContext().getResources(), false),
-                    () -> {
+                    (status) -> {
                         Toast.makeText(mTab.getContext(),
                                      R.string.paint_preview_demo_playback_failure,
                                      Toast.LENGTH_LONG)
diff --git a/chrome/browser/paint_preview/android/java/src/org/chromium/chrome/browser/paint_preview/TabbedPaintPreviewMetricsHelper.java b/chrome/browser/paint_preview/android/java/src/org/chromium/chrome/browser/paint_preview/TabbedPaintPreviewMetricsHelper.java
index 473bfd4..7438063 100644
--- a/chrome/browser/paint_preview/android/java/src/org/chromium/chrome/browser/paint_preview/TabbedPaintPreviewMetricsHelper.java
+++ b/chrome/browser/paint_preview/android/java/src/org/chromium/chrome/browser/paint_preview/TabbedPaintPreviewMetricsHelper.java
@@ -10,6 +10,7 @@
 
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.base.metrics.RecordUserAction;
+import org.chromium.components.paintpreview.player.CompositorStatus;
 
 import java.util.HashMap;
 import java.util.Map;
@@ -77,6 +78,12 @@
                 "Browser.PaintPreview.TabbedPlayer.FirstPaintBeforeTabLoad", mFirstPaintHappened);
     }
 
+    void onCompositorFailure(@CompositorStatus int status) {
+        RecordHistogram.recordEnumeratedHistogram(
+                "Browser.PaintPreview.TabbedPlayer.CompositorFailureReason", status,
+                CompositorStatus.COUNT);
+    }
+
     void recordExitMetrics(int exitCause, int snackbarShownCount) {
         if (exitCause == ExitCause.SNACK_BAR_ACTION) {
             RecordUserAction.record("PaintPreview.TabbedPlayer.Actionbar.Action");
diff --git a/chrome/browser/paint_preview/android/java/src/org/chromium/chrome/browser/paint_preview/TabbedPaintPreviewPlayer.java b/chrome/browser/paint_preview/android/java/src/org/chromium/chrome/browser/paint_preview/TabbedPaintPreviewPlayer.java
index 7d39334..d548365 100644
--- a/chrome/browser/paint_preview/android/java/src/org/chromium/chrome/browser/paint_preview/TabbedPaintPreviewPlayer.java
+++ b/chrome/browser/paint_preview/android/java/src/org/chromium/chrome/browser/paint_preview/TabbedPaintPreviewPlayer.java
@@ -161,7 +161,10 @@
                 () -> mMetricsHelper.onFirstPaint(activityCreationTimestampMs, wasBackgrounded),
                 () -> mHasUserInteraction = true,
                 ChromeColors.getPrimaryBackgroundColor(mTab.getContext().getResources(), false),
-                () -> removePaintPreview(ExitCause.COMPOSITOR_FAILURE),
+                (status) -> {
+                    mMetricsHelper.onCompositorFailure(status);
+                    removePaintPreview(ExitCause.COMPOSITOR_FAILURE);
+                },
                 /*ignoreInitialScrollOffset=*/false);
         mPlayerManager.setUserFrustrationCallback(this::showSnackbar);
         mOnDismissed = onDismissed;
diff --git a/chrome/browser/password_check/android/password_check_manager.h b/chrome/browser/password_check/android/password_check_manager.h
index e841406..c6f4c5d 100644
--- a/chrome/browser/password_check/android/password_check_manager.h
+++ b/chrome/browser/password_check/android/password_check_manager.h
@@ -228,8 +228,8 @@
 
   // Used to obtain the list of compromised credentials.
   password_manager::CompromisedCredentialsManager
-      compromised_credentials_manager_{password_store_,
-                                       &saved_passwords_presenter_};
+      compromised_credentials_manager_{&saved_passwords_presenter_,
+                                       password_store_};
 
   // Adapter used to start, monitor and stop a bulk leak check.
   password_manager::BulkLeakCheckServiceAdapter
diff --git a/chrome/browser/policy/configuration_policy_handler_list_factory.cc b/chrome/browser/policy/configuration_policy_handler_list_factory.cc
index 771f7fda..6f40e48 100644
--- a/chrome/browser/policy/configuration_policy_handler_list_factory.cc
+++ b/chrome/browser/policy/configuration_policy_handler_list_factory.cc
@@ -238,9 +238,6 @@
   { key::kAllowDeletingBrowserHistory,
     prefs::kAllowDeletingBrowserHistory,
     base::Value::Type::BOOLEAN },
-  { key::kBlockThirdPartyCookies,
-    prefs::kBlockThirdPartyCookies,
-    base::Value::Type::BOOLEAN },
   { key::kAdsSettingForIntrusiveAdsSites,
     prefs::kManagedDefaultAdsSetting,
     base::Value::Type::INTEGER },
diff --git a/chrome/browser/prerender/prerender_unittest.cc b/chrome/browser/prerender/prerender_unittest.cc
index 778ff68f..5f96d01 100644
--- a/chrome/browser/prerender/prerender_unittest.cc
+++ b/chrome/browser/prerender/prerender_unittest.cc
@@ -34,6 +34,7 @@
 #include "chrome/common/pref_names.h"
 #include "chrome/test/base/testing_browser_process.h"
 #include "chrome/test/base/testing_profile.h"
+#include "components/content_settings/core/browser/cookie_settings.h"
 #include "components/content_settings/core/common/pref_names.h"
 #include "components/prefs/pref_service.h"
 #include "components/prerender/browser/prerender_contents.h"
@@ -503,7 +504,9 @@
   GURL url("http://www.google.com/");
   ASSERT_TRUE(IsNoStatePrefetchEnabled());
 
-  profile()->GetPrefs()->SetBoolean(prefs::kBlockThirdPartyCookies, true);
+  profile()->GetPrefs()->SetInteger(
+      prefs::kCookieControlsMode,
+      static_cast<int>(content_settings::CookieControlsMode::kBlockThirdParty));
   EXPECT_FALSE(AddSimplePrerender(url));
   histogram_tester().ExpectUniqueSample(
       "Prerender.FinalStatus", FINAL_STATUS_BLOCK_THIRD_PARTY_COOKIES, 1);
diff --git a/chrome/browser/push_messaging/push_messaging_service_impl.cc b/chrome/browser/push_messaging/push_messaging_service_impl.cc
index b080bde..d5d6b534 100644
--- a/chrome/browser/push_messaging/push_messaging_service_impl.cc
+++ b/chrome/browser/push_messaging/push_messaging_service_impl.cc
@@ -31,6 +31,7 @@
 #include "chrome/browser/push_messaging/push_messaging_constants.h"
 #include "chrome/browser/push_messaging/push_messaging_features.h"
 #include "chrome/browser/push_messaging/push_messaging_service_factory.h"
+#include "chrome/browser/push_messaging/push_messaging_utils.h"
 #include "chrome/browser/ui/chrome_pages.h"
 #include "chrome/common/buildflags.h"
 #include "chrome/common/chrome_features.h"
@@ -181,13 +182,6 @@
   return web_contents ? web_contents->GetMainFrame() : nullptr;
 }
 
-bool IsVapidKey(const std::string& application_server_key) {
-  // VAPID keys are NIST P-256 public keys in uncompressed format (64 bytes),
-  // verified through its length and the 0x04 prefix.
-  return application_server_key.size() == 65 &&
-         application_server_key[0] == 0x04;
-}
-
 }  // namespace
 
 // static
@@ -730,7 +724,7 @@
   // TODO(peter): Move this check to the renderer process & Mojo message
   // validation once the flag is always enabled, and remove the
   // |render_process_id| and |render_frame_id| parameters from this method.
-  if (!IsVapidKey(application_server_key_string)) {
+  if (!push_messaging::IsVapidKey(application_server_key_string)) {
     content::RenderFrameHost* render_frame_host =
         content::RenderFrameHost::FromID(render_process_id, render_frame_id);
     content::RenderFrameHost* main_frame =
@@ -769,13 +763,14 @@
 
   GetInstanceIDDriver()
       ->GetInstanceID(app_identifier.app_id())
-      ->GetToken(NormalizeSenderInfo(application_server_key_string), kGCMScope,
-                 ttl, std::map<std::string, std::string>() /* options */,
-                 {} /* flags */,
-                 base::BindOnce(&PushMessagingServiceImpl::DidSubscribe,
-                                weak_factory_.GetWeakPtr(), app_identifier,
-                                application_server_key_string,
-                                std::move(register_callback)));
+      ->GetToken(
+          push_messaging::NormalizeSenderInfo(application_server_key_string),
+          kGCMScope, ttl, std::map<std::string, std::string>() /* options */,
+          {} /* flags */,
+          base::BindOnce(&PushMessagingServiceImpl::DidSubscribe,
+                         weak_factory_.GetWeakPtr(), app_identifier,
+                         application_server_key_string,
+                         std::move(register_callback)));
 }
 
 void PushMessagingServiceImpl::SubscribeEnd(
@@ -813,7 +808,7 @@
 
   switch (result) {
     case InstanceID::SUCCESS: {
-      const GURL endpoint = CreateEndpoint(subscription_id);
+      const GURL endpoint = push_messaging::CreateEndpoint(subscription_id);
 
       // Make sure that this subscription has associated encryption keys prior
       // to returning it to the developer - they'll need this information in
@@ -888,7 +883,7 @@
     return;
   }
 
-  const GURL endpoint = CreateEndpoint(subscription_id);
+  const GURL endpoint = push_messaging::CreateEndpoint(subscription_id);
   const std::string& app_id = app_identifier.app_id();
   base::Optional<base::Time> expiration_time = app_identifier.expiration_time();
 
@@ -899,12 +894,12 @@
 
   if (PushMessagingAppIdentifier::UseInstanceID(app_id)) {
     GetInstanceIDDriver()->GetInstanceID(app_id)->ValidateToken(
-        NormalizeSenderInfo(sender_id), kGCMScope, subscription_id,
-        std::move(validate_cb));
+        push_messaging::NormalizeSenderInfo(sender_id), kGCMScope,
+        subscription_id, std::move(validate_cb));
   } else {
     GetGCMDriver()->ValidateRegistration(
-        app_id, {NormalizeSenderInfo(sender_id)}, subscription_id,
-        std::move(validate_cb));
+        app_id, {push_messaging::NormalizeSenderInfo(sender_id)},
+        subscription_id, std::move(validate_cb));
   }
 }
 
@@ -1041,9 +1036,9 @@
     if (sender_id.empty()) {
       std::move(unregister_callback).Run(gcm::GCMClient::INVALID_PARAMETER);
     } else {
-      GetGCMDriver()->UnregisterWithSenderId(app_id,
-                                             NormalizeSenderInfo(sender_id),
-                                             std::move(unregister_callback));
+      GetGCMDriver()->UnregisterWithSenderId(
+          app_id, push_messaging::NormalizeSenderInfo(sender_id),
+          std::move(unregister_callback));
     }
 #else
     GetGCMDriver()->Unregister(app_id, std::move(unregister_callback));
@@ -1263,15 +1258,10 @@
     std::move(callback).Run(nullptr /* subscription */);
     return;
   }
-  // Currently |user_visible_only| is always true, once silent pushes are
-  // enabled, get this information from SW database.
-  auto options = blink::mojom::PushSubscriptionOptions::New();
-  options->user_visible_only = true;
-  options->application_server_key =
-      std::vector<uint8_t>(sender_id.begin(), sender_id.end());
 
   std::move(callback).Run(blink::mojom::PushSubscription::New(
-      endpoint, expiration_time, std::move(options), p256dh, auth));
+      endpoint, expiration_time, push_messaging::MakeOptions(sender_id), p256dh,
+      auth));
 }
 
 void PushMessagingServiceImpl::FirePushSubscriptionChange(
@@ -1353,19 +1343,6 @@
   remove_expired_subscriptions_callback_for_testing_ = std::move(closure);
 }
 
-std::string PushMessagingServiceImpl::NormalizeSenderInfo(
-    const std::string& application_server_key) const {
-  if (!IsVapidKey(application_server_key))
-    return application_server_key;
-
-  std::string encoded_application_server_key;
-  base::Base64UrlEncode(application_server_key,
-                        base::Base64UrlEncodePolicy::OMIT_PADDING,
-                        &encoded_application_server_key);
-
-  return encoded_application_server_key;
-}
-
 // Assumes user_visible always since this is just meant to check
 // if the permission was previously granted and not revoked.
 bool PushMessagingServiceImpl::IsPermissionSet(const GURL& origin) {
@@ -1379,19 +1356,12 @@
     gcm::GCMEncryptionProvider::EncryptionInfoCallback callback) {
   if (PushMessagingAppIdentifier::UseInstanceID(app_id)) {
     GetInstanceIDDriver()->GetInstanceID(app_id)->GetEncryptionInfo(
-        NormalizeSenderInfo(sender_id), std::move(callback));
+        push_messaging::NormalizeSenderInfo(sender_id), std::move(callback));
   } else {
     GetGCMDriver()->GetEncryptionInfo(app_id, std::move(callback));
   }
 }
 
-GURL PushMessagingServiceImpl::CreateEndpoint(
-    const std::string& subscription_id) const {
-  const GURL endpoint(kPushMessagingGcmEndpoint + subscription_id);
-  DCHECK(endpoint.is_valid());
-  return endpoint;
-}
-
 gcm::GCMDriver* PushMessagingServiceImpl::GetGCMDriver() const {
   gcm::GCMProfileService* gcm_profile_service =
       gcm::GCMProfileServiceFactory::GetForProfile(profile_);
diff --git a/chrome/browser/push_messaging/push_messaging_service_impl.h b/chrome/browser/push_messaging/push_messaging_service_impl.h
index 6ae77b77..f25de1b 100644
--- a/chrome/browser/push_messaging/push_messaging_service_impl.h
+++ b/chrome/browser/push_messaging/push_messaging_service_impl.h
@@ -299,11 +299,6 @@
       const PushMessagingAppIdentifier& app_identifier,
       blink::mojom::PushEventStatus status);
 
-  // Normalizes the |sender_info|. In most cases the |sender_info| will be
-  // passed through to the GCM Driver as-is, but NIST P-256 application server
-  // keys have to be encoded using the URL-safe variant of the base64 encoding.
-  std::string NormalizeSenderInfo(const std::string& sender_info) const;
-
   // Checks if a given origin is allowed to use Push.
   bool IsPermissionSet(const GURL& origin);
 
@@ -313,10 +308,6 @@
       const std::string& sender_id,
       gcm::GCMEncryptionProvider::EncryptionInfoCallback callback);
 
-  // Returns the URL used to send push messages to the subscription identified
-  // by |subscription_id|.
-  GURL CreateEndpoint(const std::string& subscription_id) const;
-
   gcm::GCMDriver* GetGCMDriver() const;
 
   instance_id::InstanceIDDriver* GetInstanceIDDriver() const;
diff --git a/chrome/browser/push_messaging/push_messaging_service_unittest.cc b/chrome/browser/push_messaging/push_messaging_service_unittest.cc
index f0c6c3d..11f1487 100644
--- a/chrome/browser/push_messaging/push_messaging_service_unittest.cc
+++ b/chrome/browser/push_messaging/push_messaging_service_unittest.cc
@@ -24,6 +24,7 @@
 #include "chrome/browser/push_messaging/push_messaging_features.h"
 #include "chrome/browser/push_messaging/push_messaging_service_factory.h"
 #include "chrome/browser/push_messaging/push_messaging_service_impl.h"
+#include "chrome/browser/push_messaging/push_messaging_utils.h"
 #include "chrome/test/base/testing_profile.h"
 #include "components/content_settings/core/browser/host_content_settings_map.h"
 #include "components/gcm_driver/crypto/gcm_crypto_test_helpers.h"
@@ -301,15 +302,15 @@
 
   // NIST P-256 public keys in uncompressed format will be encoded using the
   // URL-safe base64 encoding by the normalization function.
-  EXPECT_EQ(kTestEncodedP256Key, push_service->NormalizeSenderInfo(p256dh));
+  EXPECT_EQ(kTestEncodedP256Key, push_messaging::NormalizeSenderInfo(p256dh));
 
   // Any other value, binary or not, will be passed through as-is.
-  EXPECT_EQ("1234567890", push_service->NormalizeSenderInfo("1234567890"));
-  EXPECT_EQ("foo@bar.com", push_service->NormalizeSenderInfo("foo@bar.com"));
+  EXPECT_EQ("1234567890", push_messaging::NormalizeSenderInfo("1234567890"));
+  EXPECT_EQ("foo@bar.com", push_messaging::NormalizeSenderInfo("foo@bar.com"));
 
   p256dh[0] = 0x05;  // invalidate |p256dh| as a public key.
 
-  EXPECT_EQ(p256dh, push_service->NormalizeSenderInfo(p256dh));
+  EXPECT_EQ(p256dh, push_messaging::NormalizeSenderInfo(p256dh));
 }
 
 TEST_F(PushMessagingServiceTest, RemoveExpiredSubscriptions) {
diff --git a/chrome/browser/push_messaging/push_messaging_utils.cc b/chrome/browser/push_messaging/push_messaging_utils.cc
new file mode 100644
index 0000000..b54bf14
--- /dev/null
+++ b/chrome/browser/push_messaging/push_messaging_utils.cc
@@ -0,0 +1,44 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/push_messaging/push_messaging_utils.h"
+#include "base/base64url.h"
+#include "chrome/browser/push_messaging/push_messaging_constants.h"
+#include "url/gurl.h"
+
+namespace push_messaging {
+
+GURL CreateEndpoint(const std::string& subscription_id) {
+  const GURL endpoint(kPushMessagingGcmEndpoint + subscription_id);
+  DCHECK(endpoint.is_valid());
+  return endpoint;
+}
+
+blink::mojom::PushSubscriptionOptionsPtr MakeOptions(
+    const std::string& sender_id) {
+  return blink::mojom::PushSubscriptionOptions::New(
+      /*user_visible_only=*/true,
+      std::vector<uint8_t>(sender_id.begin(), sender_id.end()));
+}
+
+bool IsVapidKey(const std::string& application_server_key) {
+  // VAPID keys are NIST P-256 public keys in uncompressed format (64 bytes),
+  // verified through its length and the 0x04 prefix.
+  return application_server_key.size() == 65 &&
+         application_server_key[0] == 0x04;
+}
+
+std::string NormalizeSenderInfo(const std::string& application_server_key) {
+  if (!IsVapidKey(application_server_key))
+    return application_server_key;
+
+  std::string encoded_application_server_key;
+  base::Base64UrlEncode(application_server_key,
+                        base::Base64UrlEncodePolicy::OMIT_PADDING,
+                        &encoded_application_server_key);
+
+  return encoded_application_server_key;
+}
+
+}  // namespace push_messaging
diff --git a/chrome/browser/push_messaging/push_messaging_utils.h b/chrome/browser/push_messaging/push_messaging_utils.h
new file mode 100644
index 0000000..805deeab
--- /dev/null
+++ b/chrome/browser/push_messaging/push_messaging_utils.h
@@ -0,0 +1,34 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_PUSH_MESSAGING_PUSH_MESSAGING_UTILS_H_
+#define CHROME_BROWSER_PUSH_MESSAGING_PUSH_MESSAGING_UTILS_H_
+
+#include <string>
+#include "third_party/blink/public/mojom/push_messaging/push_messaging.mojom.h"
+
+class GURL;
+
+namespace push_messaging {
+
+// Returns the URL used to send push messages to the subscription identified
+// by |subscription_id|.
+GURL CreateEndpoint(const std::string& subscription_id);
+
+// Checks size and prefix to determine whether it is a VAPID key
+bool IsVapidKey(const std::string& application_server_key);
+
+// Normalizes the |sender_info|. In most cases the |sender_info| will be
+// passed through to the GCM Driver as-is, but NIST P-256 application server
+// keys have to be encoded using the URL-safe variant of the base64 encoding.
+std::string NormalizeSenderInfo(const std::string& sender_info);
+
+// Currently |user_visible_only| is always true, once silent pushes are
+// enabled, get this information from SW database.
+blink::mojom::PushSubscriptionOptionsPtr MakeOptions(
+    const std::string& sender_id);
+
+}  // namespace push_messaging
+
+#endif  // CHROME_BROWSER_PUSH_MESSAGING_PUSH_MESSAGING_UTILS_H_
diff --git a/chrome/browser/referrer_policy_browsertest.cc b/chrome/browser/referrer_policy_browsertest.cc
index 4bcd8e0..f364e47 100644
--- a/chrome/browser/referrer_policy_browsertest.cc
+++ b/chrome/browser/referrer_policy_browsertest.cc
@@ -41,6 +41,7 @@
 #include "third_party/blink/public/common/features.h"
 #include "third_party/blink/public/common/input/web_input_event.h"
 #include "third_party/blink/public/common/input/web_mouse_event.h"
+#include "third_party/blink/public/common/loader/referrer_utils.h"
 #include "ui/base/page_transition_types.h"
 #include "ui/base/window_open_disposition.h"
 
@@ -736,7 +737,7 @@
 // These tests assume a default policy of no-referrer-when-downgrade.
 struct ReferrerOverrideParams {
   base::Optional<base::Feature> feature_to_enable;
-  // If true, calls content::Referrer::SetForceLegacyDefaultReferrerPolicy()
+  // If true, calls blink::ReferrerUtils::SetForceLegacyDefaultReferrerPolicy()
   // to pin the default policy to no-referrer-when-downgrade.
   bool force_no_referrer_when_downgrade_default;
   network::mojom::ReferrerPolicy baseline_policy;
@@ -832,7 +833,7 @@
   ReferrerOverrideTest() {
     if (GetParam().feature_to_enable)
       scoped_feature_list_.InitAndEnableFeature(*GetParam().feature_to_enable);
-    content::Referrer::SetForceLegacyDefaultReferrerPolicy(
+    blink::ReferrerUtils::SetForceLegacyDefaultReferrerPolicy(
         GetParam().force_no_referrer_when_downgrade_default);
   }
 
diff --git a/chrome/browser/renderer_context_menu/mock_render_view_context_menu.cc b/chrome/browser/renderer_context_menu/mock_render_view_context_menu.cc
index 7b4531d..c69a9e7e 100644
--- a/chrome/browser/renderer_context_menu/mock_render_view_context_menu.cc
+++ b/chrome/browser/renderer_context_menu/mock_render_view_context_menu.cc
@@ -63,25 +63,17 @@
 void MockRenderViewContextMenu::AddMenuItemWithIcon(
     int command_id,
     const base::string16& title,
-    const gfx::ImageSkia& image) {
+    const ui::ImageModel& icon) {
   MockMenuItem item;
   item.command_id = command_id;
   item.enabled = observer_->IsCommandIdEnabled(command_id);
   item.checked = false;
   item.hidden = false;
   item.title = title;
-  item.icon = gfx::Image(image);
+  item.icon = icon;
   items_.push_back(item);
 }
 
-void MockRenderViewContextMenu::AddMenuItemWithIcon(
-    int command_id,
-    const base::string16& title,
-    const gfx::VectorIcon& icon) {
-  AddMenuItemWithIcon(command_id, title,
-                      gfx::CreateVectorIcon(icon, gfx::kPlaceholderColor));
-}
-
 void MockRenderViewContextMenu::AddCheckItem(int command_id,
                                              const base::string16& title) {
   MockMenuItem item;
@@ -120,29 +112,19 @@
     int command_id,
     int message_id,
     ui::MenuModel* model,
-    const gfx::ImageSkia& image) {
+    const ui::ImageModel& icon) {
   MockMenuItem item;
   item.command_id = command_id;
   item.enabled = observer_->IsCommandIdEnabled(command_id);
   item.checked = observer_->IsCommandIdChecked(command_id);
   item.hidden = false;
   item.title = l10n_util::GetStringUTF16(message_id);
-  item.icon = gfx::Image(image);
+  item.icon = icon;
   items_.push_back(item);
 
   AppendSubMenuItems(model);
 }
 
-void MockRenderViewContextMenu::AddSubMenuWithStringIdAndIcon(
-    int command_id,
-    int message_id,
-    ui::MenuModel* model,
-    const gfx::VectorIcon& icon) {
-  AddSubMenuWithStringIdAndIcon(
-      command_id, message_id, model,
-      gfx::CreateVectorIcon(icon, gfx::kPlaceholderColor));
-}
-
 void MockRenderViewContextMenu::AppendSubMenuItems(ui::MenuModel* model) {
   // Add items in the submenu |model| to |items_| so that the items can be
   // updated later via the RenderViewContextMenuProxy interface.
@@ -180,10 +162,10 @@
 }
 
 void MockRenderViewContextMenu::UpdateMenuIcon(int command_id,
-                                               const gfx::Image& image) {
+                                               const ui::ImageModel& icon) {
   for (auto& item : items_) {
     if (item.command_id == command_id) {
-      item.icon = image;
+      item.icon = icon;
       return;
     }
   }
diff --git a/chrome/browser/renderer_context_menu/mock_render_view_context_menu.h b/chrome/browser/renderer_context_menu/mock_render_view_context_menu.h
index 43b6bf1..3cb10dd 100644
--- a/chrome/browser/renderer_context_menu/mock_render_view_context_menu.h
+++ b/chrome/browser/renderer_context_menu/mock_render_view_context_menu.h
@@ -12,8 +12,8 @@
 #include "base/macros.h"
 #include "base/strings/string16.h"
 #include "components/renderer_context_menu/render_view_context_menu_proxy.h"
+#include "ui/base/models/image_model.h"
 #include "ui/base/models/simple_menu_model.h"
-#include "ui/gfx/image/image.h"
 
 class PrefService;
 class Profile;
@@ -39,7 +39,7 @@
     bool checked;
     bool hidden;
     base::string16 title;
-    gfx::Image icon;
+    ui::ImageModel icon;
   };
 
   explicit MockRenderViewContextMenu(bool incognito);
@@ -54,10 +54,7 @@
   void AddMenuItem(int command_id, const base::string16& title) override;
   void AddMenuItemWithIcon(int command_id,
                            const base::string16& title,
-                           const gfx::ImageSkia& image) override;
-  void AddMenuItemWithIcon(int command_id,
-                           const base::string16& title,
-                           const gfx::VectorIcon& icon) override;
+                           const ui::ImageModel& icon) override;
   void AddCheckItem(int command_id, const base::string16& title) override;
   void AddSeparator() override;
   void AddSubMenu(int command_id,
@@ -66,16 +63,12 @@
   void AddSubMenuWithStringIdAndIcon(int command_id,
                                      int message_id,
                                      ui::MenuModel* model,
-                                     const gfx::ImageSkia& image) override;
-  void AddSubMenuWithStringIdAndIcon(int command_id,
-                                     int message_id,
-                                     ui::MenuModel* model,
-                                     const gfx::VectorIcon& icon) override;
+                                     const ui::ImageModel& icon) override;
   void UpdateMenuItem(int command_id,
                       bool enabled,
                       bool hidden,
                       const base::string16& title) override;
-  void UpdateMenuIcon(int command_id, const gfx::Image& image) override;
+  void UpdateMenuIcon(int command_id, const ui::ImageModel& icon) override;
   void RemoveMenuItem(int command_id) override;
   void RemoveAdjacentSeparators() override;
   void AddSpellCheckServiceItem(bool is_checked) override;
diff --git a/chrome/browser/renderer_context_menu/quick_answers_menu_observer.cc b/chrome/browser/renderer_context_menu/quick_answers_menu_observer.cc
index 049270b..03c6b11 100644
--- a/chrome/browser/renderer_context_menu/quick_answers_menu_observer.cc
+++ b/chrome/browser/renderer_context_menu/quick_answers_menu_observer.cc
@@ -28,6 +28,8 @@
 #include "content/public/browser/storage_partition.h"
 #include "content/public/browser/web_contents.h"
 #include "ui/base/l10n/l10n_util.h"
+#include "ui/base/models/image_model.h"
+#include "ui/base/models/simple_menu_model.h"
 #include "ui/gfx/text_constants.h"
 #include "ui/gfx/text_elider.h"
 
@@ -113,8 +115,10 @@
   // TODO(llin): Update the menu item after finalizing on the design.
   auto truncated_text = TruncateString(selected_text);
 #if BUILDFLAG(GOOGLE_CHROME_BRANDING)
-  proxy_->AddMenuItemWithIcon(IDC_CONTENT_CONTEXT_QUICK_ANSWERS_INLINE_QUERY,
-                              truncated_text, kAssistantIcon);
+  proxy_->AddMenuItemWithIcon(
+      IDC_CONTENT_CONTEXT_QUICK_ANSWERS_INLINE_QUERY, truncated_text,
+      ui::ImageModel::FromVectorIcon(kAssistantIcon, /*color_id=*/-1,
+                                     ui::SimpleMenuModel::kDefaultIconSize));
 #else
   proxy_->AddMenuItem(IDC_CONTENT_CONTEXT_QUICK_ANSWERS_INLINE_QUERY,
                       truncated_text);
diff --git a/chrome/browser/resources/chromeos/multidevice_internals/log_object.html b/chrome/browser/resources/chromeos/multidevice_internals/log_object.html
index 92bef1d..c69d641 100644
--- a/chrome/browser/resources/chromeos/multidevice_internals/log_object.html
+++ b/chrome/browser/resources/chromeos/multidevice_internals/log_object.html
@@ -39,6 +39,6 @@
   <div id="item-metadata">
     <span>[[item.time]]</span>
     <div id="flex"></div>
-    <span>[[item.file]]:[[item.line]]</span>
+    <span>[[getFilenameWithLine_(item.file, item.line)]]</span>
   </div>
 </div>
diff --git a/chrome/browser/resources/chromeos/multidevice_internals/log_object.js b/chrome/browser/resources/chromeos/multidevice_internals/log_object.js
index e2f3ef32..cca7169 100644
--- a/chrome/browser/resources/chromeos/multidevice_internals/log_object.js
+++ b/chrome/browser/resources/chromeos/multidevice_internals/log_object.js
@@ -42,4 +42,18 @@
         break;
     }
   },
+
+  /**
+   * @return {string}
+   * @private
+   */
+  getFilenameWithLine_() {
+    if (!this.item) {
+      return '';
+    }
+
+    // The filename is prefixed with "../../", so replace it with "//".
+    let filename = this.item.file.replace('../../', '//');
+    return filename + ':' + this.item.line;
+  },
 });
diff --git a/chrome/browser/resources/chromeos/multidevice_internals/logging_tab.js b/chrome/browser/resources/chromeos/multidevice_internals/logging_tab.js
index 41eb864..5e3ef60e 100644
--- a/chrome/browser/resources/chromeos/multidevice_internals/logging_tab.js
+++ b/chrome/browser/resources/chromeos/multidevice_internals/logging_tab.js
@@ -39,7 +39,7 @@
   // Reduce the file path to just the file name for logging simplification.
   const file = log.file.substring(log.file.lastIndexOf('/') + 1);
 
-  return `[${log.time} ${severity} ${file} (${log.line})] ${log.text}`;
+  return `[${log.time} ${severity} ${file} (${log.line})] ${log.text}\n`;
 }
 
 Polymer({
@@ -124,7 +124,9 @@
    * @return {!Array<string>}
    */
   getSerializedLogStrings_() {
-    return this.logList_.map(logToSavedString_);
+    // Reverse the logs so that the oldest logs appear first and the newest logs
+    // appear last.
+    return this.logList_.map(logToSavedString_).reverse();
   },
 
   /**
diff --git a/chrome/browser/resources/settings/chromeos/multidevice_page/BUILD.gn b/chrome/browser/resources/settings/chromeos/multidevice_page/BUILD.gn
index 0453afe..b16515c9 100644
--- a/chrome/browser/resources/settings/chromeos/multidevice_page/BUILD.gn
+++ b/chrome/browser/resources/settings/chromeos/multidevice_page/BUILD.gn
@@ -12,6 +12,7 @@
     ":multidevice_feature_behavior",
     ":multidevice_feature_item",
     ":multidevice_feature_toggle",
+    ":multidevice_notification_access_setup_dialog",
     ":multidevice_page",
     ":multidevice_smartlock_subpage",
     ":multidevice_subpage",
@@ -62,6 +63,7 @@
     ":multidevice_browser_proxy",
     ":multidevice_constants",
     ":multidevice_feature_behavior",
+    ":multidevice_notification_access_setup_dialog",
     "..:metrics_recorder",
     "..:os_route",
     "../..:router",
@@ -74,6 +76,13 @@
   externs_list = [ "$externs_path/pending_polymer.js" ]
 }
 
+js_library("multidevice_notification_access_setup_dialog") {
+  deps = [
+    "//ui/webui/resources/js:i18n_behavior",
+    "//ui/webui/resources/js:web_ui_listener_behavior",
+  ]
+}
+
 js_library("multidevice_radio_button") {
   deps = [
     "//third_party/polymer/v1_0/components-chromium/iron-a11y-keys-behavior:iron-a11y-keys-behavior-extracted",
@@ -91,8 +100,8 @@
     "..:os_settings_routes",
     "../../prefs:prefs_behavior",
     "//ui/webui/resources/cr_elements/cr_radio_button:cr_radio_button",
-    "//ui/webui/resources/js:web_ui_listener_behavior",
     "//ui/webui/resources/js:cr",
+    "//ui/webui/resources/js:web_ui_listener_behavior",
   ]
 }
 
@@ -130,6 +139,7 @@
     ":multidevice_feature_behavior.m",
     ":multidevice_feature_item.m",
     ":multidevice_feature_toggle.m",
+    ":multidevice_notification_access_setup_dialog.m",
     ":multidevice_page.m",
     ":multidevice_radio_button.m",
     ":multidevice_smartlock_subpage.m",
@@ -140,7 +150,7 @@
 
 js_library("multidevice_browser_proxy.m") {
   sources = [ "$root_gen_dir/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_browser_proxy.m.js" ]
-  deps = [ 
+  deps = [
     ":multidevice_constants.m",
     "//ui/webui/resources/js:cr.m",
   ]
@@ -157,8 +167,8 @@
   sources = [ "$root_gen_dir/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_feature_behavior.m.js" ]
   deps = [
     ":multidevice_constants.m",
-    "//ui/webui/resources/js:i18n_behavior.m",
     "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
+    "//ui/webui/resources/js:i18n_behavior.m",
   ]
   extra_deps = [ ":modulize" ]
 }
@@ -193,19 +203,29 @@
     ":multidevice_browser_proxy.m",
     ":multidevice_constants.m",
     ":multidevice_feature_behavior.m",
+    ":multidevice_notification_access_setup_dialog.m",
     "..:metrics_recorder.m",
     "..:os_route.m",
     "../..:router.m",
     "../../controls:password_prompt_dialog.m",
     "../../prefs:prefs_behavior.m",
     "../localized_link:localized_link.m",
-    "//ui/webui/resources/js:web_ui_listener_behavior.m",
-    "//ui/webui/resources/js:assert.m",
     "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
+    "//ui/webui/resources/js:assert.m",
+    "//ui/webui/resources/js:web_ui_listener_behavior.m",
   ]
   extra_deps = [ ":multidevice_page_module" ]
 }
 
+js_library("multidevice_notification_access_setup_dialog.m") {
+  sources = [ "$root_gen_dir/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_notification_access_setup_dialog.m.js" ]
+  deps = [
+    "//ui/webui/resources/js:i18n_behavior.m",
+    "//ui/webui/resources/js:web_ui_listener_behavior.m",
+  ]
+  extra_deps = [ ":multidevice_notification_access_setup_dialog_module" ]
+}
+
 js_library("multidevice_radio_button.m") {
   sources = [ "$root_gen_dir/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_radio_button.m.js" ]
   deps = [
@@ -225,9 +245,9 @@
     "..:os_route.m",
     "..:os_settings_routes.m",
     "../../prefs:prefs_behavior.m",
+    "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
     "//ui/webui/resources/cr_elements/cr_radio_button:cr_radio_button.m",
     "//ui/webui/resources/js:web_ui_listener_behavior.m",
-    "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
   ]
   extra_deps = [ ":multidevice_smartlock_subpage_module" ]
   externs_list = [ "$externs_path/quick_unlock_private.js" ]
@@ -240,12 +260,10 @@
     ":multidevice_constants.m",
     "..:os_route.m",
     "..:os_settings_routes.m",
-    "//ui/webui/resources/cr_components/chromeos/network:network_listener_behavior.m",
     "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
+    "//ui/webui/resources/cr_components/chromeos/network:network_listener_behavior.m",
   ]
-  extra_deps = [
-    ":multidevice_subpage_module"
-  ]
+  extra_deps = [ ":multidevice_subpage_module" ]
 }
 
 js_library("multidevice_tether_item.m") {
@@ -254,10 +272,10 @@
     ":multidevice_feature_behavior.m",
     "..:os_route.m",
     "..:os_settings_routes.m",
+    "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
     "//ui/webui/resources/cr_components/chromeos/network:mojo_interface_provider.m",
     "//ui/webui/resources/cr_components/chromeos/network:network_listener_behavior.m",
     "//ui/webui/resources/cr_components/chromeos/network:onc_mojo.m",
-    "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
   ]
   extra_deps = [ ":multidevice_tether_item_module" ]
 }
@@ -267,6 +285,7 @@
     ":modulize",
     ":multidevice_feature_item_module",
     ":multidevice_feature_toggle_module",
+    ":multidevice_notification_access_setup_dialog_module",
     ":multidevice_page_module",
     ":multidevice_radio_button_module",
     ":multidevice_smartlock_subpage_module",
@@ -299,10 +318,20 @@
   html_type = "dom-module"
   migrated_imports = os_settings_migrated_imports
   namespace_rewrites = os_settings_namespace_rewrites
-  auto_imports = os_settings_auto_imports + [
-    "ui/webui/resources/html/polymer.html|Polymer,html,beforeNextRender",
-    "ui/webui/resources/html/assert.html|assert",
-  ]
+  auto_imports =
+      os_settings_auto_imports + [
+        "ui/webui/resources/html/polymer.html|Polymer,html,beforeNextRender",
+        "ui/webui/resources/html/assert.html|assert",
+      ]
+}
+
+polymer_modulizer("multidevice_notification_access_setup_dialog") {
+  js_file = "multidevice_notification_access_setup_dialog.js"
+  html_file = "multidevice_notification_access_setup_dialog.html"
+  html_type = "dom-module"
+  migrated_imports = os_settings_migrated_imports
+  namespace_rewrites = os_settings_namespace_rewrites
+  auto_imports = os_settings_auto_imports
 }
 
 polymer_modulizer("multidevice_radio_button") {
diff --git a/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_browser_proxy.js b/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_browser_proxy.js
index 8fdf6c39..e14da02 100644
--- a/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_browser_proxy.js
+++ b/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_browser_proxy.js
@@ -74,6 +74,16 @@
      * @return {!Promise<!settings.AndroidSmsInfo>} Android SMS Info
      */
     getAndroidSmsInfo() {}
+
+    /**
+     * Attempts the phone hub notification access setup flow.
+     */
+    attemptNotificationSetup() {}
+
+    /**
+     * Cancels the phone hub notification access setup flow.
+     */
+    cancelNotificationSetup() {}
   }
 
   /**
@@ -130,6 +140,16 @@
     getAndroidSmsInfo() {
       return cr.sendWithPromise('getAndroidSmsInfo');
     }
+
+    /** @override */
+    attemptNotificationSetup() {
+      chrome.send('attemptNotificationSetup');
+    }
+
+    /** @override */
+    cancelNotificationSetup() {
+      chrome.send('cancelNotificationSetup');
+    }
   }
 
   cr.addSingletonGetter(MultiDeviceBrowserProxyImpl);
diff --git a/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_constants.js b/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_constants.js
index 7d396700a..be53a8f1c 100644
--- a/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_constants.js
+++ b/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_constants.js
@@ -89,7 +89,8 @@
    *   phoneHubNotificationsState: !settings.MultiDeviceFeatureState,
    *   phoneHubNotificationBadgeState: !settings.MultiDeviceFeatureState,
    *   phoneHubTaskContinuationState: !settings.MultiDeviceFeatureState,
-   *   isAndroidSmsPairingComplete: boolean
+   *   isAndroidSmsPairingComplete: boolean,
+   *   isNotificationAccessGranted: boolean
    * }}
    */
   /* #export */ let MultiDevicePageContentData;
diff --git a/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_notification_access_setup_dialog.html b/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_notification_access_setup_dialog.html
new file mode 100644
index 0000000..a65dd3d
--- /dev/null
+++ b/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_notification_access_setup_dialog.html
@@ -0,0 +1,48 @@
+<link rel="import" href="chrome://resources/html/polymer.html">
+
+<link rel="import" href="chrome://resources/html/assert.html">
+<link rel="import" href="chrome://resources/html/i18n_behavior.html">
+<link rel="import" href="chrome://resources/html/web_ui_listener_behavior.html">
+<link rel="import" href="chrome://resources/cr_elements/cr_button/cr_button.html">
+<link rel="import" href="chrome://resources/cr_elements/cr_dialog/cr_dialog.html">
+<link rel="import" href="chrome://resources/cr_elements/shared_style_css.html">
+<link rel="import" href="multidevice_browser_proxy.html">
+<link rel="import" href="../../settings_shared_css.html">
+
+<dom-module id="settings-multidevice-notification-access-setup-dialog">
+  <template>
+    <style include="cr-shared-style settings-shared">
+      cr-dialog::part(dialog) {
+        width: 320px;
+      }
+
+      div[slot='body'] {
+        height: 100px;
+      }
+    </style>
+    <cr-dialog id="dialog" close-text="$i18n{close}">
+      <div id="dialogTitle" slot="title">[[title_]]</div>
+      <div id="dialogBody" slot="body">[[description_]]</div>
+      <div id="buttonContainer" slot="button-container">
+        <template is="dom-if" if="[[showCancelButton_(setupState_)]]" restamp>
+          <cr-button id="cancelButton" class="cancel-button"
+              on-click="onCancelClicked_">
+            $i18n{cancel}
+          </cr-button>
+        </template>
+        <template is="dom-if" if="[[showOkButton_(setupState_)]]" restamp>
+          <cr-button id="okButton" on-click="onOkayButtonClicked_">
+            $i18n{ok}
+          </cr-button>
+        </template>
+        <template is="dom-if" if="[[!setupState_]]" restamp>
+          <cr-button id="confirmButton" class="action-button"
+              on-click="onConfirmButtonClicked_">
+            $i18n{confirm}
+          </cr-button>
+        </template>
+      </div>
+    </cr-dialog>
+  </template>
+  <script src="multidevice_notification_access_setup_dialog.js"></script>
+</dom-module>
diff --git a/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_notification_access_setup_dialog.js b/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_notification_access_setup_dialog.js
new file mode 100644
index 0000000..c2d0b8d
--- /dev/null
+++ b/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_notification_access_setup_dialog.js
@@ -0,0 +1,151 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview
+ * This element provides the Phone Hub notification access setup flow that, when
+ * successfully completed, enables the feature that allows a user's phone
+ * notifications to be mirrored on their Chromebook.
+ */
+
+/**
+ * Numerical values should not be changed because they must stay in sync with
+ * notification_access_setup_operation.h, with the exception of NOT_STARTED.
+ * @enum{number}
+ */
+/* #export */ const NotificationAccessSetupOperationStatus = {
+  NOT_STARTED: 0,
+  CONNECTING: 1,
+  TIMED_OUT_CONNECTING: 2,
+  CONNECTION_DISCONNECTED: 3,
+  SENT_MESSAGE_TO_PHONE_AND_WAITING_FOR_RESPONSE: 4,
+  COMPLETED_SUCCESSFULLY: 5,
+};
+
+Polymer({
+  is: 'settings-multidevice-notification-access-setup-dialog',
+
+  behaviors: [
+    I18nBehavior,
+    WebUIListenerBehavior,
+  ],
+
+  properties: {
+    /** @private {NotificationAccessSetupOperationStatus} */
+    setupState_: {
+      type: Number,
+      value: NotificationAccessSetupOperationStatus.NOT_STARTED,
+    },
+
+    /** @private */
+    title_: {
+      type: String,
+      computed: 'getTitle_(setupState_)',
+    },
+
+    /** @private */
+    description_: {
+      type: String,
+      computed: 'getDescription_(setupState_)',
+    }
+  },
+
+  /** @private {?settings.MultiDeviceBrowserProxy} */
+  browserProxy_: null,
+
+  /** @override */
+  ready() {
+    this.browserProxy_ = settings.MultiDeviceBrowserProxyImpl.getInstance();
+  },
+
+  /** @override */
+  attached() {
+    this.addWebUIListener(
+        'settings.onNotificationAccessSetupStatusChanged',
+        this.onSetupStateChanged_.bind(this));
+    this.$.dialog.showModal();
+  },
+
+  /**
+   * @param {!NotificationAccessSetupOperationStatus} setupState
+   * @private
+   */
+  onSetupStateChanged_(setupState) {
+    this.setupState_ = setupState;
+  },
+
+  /** @private */
+  showCancelButton_() {
+    return this.setupState_ ===
+        NotificationAccessSetupOperationStatus.NOT_STARTED ||
+        this.setupState_ ===
+        NotificationAccessSetupOperationStatus.CONNECTING ||
+        this.setupState_ ===
+        NotificationAccessSetupOperationStatus
+            .SENT_MESSAGE_TO_PHONE_AND_WAITING_FOR_RESPONSE;
+  },
+
+  /** @private */
+  showOkButton_() {
+    return this.setupState_ ===
+        NotificationAccessSetupOperationStatus.COMPLETED_SUCCESSFULLY;
+  },
+
+  /** @private */
+  onConfirmButtonClicked_() {
+    this.browserProxy_.attemptNotificationSetup();
+  },
+
+  /** @private */
+  onCancelClicked_() {
+    this.browserProxy_.cancelNotificationSetup();
+    this.$.dialog.close();
+  },
+
+  /** @private */
+  onOkayButtonClicked_() {
+    this.$.dialog.close();
+  },
+
+  /**
+   * @return {string} The title of the dialog.
+   * @private
+   */
+  getTitle_() {
+    const Status = NotificationAccessSetupOperationStatus;
+    switch (this.setupState_) {
+      case Status.NOT_STARTED:
+        return this.i18n('multideviceNotificationAccessSetupAckTitle');
+      case Status.CONNECTING:
+      case Status.SENT_MESSAGE_TO_PHONE_AND_WAITING_FOR_RESPONSE:
+        return this.i18n('multideviceNotificationAccessSetupConnectingTitle');
+      case Status.COMPLETED_SUCCESSFULLY:
+        return this.i18n('multideviceNotificationAccessSetupCompletedTitle');
+      case Status.TIMED_OUT_CONNECTING:
+      case Status.CONNECTION_DISCONNECTED:
+      default:
+        return '';
+    }
+  },
+
+  /**
+   * @return {string} The body text of the dialog.
+   * @private
+   */
+  getDescription_() {
+    const Status = NotificationAccessSetupOperationStatus;
+    switch (this.setupState_) {
+      case Status.NOT_STARTED:
+      case Status.CONNECTING:
+      case Status.SENT_MESSAGE_TO_PHONE_AND_WAITING_FOR_RESPONSE:
+        return this.i18n('multideviceNotificationAccessSetupInstructions');
+      case Status.COMPLETED_SUCCESSFULLY:
+        return this.i18n('multideviceNotificationAccessSetupCompletedSummary');
+      case Status.TIMED_OUT_CONNECTING:
+      case Status.CONNECTION_DISCONNECTED:
+      default:
+        return '';
+    }
+  },
+});
diff --git a/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_page.html b/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_page.html
index a3c918c..dbc0995d 100644
--- a/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_page.html
+++ b/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_page.html
@@ -23,6 +23,7 @@
 <link rel="import" href="multidevice_constants.html">
 <link rel="import" href="multidevice_feature_behavior.html">
 <link rel="import" href="multidevice_feature_toggle.html">
+<link rel="import" href="multidevice_notification_access_setup_dialog.html">
 <link rel="import" href="multidevice_smartlock_subpage.html">
 <link rel="import" href="multidevice_subpage.html">
 
@@ -165,6 +166,11 @@
           on-token-obtained="onTokenObtained_">
       </settings-password-prompt-dialog>
     </template>
+    <template is="dom-if" if="[[showNotificationAccessSetupDialog_]]" restamp>
+      <settings-multidevice-notification-access-setup-dialog
+          on-close="onHideNotificationSetupAccessDialog_">
+      </settings-multidevice-notification-access-setup-dialog>
+    </template>
   </template>
   <script src="multidevice_page.js"></script>
 </dom-module>
diff --git a/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_page.js b/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_page.js
index a45d3727..3b43aac7 100644
--- a/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_page.js
+++ b/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_page.js
@@ -62,6 +62,12 @@
       value: false,
     },
 
+    /** @private {boolean} */
+    showNotificationAccessSetupDialog_: {
+      type: Boolean,
+      value: false,
+    },
+
     /**
      * The value of the Nearby Share feature flag which controls if the
      * Nearby Share settings and subpage are accessible.
@@ -309,19 +315,27 @@
     const feature = event.detail.feature;
     const enabled = event.detail.enabled;
 
-    // Disabling any feature does not require authentication, and enable some
-    // features does not require authentication.
-    if (!enabled || !this.isAuthenticationRequiredToEnable_(feature)) {
-      this.browserProxy_.setFeatureEnabledState(feature, enabled);
-      settings.recordSettingChange();
-      return;
-    }
-
     // If the feature required authentication to be enabled, open the password
     // prompt dialog. This is required every time the user enables a security-
     // sensitive feature (i.e., use of stale auth tokens is not acceptable).
-    this.featureToBeEnabledOnceAuthenticated_ = feature;
-    this.openPasswordPromptDialog_();
+    if (enabled && this.isAuthenticationRequiredToEnable_(feature)) {
+      this.featureToBeEnabledOnceAuthenticated_ = feature;
+      this.openPasswordPromptDialog_();
+      return;
+    }
+
+    // If the feature to enable is Phone Hub Notifications, notification access
+    // must have been granted before the feature can be enabled.
+    if (feature === settings.MultiDeviceFeature.PHONE_HUB_NOTIFICATIONS &&
+        enabled && !this.pageContentData.isNotificationAccessGranted) {
+      this.showNotificationAccessSetupDialog_ = true;
+      return;
+    }
+
+    // Disabling any feature does not require authentication, and enable some
+    // features does not require authentication.
+    this.browserProxy_.setFeatureEnabledState(feature, enabled);
+    settings.recordSettingChange();
   },
 
   /**
@@ -436,4 +450,9 @@
       settings.Router.getInstance().navigateTo(settings.routes.NEARBY_SHARE);
     }
   },
+
+  /** @private */
+  onHideNotificationSetupAccessDialog_() {
+    this.showNotificationAccessSetupDialog_ = false;
+  },
 });
diff --git a/chrome/browser/resources/settings/chromeos/os_settings_resources_v3.grdp b/chrome/browser/resources/settings/chromeos/os_settings_resources_v3.grdp
index 21ac725..b972b63 100644
--- a/chrome/browser/resources/settings/chromeos/os_settings_resources_v3.grdp
+++ b/chrome/browser/resources/settings/chromeos/os_settings_resources_v3.grdp
@@ -503,6 +503,11 @@
            use_base_dir="false"
            compress="false"
            type="BINDATA" />
+  <include name="IDR_OS_SETTINGS_MULTIDEVICE_NOTIFICATIONS_ACCESS_SETUP_DIALOG_M_JS"
+           file="${root_gen_dir}/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_notification_access_setup_dialog.m.js"
+           use_base_dir="false"
+           compress="false"
+           type="BINDATA" />
   <include name="IDR_OS_SETTINGS_MULTIDEVICE_SUBPAGE_M_JS"
            file="${root_gen_dir}/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_subpage.m.js"
            use_base_dir="false"
diff --git a/chrome/browser/resources/settings/os_settings_resources.grd b/chrome/browser/resources/settings/os_settings_resources.grd
index 1c5b05f..9d581f57 100644
--- a/chrome/browser/resources/settings/os_settings_resources.grd
+++ b/chrome/browser/resources/settings/os_settings_resources.grd
@@ -1274,6 +1274,12 @@
       <structure name="IDR_OS_SETTINGS_MULTIDEVICE_PAGE_JS"
                  file="chromeos/multidevice_page/multidevice_page.js"
                  compress="false" type="chrome_html" />
+      <structure name="IDR_OS_SETTINGS_MULTIDEVICE_NOTIFICATIONS_ACCESS_SETUP_DIALOG_HTML"
+                 file="chromeos/multidevice_page/multidevice_notification_access_setup_dialog.html"
+                 compress="false" type="chrome_html" />
+      <structure name="IDR_OS_SETTINGS_MULTIDEVICE_NOTIFICATIONS_ACCESS_SETUP_DIALOG_JS"
+                 file="chromeos/multidevice_page/multidevice_notification_access_setup_dialog.js"
+                 compress="false" type="chrome_html" />
       <structure name="IDR_OS_SETTINGS_MULTIDEVICE_RADIO_BUTTON_HTML"
                  file="chromeos/multidevice_page/multidevice_radio_button.html"
                  compress="false" type="chrome_html" />
diff --git a/chrome/browser/sharing/click_to_call/click_to_call_context_menu_observer.cc b/chrome/browser/sharing/click_to_call/click_to_call_context_menu_observer.cc
index 425ead96..cbb4790 100644
--- a/chrome/browser/sharing/click_to_call/click_to_call_context_menu_observer.cc
+++ b/chrome/browser/sharing/click_to_call/click_to_call_context_menu_observer.cc
@@ -15,8 +15,8 @@
 #include "chrome/browser/sharing/sharing_constants.h"
 #include "chrome/grit/generated_resources.h"
 #include "components/sync_device_info/device_info.h"
-#include "components/vector_icons/vector_icons.h"
 #include "ui/base/l10n/l10n_util.h"
+#include "ui/base/models/image_model.h"
 #include "ui/gfx/color_palette.h"
 #include "ui/gfx/paint_vector_icon.h"
 
@@ -78,7 +78,9 @@
         l10n_util::GetStringFUTF16(
             IDS_CONTENT_CONTEXT_SHARING_CLICK_TO_CALL_SINGLE_DEVICE,
             base::UTF8ToUTF16(devices_[0]->client_name())),
-        vector_icons::kCallIcon);
+        ui::ImageModel::FromVectorIcon(controller_->GetVectorIcon(),
+                                       /*color_id=*/-1,
+                                       ui::SimpleMenuModel::kDefaultIconSize));
 #endif
   } else {
     BuildSubMenu();
@@ -92,7 +94,10 @@
     proxy_->AddSubMenuWithStringIdAndIcon(
         IDC_CONTENT_CONTEXT_SHARING_CLICK_TO_CALL_MULTIPLE_DEVICES,
         IDS_CONTENT_CONTEXT_SHARING_CLICK_TO_CALL_MULTIPLE_DEVICES,
-        sub_menu_model_.get(), vector_icons::kCallIcon);
+        sub_menu_model_.get(),
+        ui::ImageModel::FromVectorIcon(controller_->GetVectorIcon(),
+                                       /*color_id=*/-1,
+                                       ui::SimpleMenuModel::kDefaultIconSize));
 #endif
   }
 }
diff --git a/chrome/browser/sharing/shared_clipboard/shared_clipboard_context_menu_observer.cc b/chrome/browser/sharing/shared_clipboard/shared_clipboard_context_menu_observer.cc
index c1428c70..dc5fffe 100644
--- a/chrome/browser/sharing/shared_clipboard/shared_clipboard_context_menu_observer.cc
+++ b/chrome/browser/sharing/shared_clipboard/shared_clipboard_context_menu_observer.cc
@@ -15,6 +15,7 @@
 #include "chrome/grit/generated_resources.h"
 #include "components/sync_device_info/device_info.h"
 #include "ui/base/l10n/l10n_util.h"
+#include "ui/base/models/image_model.h"
 #include "ui/gfx/color_palette.h"
 #include "ui/gfx/paint_vector_icon.h"
 
@@ -73,7 +74,9 @@
         l10n_util::GetStringFUTF16(
             IDS_CONTENT_CONTEXT_SHARING_SHARED_CLIPBOARD_SINGLE_DEVICE,
             base::UTF8ToUTF16(devices_[0]->client_name())),
-        controller_->GetVectorIcon());
+        ui::ImageModel::FromVectorIcon(controller_->GetVectorIcon(),
+                                       /*color_id=*/-1,
+                                       ui::SimpleMenuModel::kDefaultIconSize));
 #endif
   } else {
     BuildSubMenu();
@@ -87,7 +90,10 @@
     proxy_->AddSubMenuWithStringIdAndIcon(
         IDC_CONTENT_CONTEXT_SHARING_SHARED_CLIPBOARD_MULTIPLE_DEVICES,
         IDS_CONTENT_CONTEXT_SHARING_SHARED_CLIPBOARD_MULTIPLE_DEVICES,
-        sub_menu_model_.get(), controller_->GetVectorIcon());
+        sub_menu_model_.get(),
+        ui::ImageModel::FromVectorIcon(controller_->GetVectorIcon(),
+                                       /*color_id=*/-1,
+                                       ui::SimpleMenuModel::kDefaultIconSize));
 #endif
   }
 }
diff --git a/chrome/browser/signin/dice_web_signin_interceptor.cc b/chrome/browser/signin/dice_web_signin_interceptor.cc
index 1902261..9972a78e 100644
--- a/chrome/browser/signin/dice_web_signin_interceptor.cc
+++ b/chrome/browser/signin/dice_web_signin_interceptor.cc
@@ -8,6 +8,7 @@
 
 #include "base/check.h"
 #include "base/i18n/case_conversion.h"
+#include "base/metrics/histogram_functions.h"
 #include "base/optional.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/threading/thread_task_runner_handle.h"
@@ -33,6 +34,11 @@
 
 namespace {
 
+void RecordSigninInterceptionHeuristicOutcome(
+    SigninInterceptionHeuristicOutcome outcome) {
+  base::UmaHistogramEnumeration("Signin.Intercept.HeuristicOutcome", outcome);
+}
+
 bool IsProfileCreationAllowed() {
   PrefService* service = g_browser_process->local_state();
   DCHECK(service);
@@ -93,16 +99,27 @@
   if (!base::FeatureList::IsEnabled(kDiceWebSigninInterceptionFeature))
     return;
 
-  // Do not intercept signins from the Sync startup flow. Note: |is_sync_signin|
-  // is an approximation, and in rare cases it may be true when in fact the
-  // signin was not a sync signin. In this case the interception is missed.
-  if (is_sync_signin)
+  if (is_sync_signin) {
+    // Do not intercept signins from the Sync startup flow.
+    // Note: |is_sync_signin| is an approximation, and in rare cases it may be
+    // true when in fact the signin was not a sync signin. In this case the
+    // interception is missed.
+    RecordSigninInterceptionHeuristicOutcome(
+        SigninInterceptionHeuristicOutcome::kAbortSyncSignin);
     return;
-
-  if (is_interception_in_progress_)
-    return;  // Multiple concurrent interceptions are not supported.
-  if (!is_new_account)
-    return;  // Do not intercept reauth.
+  }
+  if (is_interception_in_progress_) {
+    // Multiple concurrent interceptions are not supported.
+    RecordSigninInterceptionHeuristicOutcome(
+        SigninInterceptionHeuristicOutcome::kAbortInterceptInProgress);
+    return;
+  }
+  if (!is_new_account) {
+    // Do not intercept reauth.
+    RecordSigninInterceptionHeuristicOutcome(
+        SigninInterceptionHeuristicOutcome::kAbortAccountNotNew);
+    return;
+  }
 
   account_id_ = account_id;
   is_interception_in_progress_ = true;
@@ -124,23 +141,34 @@
         web_contents, bubble_parameters,
         base::BindOnce(&DiceWebSigninInterceptor::OnProfileSwitchChoice,
                        base::Unretained(this)));
-
+    was_interception_ui_displayed_ = true;
+    RecordSigninInterceptionHeuristicOutcome(
+        SigninInterceptionHeuristicOutcome::kInterceptProfileSwitch);
     return;
   }
 
-  if (identity_manager_->GetAccountsWithRefreshTokens().size() <= 1u ||
-      !IsProfileCreationAllowed()) {
+  if (identity_manager_->GetAccountsWithRefreshTokens().size() <= 1u) {
     // Enterprise and multi-user bubbles are only shown if there are multiple
-    // accounts and profile creation is allowed.
+    // accounts.
+    RecordSigninInterceptionHeuristicOutcome(
+        SigninInterceptionHeuristicOutcome::kAbortSingleAccount);
+    Reset();
+    return;
+  }
+  if (!IsProfileCreationAllowed()) {
+    RecordSigninInterceptionHeuristicOutcome(
+        SigninInterceptionHeuristicOutcome::kAbortProfileCreationDisallowed);
     Reset();
     return;
   }
 
+  account_info_fetch_start_time_ = base::TimeTicks::Now();
   if (account_info->IsValid()) {
     OnExtendedAccountInfoUpdated(*account_info);
   } else {
     on_account_info_update_timeout_.Reset(base::BindOnce(
-        &DiceWebSigninInterceptor::Reset, base::Unretained(this)));
+        &DiceWebSigninInterceptor::OnExtendedAccountInfoFetchTimeout,
+        base::Unretained(this)));
     base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
         FROM_HERE, on_account_info_update_timeout_.callback(),
         base::TimeDelta::FromSeconds(5));
@@ -161,6 +189,10 @@
 }
 
 void DiceWebSigninInterceptor::Shutdown() {
+  if (is_interception_in_progress_ && !was_interception_ui_displayed_) {
+    RecordSigninInterceptionHeuristicOutcome(
+        SigninInterceptionHeuristicOutcome::kAbortShutdown);
+  }
   Reset();
 }
 
@@ -171,6 +203,9 @@
   is_interception_in_progress_ = false;
   account_id_ = CoreAccountId();
   dice_signed_in_profile_creator_.reset();
+  was_interception_ui_displayed_ = false;
+  account_info_fetch_start_time_ = base::TimeTicks();
+  profile_creation_start_time_ = base::TimeTicks();
 }
 
 bool DiceWebSigninInterceptor::ShouldShowProfileSwitchBubble(
@@ -243,6 +278,9 @@
 
   account_info_update_observer_.RemoveAll();
   on_account_info_update_timeout_.Cancel();
+  base::UmaHistogramTimes(
+      "Signin.Intercept.AccountInfoFetchDuration",
+      base::TimeTicks::Now() - account_info_fetch_start_time_);
 
   base::Optional<SigninInterceptionType> interception_type;
 
@@ -253,6 +291,8 @@
 
   if (!interception_type) {
     // Signin should not be intercepted.
+    RecordSigninInterceptionHeuristicOutcome(
+        SigninInterceptionHeuristicOutcome::kAbortAccountInfoNotCompatible);
     Reset();
     return;
   }
@@ -263,6 +303,17 @@
       web_contents(), bubble_parameters,
       base::BindOnce(&DiceWebSigninInterceptor::OnProfileCreationChoice,
                      base::Unretained(this)));
+  was_interception_ui_displayed_ = true;
+  RecordSigninInterceptionHeuristicOutcome(
+      *interception_type == SigninInterceptionType::kEnterprise
+          ? SigninInterceptionHeuristicOutcome::kInterceptEnterprise
+          : SigninInterceptionHeuristicOutcome::kInterceptMultiUser);
+}
+
+void DiceWebSigninInterceptor::OnExtendedAccountInfoFetchTimeout() {
+  RecordSigninInterceptionHeuristicOutcome(
+      SigninInterceptionHeuristicOutcome::kAbortAccountInfoTimeout);
+  Reset();
 }
 
 void DiceWebSigninInterceptor::OnProfileCreationChoice(bool create) {
@@ -271,6 +322,7 @@
     return;
   }
 
+  profile_creation_start_time_ = base::TimeTicks::Now();
   base::string16 profile_name;
   base::Optional<AccountInfo> account_info =
       identity_manager_
@@ -310,6 +362,9 @@
     Profile* new_profile) {
   DCHECK(dice_signed_in_profile_creator_);
   dice_signed_in_profile_creator_.reset();
+  base::UmaHistogramTimes(
+      "Signin.Intercept.ProfileCreationDuration",
+      base::TimeTicks::Now() - profile_creation_start_time_);
 
   if (!new_profile) {
     Reset();
diff --git a/chrome/browser/signin/dice_web_signin_interceptor.h b/chrome/browser/signin/dice_web_signin_interceptor.h
index c587a40d..8c9672d 100644
--- a/chrome/browser/signin/dice_web_signin_interceptor.h
+++ b/chrome/browser/signin/dice_web_signin_interceptor.h
@@ -12,6 +12,7 @@
 #include "base/feature_list.h"
 #include "base/gtest_prod_util.h"
 #include "base/scoped_observer.h"
+#include "base/time/time.h"
 #include "components/keyed_service/core/keyed_service.h"
 #include "components/signin/public/identity_manager/identity_manager.h"
 #include "content/public/browser/web_contents_observer.h"
@@ -27,6 +28,37 @@
 class Profile;
 class ProfileAttributesStorage;
 
+// Outcome of the interception heuristic (decision whether the interception
+// bubble is shown or not).
+// These values are persisted to logs. Entries should not be renumbered and
+// numeric values should never be reused.
+enum class SigninInterceptionHeuristicOutcome {
+  // Interception succeeded:
+  kInterceptProfileSwitch = 0,
+  kInterceptMultiUser = 1,
+  kInterceptEnterprise = 2,
+
+  // Interception aborted:
+  // This is a "Sync" sign in and not a "web" sign in.
+  kAbortSyncSignin = 3,
+  // Another interception is already in progress.
+  kAbortInterceptInProgress = 4,
+  // This is not a new account (reauth).
+  kAbortAccountNotNew = 5,
+  // New profile is not offered when there is only one account.
+  kAbortSingleAccount = 6,
+  // Extended account info could not be downloaded.
+  kAbortAccountInfoTimeout = 7,
+  // Account info not compatible with interception (e.g. same Gaia name).
+  kAbortAccountInfoNotCompatible = 8,
+  // Profile creation disallowed.
+  kAbortProfileCreationDisallowed = 9,
+  // The interceptor was shut down before the heuristic completed.
+  kAbortShutdown = 10,
+
+  kMaxValue = kAbortShutdown,
+};
+
 // Called after web signed in, after a successful token exchange through Dice.
 // The DiceWebSigninInterceptor may offer the user to create a new profile or
 // switch to another existing profile.
@@ -137,6 +169,9 @@
   // signin::IdentityManager::Observer:
   void OnExtendedAccountInfoUpdated(const AccountInfo& info) override;
 
+  // Called when the extended account info was not updated after a timeout.
+  void OnExtendedAccountInfoFetchTimeout();
+
   // Called after the user chose whether a new profile would be created.
   void OnProfileCreationChoice(bool create);
   // Called after the user chose whether the session should continue in a new
@@ -165,6 +200,10 @@
   // is cancelled if the account info cannot be fetched quickly.
   base::CancelableOnceCallback<void()> on_account_info_update_timeout_;
   std::unique_ptr<DiceSignedInProfileCreator> dice_signed_in_profile_creator_;
+  // Used for metrics:
+  bool was_interception_ui_displayed_ = false;
+  base::TimeTicks account_info_fetch_start_time_;
+  base::TimeTicks profile_creation_start_time_;
 };
 
 #endif  // CHROME_BROWSER_SIGNIN_DICE_WEB_SIGNIN_INTERCEPTOR_H_
diff --git a/chrome/browser/signin/dice_web_signin_interceptor_browsertest.cc b/chrome/browser/signin/dice_web_signin_interceptor_browsertest.cc
index 4fdc929..60aaf927 100644
--- a/chrome/browser/signin/dice_web_signin_interceptor_browsertest.cc
+++ b/chrome/browser/signin/dice_web_signin_interceptor_browsertest.cc
@@ -6,6 +6,7 @@
 
 #include "base/scoped_observer.h"
 #include "base/strings/utf_string_conversions.h"
+#include "base/test/metrics/histogram_tester.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/profiles/profile_attributes_entry.h"
 #include "chrome/browser/profiles/profile_attributes_storage.h"
@@ -147,6 +148,7 @@
 
 // Tests the complete interception flow including profile and browser creation.
 IN_PROC_BROWSER_TEST_F(DiceWebSigninInterceptorBrowserTest, InterceptionTest) {
+  base::HistogramTester histogram_tester;
   // Setup profile for interception.
   identity_test_env()->MakeAccountAvailable("alice@example.com");
   AccountInfo account_info =
@@ -211,4 +213,12 @@
   EXPECT_EQ(browser()->tab_strip_model()->count(), original_tab_count - 1);
   EXPECT_EQ(added_browser->tab_strip_model()->GetActiveWebContents()->GetURL(),
             intercepted_url);
+
+  histogram_tester.ExpectUniqueSample(
+      "Signin.Intercept.HeuristicOutcome",
+      SigninInterceptionHeuristicOutcome::kInterceptMultiUser, 1);
+  histogram_tester.ExpectTotalCount("Signin.Intercept.AccountInfoFetchDuration",
+                                    1);
+  histogram_tester.ExpectTotalCount("Signin.Intercept.ProfileCreationDuration",
+                                    1);
 }
diff --git a/chrome/browser/signin/dice_web_signin_interceptor_unittest.cc b/chrome/browser/signin/dice_web_signin_interceptor_unittest.cc
index fc4d1c6..68f30c26 100644
--- a/chrome/browser/signin/dice_web_signin_interceptor_unittest.cc
+++ b/chrome/browser/signin/dice_web_signin_interceptor_unittest.cc
@@ -8,6 +8,7 @@
 
 #include "base/callback.h"
 #include "base/strings/utf_string_conversions.h"
+#include "base/test/metrics/histogram_tester.h"
 #include "base/test/scoped_feature_list.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/profiles/profile_attributes_entry.h"
@@ -307,21 +308,31 @@
   entry->SetAuthInfo(account_info.gaia, base::UTF8ToUTF16("Bob"),
                      /*is_consented_primary_account=*/false);
 
+  std::unique_ptr<base::HistogramTester> histogram_tester =
+      std::make_unique<base::HistogramTester>();
   // Check that Sync signin is not intercepted.
   interceptor()->MaybeInterceptWebSignin(web_contents(),
                                          account_info.account_id,
                                          /*is_new_account=*/true,
                                          /*is_sync_signin=*/true);
   testing::Mock::VerifyAndClearExpectations(mock_delegate());
+  histogram_tester->ExpectUniqueSample(
+      "Signin.Intercept.HeuristicOutcome",
+      SigninInterceptionHeuristicOutcome::kAbortSyncSignin, 1);
 
   // Check that reauth is not intercepted.
+  histogram_tester = std::make_unique<base::HistogramTester>();
   interceptor()->MaybeInterceptWebSignin(web_contents(),
                                          account_info.account_id,
                                          /*is_new_account=*/false,
                                          /*is_sync_signin=*/false);
   testing::Mock::VerifyAndClearExpectations(mock_delegate());
+  histogram_tester->ExpectUniqueSample(
+      "Signin.Intercept.HeuristicOutcome",
+      SigninInterceptionHeuristicOutcome::kAbortAccountNotNew, 1);
 
   // Check that interception works otherwise, as a sanity check.
+  histogram_tester = std::make_unique<base::HistogramTester>();
   DiceWebSigninInterceptor::Delegate::BubbleParameters expected_parameters = {
       DiceWebSigninInterceptor::SigninInterceptionType::kProfileSwitch,
       account_info, AccountInfo()};
@@ -332,6 +343,9 @@
                                          account_info.account_id,
                                          /*is_new_account=*/true,
                                          /*is_sync_signin=*/false);
+  histogram_tester->ExpectUniqueSample(
+      "Signin.Intercept.HeuristicOutcome",
+      SigninInterceptionHeuristicOutcome::kInterceptProfileSwitch, 1);
 }
 
 TEST_F(DiceWebSigninInterceptorTest, InterceptionInProgress) {
@@ -362,8 +376,12 @@
   EXPECT_TRUE(interceptor()->is_interception_in_progress_);
 
   // Check that there is no interception while another one is in progress.
+  base::HistogramTester histogram_tester;
   MaybeIntercept(account_info.account_id);
   testing::Mock::VerifyAndClearExpectations(mock_delegate());
+  histogram_tester.ExpectUniqueSample(
+      "Signin.Intercept.HeuristicOutcome",
+      SigninInterceptionHeuristicOutcome::kAbortInterceptInProgress, 1);
 
   // Complete the interception that was in progress.
   std::move(delegate_callback).Run(false);
@@ -378,6 +396,7 @@
 
 // Interception other than the profile switch require at least 2 accounts.
 TEST_F(DiceWebSigninInterceptorTest, NoInterceptionWithOneAccount) {
+  base::HistogramTester histogram_tester;
   AccountInfo account_info =
       identity_test_env()->MakeAccountAvailable("bob@example.com");
   // Interception aborts even if the account info is not available.
@@ -389,11 +408,15 @@
           ->IsValid());
   MaybeIntercept(account_info.account_id);
   EXPECT_FALSE(interceptor()->is_interception_in_progress_);
+  histogram_tester.ExpectUniqueSample(
+      "Signin.Intercept.HeuristicOutcome",
+      SigninInterceptionHeuristicOutcome::kAbortSingleAccount, 1);
 }
 
 // When profile creation is disallowed, profile switch interception is still
 // enabled, but others are disabled.
 TEST_F(DiceWebSigninInterceptorTest, ProfileCreationDisallowed) {
+  base::HistogramTester histogram_tester;
   g_browser_process->local_state()->SetBoolean(prefs::kBrowserAddPersonEnabled,
                                                false);
   // Setup for profile switch interception.
@@ -411,6 +434,9 @@
   // Interception that would offer creating a new profile does not work.
   MaybeIntercept(other_account_info.account_id);
   EXPECT_FALSE(interceptor()->is_interception_in_progress_);
+  histogram_tester.ExpectUniqueSample(
+      "Signin.Intercept.HeuristicOutcome",
+      SigninInterceptionHeuristicOutcome::kAbortProfileCreationDisallowed, 1);
 
   // Profile switch interception still works.
   DiceWebSigninInterceptor::Delegate::BubbleParameters expected_parameters = {
@@ -423,6 +449,7 @@
 }
 
 TEST_F(DiceWebSigninInterceptorTest, WaitForAccountInfoAvailable) {
+  base::HistogramTester histogram_tester;
   AccountInfo primary_account_info =
       identity_test_env()->MakeUnconsentedPrimaryAccountAvailable(
           "bob@example.com");
@@ -442,9 +469,12 @@
   MakeValidAccountInfo(&account_info);
   account_info.hosted_domain = "example.com";
   identity_test_env()->UpdateAccountInfoForAccount(account_info);
+  histogram_tester.ExpectTotalCount("Signin.Intercept.AccountInfoFetchDuration",
+                                    1);
 }
 
 TEST_F(DiceWebSigninInterceptorTest, AccountInfoAlreadyAvailable) {
+  base::HistogramTester histogram_tester;
   AccountInfo primary_account_info =
       identity_test_env()->MakeUnconsentedPrimaryAccountAvailable(
           "bob@example.com");
@@ -462,9 +492,15 @@
               ShowSigninInterceptionBubble(web_contents(), expected_parameters,
                                            testing::_));
   MaybeIntercept(account_info.account_id);
+  histogram_tester.ExpectTotalCount("Signin.Intercept.AccountInfoFetchDuration",
+                                    1);
+  histogram_tester.ExpectUniqueSample(
+      "Signin.Intercept.HeuristicOutcome",
+      SigninInterceptionHeuristicOutcome::kInterceptEnterprise, 1);
 }
 
 TEST_F(DiceWebSigninInterceptorTest, MultiUserInterception) {
+  base::HistogramTester histogram_tester;
   AccountInfo primary_account_info =
       identity_test_env()->MakeUnconsentedPrimaryAccountAvailable(
           "bob@example.com");
@@ -481,4 +517,7 @@
               ShowSigninInterceptionBubble(web_contents(), expected_parameters,
                                            testing::_));
   MaybeIntercept(account_info.account_id);
+  histogram_tester.ExpectUniqueSample(
+      "Signin.Intercept.HeuristicOutcome",
+      SigninInterceptionHeuristicOutcome::kInterceptMultiUser, 1);
 }
diff --git a/chrome/browser/site_isolation/chrome_site_per_process_browsertest.cc b/chrome/browser/site_isolation/chrome_site_per_process_browsertest.cc
index ccf6c28..d91d2cf5 100644
--- a/chrome/browser/site_isolation/chrome_site_per_process_browsertest.cc
+++ b/chrome/browser/site_isolation/chrome_site_per_process_browsertest.cc
@@ -1254,8 +1254,8 @@
 // Tests that a same-site iframe runs its beforeunload handler when closing a
 // tab.  Same as the test above, but for a same-site rather than cross-site
 // iframe.  See https://crbug.com/1010456.
-// Flaky on Linux and ChromeOS (crbug.com/1033002)
-#if defined(OS_LINUX) || defined(OS_CHROMEOS)
+// Flaky on Linux, ChromeOS and Windows (crbug.com/1033002)
+#if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_WIN)
 #define MAYBE_TabCloseWithSameSiteBeforeUnloadIframe \
   DISABLED_TabCloseWithSameSiteBeforeUnloadIframe
 #else
diff --git a/chrome/browser/storage_access_api/api_browsertest.cc b/chrome/browser/storage_access_api/api_browsertest.cc
index 7ee7df8..32d33729 100644
--- a/chrome/browser/storage_access_api/api_browsertest.cc
+++ b/chrome/browser/storage_access_api/api_browsertest.cc
@@ -73,8 +73,11 @@
   }
 
   void SetBlockThirdPartyCookies(bool value) {
-    browser()->profile()->GetPrefs()->SetBoolean(prefs::kBlockThirdPartyCookies,
-                                                 value);
+    browser()->profile()->GetPrefs()->SetInteger(
+        prefs::kCookieControlsMode,
+        static_cast<int>(
+            value ? content_settings::CookieControlsMode::kBlockThirdParty
+                  : content_settings::CookieControlsMode::kOff));
   }
 
   void NavigateToPageWithFrame(const std::string& host) {
diff --git a/chrome/browser/sync/chrome_sync_client.cc b/chrome/browser/sync/chrome_sync_client.cc
index 32fb8cdc..0d161a2e 100644
--- a/chrome/browser/sync/chrome_sync_client.cc
+++ b/chrome/browser/sync/chrome_sync_client.cc
@@ -221,9 +221,16 @@
 #if defined(OS_ANDROID)
   trusted_vault_client_ = std::make_unique<TrustedVaultClientAndroid>();
 #else
+  // TODO(crbug.com/1113597): consider destroying/notifying
+  // |trusted_vault_client_| upon IdentityManager shutdown, to avoid its usages
+  // afterwards. This can be done by tranferring |trusted_vault_client_|
+  // ownership to ProfileSyncService and acting on
+  // ProfileSyncService::Shutdown() or by handling
+  // IdentityManagerFactory::Observer::IdentityManagerShutdown().
   trusted_vault_client_ =
       std::make_unique<syncer::StandaloneTrustedVaultClient>(
-          profile_->GetPath().Append(kTrustedVaultFilename));
+          profile_->GetPath().Append(kTrustedVaultFilename),
+          IdentityManagerFactory::GetForProfile(profile_));
 #endif  // defined(OS_ANDROID)
 }
 
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index 229c781..ff9eaef 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -2525,6 +2525,8 @@
       "//chromeos/assistant:buildflags",
       "//chromeos/audio",
       "//chromeos/components/account_manager",
+      "//chromeos/components/bloom/public/cpp",
+      "//chromeos/components/bloom/public/cpp:bloom_controller_factory",
       "//chromeos/components/camera_app_ui",
       "//chromeos/components/help_app_ui",
       "//chromeos/components/local_search_service:local_search_service",
@@ -3162,13 +3164,7 @@
     }
 
     if (use_x11) {
-      sources += [
-        "views/frame/native_browser_frame_factory_aurax11.cc",
-        "views/javascript_app_modal_dialog_views_x11.cc",
-        "views/javascript_app_modal_dialog_views_x11.h",
-        "views/javascript_app_modal_event_blocker_x11.cc",
-        "views/javascript_app_modal_event_blocker_x11.h",
-      ]
+      sources += [ "views/frame/native_browser_frame_factory_aurax11.cc" ]
       deps += [
         "//ui/events/devices",
         "//ui/events/devices/x11",
@@ -3575,6 +3571,8 @@
       "views/location_bar/permission_chip.h",
       "views/location_bar/selected_keyword_view.cc",
       "views/location_bar/selected_keyword_view.h",
+      "views/location_bar/star_menu_model.cc",
+      "views/location_bar/star_menu_model.h",
       "views/location_bar/star_view.cc",
       "views/location_bar/star_view.h",
       "views/location_bar/zoom_bubble_view.cc",
@@ -4305,7 +4303,11 @@
       deps += [ "//apps/ui/views" ]
     }
     if (use_aura) {
-      sources += [ "views/chrome_javascript_app_modal_view_factory_views.cc" ]
+      sources += [
+        "views/chrome_javascript_app_modal_view_factory_views.cc",
+        "views/javascript_app_modal_event_blocker_x11.cc",
+        "views/javascript_app_modal_event_blocker_x11.h",
+      ]
       deps += [ "//ui/wm/public" ]
     }
   }
diff --git a/chrome/browser/ui/app_list/app_context_menu_unittest.cc b/chrome/browser/ui/app_list/app_context_menu_unittest.cc
index bb002b57..16b210c9 100644
--- a/chrome/browser/ui/app_list/app_context_menu_unittest.cc
+++ b/chrome/browser/ui/app_list/app_context_menu_unittest.cc
@@ -44,6 +44,7 @@
 #include "components/keyed_service/core/keyed_service.h"
 #include "components/services/app_service/public/cpp/app_update.h"
 #include "extensions/common/manifest_constants.h"
+#include "services/data_decoder/public/cpp/test_support/in_process_data_decoder.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/base/ui_base_features.h"
 #include "ui/display/test/test_screen.h"
@@ -161,10 +162,7 @@
     }
   }
 
-  ~AppContextMenuTest() override {
-    // Release profile file in order to keep right sequence.
-    profile_.reset();
-  }
+  ~AppContextMenuTest() override = default;
 
   void SetUp() override {
     AppListTestBase::SetUp();
@@ -334,8 +332,11 @@
     ValidateMenuState(menu_model.get(), states);
   }
 
+  apps::AppServiceTest& app_service_test() { return app_service_test_; }
+
  private:
   base::test::ScopedFeatureList scoped_feature_list_;
+  data_decoder::test::InProcessDataDecoder in_process_data_decoder_;
   display::test::TestScreen test_screen_;
   std::unique_ptr<KeyedService> menu_manager_;
   std::unique_ptr<FakeAppListControllerDelegate> controller_;
@@ -379,8 +380,7 @@
 }
 
 TEST_P(AppContextMenuTest, ArcMenu) {
-  apps::AppServiceTest app_service_test;
-  app_service_test.SetUp(profile());
+  app_service_test().SetUp(profile());
   ArcAppTest arc_test;
   arc_test.SetUp(profile());
 
@@ -389,7 +389,7 @@
   controller()->SetAppPinnable(app_id, AppListControllerDelegate::PIN_EDITABLE);
 
   arc_test.app_instance()->SendRefreshAppList(arc_test.fake_apps());
-  app_service_test.FlushMojoCalls();
+  app_service_test().FlushMojoCalls();
 
   std::unique_ptr<AppServiceAppItem> item = GetAppListItem(profile(), app_id);
 
@@ -412,7 +412,7 @@
   EXPECT_EQ(0u, arc_test.app_instance()->launch_requests().size());
 
   menu->ActivatedAt(0);
-  app_service_test.FlushMojoCalls();
+  app_service_test().FlushMojoCalls();
 
   const std::vector<std::unique_ptr<arc::FakeAppInstance::Request>>&
       launch_requests = arc_test.app_instance()->launch_requests();
@@ -449,7 +449,7 @@
   // Test launching app shortcut item.
   EXPECT_EQ(0, arc_test.app_instance()->launch_app_shortcut_item_count());
   menu->ActivatedAt(menu->GetItemCount() - 1);
-  app_service_test.FlushMojoCalls();
+  app_service_test().FlushMojoCalls();
   EXPECT_EQ(1, arc_test.app_instance()->launch_app_shortcut_item_count());
 
   // This makes all apps non-ready.
@@ -484,7 +484,7 @@
   // Uninstall all apps.
   arc_test.app_instance()->SendRefreshAppList(
       std::vector<arc::mojom::AppInfo>());
-  app_service_test.FlushMojoCalls();
+  app_service_test().FlushMojoCalls();
   controller()->SetAppOpen(app_id, false);
 
   // No app available case.
@@ -493,8 +493,7 @@
 }
 
 TEST_P(AppContextMenuTest, ArcMenuShortcut) {
-  apps::AppServiceTest app_service_test;
-  app_service_test.SetUp(profile());
+  app_service_test().SetUp(profile());
   ArcAppTest arc_test;
   arc_test.SetUp(profile());
 
@@ -503,7 +502,7 @@
   controller()->SetAppPinnable(app_id, AppListControllerDelegate::PIN_EDITABLE);
 
   arc_test.app_instance()->SendInstallShortcuts(arc_test.fake_shortcuts());
-  app_service_test.FlushMojoCalls();
+  app_service_test().FlushMojoCalls();
 
   std::unique_ptr<AppServiceAppItem> item = GetAppListItem(profile(), app_id);
 
@@ -558,13 +557,12 @@
 }
 
 TEST_P(AppContextMenuTest, ArcMenuStickyItem) {
-  apps::AppServiceTest app_service_test;
-  app_service_test.SetUp(profile());
+  app_service_test().SetUp(profile());
   ArcAppTest arc_test;
   arc_test.SetUp(profile());
 
   arc_test.app_instance()->SendRefreshAppList(arc_test.fake_apps());
-  app_service_test.FlushMojoCalls();
+  app_service_test().FlushMojoCalls();
 
   {
     // Verify menu of store
diff --git a/chrome/browser/ui/ash/assistant/assistant_client_impl.cc b/chrome/browser/ui/ash/assistant/assistant_client_impl.cc
index 7300a78b..b8a3185 100644
--- a/chrome/browser/ui/ash/assistant/assistant_client_impl.cc
+++ b/chrome/browser/ui/ash/assistant/assistant_client_impl.cc
@@ -7,6 +7,7 @@
 #include <utility>
 
 #include "ash/public/cpp/assistant/assistant_interface_binder.h"
+#include "ash/public/cpp/assistant/controller/assistant_interaction_controller.h"
 #include "ash/public/cpp/network_config_service.h"
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/chromeos/assistant/assistant_util.h"
@@ -18,6 +19,8 @@
 #include "chrome/browser/ui/ash/assistant/assistant_web_view_factory_impl.h"
 #include "chrome/browser/ui/ash/assistant/conversation_starters_client_impl.h"
 #include "chrome/browser/ui/ash/assistant/device_actions_delegate_impl.h"
+#include "chromeos/components/bloom/public/cpp/bloom_controller.h"
+#include "chromeos/components/bloom/public/cpp/bloom_controller_factory.h"
 #include "chromeos/constants/chromeos_features.h"
 #include "chromeos/constants/chromeos_switches.h"
 #include "chromeos/services/assistant/public/cpp/features.h"
@@ -85,6 +88,13 @@
     conversation_starters_client_ =
         std::make_unique<ConversationStartersClientImpl>(profile_);
   }
+
+  if (chromeos::assistant::features::IsBloomEnabled()) {
+    bloom_controller_ = chromeos::bloom::BloomControllerFactory::Create(
+        profile->GetURLLoaderFactory(),
+        IdentityManagerFactory::GetForProfile(profile),
+        ash::AssistantInteractionController::Get());
+  }
 }
 
 void AssistantClientImpl::MaybeStartAssistantOptInFlow() {
diff --git a/chrome/browser/ui/ash/assistant/assistant_client_impl.h b/chrome/browser/ui/ash/assistant/assistant_client_impl.h
index de0fe36..9d83e64 100644
--- a/chrome/browser/ui/ash/assistant/assistant_client_impl.h
+++ b/chrome/browser/ui/ash/assistant/assistant_client_impl.h
@@ -23,6 +23,12 @@
 #include "mojo/public/cpp/bindings/receiver.h"
 #include "mojo/public/cpp/bindings/remote.h"
 
+namespace chromeos {
+namespace bloom {
+class BloomController;
+}  // namespace bloom
+}  // namespace chromeos
+
 class AssistantSetup;
 class AssistantWebViewFactoryImpl;
 class ConversationStartersClientImpl;
@@ -100,6 +106,7 @@
   std::unique_ptr<AssistantSetup> assistant_setup_;
   std::unique_ptr<AssistantWebViewFactoryImpl> assistant_web_view_factory_;
   std::unique_ptr<ConversationStartersClientImpl> conversation_starters_client_;
+  std::unique_ptr<chromeos::bloom::BloomController> bloom_controller_;
 
   bool initialized_ = false;
 
diff --git a/chrome/browser/ui/browser_commands.cc b/chrome/browser/ui/browser_commands.cc
index dc39693..d332f391 100644
--- a/chrome/browser/ui/browser_commands.cc
+++ b/chrome/browser/ui/browser_commands.cc
@@ -57,6 +57,7 @@
 #include "chrome/browser/ui/location_bar/location_bar.h"
 #include "chrome/browser/ui/passwords/manage_passwords_ui_controller.h"
 #include "chrome/browser/ui/qrcode_generator/qrcode_generator_bubble_controller.h"
+#include "chrome/browser/ui/read_later/reading_list_model_factory.h"
 #include "chrome/browser/ui/scoped_tabbed_browser_displayer.h"
 #include "chrome/browser/ui/send_tab_to_self/send_tab_to_self_bubble_controller.h"
 #include "chrome/browser/ui/status_bubble.h"
@@ -87,6 +88,8 @@
 #include "components/google/core/common/google_util.h"
 #include "components/omnibox/browser/omnibox_prefs.h"
 #include "components/prefs/pref_service.h"
+#include "components/reading_list/core/reading_list_entry.h"
+#include "components/reading_list/core/reading_list_model.h"
 #include "components/services/app_service/public/mojom/types.mojom.h"
 #include "components/sessions/core/live_tab_context.h"
 #include "components/sessions/core/tab_restore_service.h"
@@ -208,6 +211,27 @@
                                                  TabStripModel::ADD_ACTIVE);
 }
 
+bool GetActiveTabURLAndTitleToSave(Browser* browser,
+                                   GURL* url,
+                                   base::string16* title) {
+  content::WebContents* web_contents =
+      browser->tab_strip_model()->GetActiveWebContents();
+  // |web_contents| can be nullptr if the last tab in the browser was closed
+  // but the browser wasn't closed yet. https://crbug.com/799668
+  if (!web_contents)
+    return false;
+  chrome::GetURLAndTitleToBookmark(web_contents, url, title);
+  return true;
+}
+
+ReadingListModel* GetReadingListModel(Browser* browser) {
+  ReadingListModel* model =
+      ReadingListModelFactory::GetForBrowserContext(browser->profile());
+  if (!model || !model->loaded())
+    return nullptr;  // Ignore requests until model has loaded.
+  return model;
+}
+
 }  // namespace
 
 using base::UserMetricsAction;
@@ -1006,6 +1030,45 @@
          CanBookmarkCurrentTab(browser);
 }
 
+bool MoveCurrentTabToReadLater(Browser* browser) {
+  GURL url;
+  base::string16 title;
+  ReadingListModel* model = GetReadingListModel(browser);
+  if (!model || !GetActiveTabURLAndTitleToSave(browser, &url, &title))
+    return false;
+  model->AddEntry(url, base::UTF16ToUTF8(title),
+                  reading_list::EntrySource::ADDED_VIA_CURRENT_APP);
+  // Close current tab.
+  int index = browser->tab_strip_model()->active_index();
+  browser->tab_strip_model()->CloseWebContentsAt(
+      index, TabStripModel::CLOSE_CREATE_HISTORICAL_TAB |
+                 TabStripModel::CLOSE_USER_GESTURE);
+  return true;
+}
+
+bool MarkCurrentTabAsReadInReadLater(Browser* browser) {
+  GURL url;
+  base::string16 title;
+  ReadingListModel* model = GetReadingListModel(browser);
+  if (!model || !GetActiveTabURLAndTitleToSave(browser, &url, &title))
+    return false;
+  const ReadingListEntry* entry = model->GetEntryByURL(url);
+  // Mark current tab as read.
+  if (entry && !entry->IsRead())
+    model->SetReadStatus(url, true);
+  return entry != nullptr;
+}
+
+bool IsCurrentTabUnreadInReadLater(Browser* browser) {
+  GURL url;
+  base::string16 title;
+  ReadingListModel* model = GetReadingListModel(browser);
+  if (!model || !GetActiveTabURLAndTitleToSave(browser, &url, &title))
+    return false;
+  const ReadingListEntry* entry = model->GetEntryByURL(url);
+  return entry && !entry->IsRead();
+}
+
 void SaveCreditCard(Browser* browser) {
   WebContents* web_contents =
       browser->tab_strip_model()->GetActiveWebContents();
diff --git a/chrome/browser/ui/browser_commands.h b/chrome/browser/ui/browser_commands.h
index 07c068bb..a153d33 100644
--- a/chrome/browser/ui/browser_commands.h
+++ b/chrome/browser/ui/browser_commands.h
@@ -140,6 +140,9 @@
 bool CanBookmarkCurrentTab(const Browser* browser);
 void BookmarkAllTabs(Browser* browser);
 bool CanBookmarkAllTabs(const Browser* browser);
+bool MoveCurrentTabToReadLater(Browser* browser);
+bool MarkCurrentTabAsReadInReadLater(Browser* browser);
+bool IsCurrentTabUnreadInReadLater(Browser* browser);
 void SaveCreditCard(Browser* browser);
 void MigrateLocalCards(Browser* browser);
 void MaybeShowSaveLocalCardSignInPromo(Browser* browser);
diff --git a/chrome/browser/ui/cocoa/chrome_command_dispatcher_delegate.mm b/chrome/browser/ui/cocoa/chrome_command_dispatcher_delegate.mm
index 8673014..dd8886a 100644
--- a/chrome/browser/ui/cocoa/chrome_command_dispatcher_delegate.mm
+++ b/chrome/browser/ui/cocoa/chrome_command_dispatcher_delegate.mm
@@ -5,6 +5,7 @@
 #import "chrome/browser/ui/cocoa/chrome_command_dispatcher_delegate.h"
 
 #include "base/check.h"
+#include "chrome/app/chrome_command_ids.h"
 #include "chrome/browser/global_keyboard_shortcuts_mac.h"
 #include "components/remote_cocoa/app_shim/native_widget_ns_window_bridge.h"
 #include "components/remote_cocoa/common/native_widget_ns_window_host.mojom.h"
@@ -91,6 +92,13 @@
   // By not passing the event to AppKit, we do lose out on the brief
   // highlighting of the NSMenu.
   CommandForKeyEventResult result = CommandForKeyEvent(event);
+  // Ignore new tab/window events if |event| is a key repeat to prevent
+  // users from accidentally opening too many empty tabs or windows.
+  if (event.isARepeat && (result.chrome_command == IDC_NEW_TAB ||
+                          result.chrome_command == IDC_NEW_WINDOW)) {
+    return ui::PerformKeyEquivalentResult::kDrop;
+  }
+
   if (result.found()) {
     auto* bridge =
         remote_cocoa::NativeWidgetNSWindowBridge::GetFromNativeWindow(window);
diff --git a/chrome/browser/ui/find_bar/find_bar_host_browsertest.cc b/chrome/browser/ui/find_bar/find_bar_host_browsertest.cc
index bfe61c7..742b89b 100644
--- a/chrome/browser/ui/find_bar/find_bar_host_browsertest.cc
+++ b/chrome/browser/ui/find_bar/find_bar_host_browsertest.cc
@@ -469,7 +469,13 @@
 }
 
 // Search Back and Forward on a single occurrence.
-IN_PROC_BROWSER_TEST_F(FindInPageControllerTest, SingleOccurrence) {
+// TODO(crbug.com/1119361): Test is flaky on ChromeOS.
+#if defined(OS_CHROMEOS)
+#define MAYBE_SingleOccurrence DISABLED_SingleOccurrence
+#else
+#define MAYBE_SingleOccurrence SingleOccurrence
+#endif
+IN_PROC_BROWSER_TEST_F(FindInPageControllerTest, MAYBE_SingleOccurrence) {
   WebContents* web_contents =
       browser()->tab_strip_model()->GetActiveWebContents();
   ui_test_utils::NavigateToURL(browser(), GetURL("FindRandomTests.html"));
diff --git a/chrome/browser/ui/passwords/well_known_change_password_navigation_throttle.cc b/chrome/browser/ui/passwords/well_known_change_password_navigation_throttle.cc
index e73cf6d..3555244 100644
--- a/chrome/browser/ui/passwords/well_known_change_password_navigation_throttle.cc
+++ b/chrome/browser/ui/passwords/well_known_change_password_navigation_throttle.cc
@@ -8,7 +8,6 @@
 #include "chrome/browser/password_manager/change_password_url_service_factory.h"
 #include "components/password_manager/core/browser/change_password_url_service.h"
 #include "components/password_manager/core/browser/well_known_change_password_state.h"
-#include "components/password_manager/core/browser/well_known_change_password_util.h"
 #include "components/password_manager/core/common/password_manager_features.h"
 #include "components/ukm/content/source_url_recorder.h"
 #include "content/public/browser/browser_context.h"
@@ -29,6 +28,7 @@
 using content::NavigationThrottle;
 using content::WebContents;
 using password_manager::IsWellKnownChangePasswordUrl;
+using password_manager::WellKnownChangePasswordResult;
 using password_manager::WellKnownChangePasswordState;
 
 // Used to scope the posted navigation task to the lifetime of |web_contents|.
diff --git a/chrome/browser/ui/passwords/well_known_change_password_navigation_throttle.h b/chrome/browser/ui/passwords/well_known_change_password_navigation_throttle.h
index a67c340..3b843b3 100644
--- a/chrome/browser/ui/passwords/well_known_change_password_navigation_throttle.h
+++ b/chrome/browser/ui/passwords/well_known_change_password_navigation_throttle.h
@@ -10,6 +10,7 @@
 #include "content/public/browser/navigation_throttle.h"
 
 #include "components/password_manager/core/browser/well_known_change_password_state.h"
+#include "components/password_manager/core/browser/well_known_change_password_util.h"
 #include "services/metrics/public/cpp/ukm_source_id.h"
 
 class GURL;
@@ -22,15 +23,6 @@
 class ChangePasswordUrlService;
 }  // namespace password_manager
 
-// Used to report UKMs about the support for .well-known/change-password.
-// These values are persisted to logs. Entries should not be renumbered and
-// numeric values should never be reused.
-enum class WellKnownChangePasswordResult {
-  kFallbackToOriginUrl = 0,
-  kFallbackToOverrideUrl = 1,
-  kUsedWellKnownChangePassword = 2,
-};
-
 // This NavigationThrottle checks whether a site supports the
 // .well-known/change-password url. To check whether a site supports the
 // change-password url, we also request a .well-known path that is defined to
@@ -63,7 +55,7 @@
   // Redirects to a given URL in the same tab.
   void Redirect(const GURL& url);
   // Records the given UKM metric.
-  void RecordMetric(WellKnownChangePasswordResult result);
+  void RecordMetric(password_manager::WellKnownChangePasswordResult result);
 
   password_manager::WellKnownChangePasswordState
       well_known_change_password_state_{this};
diff --git a/chrome/browser/ui/passwords/well_known_change_password_navigation_throttle_browsertest.cc b/chrome/browser/ui/passwords/well_known_change_password_navigation_throttle_browsertest.cc
index c34a70c..a65aae32 100644
--- a/chrome/browser/ui/passwords/well_known_change_password_navigation_throttle_browsertest.cc
+++ b/chrome/browser/ui/passwords/well_known_change_password_navigation_throttle_browsertest.cc
@@ -44,6 +44,7 @@
 using net::test_server::HttpResponse;
 using password_manager::kWellKnownChangePasswordPath;
 using password_manager::kWellKnownNotExistingResourcePath;
+using password_manager::WellKnownChangePasswordResult;
 
 constexpr char kMockChangePasswordPath[] = "/change-password-override";
 
diff --git a/chrome/browser/ui/views/apps/app_dialog/app_uninstall_dialog_view.cc b/chrome/browser/ui/views/apps/app_dialog/app_uninstall_dialog_view.cc
index e200e39..e3916e22 100644
--- a/chrome/browser/ui/views/apps/app_dialog/app_uninstall_dialog_view.cc
+++ b/chrome/browser/ui/views/apps/app_dialog/app_uninstall_dialog_view.cc
@@ -204,14 +204,13 @@
                                     google_util::ALLOW_SUBDOMAIN)) {
     replacements.push_back(learn_more_text);
 
+    checkbox_label = std::make_unique<views::StyledLabel>(this);
     std::vector<size_t> offsets;
-    checkbox_label = std::make_unique<views::StyledLabel>(
-        l10n_util::GetStringFUTF16(
-            IDS_APP_UNINSTALL_PROMPT_REMOVE_DATA_CHECKBOX_FOR_GOOGLE,
-            replacements, &offsets),
-        this);
+    checkbox_label->SetText(l10n_util::GetStringFUTF16(
+        IDS_APP_UNINSTALL_PROMPT_REMOVE_DATA_CHECKBOX_FOR_GOOGLE, replacements,
+        &offsets));
     DCHECK_EQ(replacements.size(), offsets.size());
-    offset = offsets[offsets.size() - 1];
+    offset = offsets.back();
   } else {
     auto domain = net::registry_controlled_domains::GetDomainAndRegistry(
         app_launch_url,
@@ -222,14 +221,13 @@
     replacements.push_back(base::ASCIIToUTF16(domain));
     replacements.push_back(learn_more_text);
 
+    checkbox_label = std::make_unique<views::StyledLabel>(this);
     std::vector<size_t> offsets;
-    checkbox_label = std::make_unique<views::StyledLabel>(
-        l10n_util::GetStringFUTF16(
-            IDS_APP_UNINSTALL_PROMPT_REMOVE_DATA_CHECKBOX_FOR_NON_GOOGLE,
-            replacements, &offsets),
-        this);
+    checkbox_label->SetText(l10n_util::GetStringFUTF16(
+        IDS_APP_UNINSTALL_PROMPT_REMOVE_DATA_CHECKBOX_FOR_NON_GOOGLE,
+        replacements, &offsets));
     DCHECK_EQ(replacements.size(), offsets.size());
-    offset = offsets[offsets.size() - 1];
+    offset = offsets.back();
   }
 
   checkbox_label->AddStyleRange(
diff --git a/chrome/browser/ui/views/autofill/payments/payments_view_util.cc b/chrome/browser/ui/views/autofill/payments/payments_view_util.cc
index 3433e1a..4a9bb281 100644
--- a/chrome/browser/ui/views/autofill/payments/payments_view_util.cc
+++ b/chrome/browser/ui/views/autofill/payments/payments_view_util.cc
@@ -145,8 +145,8 @@
 LegalMessageView::CreateLegalMessageLineLabel(
     const LegalMessageLine& line,
     views::StyledLabelListener* listener) {
-  std::unique_ptr<views::StyledLabel> label =
-      std::make_unique<views::StyledLabel>(line.text(), listener);
+  auto label = std::make_unique<views::StyledLabel>(listener);
+  label->SetText(line.text());
   label->SetTextContext(CONTEXT_BODY_TEXT_LARGE);
   label->SetDefaultTextStyle(views::style::STYLE_SECONDARY);
   for (const LegalMessageLine::Link& link : line.links()) {
diff --git a/chrome/browser/ui/views/chrome_javascript_app_modal_view_factory_views.cc b/chrome/browser/ui/views/chrome_javascript_app_modal_view_factory_views.cc
index 22431a9..afe16d2 100644
--- a/chrome/browser/ui/views/chrome_javascript_app_modal_view_factory_views.cc
+++ b/chrome/browser/ui/views/chrome_javascript_app_modal_view_factory_views.cc
@@ -6,26 +6,41 @@
 
 #include "base/macros.h"
 #include "build/build_config.h"
+#include "chrome/browser/ui/blocked_content/popunder_preventer.h"
+#include "chrome/browser/ui/views/javascript_app_modal_event_blocker_x11.h"
 #include "components/constrained_window/constrained_window_views.h"
+#include "components/javascript_dialogs/app_modal_dialog_controller.h"
 #include "components/javascript_dialogs/app_modal_dialog_manager.h"
+#include "components/javascript_dialogs/views/app_modal_dialog_view_views.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_contents_delegate.h"
 
-#if defined(USE_X11)
-#include "chrome/browser/ui/views/javascript_app_modal_dialog_views_x11.h"
+#if defined(USE_OZONE)
 #include "ui/base/ui_base_features.h"
+#include "ui/ozone/public/ozone_platform.h"
 #endif
 
-#include "chrome/browser/ui/blocked_content/popunder_preventer.h"
-#include "components/javascript_dialogs/app_modal_dialog_controller.h"
-#include "components/javascript_dialogs/views/app_modal_dialog_view_views.h"
-
 #if defined(USE_AURA)
 #include "ui/aura/window.h"
 #endif
 
 namespace {
 
+bool UseEventBlocker() {
+#if defined(USE_OZONE)
+  if (features::IsUsingOzonePlatform()) {
+    return ui::OzonePlatform::GetInstance()
+        ->GetPlatformProperties()
+        .app_modal_dialogs_use_event_blocker;
+  }
+#endif
+#if defined(USE_X11)
+  return true;
+#else
+  return false;
+#endif
+}
+
 class ChromeJavaScriptAppModalDialogViews
     : public javascript_dialogs::AppModalDialogViewViews {
  public:
@@ -35,7 +50,27 @@
         popunder_preventer_(parent->web_contents()) {}
   ~ChromeJavaScriptAppModalDialogViews() override = default;
 
+  // JavaScriptAppModalDialogViews:
+  void ShowAppModalDialog() override {
+    // BrowserView::CanActivate() ensures that other browser windows cannot be
+    // activated while the dialog is visible.  Block events to other browser
+    // windows so that the user cannot interact with them.  This hack is
+    // unnecessary on Windows and Chrome OS.
+    // TODO(pkotwicz): Find a better way of doing this and remove this hack.
+    if (UseEventBlocker() && !event_blocker_.get()) {
+      event_blocker_ = std::make_unique<JavascriptAppModalEventBlockerX11>(
+          GetWidget()->GetNativeView());
+    }
+    AppModalDialogViewViews::ShowAppModalDialog();
+  }
+
+  // views::DialogDelegate:
+  void WindowClosing() override { event_blocker_.reset(); }
+
  private:
+  // Blocks events to other browser windows while the dialog is open.
+  std::unique_ptr<JavascriptAppModalEventBlockerX11> event_blocker_;
+
   PopunderPreventer popunder_preventer_;
 
   DISALLOW_COPY_AND_ASSIGN(ChromeJavaScriptAppModalDialogViews);
@@ -43,13 +78,8 @@
 
 javascript_dialogs::AppModalDialogView* CreateNativeJavaScriptDialog(
     javascript_dialogs::AppModalDialogController* dialog) {
-  javascript_dialogs::AppModalDialogViewViews* d = nullptr;
-#if defined(USE_X11)
-  if (!features::IsUsingOzonePlatform())
-    d = new JavaScriptAppModalDialogViewsX11(dialog);
-#endif
-  if (!d)
-    d = new ChromeJavaScriptAppModalDialogViews(dialog);
+  javascript_dialogs::AppModalDialogViewViews* d =
+      new ChromeJavaScriptAppModalDialogViews(dialog);
   dialog->web_contents()->GetDelegate()->ActivateContents(
       dialog->web_contents());
   gfx::NativeWindow parent_window =
diff --git a/chrome/browser/ui/views/crostini/crostini_package_install_failure_view.cc b/chrome/browser/ui/views/crostini/crostini_package_install_failure_view.cc
index 7e7009c..0115627 100644
--- a/chrome/browser/ui/views/crostini/crostini_package_install_failure_view.cc
+++ b/chrome/browser/ui/views/crostini/crostini_package_install_failure_view.cc
@@ -52,11 +52,10 @@
   set_margins(provider->GetDialogInsetsForContentType(
       views::DialogContentType::TEXT, views::DialogContentType::TEXT));
 
-  views::StyledLabel* message_label = new views::StyledLabel(
-      l10n_util::GetStringUTF16(
-          IDS_CROSTINI_PACKAGE_INSTALL_FAILURE_VIEW_MESSAGE),
-      nullptr);
-  AddChildView(message_label);
+  views::StyledLabel* message_label =
+      AddChildView(std::make_unique<views::StyledLabel>());
+  message_label->SetText(l10n_util::GetStringUTF16(
+      IDS_CROSTINI_PACKAGE_INSTALL_FAILURE_VIEW_MESSAGE));
 
   views::MessageBoxView* error_box =
       new views::MessageBoxView(base::UTF8ToUTF16(error_message));
diff --git a/chrome/browser/ui/views/device_chooser_content_view.cc b/chrome/browser/ui/views/device_chooser_content_view.cc
index 0f2cd25..399436d 100644
--- a/chrome/browser/ui/views/device_chooser_content_view.cc
+++ b/chrome/browser/ui/views/device_chooser_content_view.cc
@@ -157,7 +157,8 @@
   size_t offset = 0;
   base::string16 text = l10n_util::GetStringFUTF16(
       IDS_BLUETOOTH_DEVICE_CHOOSER_TURN_ADAPTER_OFF, link_text, &offset);
-  auto adapter_off_help = std::make_unique<views::StyledLabel>(text, this);
+  auto adapter_off_help = std::make_unique<views::StyledLabel>(this);
+  adapter_off_help->SetText(text);
   adapter_off_help->AddStyleRange(
       gfx::Range(0, link_text.size()),
       views::StyledLabel::RangeStyleInfo::CreateForLink());
diff --git a/chrome/browser/ui/views/download/download_item_view.cc b/chrome/browser/ui/views/download/download_item_view.cc
index 6aa83f7..86b58f0 100644
--- a/chrome/browser/ui/views/download/download_item_view.cc
+++ b/chrome/browser/ui/views/download/download_item_view.cc
@@ -257,8 +257,7 @@
 
   open_button_ = AddChildView(std::make_unique<TransparentButton>(this));
 
-  file_name_label_ = AddChildView(
-      std::make_unique<views::StyledLabel>(base::string16(), nullptr));
+  file_name_label_ = AddChildView(std::make_unique<views::StyledLabel>());
   file_name_label_->SetTextContext(CONTEXT_DOWNLOAD_SHELF);
   file_name_label_->GetViewAccessibility().OverrideIsIgnored(true);
   const base::string16 filename = ElidedFilename(*file_name_label_);
@@ -270,13 +269,11 @@
       base::string16(), CONTEXT_DOWNLOAD_SHELF_STATUS));
   status_label_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
 
-  warning_label_ = AddChildView(
-      std::make_unique<views::StyledLabel>(base::string16(), nullptr));
+  warning_label_ = AddChildView(std::make_unique<views::StyledLabel>());
   warning_label_->SetTextContext(CONTEXT_DOWNLOAD_SHELF);
   warning_label_->set_can_process_events_within_subtree(false);
 
-  deep_scanning_label_ = AddChildView(
-      std::make_unique<views::StyledLabel>(base::string16(), nullptr));
+  deep_scanning_label_ = AddChildView(std::make_unique<views::StyledLabel>());
   deep_scanning_label_->SetTextContext(CONTEXT_DOWNLOAD_SHELF);
   deep_scanning_label_->set_can_process_events_within_subtree(false);
 
diff --git a/chrome/browser/ui/views/extensions/request_file_system_dialog_view.cc b/chrome/browser/ui/views/extensions/request_file_system_dialog_view.cc
index cd452a4..1b3dff4b 100644
--- a/chrome/browser/ui/views/extensions/request_file_system_dialog_view.cc
+++ b/chrome/browser/ui/views/extensions/request_file_system_dialog_view.cc
@@ -92,7 +92,9 @@
                : IDS_FILE_SYSTEM_REQUEST_FILE_SYSTEM_DIALOG_MESSAGE,
       app_name, volume_name, &placeholder_offsets);
 
-  views::StyledLabel* const label = new views::StyledLabel(message, nullptr);
+  views::StyledLabel* const label =
+      AddChildView(std::make_unique<views::StyledLabel>());
+  label->SetText(message);
   views::StyledLabel::RangeStyleInfo bold_style;
   bold_style.text_style = STYLE_EMPHASIZED;
 
@@ -106,6 +108,4 @@
       bold_style);
 
   SetLayoutManager(std::make_unique<views::FillLayout>());
-
-  AddChildView(label);
 }
diff --git a/chrome/browser/ui/views/hover_button.cc b/chrome/browser/ui/views/hover_button.cc
index e07767d..328b958 100644
--- a/chrome/browser/ui/views/hover_button.cc
+++ b/chrome/browser/ui/views/hover_button.cc
@@ -133,8 +133,8 @@
   // |label_wrapper| will hold both the title and subtitle if it exists.
   auto label_wrapper = std::make_unique<views::View>();
 
-  title_ = label_wrapper->AddChildView(
-      std::make_unique<views::StyledLabel>(title, nullptr));
+  title_ = label_wrapper->AddChildView(std::make_unique<views::StyledLabel>());
+  title_->SetText(title);
   // Allow the StyledLabel for title to assume its preferred size on a single
   // line and let the flex layout attenuate its width if necessary.
   title_->SizeToFit(0);
@@ -232,10 +232,10 @@
           : base::JoinString({title_->GetText(), subtitle_->GetText()},
                              base::ASCIIToUTF16("\n"));
 
-  // |views::StyledLabel|s only add tooltips for any links they may have.
-  // However, since |HoverButton| will never insert a link inside its child
-  // |StyledLabel|, decide whether it needs a tooltip by checking whether the
-  // available space is smaller than its preferred size.
+  // views::StyledLabels only add tooltips for any links they may have. However,
+  // since HoverButton will never insert a link inside its child StyledLabel,
+  // decide whether it needs a tooltip by checking whether the available space
+  // is smaller than its preferred size.
   const bool needs_tooltip =
       label_wrapper_->GetPreferredSize().width() > label_wrapper_->width();
   SetTooltipText(needs_tooltip ? accessible_name : base::string16());
diff --git a/chrome/browser/ui/views/javascript_app_modal_dialog_views_x11.cc b/chrome/browser/ui/views/javascript_app_modal_dialog_views_x11.cc
deleted file mode 100644
index 31599da..0000000
--- a/chrome/browser/ui/views/javascript_app_modal_dialog_views_x11.cc
+++ /dev/null
@@ -1,40 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/ui/views/javascript_app_modal_dialog_views_x11.h"
-
-#include "chrome/browser/ui/blocked_content/popunder_preventer.h"
-#include "chrome/browser/ui/browser_dialogs.h"
-#include "chrome/browser/ui/views/javascript_app_modal_event_blocker_x11.h"
-#include "components/javascript_dialogs/app_modal_dialog_controller.h"
-#include "ui/views/widget/widget.h"
-
-JavaScriptAppModalDialogViewsX11::JavaScriptAppModalDialogViewsX11(
-    javascript_dialogs::AppModalDialogController* parent)
-    : javascript_dialogs::AppModalDialogViewViews(parent),
-      popunder_preventer_(new PopunderPreventer(parent->web_contents())) {
-  chrome::RecordDialogCreation(
-      chrome::DialogIdentifier::JAVA_SCRIPT_APP_MODAL_X11);
-}
-
-JavaScriptAppModalDialogViewsX11::~JavaScriptAppModalDialogViewsX11() {
-}
-
-void JavaScriptAppModalDialogViewsX11::ShowAppModalDialog() {
-  // BrowserView::CanActivate() ensures that other browser windows cannot be
-  // activated for long while the dialog is visible. Block events to other
-  // browser windows so that the user cannot interact with other browser windows
-  // in the short time that the other browser windows are active. This hack is
-  // unnecessary on Windows and Chrome OS.
-  // TODO(pkotwicz): Find a better way of doing this and remove this hack.
-  if (!event_blocker_x11_.get()) {
-    event_blocker_x11_.reset(
-        new JavascriptAppModalEventBlockerX11(GetWidget()->GetNativeView()));
-  }
-  GetWidget()->Show();
-}
-
-void JavaScriptAppModalDialogViewsX11::WindowClosing() {
-  event_blocker_x11_.reset();
-}
diff --git a/chrome/browser/ui/views/javascript_app_modal_dialog_views_x11.h b/chrome/browser/ui/views/javascript_app_modal_dialog_views_x11.h
deleted file mode 100644
index 68103f3..0000000
--- a/chrome/browser/ui/views/javascript_app_modal_dialog_views_x11.h
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_UI_VIEWS_JAVASCRIPT_APP_MODAL_DIALOG_VIEWS_X11_H_
-#define CHROME_BROWSER_UI_VIEWS_JAVASCRIPT_APP_MODAL_DIALOG_VIEWS_X11_H_
-
-#include <memory>
-
-#include "base/macros.h"
-#include "components/javascript_dialogs/views/app_modal_dialog_view_views.h"
-
-class JavascriptAppModalEventBlockerX11;
-class PopunderPreventer;
-
-// AppModalDialogView implementation for linux desktop.
-class JavaScriptAppModalDialogViewsX11
-    : public javascript_dialogs::AppModalDialogViewViews {
- public:
-  explicit JavaScriptAppModalDialogViewsX11(
-      javascript_dialogs::AppModalDialogController* parent);
-  ~JavaScriptAppModalDialogViewsX11() override;
-
-  // JavaScriptAppModalDialogViews:
-  void ShowAppModalDialog() override;
-
-  // views::DialogDelegate:
-  void WindowClosing() override;
-
- private:
-  // Blocks events to other browser windows while the dialog is open.
-  std::unique_ptr<JavascriptAppModalEventBlockerX11> event_blocker_x11_;
-
-  std::unique_ptr<PopunderPreventer> popunder_preventer_;
-
-  DISALLOW_COPY_AND_ASSIGN(JavaScriptAppModalDialogViewsX11);
-};
-
-#endif  // CHROME_BROWSER_UI_VIEWS_JAVASCRIPT_APP_MODAL_DIALOG_VIEWS_X11_H_
diff --git a/chrome/browser/ui/views/layout_provider_unittest.cc b/chrome/browser/ui/views/layout_provider_unittest.cc
index f431b8da..0ba09e4 100644
--- a/chrome/browser/ui/views/layout_provider_unittest.cc
+++ b/chrome/browser/ui/views/layout_provider_unittest.cc
@@ -394,7 +394,8 @@
   // ChromeTypographyProvider::GetLineHeight(), which is body text.
   EXPECT_EQ(kBodyLineHeight,
             views::style::GetLineHeight(views::style::CONTEXT_LABEL, kStyle));
-  views::StyledLabel styled_label(base::ASCIIToUTF16("test"), nullptr);
+  views::StyledLabel styled_label;
+  styled_label.SetText(base::ASCIIToUTF16("test"));
   constexpr int kStyledLabelWidth = 200;  // Enough to avoid wrapping.
   styled_label.SizeToFit(kStyledLabelWidth);
   EXPECT_EQ(kBodyLineHeight, styled_label.height());
diff --git a/chrome/browser/ui/views/location_bar/star_menu_model.cc b/chrome/browser/ui/views/location_bar/star_menu_model.cc
new file mode 100644
index 0000000..e987992
--- /dev/null
+++ b/chrome/browser/ui/views/location_bar/star_menu_model.cc
@@ -0,0 +1,36 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/views/location_bar/star_menu_model.h"
+
+#include "chrome/grit/generated_resources.h"
+#include "components/omnibox/browser/vector_icons.h"
+#include "components/vector_icons/vector_icons.h"
+#include "ui/base/models/image_model.h"
+
+StarMenuModel::StarMenuModel(ui::SimpleMenuModel::Delegate* delegate,
+                             bool bookmarked,
+                             bool exists_as_unread_in_read_later)
+    : SimpleMenuModel(delegate) {
+  Build(bookmarked, exists_as_unread_in_read_later);
+}
+
+StarMenuModel::~StarMenuModel() = default;
+
+void StarMenuModel::Build(bool bookmarked,
+                          bool exists_as_unread_in_read_later) {
+  AddItemWithStringIdAndIcon(
+      CommandBookmark,
+      bookmarked ? IDS_STAR_VIEW_MENU_EDIT_BOOKMARK
+                 : IDS_STAR_VIEW_MENU_ADD_BOOKMARK,
+      ui::ImageModel::FromVectorIcon(omnibox::kStarIcon));
+  // TODO(corising): Replace placeholder folder icon with read-later icon once
+  // available.
+  AddItemWithStringIdAndIcon(
+      exists_as_unread_in_read_later ? CommandMarkAsRead
+                                     : CommandMoveToReadLater,
+      exists_as_unread_in_read_later ? IDS_STAR_VIEW_MENU_MARK_AS_READ
+                                     : IDS_STAR_VIEW_MENU_MOVE_TO_READ_LATER,
+      ui::ImageModel::FromVectorIcon(vector_icons::kFolderIcon));
+}
diff --git a/chrome/browser/ui/views/location_bar/star_menu_model.h b/chrome/browser/ui/views/location_bar/star_menu_model.h
new file mode 100644
index 0000000..e9e0bb7
--- /dev/null
+++ b/chrome/browser/ui/views/location_bar/star_menu_model.h
@@ -0,0 +1,30 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_VIEWS_LOCATION_BAR_STAR_MENU_MODEL_H_
+#define CHROME_BROWSER_UI_VIEWS_LOCATION_BAR_STAR_MENU_MODEL_H_
+
+#include "base/macros.h"
+#include "ui/base/models/simple_menu_model.h"
+
+class StarMenuModel : public ui::SimpleMenuModel {
+ public:
+  StarMenuModel(ui::SimpleMenuModel::Delegate* delegate,
+                bool bookmarked,
+                bool exists_as_unread_in_read_later);
+  StarMenuModel(const StarMenuModel&) = delete;
+  StarMenuModel& operator=(const StarMenuModel&) = delete;
+  ~StarMenuModel() override;
+
+  enum StarMenuCommand {
+    CommandBookmark,
+    CommandMoveToReadLater,
+    CommandMarkAsRead
+  };
+
+ private:
+  void Build(bool bookmarked, bool exists_as_unread_in_read_later);
+};
+
+#endif  // CHROME_BROWSER_UI_VIEWS_LOCATION_BAR_STAR_MENU_MODEL_H_
diff --git a/chrome/browser/ui/views/location_bar/star_view.cc b/chrome/browser/ui/views/location_bar/star_view.cc
index 05edc8e..2f89bca 100644
--- a/chrome/browser/ui/views/location_bar/star_view.cc
+++ b/chrome/browser/ui/views/location_bar/star_view.cc
@@ -15,9 +15,11 @@
 #include "chrome/browser/ui/bookmarks/bookmark_stats.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_commands.h"
+#include "chrome/browser/ui/ui_features.h"
 #include "chrome/browser/ui/view_ids.h"
 #include "chrome/browser/ui/views/bookmarks/bookmark_bubble_view.h"
 #include "chrome/browser/ui/views/in_product_help/feature_promo_bubble_view.h"
+#include "chrome/browser/ui/views/location_bar/star_menu_model.h"
 #include "chrome/grit/generated_resources.h"
 #include "components/bookmarks/common/bookmark_pref_names.h"
 #include "components/omnibox/browser/vector_icons.h"
@@ -27,6 +29,7 @@
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/gfx/color_utils.h"
 #include "ui/gfx/paint_vector_icon.h"
+#include "ui/views/controls/menu/menu_runner.h"
 
 StarView::StarView(CommandUpdater* command_updater,
                    Browser* browser,
@@ -38,6 +41,7 @@
                          page_action_icon_delegate),
       browser_(browser) {
   DCHECK(browser_);
+
   edit_bookmarks_enabled_.Init(
       bookmarks::prefs::kEditBookmarksEnabled, browser_->profile()->GetPrefs(),
       base::BindRepeating(&StarView::EditBookmarksPrefUpdated,
@@ -71,8 +75,19 @@
 }
 
 void StarView::ExecuteCommand(ExecuteSource source) {
-  OnExecuting(source);
-  chrome::BookmarkCurrentTab(browser_);
+  if (base::FeatureList::IsEnabled(features::kReadLater)) {
+    menu_model_ = std::make_unique<StarMenuModel>(
+        this, active(), chrome::IsCurrentTabUnreadInReadLater(browser_));
+    menu_runner_ = std::make_unique<views::MenuRunner>(
+        menu_model_.get(),
+        views::MenuRunner::HAS_MNEMONICS | views::MenuRunner::FIXED_ANCHOR);
+    menu_runner_->RunMenuAt(GetWidget(), nullptr, GetAnchorBoundsInScreen(),
+                            views::MenuAnchorPosition::kTopRight,
+                            ui::MENU_SOURCE_NONE);
+  } else {
+    OnExecuting(source);
+    chrome::BookmarkCurrentTab(browser_);
+  }
 }
 
 views::BubbleDialogDelegate* StarView::GetBubble() const {
@@ -95,3 +110,27 @@
 void StarView::EditBookmarksPrefUpdated() {
   Update();
 }
+
+void StarView::ExecuteCommand(int command_id, int event_flags) {
+  switch (command_id) {
+    case StarMenuModel::CommandBookmark:
+      chrome::BookmarkCurrentTab(browser_);
+      break;
+    case StarMenuModel::CommandMoveToReadLater:
+      chrome::MoveCurrentTabToReadLater(browser_);
+      break;
+    case StarMenuModel::CommandMarkAsRead:
+      chrome::MarkCurrentTabAsReadInReadLater(browser_);
+      break;
+    default:
+      NOTREACHED();
+  }
+}
+
+void StarView::MenuClosed(ui::SimpleMenuModel* source) {
+  if (!GetBubble() || !GetBubble()->GetWidget() ||
+      !GetBubble()->GetWidget()->IsVisible()) {
+    SetHighlighted(false);
+  }
+  menu_runner_.reset();
+}
diff --git a/chrome/browser/ui/views/location_bar/star_view.h b/chrome/browser/ui/views/location_bar/star_view.h
index 2c8c611e..40824d7e 100644
--- a/chrome/browser/ui/views/location_bar/star_view.h
+++ b/chrome/browser/ui/views/location_bar/star_view.h
@@ -5,16 +5,21 @@
 #ifndef CHROME_BROWSER_UI_VIEWS_LOCATION_BAR_STAR_VIEW_H_
 #define CHROME_BROWSER_UI_VIEWS_LOCATION_BAR_STAR_VIEW_H_
 
+#include <memory>
+
 #include "base/macros.h"
 #include "base/scoped_observer.h"
 #include "chrome/browser/ui/views/page_action/page_action_icon_view.h"
 #include "components/prefs/pref_member.h"
+#include "ui/base/models/simple_menu_model.h"
 
 class Browser;
 class CommandUpdater;
+class StarMenuModel;
 
 // The star icon to show a bookmark bubble.
-class StarView : public PageActionIconView {
+class StarView : public PageActionIconView,
+                 public ui::SimpleMenuModel::Delegate {
  public:
   StarView(CommandUpdater* command_updater,
            Browser* browser,
@@ -22,6 +27,8 @@
            PageActionIconView::Delegate* page_action_icon_delegate);
   ~StarView() override;
 
+  StarMenuModel* menu_model_for_test() { return menu_model_.get(); }
+
  protected:
   // PageActionIconView:
   void UpdateImpl() override;
@@ -36,8 +43,15 @@
   void EditBookmarksPrefUpdated();
   bool IsBookmarkStarHiddenByExtension() const;
 
+  // ui::SimpleMenuModel::Delegate:
+  void ExecuteCommand(int command_id, int event_flags) override;
+  void MenuClosed(ui::SimpleMenuModel* source) override;
+
   Browser* const browser_;
 
+  std::unique_ptr<views::MenuRunner> menu_runner_;
+  std::unique_ptr<StarMenuModel> menu_model_;
+
   BooleanPrefMember edit_bookmarks_enabled_;
 
   DISALLOW_COPY_AND_ASSIGN(StarView);
diff --git a/chrome/browser/ui/views/location_bar/star_view_interactive_uitest.cc b/chrome/browser/ui/views/location_bar/star_view_interactive_uitest.cc
index 5794245..3b277b4 100644
--- a/chrome/browser/ui/views/location_bar/star_view_interactive_uitest.cc
+++ b/chrome/browser/ui/views/location_bar/star_view_interactive_uitest.cc
@@ -10,9 +10,13 @@
 #include "base/strings/utf_string_conversions.h"
 #include "build/build_config.h"
 #include "chrome/browser/bookmarks/bookmark_model_factory.h"
+#include "chrome/browser/ui/read_later/read_later_test_utils.h"
+#include "chrome/browser/ui/read_later/reading_list_model_factory.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/browser/ui/ui_features.h"
 #include "chrome/browser/ui/views/bookmarks/bookmark_bubble_view.h"
 #include "chrome/browser/ui/views/frame/browser_view.h"
+#include "chrome/browser/ui/views/location_bar/star_menu_model.h"
 #include "chrome/browser/ui/views/page_action/page_action_icon_view.h"
 #include "chrome/browser/ui/views/toolbar/toolbar_view.h"
 #include "chrome/common/pref_names.h"
@@ -23,12 +27,15 @@
 #include "components/bookmarks/browser/bookmark_utils.h"
 #include "components/bookmarks/test/bookmark_test_helpers.h"
 #include "components/prefs/pref_service.h"
+#include "components/reading_list/core/reading_list_model.h"
+#include "components/reading_list/core/reading_list_model_observer.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/browser_test_utils.h"
 #include "content/public/test/test_utils.h"
 #include "ui/base/ui_base_switches.h"
 #include "ui/events/event_utils.h"
 #include "ui/views/animation/test/ink_drop_host_view_test_api.h"
+#include "ui/views/test/button_test_api.h"
 
 namespace {
 
@@ -120,4 +127,109 @@
   }
 }
 
+class StarViewTestWithReadLaterEnabled : public InProcessBrowserTest {
+ public:
+  StarViewTestWithReadLaterEnabled() {
+    feature_list_.InitAndEnableFeature(features::kReadLater);
+  }
+  StarViewTestWithReadLaterEnabled(const StarViewTestWithReadLaterEnabled&) =
+      delete;
+  StarViewTestWithReadLaterEnabled& operator=(
+      const StarViewTestWithReadLaterEnabled&) = delete;
+  ~StarViewTestWithReadLaterEnabled() override = default;
+
+  StarView* GetStarIcon() {
+    return static_cast<StarView*>(
+        BrowserView::GetBrowserViewForBrowser(browser())
+            ->toolbar_button_provider()
+            ->GetPageActionIconView(PageActionIconType::kBookmarkStar));
+  }
+
+  void OpenStarViewMenu(StarView* star_icon) {
+    ui::MouseEvent pressed_event(
+        ui::ET_MOUSE_PRESSED, gfx::Point(), gfx::Point(), ui::EventTimeForNow(),
+        ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON);
+    ui::MouseEvent released_event(ui::ET_MOUSE_RELEASED, gfx::Point(),
+                                  gfx::Point(), ui::EventTimeForNow(),
+                                  ui::EF_LEFT_MOUSE_BUTTON,
+                                  ui::EF_LEFT_MOUSE_BUTTON);
+
+    views::test::ButtonTestApi(star_icon).NotifyClick(pressed_event);
+    views::test::ButtonTestApi(star_icon).NotifyClick(released_event);
+  }
+
+ private:
+  base::test::ScopedFeatureList feature_list_;
+};
+
+// Verifies clicking the bookmark button in the StarView's menu bookmarks the
+// page.
+IN_PROC_BROWSER_TEST_F(StarViewTestWithReadLaterEnabled,
+                       AddBookmarkFromStarViewMenuBookmarksUrl) {
+  bookmarks::BookmarkModel* bookmark_model =
+      BookmarkModelFactory::GetForBrowserContext(browser()->profile());
+  bookmarks::test::WaitForBookmarkModelToLoad(bookmark_model);
+
+  StarView* star_icon = GetStarIcon();
+  const GURL current_url =
+      browser()->tab_strip_model()->GetActiveWebContents()->GetVisibleURL();
+
+  // The page should not initially be bookmarked.
+  EXPECT_FALSE(bookmark_model->IsBookmarked(current_url));
+  EXPECT_FALSE(star_icon->active());
+
+  OpenStarViewMenu(star_icon);
+
+  // The page should not be bookmarked when the menu is opened.
+  EXPECT_FALSE(bookmark_model->IsBookmarked(current_url));
+  EXPECT_FALSE(star_icon->active());
+
+  StarMenuModel* menu_model = star_icon->menu_model_for_test();
+
+  // Activate "Add Bookmark" button in the menu and verify the bookmark is
+  // added.
+  const int bookmark_command_index =
+      menu_model->GetIndexOfCommandId(StarMenuModel::CommandBookmark);
+  menu_model->ActivatedAt(bookmark_command_index);
+
+  EXPECT_TRUE(bookmark_model->IsBookmarked(current_url));
+  EXPECT_TRUE(star_icon->active());
+}
+
+// Verifies clicking the Read Later button in the StarView's menu saves the page
+// to the read later model.
+IN_PROC_BROWSER_TEST_F(StarViewTestWithReadLaterEnabled,
+                       AddToReadLaterFromStarViewMenuSavesUrlToReadLater) {
+  ReadingListModel* reading_list_model =
+      ReadingListModelFactory::GetForBrowserContext(browser()->profile());
+  test::ReadingListLoadObserver(reading_list_model).Wait();
+  GURL url("http://www.test.com/");
+  ui_test_utils::NavigateToURL(browser(), url);
+
+  StarView* star_icon = GetStarIcon();
+  const GURL current_url =
+      browser()->tab_strip_model()->GetActiveWebContents()->GetVisibleURL();
+
+  // The page should not initially be in model.
+  EXPECT_EQ(reading_list_model->GetEntryByURL(current_url), nullptr);
+  EXPECT_FALSE(star_icon->active());
+
+  OpenStarViewMenu(star_icon);
+
+  // The page should not be bookmarked when the menu is opened.
+  EXPECT_EQ(reading_list_model->GetEntryByURL(current_url), nullptr);
+  EXPECT_FALSE(star_icon->active());
+
+  StarMenuModel* menu_model = star_icon->menu_model_for_test();
+
+  // // Activate "Add to Read later" button in the menu and verify the entry is
+  // added.
+  const int read_later_command_index =
+      menu_model->GetIndexOfCommandId(StarMenuModel::CommandMoveToReadLater);
+  menu_model->ActivatedAt(read_later_command_index);
+
+  EXPECT_NE(reading_list_model->GetEntryByURL(current_url), nullptr);
+  EXPECT_FALSE(star_icon->active());
+}
+
 }  // namespace
diff --git a/chrome/browser/ui/views/media_router/cloud_services_dialog_view.cc b/chrome/browser/ui/views/media_router/cloud_services_dialog_view.cc
index a132762..66d54583 100644
--- a/chrome/browser/ui/views/media_router/cloud_services_dialog_view.cc
+++ b/chrome/browser/ui/views/media_router/cloud_services_dialog_view.cc
@@ -114,10 +114,11 @@
       views::StyledLabel::RangeStyleInfo::CreateForLink();
   link_style.disable_line_wrapping = false;
 
-  views::StyledLabel* body_text = new views::StyledLabel(text, this);
+  views::StyledLabel* body_text =
+      AddChildView(std::make_unique<views::StyledLabel>(this));
+  body_text->SetText(text);
   body_text->SetHorizontalAlignment(gfx::ALIGN_LEFT);
   body_text->AddStyleRange(learn_more_range, link_style);
-  AddChildView(body_text);
 }
 
 void CloudServicesDialogView::WindowClosing() {
diff --git a/chrome/browser/ui/views/native_file_system/native_file_system_ui_helpers.cc b/chrome/browser/ui/views/native_file_system/native_file_system_ui_helpers.cc
index 4f27f26..3ebc61a 100644
--- a/chrome/browser/ui/views/native_file_system/native_file_system_ui_helpers.cc
+++ b/chrome/browser/ui/views/native_file_system/native_file_system_ui_helpers.cc
@@ -20,9 +20,9 @@
       url_formatter::FormatOriginForSecurityDisplay(
           origin, url_formatter::SchemeDisplay::OMIT_CRYPTOGRAPHIC);
   size_t offset;
-  auto label = std::make_unique<views::StyledLabel>(
-      l10n_util::GetStringFUTF16(message_id, formatted_origin, &offset),
-      nullptr);
+  auto label = std::make_unique<views::StyledLabel>();
+  label->SetText(
+      l10n_util::GetStringFUTF16(message_id, formatted_origin, &offset));
   label->SetTextContext(text_context);
   label->SetDefaultTextStyle(show_emphasis ? views::style::STYLE_SECONDARY
                                            : views::style::STYLE_PRIMARY);
@@ -47,10 +47,9 @@
           origin, url_formatter::SchemeDisplay::OMIT_CRYPTOGRAPHIC);
   base::string16 formatted_path = path.BaseName().LossyDisplayName();
   std::vector<size_t> offsets;
-  auto label = std::make_unique<views::StyledLabel>(
-      l10n_util::GetStringFUTF16(message_id, formatted_origin, formatted_path,
-                                 &offsets),
-      nullptr);
+  auto label = std::make_unique<views::StyledLabel>();
+  label->SetText(l10n_util::GetStringFUTF16(message_id, formatted_origin,
+                                            formatted_path, &offsets));
   DCHECK_GE(offsets.size(), 2u);
 
   label->SetTextContext(text_context);
diff --git a/chrome/browser/ui/views/page_info/page_info_bubble_view.cc b/chrome/browser/ui/views/page_info/page_info_bubble_view.cc
index cffc64f..14b41e3 100644
--- a/chrome/browser/ui/views/page_info/page_info_bubble_view.cc
+++ b/chrome/browser/ui/views/page_info/page_info_bubble_view.cc
@@ -220,8 +220,8 @@
 
   layout->StartRow(views::GridLayout::kFixedSize, label_column_status);
 
-  auto security_details_label = std::make_unique<views::StyledLabel>(
-      base::string16(), styled_label_listener);
+  auto security_details_label =
+      std::make_unique<views::StyledLabel>(styled_label_listener);
   security_details_label->SetID(
       PageInfoBubbleView::VIEW_ID_PAGE_INFO_LABEL_SECURITY_DETAILS);
   security_details_label_ =
@@ -280,8 +280,10 @@
 
   base::string16 text = base::ReplaceStringPlaceholders(
       base::ASCIIToUTF16("$1 $2"), subst, &offsets);
-  auto reset_cert_decisions_label =
-      std::make_unique<views::StyledLabel>(text, styled_label_listener_);
+  views::StyledLabel* reset_cert_decisions_label =
+      reset_decisions_label_container_->AddChildView(
+          std::make_unique<views::StyledLabel>(styled_label_listener_));
+  reset_cert_decisions_label->SetText(text);
   reset_cert_decisions_label->SetID(
       PageInfoBubbleView::VIEW_ID_PAGE_INFO_LABEL_RESET_CERTIFICATE_DECISIONS);
   gfx::Range link_range(offsets[1], text.length());
@@ -293,8 +295,6 @@
   reset_cert_decisions_label->AddStyleRange(link_range, link_style);
   // Fit the styled label to occupy available width.
   reset_cert_decisions_label->SizeToFit(0);
-  reset_decisions_label_container_->AddChildView(
-      std::move(reset_cert_decisions_label));
 
   // Now that it contains a label, the container needs padding at the top.
   reset_decisions_label_container_->SetBorder(views::CreateEmptyBorder(
diff --git a/chrome/browser/ui/views/page_info/page_info_hover_button.cc b/chrome/browser/ui/views/page_info/page_info_hover_button.cc
index a18fc7a..f3443aa 100644
--- a/chrome/browser/ui/views/page_info/page_info_hover_button.cc
+++ b/chrome/browser/ui/views/page_info/page_info_hover_button.cc
@@ -59,15 +59,14 @@
 
   icon_view_ = grid_layout->AddView(std::move(icon));
 
-  auto title_label =
-      std::make_unique<views::StyledLabel>(base::string16(), nullptr);
+  auto title_label = std::make_unique<views::StyledLabel>();
   title_label->SetTextContext(views::style::CONTEXT_LABEL);
-  // |views::StyledLabel|s are all multi-line. With a layout manager,
-  // |StyledLabel| will try use the available space to size itself, and long
-  // titles will wrap to the next line (for smaller |PageInfoHoverButton|s, this
-  // will also cover up |subtitle_|). Wrap it in a parent view with no layout
-  // manager to ensure it keeps its original size set by SizeToFit() above. Long
-  // titles will then be truncated.
+  // views::StyledLabels are all multi-line. With a layout manager, StyledLabel
+  // will try use the available space to size itself, and long titles will wrap
+  // to the next line (for smaller PageInfoHoverButtons, this will also cover up
+  // |subtitle_|). Wrap it in a parent view with no layout manager to ensure it
+  // keeps its original size set by SizeToFit() above. Long titles will then be
+  // truncated.
   auto title_wrapper = std::make_unique<views::View>();
   title_ = title_wrapper->AddChildView(std::move(title_label));
   SetTitleText(title_resource_id, secondary_text);
diff --git a/chrome/browser/ui/views/page_info/safety_tip_page_info_bubble_view.cc b/chrome/browser/ui/views/page_info/safety_tip_page_info_bubble_view.cc
index 3e925b6..3889bf3 100644
--- a/chrome/browser/ui/views/page_info/safety_tip_page_info_bubble_view.cc
+++ b/chrome/browser/ui/views/page_info/safety_tip_page_info_bubble_view.cc
@@ -94,7 +94,8 @@
       kSizeDeltaInPixels, gfx::Font::FontStyle::NORMAL,
       gfx::Font::Weight::NORMAL);
 
-  auto new_title = std::make_unique<views::StyledLabel>(title_text, nullptr);
+  auto new_title = std::make_unique<views::StyledLabel>();
+  new_title->SetText(title_text);
   new_title->AddStyleRange(gfx::Range(0, title_text.length()), name_style);
   GetBubbleFrameView()->SetTitleView(std::move(new_title));
 
@@ -171,7 +172,8 @@
   // More info button.
   auto info_text =
       l10n_util::GetStringUTF16(IDS_PAGE_INFO_SAFETY_TIP_MORE_INFO_LINK);
-  auto info_link = std::make_unique<views::StyledLabel>(info_text, this);
+  auto info_link = std::make_unique<views::StyledLabel>(this);
+  info_link->SetText(info_text);
   views::StyledLabel::RangeStyleInfo link_style =
       views::StyledLabel::RangeStyleInfo::CreateForLink();
   gfx::Range details_range(0, info_text.length());
diff --git a/chrome/browser/ui/views/passwords/password_generation_confirmation_view.cc b/chrome/browser/ui/views/passwords/password_generation_confirmation_view.cc
index 7bf196c..0113a46a 100644
--- a/chrome/browser/ui/views/passwords/password_generation_confirmation_view.cc
+++ b/chrome/browser/ui/views/passwords/password_generation_confirmation_view.cc
@@ -32,8 +32,8 @@
 
   SetButtons(ui::DIALOG_BUTTON_NONE);
 
-  auto label = std::make_unique<views::StyledLabel>(
-      controller_.save_confirmation_text(), this);
+  auto label = std::make_unique<views::StyledLabel>(this);
+  label->SetText(controller_.save_confirmation_text());
   label->SetTextContext(CONTEXT_BODY_TEXT_LARGE);
   label->SetDefaultTextStyle(views::style::STYLE_SECONDARY);
   auto link_style = views::StyledLabel::RangeStyleInfo::CreateForLink();
diff --git a/chrome/browser/ui/views/passwords/post_save_compromised_bubble_view.cc b/chrome/browser/ui/views/passwords/post_save_compromised_bubble_view.cc
index 3adb5efd..7ca73d76 100644
--- a/chrome/browser/ui/views/passwords/post_save_compromised_bubble_view.cc
+++ b/chrome/browser/ui/views/passwords/post_save_compromised_bubble_view.cc
@@ -32,8 +32,8 @@
     SetButtonLabel(ui::DIALOG_BUTTON_OK, std::move(button));
   }
 
-  auto label =
-      std::make_unique<views::StyledLabel>(controller_.GetBody(), this);
+  auto label = std::make_unique<views::StyledLabel>(this);
+  label->SetText(controller_.GetBody());
   label->SetTextContext(CONTEXT_BODY_TEXT_LARGE);
   label->SetDefaultTextStyle(views::style::STYLE_SECONDARY);
   gfx::Range range = controller_.GetSettingLinkRange();
diff --git a/chrome/browser/ui/views/payments/credit_card_editor_view_controller.cc b/chrome/browser/ui/views/payments/credit_card_editor_view_controller.cc
index 1cfab8a..ce5970a 100644
--- a/chrome/browser/ui/views/payments/credit_card_editor_view_controller.cc
+++ b/chrome/browser/ui/views/payments/credit_card_editor_view_controller.cc
@@ -258,9 +258,10 @@
             .release());
 
     // "Edit" link.
+    auto edit_link = std::make_unique<views::StyledLabel>(this);
     base::string16 link_text =
         l10n_util::GetStringUTF16(IDS_AUTOFILL_WALLET_MANAGEMENT_LINK_TEXT);
-    auto edit_link = std::make_unique<views::StyledLabel>(link_text, this);
+    edit_link->SetText(link_text);
     edit_link->SetID(
         static_cast<int>(DialogViewID::GOOGLE_PAYMENTS_EDIT_LINK_LABEL));
     edit_link->AddStyleRange(
diff --git a/chrome/browser/ui/views/payments/payment_sheet_view_controller.cc b/chrome/browser/ui/views/payments/payment_sheet_view_controller.cc
index c5465bb..b18ec56 100644
--- a/chrome/browser/ui/views/payments/payment_sheet_view_controller.cc
+++ b/chrome/browser/ui/views/payments/payment_sheet_view_controller.cc
@@ -981,6 +981,9 @@
   // BEGIN_LINK and END_LINK. Find the beginning of the link range and the
   // length of the "settings" part, then remove the BEGIN_LINK and END_LINK
   // parts and linkify "settings".
+  // TODO(pkasting): Remove these BEGIN/END_LINK tags and use a substitution for
+  // "Settings", allowing this code to use the offset-returning versions of the
+  // l10n getters.
   base::string16 begin_tag = base::UTF8ToUTF16("BEGIN_LINK");
   base::string16 end_tag = base::UTF8ToUTF16("END_LINK");
   size_t link_begin = data_source.find(begin_tag);
@@ -993,8 +996,8 @@
   data_source.erase(link_end, end_tag.size());
   data_source.erase(link_begin, begin_tag.size());
 
-  std::unique_ptr<views::StyledLabel> data_source_label =
-      std::make_unique<views::StyledLabel>(data_source, this);
+  auto data_source_label = std::make_unique<views::StyledLabel>(this);
+  data_source_label->SetText(data_source);
   data_source_label->SetBorder(views::CreateEmptyBorder(22, 0, 0, 0));
   data_source_label->SetID(static_cast<int>(DialogViewID::DATA_SOURCE_LABEL));
   data_source_label->SetDefaultTextStyle(views::style::STYLE_DISABLED);
diff --git a/chrome/browser/ui/views/platform_keys_certificate_selector_chromeos.cc b/chrome/browser/ui/views/platform_keys_certificate_selector_chromeos.cc
index 73206a5..0b76c52b 100644
--- a/chrome/browser/ui/views/platform_keys_certificate_selector_chromeos.cc
+++ b/chrome/browser/ui/views/platform_keys_certificate_selector_chromeos.cc
@@ -82,12 +82,10 @@
 void PlatformKeysCertificateSelector::Init() {
   const base::string16 name = base::ASCIIToUTF16(extension_name_);
 
+  auto label = std::make_unique<views::StyledLabel>();
   size_t offset;
-  const base::string16 text = l10n_util::GetStringFUTF16(
-      IDS_PLATFORM_KEYS_SELECT_CERT_DIALOG_TEXT, name, &offset);
-
-  std::unique_ptr<views::StyledLabel> label(
-      new views::StyledLabel(text, nullptr /* no listener */));
+  label->SetText(l10n_util::GetStringFUTF16(
+      IDS_PLATFORM_KEYS_SELECT_CERT_DIALOG_TEXT, name, &offset));
 
   views::StyledLabel::RangeStyleInfo bold_style;
   bold_style.text_style = STYLE_EMPHASIZED;
diff --git a/chrome/browser/ui/views/safe_browsing/password_reuse_modal_warning_dialog.cc b/chrome/browser/ui/views/safe_browsing/password_reuse_modal_warning_dialog.cc
index 9f1a768..6a4214a5 100644
--- a/chrome/browser/ui/views/safe_browsing/password_reuse_modal_warning_dialog.cc
+++ b/chrome/browser/ui/views/safe_browsing/password_reuse_modal_warning_dialog.cc
@@ -208,7 +208,8 @@
 
   // Bold the domains in the message body label.
   views::StyledLabel* const styled_message_body_label =
-      new views::StyledLabel(message_body, nullptr);
+      content->AddChildView(std::make_unique<views::StyledLabel>());
+  styled_message_body_label->SetText(message_body);
   views::StyledLabel::RangeStyleInfo bold_style;
   bold_style.text_style = STYLE_EMPHASIZED;
   for (size_t idx = 0; idx < placeholder_offsets.size(); idx++) {
@@ -218,7 +219,6 @@
         bold_style);
   }
   styled_message_body_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
-  content->AddChildView(std::move(styled_message_body_label));
   AddChildView(std::move(illustration));
   AddChildView(std::move(content));
 }
diff --git a/chrome/browser/ui/views/safe_browsing/prompt_for_scanning_modal_dialog.cc b/chrome/browser/ui/views/safe_browsing/prompt_for_scanning_modal_dialog.cc
index cd40003..13d0b312 100644
--- a/chrome/browser/ui/views/safe_browsing/prompt_for_scanning_modal_dialog.cc
+++ b/chrome/browser/ui/views/safe_browsing/prompt_for_scanning_modal_dialog.cc
@@ -80,7 +80,8 @@
       &offsets);
 
   // Add the message label.
-  auto label = std::make_unique<views::StyledLabel>(message_text, this);
+  auto label = std::make_unique<views::StyledLabel>(this);
+  label->SetText(message_text);
 
   gfx::Range learn_more_range(offsets[1], message_text.length());
   views::StyledLabel::RangeStyleInfo link_style =
diff --git a/chrome/browser/ui/views/session_crashed_bubble_view.cc b/chrome/browser/ui/views/session_crashed_bubble_view.cc
index 4330e2e..5a50d3b 100644
--- a/chrome/browser/ui/views/session_crashed_bubble_view.cc
+++ b/chrome/browser/ui/views/session_crashed_bubble_view.cc
@@ -239,11 +239,12 @@
   size_t offset;
   base::string16 link_text =
       l10n_util::GetStringUTF16(IDS_SESSION_CRASHED_BUBBLE_UMA_LINK_TEXT);
+  auto uma_label = std::make_unique<views::StyledLabel>(this);
   base::string16 uma_text = l10n_util::GetStringFUTF16(
       IDS_SESSION_CRASHED_VIEW_UMA_OPTIN,
       link_text,
       &offset);
-  auto uma_label = std::make_unique<views::StyledLabel>(uma_text, this);
+  uma_label->SetText(uma_text);
   uma_label->AddStyleRange(gfx::Range(offset, offset + link_text.length()),
                            views::StyledLabel::RangeStyleInfo::CreateForLink());
   views::StyledLabel::RangeStyleInfo uma_style;
diff --git a/chrome/browser/ui/views/settings_reset_prompt_dialog.cc b/chrome/browser/ui/views/settings_reset_prompt_dialog.cc
index e2d3f97f..d921156 100644
--- a/chrome/browser/ui/views/settings_reset_prompt_dialog.cc
+++ b/chrome/browser/ui/views/settings_reset_prompt_dialog.cc
@@ -68,13 +68,13 @@
   SetLayoutManager(std::make_unique<views::FillLayout>());
 
   views::StyledLabel* dialog_label =
-      new views::StyledLabel(controller_->GetMainText(), /*listener=*/nullptr);
+      AddChildView(std::make_unique<views::StyledLabel>());
+  dialog_label->SetText(controller_->GetMainText());
   dialog_label->SetTextContext(CONTEXT_BODY_TEXT_LARGE);
   dialog_label->SetDefaultTextStyle(views::style::STYLE_SECONDARY);
   views::StyledLabel::RangeStyleInfo url_style;
   url_style.text_style = STYLE_EMPHASIZED_SECONDARY;
   dialog_label->AddStyleRange(controller_->GetMainTextUrlRange(), url_style);
-  AddChildView(dialog_label);
 }
 
 SettingsResetPromptDialog::~SettingsResetPromptDialog() {
diff --git a/chrome/browser/ui/views/sharing/sharing_dialog_view.cc b/chrome/browser/ui/views/sharing/sharing_dialog_view.cc
index 9301209..2a860802 100644
--- a/chrome/browser/ui/views/sharing/sharing_dialog_view.cc
+++ b/chrome/browser/ui/views/sharing/sharing_dialog_view.cc
@@ -126,10 +126,10 @@
   DCHECK_NE(0, data.help_link_text_id);
   const base::string16 link = l10n_util::GetStringUTF16(data.help_link_text_id);
   size_t offset;
-  const base::string16 text =
-      show_origin ? PrepareHelpTextWithOrigin(data, link, &offset)
-                  : PrepareHelpTextWithoutOrigin(data, link, &offset);
-  auto label = std::make_unique<views::StyledLabel>(text, listener);
+  auto label = std::make_unique<views::StyledLabel>(listener);
+  label->SetText(show_origin
+                     ? PrepareHelpTextWithOrigin(data, link, &offset)
+                     : PrepareHelpTextWithoutOrigin(data, link, &offset));
   views::StyledLabel::RangeStyleInfo link_style =
       views::StyledLabel::RangeStyleInfo::CreateForLink();
   label->AddStyleRange(gfx::Range(offset, offset + link.length()), link_style);
diff --git a/chrome/browser/ui/views/sync/profile_signin_confirmation_dialog_views.cc b/chrome/browser/ui/views/sync/profile_signin_confirmation_dialog_views.cc
index b0f51ed..80ff2b9 100644
--- a/chrome/browser/ui/views/sync/profile_signin_confirmation_dialog_views.cc
+++ b/chrome/browser/ui/views/sync/profile_signin_confirmation_dialog_views.cc
@@ -153,7 +153,8 @@
       l10n_util::GetStringFUTF16(
           IDS_ENTERPRISE_SIGNIN_ALERT,
           domain, &offset);
-  auto prompt_label = std::make_unique<views::StyledLabel>(prompt_text, this);
+  auto prompt_label = std::make_unique<views::StyledLabel>(this);
+  prompt_label->SetText(prompt_text);
   prompt_label->SetDisplayedOnBackgroundColor(kPromptBarBackgroundColor);
 
   views::StyledLabel::RangeStyleInfo bold_style;
@@ -178,8 +179,8 @@
           IDS_ENTERPRISE_SIGNIN_EXPLANATION_WITH_PROFILE_CREATION :
           IDS_ENTERPRISE_SIGNIN_EXPLANATION_WITHOUT_PROFILE_CREATION,
           username, learn_more_text, &offsets);
-  auto explanation_label =
-      std::make_unique<views::StyledLabel>(signin_explanation_text, this);
+  auto explanation_label = std::make_unique<views::StyledLabel>(this);
+  explanation_label->SetText(signin_explanation_text);
   explanation_label->AddStyleRange(
       gfx::Range(offsets[1], offsets[1] + learn_more_text.size()),
       views::StyledLabel::RangeStyleInfo::CreateForLink());
diff --git a/chrome/browser/ui/views/toolbar/home_button.cc b/chrome/browser/ui/views/toolbar/home_button.cc
index 1bbffb4..868b8e8 100644
--- a/chrome/browser/ui/views/toolbar/home_button.cc
+++ b/chrome/browser/ui/views/toolbar/home_button.cc
@@ -106,9 +106,10 @@
       l10n_util::GetStringUTF16(IDS_ONE_CLICK_BUBBLE_UNDO);
   std::vector<base::string16> message = {
       l10n_util::GetStringUTF16(IDS_TOOLBAR_INFORM_SET_HOME_PAGE), undo_string};
-  views::StyledLabel* label = new views::StyledLabel(
-      base::JoinString(message, base::StringPiece16(base::ASCIIToUTF16(" "))),
-      this);
+  views::StyledLabel* label =
+      AddChildView(std::make_unique<views::StyledLabel>(this));
+  label->SetText(
+      base::JoinString(message, base::StringPiece16(base::ASCIIToUTF16(" "))));
 
   gfx::Range undo_range(label->GetText().length() - undo_string.length(),
                         label->GetText().length());
@@ -117,7 +118,6 @@
 
   // Ensure StyledLabel has a cached size to return in GetPreferredSize().
   label->SizeToFit(0);
-  AddChildView(label);
 }
 
 void HomePageUndoBubble::StyledLabelLinkClicked(views::StyledLabel* label,
diff --git a/chrome/browser/ui/webui/settings/chromeos/multidevice_handler.cc b/chrome/browser/ui/webui/settings/chromeos/multidevice_handler.cc
index b359de4..fedbb19 100644
--- a/chrome/browser/ui/webui/settings/chromeos/multidevice_handler.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/multidevice_handler.cc
@@ -16,7 +16,6 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/webui/chromeos/multidevice_setup/multidevice_setup_dialog.h"
 #include "chromeos/components/multidevice/logging/logging.h"
-#include "chromeos/components/phonehub/notification_access_manager.h"
 #include "chromeos/components/proximity_auth/proximity_auth_pref_names.h"
 #include "chromeos/constants/chromeos_features.h"
 #include "chromeos/services/multidevice_setup/public/cpp/prefs.h"
@@ -44,6 +43,7 @@
 const char kPageContentDataPhoneHubTaskContinuationStateKey[] =
     "phoneHubTaskContinuationState";
 const char kPageContentDataSmartLockStateKey[] = "smartLockState";
+const char kIsNotificationAccessGranted[] = "isNotificationAccessGranted";
 const char kIsAndroidSmsPairingComplete[] = "isAndroidSmsPairingComplete";
 
 constexpr char kAndroidSmsInfoOriginKey[] = "origin";
@@ -73,7 +73,8 @@
       android_sms_app_manager_(android_sms_app_manager),
       multidevice_setup_observer_(this),
       android_sms_pairing_state_tracker_observer_(this),
-      android_sms_app_manager_observer_(this) {
+      android_sms_app_manager_observer_(this),
+      notification_access_manager_observer_(this) {
   pref_change_registrar_.Init(prefs_);
 }
 
@@ -134,6 +135,9 @@
   if (multidevice_setup_client_)
     multidevice_setup_observer_.Add(multidevice_setup_client_);
 
+  if (notification_access_manager_)
+    notification_access_manager_observer_.Add(notification_access_manager_);
+
   if (android_sms_pairing_state_tracker_) {
     android_sms_pairing_state_tracker_observer_.Add(
         android_sms_pairing_state_tracker_);
@@ -160,8 +164,10 @@
   if (multidevice_setup_client_)
     multidevice_setup_observer_.Remove(multidevice_setup_client_);
 
-  if (notification_access_manager_)
+  if (notification_access_manager_) {
+    notification_access_manager_observer_.Remove(notification_access_manager_);
     notification_access_operation_.reset();
+  }
 
   if (android_sms_pairing_state_tracker_) {
     android_sms_pairing_state_tracker_observer_.Remove(
@@ -189,6 +195,10 @@
   NotifyAndroidSmsInfoChange();
 }
 
+void MultideviceHandler::OnNotificationAccessChanged() {
+  UpdatePageContent();
+}
+
 void MultideviceHandler::OnPairingStateChanged() {
   UpdatePageContent();
   NotifyAndroidSmsInfoChange();
@@ -385,7 +395,7 @@
 
 void MultideviceHandler::OnStatusChange(
     phonehub::NotificationAccessSetupOperation::Status new_status) {
-  FireWebUIListener("notification-access-setup-operation-status-changed",
+  FireWebUIListener("settings.onNotificationAccessSetupStatusChanged",
                     base::Value(static_cast<int32_t>(new_status)));
 
   if (phonehub::NotificationAccessSetupOperation::IsFinalStatus(new_status))
@@ -459,6 +469,12 @@
           ? android_sms_pairing_state_tracker_->IsAndroidSmsPairingComplete()
           : false);
 
+  page_content_dictionary->SetBoolean(
+      kIsNotificationAccessGranted,
+      notification_access_manager_
+          ? notification_access_manager_->HasAccessBeenGranted()
+          : false);
+
   return page_content_dictionary;
 }
 
diff --git a/chrome/browser/ui/webui/settings/chromeos/multidevice_handler.h b/chrome/browser/ui/webui/settings/chromeos/multidevice_handler.h
index 56fdd03..e4217ae 100644
--- a/chrome/browser/ui/webui/settings/chromeos/multidevice_handler.h
+++ b/chrome/browser/ui/webui/settings/chromeos/multidevice_handler.h
@@ -12,6 +12,7 @@
 #include "chrome/browser/chromeos/android_sms/android_sms_service_factory.h"
 #include "chrome/browser/ui/webui/settings/settings_page_ui_handler.h"
 #include "chromeos/components/multidevice/remote_device_ref.h"
+#include "chromeos/components/phonehub/notification_access_manager.h"
 #include "chromeos/components/phonehub/notification_access_setup_operation.h"
 #include "chromeos/services/multidevice_setup/public/cpp/multidevice_setup_client.h"
 #include "chromeos/services/multidevice_setup/public/mojom/multidevice_setup.mojom-forward.h"
@@ -37,6 +38,7 @@
       public multidevice_setup::MultiDeviceSetupClient::Observer,
       public multidevice_setup::AndroidSmsPairingStateTracker::Observer,
       public android_sms::AndroidSmsAppManager::Observer,
+      public phonehub::NotificationAccessManager::Observer,
       public phonehub::NotificationAccessSetupOperation::Delegate {
  public:
   MultideviceHandler(
@@ -69,6 +71,9 @@
   void OnStatusChange(
       phonehub::NotificationAccessSetupOperation::Status new_status) override;
 
+  // phonehub::NotificationAccessManager::Observer:
+  void OnNotificationAccessChanged() override;
+
   // multidevice_setup::AndroidSmsPairingStateTracker::Observer:
   void OnPairingStateChanged() override;
 
@@ -141,6 +146,9 @@
   ScopedObserver<android_sms::AndroidSmsAppManager,
                  android_sms::AndroidSmsAppManager::Observer>
       android_sms_app_manager_observer_;
+  ScopedObserver<phonehub::NotificationAccessManager,
+                 phonehub::NotificationAccessManager::Observer>
+      notification_access_manager_observer_;
 
   // Used to cancel callbacks when JavaScript becomes disallowed.
   base::WeakPtrFactory<MultideviceHandler> callback_weak_ptr_factory_{this};
diff --git a/chrome/browser/ui/webui/settings/chromeos/multidevice_handler_unittest.cc b/chrome/browser/ui/webui/settings/chromeos/multidevice_handler_unittest.cc
index 96a3b82..846566d 100644
--- a/chrome/browser/ui/webui/settings/chromeos/multidevice_handler_unittest.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/multidevice_handler_unittest.cc
@@ -361,12 +361,18 @@
     fake_notification_access_manager()->SetNotificationSetupOperationStatus(
         status);
 
+    bool completed_successfully = status ==
+                                  phonehub::NotificationAccessSetupOperation::
+                                      Status::kCompletedSuccessfully;
+    if (completed_successfully)
+      call_data_count_before_call++;
+
     EXPECT_EQ(call_data_count_before_call + 1u,
               test_web_ui()->call_data().size());
     const content::TestWebUI::CallData& call_data =
         CallDataAtIndex(call_data_count_before_call);
     EXPECT_EQ("cr.webUIListenerCallback", call_data.function_name());
-    EXPECT_EQ("notification-access-setup-operation-status-changed",
+    EXPECT_EQ("settings.onNotificationAccessSetupStatusChanged",
               call_data.arg1()->GetString());
     EXPECT_EQ(call_data.arg2()->GetInt(), static_cast<int32_t>(status));
   }
diff --git a/chrome/browser/ui/webui/settings/chromeos/multidevice_section.cc b/chrome/browser/ui/webui/settings/chromeos/multidevice_section.cc
index c1d1df9..8107e64 100644
--- a/chrome/browser/ui/webui/settings/chromeos/multidevice_section.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/multidevice_section.cc
@@ -246,6 +246,16 @@
        IDS_SETTINGS_MULTIDEVICE_PHONE_HUB_NOTIFICATION_BADGE_SECTION_TITLE},
       {"multidevicePhoneHubTaskContinuationItemTitle",
        IDS_SETTINGS_MULTIDEVICE_PHONE_HUB_TASK_CONTINUATION_SECTION_TITLE},
+      {"multideviceNotificationAccessSetupAckTitle",
+       IDS_SETTINGS_MULTIDEVICE_NOTIFICATION_ACCESS_SETUP_DIALOG_ACK_TITLE},
+      {"multideviceNotificationAccessSetupConnectingTitle",
+       IDS_SETTINGS_MULTIDEVICE_NOTIFICATION_ACCESS_SETUP_DIALOG_CONNECTING_TITLE},
+      {"multideviceNotificationAccessSetupInstructions",
+       IDS_SETTINGS_MULTIDEVICE_NOTIFICATION_ACCESS_SETUP_DIALOG_INSTRUCTIONS},
+      {"multideviceNotificationAccessSetupCompletedTitle",
+       IDS_SETTINGS_MULTIDEVICE_NOTIFICATION_ACCESS_SETUP_DIALOG_COMPLETED_TITLE},
+      {"multideviceNotificationAccessSetupCompletedSummary",
+       IDS_SETTINGS_MULTIDEVICE_NOTIFICATION_ACCESS_SETUP_DIALOG_COMPLETED_SUMMARY},
       {"multideviceInstantTetheringItemTitle",
        IDS_SETTINGS_MULTIDEVICE_INSTANT_TETHERING},
       {"multideviceInstantTetheringItemSummary",
diff --git a/chrome/services/sharing/nearby/decoder/BUILD.gn b/chrome/services/sharing/nearby/decoder/BUILD.gn
index 1d7ac30..f4845dc23 100644
--- a/chrome/services/sharing/nearby/decoder/BUILD.gn
+++ b/chrome/services/sharing/nearby/decoder/BUILD.gn
@@ -37,8 +37,17 @@
   ]
 }
 
-fuzzer_test("nearby_decoder_fuzzer") {
-  sources = [ "nearby_decoder_fuzzer.cc" ]
+fuzzer_test("nearby_decoder_decode_advertisement_fuzzer") {
+  sources = [ "nearby_decoder_decode_advertisement_fuzzer.cc" ]
+  deps = [
+    ":decoder",
+    "//base",
+    "//mojo/core/embedder",
+  ]
+}
+
+fuzzer_test("nearby_decoder_decode_frame_fuzzer") {
+  sources = [ "nearby_decoder_decode_frame_fuzzer.cc" ]
   deps = [
     ":decoder",
     "//base",
diff --git a/chrome/services/sharing/nearby/decoder/DEPS b/chrome/services/sharing/nearby/decoder/DEPS
index fa225c4..4775b2e0 100644
--- a/chrome/services/sharing/nearby/decoder/DEPS
+++ b/chrome/services/sharing/nearby/decoder/DEPS
@@ -1,5 +1,5 @@
 specific_include_rules = {
-  "nearby_decoder_fuzzer.cc": [
+  ".*_fuzzer\.cc": [
     "+mojo/core/embedder/embedder.h",
   ]
 }
diff --git a/chrome/services/sharing/nearby/decoder/nearby_decoder_fuzzer.cc b/chrome/services/sharing/nearby/decoder/nearby_decoder_decode_advertisement_fuzzer.cc
similarity index 100%
rename from chrome/services/sharing/nearby/decoder/nearby_decoder_fuzzer.cc
rename to chrome/services/sharing/nearby/decoder/nearby_decoder_decode_advertisement_fuzzer.cc
diff --git a/chrome/services/sharing/nearby/decoder/nearby_decoder_decode_frame_fuzzer.cc b/chrome/services/sharing/nearby/decoder/nearby_decoder_decode_frame_fuzzer.cc
new file mode 100644
index 0000000..cd68c0bb
--- /dev/null
+++ b/chrome/services/sharing/nearby/decoder/nearby_decoder_decode_frame_fuzzer.cc
@@ -0,0 +1,51 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/services/sharing/nearby/decoder/nearby_decoder.h"
+
+#include <stddef.h>
+#include <stdint.h>
+#include <memory>
+#include <vector>
+
+#include "base/logging.h"
+#include "base/no_destructor.h"
+#include "base/run_loop.h"
+#include "base/task/single_thread_task_executor.h"
+#include "chrome/services/sharing/public/mojom/nearby_decoder.mojom.h"
+#include "chrome/services/sharing/public/mojom/nearby_decoder_types.mojom.h"
+#include "mojo/core/embedder/embedder.h"
+#include "mojo/public/cpp/bindings/remote.h"
+
+struct Environment {
+  Environment() {
+    mojo::core::Init();
+    // Disable noisy logging as per "libFuzzer in Chrome" documentation:
+    // testing/libfuzzer/getting_started.md#Disable-noisy-error-message-logging.
+    logging::SetMinLogLevel(logging::LOG_FATAL);
+
+    // Create instance once to be reused between fuzzing rounds.
+    decoder = std::make_unique<sharing::NearbySharingDecoder>(
+        remote.BindNewPipeAndPassReceiver());
+  }
+
+  base::SingleThreadTaskExecutor task_executor;
+  mojo::Remote<sharing::mojom::NearbySharingDecoder> remote;
+  std::unique_ptr<sharing::NearbySharingDecoder> decoder;
+};
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+  static base::NoDestructor<Environment> environment;
+
+  std::vector<uint8_t> buffer(data, data + size);
+  base::RunLoop run_loop;
+  environment->decoder->DecodeFrame(
+      buffer,
+      base::BindOnce([](base::RunLoop* run_loop,
+                        sharing::mojom::FramePtr frame) { run_loop->Quit(); },
+                     &run_loop));
+  run_loop.Run();
+
+  return 0;
+}
diff --git a/chrome/services/sharing/nearby/nearby_connections_unittest.cc b/chrome/services/sharing/nearby/nearby_connections_unittest.cc
index 9c25ebe..77a5318 100644
--- a/chrome/services/sharing/nearby/nearby_connections_unittest.cc
+++ b/chrome/services/sharing/nearby/nearby_connections_unittest.cc
@@ -649,7 +649,7 @@
   payload_progress_run_loop.Run();
 }
 
-// TOOD(crbug/1076008): Re-enable test after upprev NearbyConnections.
+// TODO(crbug/1076008): Re-enable test after upprev NearbyConnections.
 TEST_F(NearbyConnectionsTest, DISABLED_SendBytesPayload) {
   const std::vector<uint8_t> expected_payload(std::begin(kPayload),
                                               std::end(kPayload));
@@ -693,7 +693,7 @@
   send_payload_run_loop.Run();
 }
 
-// TOOD(crbug/1076008): Re-enable test after upprev NearbyConnections.
+// TODO(crbug/1076008): Re-enable test after upprev NearbyConnections.
 TEST_F(NearbyConnectionsTest, DISABLED_SendBytesPayloadCancelled) {
   const std::vector<uint8_t> expected_payload(std::begin(kPayload),
                                               std::end(kPayload));
@@ -751,7 +751,7 @@
   cancel_payload_run_loop.Run();
 }
 
-// TOOD(crbug/1076008): Re-enable test after upprev NearbyConnections.
+// TODO(crbug/1076008): Re-enable test after upprev NearbyConnections.
 TEST_F(NearbyConnectionsTest, DISABLED_SendFilePayload) {
   const std::vector<uint8_t> expected_payload(std::begin(kPayload),
                                               std::end(kPayload));
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 07d4b69c..c6c9da6 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -4986,7 +4986,6 @@
       "../browser/extensions/update_install_gate_unittest.cc",
       "../browser/extensions/updater/extension_update_client_command_line_config_policy_unittest.cc",
       "../browser/extensions/updater/extension_updater_unittest.cc",
-      "../browser/extensions/updater/parallel_unpacker_unittest.cc",
       "../browser/extensions/user_script_listener_unittest.cc",
       "../browser/extensions/warning_badge_service_unittest.cc",
       "../browser/extensions/webstore_installer_unittest.cc",
@@ -6195,6 +6194,8 @@
 
     if (toolkit_views) {
       sources += [
+        "../browser/ui/read_later/read_later_test_utils.cc",
+        "../browser/ui/read_later/read_later_test_utils.h",
         "../browser/ui/views/bookmarks/bookmark_bar_view_test.cc",
         "../browser/ui/views/bookmarks/bookmark_bar_view_test_helper.h",
         "../browser/ui/views/certificate_selector_browsertest.cc",
diff --git a/chrome/test/data/ads_observer/ad_with_incomplete_resource.html b/chrome/test/data/ads_observer/ad_with_incomplete_resource.html
index 38a8c05..b5df233c 100644
--- a/chrome/test/data/ads_observer/ad_with_incomplete_resource.html
+++ b/chrome/test/data/ads_observer/ad_with_incomplete_resource.html
@@ -6,6 +6,10 @@
   let adIframe = createAdIframe();
   // "incomplete_resource.js" should never be a complete resource load.
   adIframe.srcdoc = "<link rel='preload' href='incomplete_resource.js' as='script'>";
+
+  function advanceSrcdoc() {
+    adIframe.srcdoc =  "<link rel='preload' href='incomplete_resource2.js' as='script'>";
+  }
 </script>
 
 </body>
diff --git a/chrome/test/data/policy/policy_test_cases.json b/chrome/test/data/policy/policy_test_cases.json
index 72f82c9..a773a8c 100644
--- a/chrome/test/data/policy/policy_test_cases.json
+++ b/chrome/test/data/policy/policy_test_cases.json
@@ -1743,8 +1743,12 @@
     "can_be_recommended": true,
     "policy_pref_mapping_test": [
       {
+        "policies": { "BlockThirdPartyCookies": false },
+        "prefs": { "profile.cookie_controls_mode": { "value": 0} }
+      },
+      {
         "policies": { "BlockThirdPartyCookies": true },
-        "prefs": { "profile.block_third_party_cookies": {} }
+        "prefs": { "profile.cookie_controls_mode": { "value": 1} }
       }
     ]
   },
diff --git a/chrome/test/data/webui/settings/chromeos/multidevice_notification_access_setup_dialog_tests.js b/chrome/test/data/webui/settings/chromeos/multidevice_notification_access_setup_dialog_tests.js
new file mode 100644
index 0000000..db838dd
--- /dev/null
+++ b/chrome/test/data/webui/settings/chromeos/multidevice_notification_access_setup_dialog_tests.js
@@ -0,0 +1,97 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// clang-format off
+// #import 'chrome://os-settings/chromeos/os_settings.js';
+
+// #import {assertEquals, assertFalse, assertNotEquals, assertTrue} from '../../chai_assert.js';
+// #import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+
+/**
+ * @fileoverview
+ * Suite of tests for multidevice-notification-access-setup-dialog element.
+ */
+suite('Multidevice', () => {
+  /** @type {?TestMultideviceBrowserProxy} */
+  let browserProxy;
+
+  /** @type {?MultideviceNotificationsOptInDialog} */
+  let notificationAccessSetupDialog = null;
+
+  /** @type {?HTMLElement} */
+  let buttonContainer = null;
+
+  /**
+   * @param {NotificationAccessSetupOperationStatus} status
+   */
+  function simulateStatusChanged(status) {
+    cr.webUIListenerCallback('settings.onNotificationAccessSetupStatusChanged',
+        status);
+    Polymer.dom.flush();
+  }
+
+  setup(() => {
+    PolymerTest.clearBody();
+    browserProxy = new multidevice.TestMultideviceBrowserProxy();
+    settings.MultiDeviceBrowserProxyImpl.instance_ = browserProxy;
+
+    notificationAccessSetupDialog =
+        document.createElement(
+            'settings-multidevice-notification-access-setup-dialog');
+    document.body.appendChild(notificationAccessSetupDialog);
+    Polymer.dom.flush();
+    buttonContainer =
+        assert(notificationAccessSetupDialog.$$('#buttonContainer'));
+  });
+
+  test('Test success flow', async () => {
+    // The cancel and confirm buttons should be visible, and ok button hidden.
+    assertTrue(!!buttonContainer.querySelector('#cancelButton'));
+    assertTrue(!!buttonContainer.querySelector('#confirmButton'));
+    assertFalse(!!buttonContainer.querySelector('#okButton'));
+    buttonContainer.querySelector('#confirmButton').click();
+    assertEquals(browserProxy.getCallCount('attemptNotificationSetup'), 1);
+
+    simulateStatusChanged(NotificationAccessSetupOperationStatus.CONNECTING);
+
+    // The ok and confirm buttons should be hidden, and cancel button visible.
+    assertTrue(!!buttonContainer.querySelector('#cancelButton'));
+    assertFalse(!!buttonContainer.querySelector('#confirmButton'));
+    assertFalse(!!buttonContainer.querySelector('#okButton'));
+
+    simulateStatusChanged(
+        NotificationAccessSetupOperationStatus.COMPLETED_SUCCESSFULLY);
+
+    // The cancel and confirm buttons should be hidden, and ok button visible.
+    assertFalse(!!buttonContainer.querySelector('#cancelButton'));
+    assertFalse(!!buttonContainer.querySelector('#confirmButton'));
+    assertTrue(!!buttonContainer.querySelector('#okButton'));
+
+    assertTrue(notificationAccessSetupDialog.$$('#dialog').open);
+    buttonContainer.querySelector('#okButton').click();
+    assertFalse(notificationAccessSetupDialog.$$('#dialog').open);
+  });
+
+  test('Test cancel during connecting flow', async () => {
+    // The cancel and confirm buttons should be visible, and ok button hidden.
+    assertTrue(!!buttonContainer.querySelector('#cancelButton'));
+    assertTrue(!!buttonContainer.querySelector('#confirmButton'));
+    assertFalse(!!buttonContainer.querySelector('#okButton'));
+    buttonContainer.querySelector('#confirmButton').click();
+    assertEquals(browserProxy.getCallCount('attemptNotificationSetup'), 1);
+
+    simulateStatusChanged(NotificationAccessSetupOperationStatus.CONNECTING);
+
+    // The ok and confirm buttons should be hidden, and cancel button visible.
+    assertTrue(!!buttonContainer.querySelector('#cancelButton'));
+    assertFalse(!!buttonContainer.querySelector('#confirmButton'));
+    assertFalse(!!buttonContainer.querySelector('#okButton'));
+
+    buttonContainer.querySelector('#cancelButton').click();
+    assertEquals(browserProxy.getCallCount('cancelNotificationSetup'), 1);
+
+    assertFalse(notificationAccessSetupDialog.$$('#dialog').open);
+  });
+
+});
diff --git a/chrome/test/data/webui/settings/chromeos/multidevice_page_tests.js b/chrome/test/data/webui/settings/chromeos/multidevice_page_tests.js
index ab03c40..6b6725b 100644
--- a/chrome/test/data/webui/settings/chromeos/multidevice_page_tests.js
+++ b/chrome/test/data/webui/settings/chromeos/multidevice_page_tests.js
@@ -41,17 +41,41 @@
         multidevice.createFakePageContentData(newMode, opt_newHostDeviceName));
   }
 
+  /**
+   * @param {settings.MultiDeviceFeatureState} newState
+   */
   function setSuiteState(newState) {
     setPageContentData(Object.assign(
         {}, multidevicePage.pageContentData, {betterTogetherState: newState}));
   }
 
+  /**
+   * @param {settings.MultiDeviceFeatureState} newState
+   */
   function setSmartLockState(newState) {
     setPageContentData(Object.assign(
         {}, multidevicePage.pageContentData, {smartLockState: newState}));
   }
 
   /**
+   * @param {settings.MultiDeviceFeatureState} newState
+   */
+  function setPhoneHubNotificationsState(newState) {
+    setPageContentData(Object.assign(
+        {}, multidevicePage.pageContentData,
+        {phoneHubNotificationsState: newState}));
+  }
+
+  /**
+   * @param {boolean} accessGranted
+   */
+  function setPhoneHubNotificationAccessGranted(accessGranted) {
+    setPageContentData(Object.assign(
+        {}, multidevicePage.pageContentData,
+        {isNotificationAccessGranted: accessGranted}));
+  }
+
+  /**
    * @param {!settings.MultiDeviceFeature} feature The feature to change.
    * @param {boolean} enabled Whether to enable or disable the feature.
    * @param {boolean} authRequired Whether authentication is required for the
@@ -77,10 +101,19 @@
       // Simulate closing the password prompt dialog
       multidevicePage.$$('#multidevicePasswordPrompt').fire('close');
       Polymer.dom.flush();
-    } else {
-      assertFalse(multidevicePage.showPasswordPromptDialog_);
     }
 
+    if (enabled &&
+        feature === settings.MultiDeviceFeature.PHONE_HUB_NOTIFICATIONS) {
+      const accessDialog = multidevicePage.$$(
+          'settings-multidevice-notification-access-setup-dialog');
+      assertEquals(
+          !accessDialog,
+          multidevicePage.pageContentData.isNotificationAccessGranted);
+      return;
+    }
+
+    assertFalse(multidevicePage.showPasswordPromptDialog_);
     return browserProxy.whenCalled('setFeatureEnabledState').then(params => {
       assertEquals(feature, params[0]);
       assertEquals(enabled, params[1]);
@@ -184,6 +217,36 @@
     assertFalse(!!multidevicePage.$$('cr-policy-indicator'));
   });
 
+  test('Phone hub notification access setup dialog', () => {
+    setPhoneHubNotificationsState(
+        settings.MultiDeviceFeatureState.DISABLED_BY_USER);
+    assertFalse(!!multidevicePage.$$(
+        'settings-multidevice-notification-access-setup-dialog'));
+
+    setPhoneHubNotificationAccessGranted(false);
+    simulateFeatureStateChangeRequest(
+        settings.MultiDeviceFeature.PHONE_HUB_NOTIFICATIONS,
+        /*enabled=*/ true, /*authRequired=*/ false);
+
+    // Close the dialog.
+    multidevicePage.showNotificationAccessSetupDialog_ = false;
+
+    setPhoneHubNotificationAccessGranted(false);
+    simulateFeatureStateChangeRequest(
+        settings.MultiDeviceFeature.PHONE_HUB_NOTIFICATIONS,
+        /*enabled=*/ false, /*authRequired=*/ false);
+
+    setPhoneHubNotificationAccessGranted(true);
+    simulateFeatureStateChangeRequest(
+        settings.MultiDeviceFeature.PHONE_HUB_NOTIFICATIONS,
+        /*enabled=*/ true, /*authRequired=*/ false);
+
+    multidevicePage.pageContentData.isNotificationAccessGranted = true;
+    simulateFeatureStateChangeRequest(
+        settings.MultiDeviceFeature.PHONE_HUB_NOTIFICATIONS,
+        /*enabled=*/ false, /*authRequired=*/ false);
+  });
+
   test('Disabling features never requires authentication', () => {
     const Feature = settings.MultiDeviceFeature;
 
diff --git a/chrome/test/data/webui/settings/chromeos/os_settings_browsertest.js b/chrome/test/data/webui/settings/chromeos/os_settings_browsertest.js
index d9f35c4..e014015 100644
--- a/chrome/test/data/webui/settings/chromeos/os_settings_browsertest.js
+++ b/chrome/test/data/webui/settings/chromeos/os_settings_browsertest.js
@@ -930,6 +930,31 @@
   mocha.run();
 });
 
+// Test fixture for the multidevice Notification access dialog flow.
+// eslint-disable-next-line no-var
+var OSSettingsMultideviceNotificationAccessDialogTest =
+    class extends OSSettingsBrowserTest {
+  /** @override */
+  get browsePreload() {
+    return super.browsePreload + 'chromeos/multidevice_page/' +
+        'multidevice_notification_access_setup_dialog.html';
+  }
+
+  /** @override */
+  get extraLibraries() {
+    return super.extraLibraries.concat([
+      BROWSER_SETTINGS_PATH + '../test_browser_proxy.js',
+      'test_multidevice_browser_proxy.js',
+      'multidevice_notification_access_setup_dialog_tests.js',
+    ]);
+  }
+};
+
+TEST_F(
+    'OSSettingsMultideviceNotificationAccessDialogTest', 'AllJsTests', () => {
+      mocha.run();
+    });
+
 // Test fixture for the multidevice settings subpage feature toggle.
 // eslint-disable-next-line no-var
 var OSSettingsMultideviceFeatureToggleTest =
diff --git a/chrome/test/data/webui/settings/chromeos/test_multidevice_browser_proxy.js b/chrome/test/data/webui/settings/chromeos/test_multidevice_browser_proxy.js
index 4eb34b04..141c4ab9 100644
--- a/chrome/test/data/webui/settings/chromeos/test_multidevice_browser_proxy.js
+++ b/chrome/test/data/webui/settings/chromeos/test_multidevice_browser_proxy.js
@@ -56,6 +56,8 @@
         'setSmartLockSignInEnabled',
         'getSmartLockSignInAllowed',
         'getAndroidSmsInfo',
+        'attemptNotificationSetup',
+        'cancelNotificationSetup',
       ]);
       this.data = createFakePageContentData(
           settings.MultiDeviceSettingsMode.NO_HOST_SET);
@@ -113,6 +115,16 @@
       this.methodCalled('getAndroidSmsInfo');
       return Promise.resolve(this.androidSmsInfo);
     }
+
+    /** @override */
+    attemptNotificationSetup() {
+      this.methodCalled('attemptNotificationSetup');
+    }
+
+    /** @override */
+    cancelNotificationSetup() {
+      this.methodCalled('cancelNotificationSetup');
+    }
   }
 
   // #cr_define_end
diff --git a/chrome/tools/build/mac/FILES.cfg b/chrome/tools/build/mac/FILES.cfg
index 3f19bd2..02691bf 100644
--- a/chrome/tools/build/mac/FILES.cfg
+++ b/chrome/tools/build/mac/FILES.cfg
@@ -146,4 +146,10 @@
     'archive': 'updater.zip',
     'optional': ['official'], # TODO(crbug.com/1024318): Make non-optional.
   },
+  # Enterprise policy templates:
+  {
+    'filename': 'gen/chrome/app/policy/mac/jamf.json',
+    'buildtype': ['official'],
+    'optional': ['official'],
+  }
 ]
diff --git a/chromecast/media/DEPS b/chromecast/media/DEPS
index 56829e6..613654d6 100644
--- a/chromecast/media/DEPS
+++ b/chromecast/media/DEPS
@@ -5,7 +5,6 @@
   "+content/public/test/test_browser_thread.h",
   "+content/public/test/test_utils.h",
   "+content/renderer/media/audio/audio_device_factory.h",
-  "+content/renderer/media/audio/audio_output_ipc_factory.h",
   "+media/audio",
   "+media/base",
   "+media/cdm",
@@ -17,6 +16,7 @@
   "+ui/gfx/overlay_transform.h",
   "+services/service_manager/public",
   "+third_party/blink/public/platform/audio/web_audio_device_source_type.h",
+  "+third_party/blink/public/web/modules/media/audio/web_audio_output_ipc_factory.h",
   "+third_party/widevine/cdm/buildflags.h",
   "+third_party/widevine/cdm/widevine_cdm_common.h",
 ]
diff --git a/chromecast/media/audio/cast_audio_device_factory.cc b/chromecast/media/audio/cast_audio_device_factory.cc
index 3ca3e601..e710b89 100644
--- a/chromecast/media/audio/cast_audio_device_factory.cc
+++ b/chromecast/media/audio/cast_audio_device_factory.cc
@@ -6,12 +6,13 @@
 
 #include <string>
 
-#include "content/renderer/media/audio/audio_output_ipc_factory.h"
+#include "base/logging.h"
 #include "media/audio/audio_output_device.h"
 #include "media/base/audio_capturer_source.h"
 #include "media/base/audio_parameters.h"
 #include "media/base/audio_renderer_sink.h"
 #include "media/base/output_device_info.h"
+#include "third_party/blink/public/web/modules/media/audio/web_audio_output_ipc_factory.h"
 
 namespace chromecast {
 namespace media {
@@ -82,8 +83,8 @@
     const ::media::AudioSinkParameters& params,
     base::TimeDelta auth_timeout) {
   auto device = base::MakeRefCounted<::media::AudioOutputDevice>(
-      content::AudioOutputIPCFactory::get()->CreateAudioOutputIPC(frame_token),
-      content::AudioOutputIPCFactory::get()->io_task_runner(), params,
+      blink::WebAudioOutputIPCFactory::get()->CreateAudioOutputIPC(frame_token),
+      blink::WebAudioOutputIPCFactory::get()->io_task_runner(), params,
       auth_timeout);
   device->RequestDeviceAuthorization();
   return device;
diff --git a/chromeos/CHROMEOS_LKGM b/chromeos/CHROMEOS_LKGM
index 77258aa..77085de 100644
--- a/chromeos/CHROMEOS_LKGM
+++ b/chromeos/CHROMEOS_LKGM
@@ -1 +1 @@
-13428.0.0
\ No newline at end of file
+13430.0.0
\ No newline at end of file
diff --git a/chromeos/components/bloom/BUILD.gn b/chromeos/components/bloom/BUILD.gn
new file mode 100644
index 0000000..a81786f
--- /dev/null
+++ b/chromeos/components/bloom/BUILD.gn
@@ -0,0 +1,18 @@
+# Copyright 2020 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+source_set("bloom") {
+  sources = [
+    "bloom_controller_impl.cc",
+    "bloom_controller_impl.h",
+  ]
+
+  deps = [
+    "//ash/public/cpp",
+    "//base",
+    "//chromeos/components/bloom/public/cpp",
+    "//components/signin/public/identity_manager",
+    "//services/network/public/cpp",
+  ]
+}
diff --git a/chromeos/components/bloom/DEPS b/chromeos/components/bloom/DEPS
new file mode 100644
index 0000000..c00284d3
--- /dev/null
+++ b/chromeos/components/bloom/DEPS
@@ -0,0 +1,5 @@
+include_rules = [
+  "+ash/public",
+  "+components/signin/public/identity_manager",
+  "+services/network/public",
+]
diff --git a/chromeos/components/bloom/OWNERS b/chromeos/components/bloom/OWNERS
new file mode 100644
index 0000000..0c730475
--- /dev/null
+++ b/chromeos/components/bloom/OWNERS
@@ -0,0 +1,3 @@
+file://chromeos/assistant/OWNERS
+
+# COMPONENT: UI>Shell>Assistant
diff --git a/chromeos/components/bloom/bloom_controller_impl.cc b/chromeos/components/bloom/bloom_controller_impl.cc
new file mode 100644
index 0000000..67d06b9
--- /dev/null
+++ b/chromeos/components/bloom/bloom_controller_impl.cc
@@ -0,0 +1,44 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chromeos/components/bloom/bloom_controller_impl.h"
+
+#include "ash/public/cpp/assistant/controller/assistant_interaction_controller.h"
+
+namespace chromeos {
+namespace bloom {
+
+BloomControllerImpl::BloomControllerImpl(
+    signin::IdentityManager* identity_manager,
+    ash::AssistantInteractionController* assistant_interaction_controller)
+    : identity_manager_(identity_manager),
+      assistant_interaction_controller_(assistant_interaction_controller) {
+  DCHECK(identity_manager_);
+  DCHECK(assistant_interaction_controller_);
+}
+
+BloomControllerImpl::~BloomControllerImpl() = default;
+
+void BloomControllerImpl::StartInteraction() {
+  // TODO(jeroendh): Implement
+}
+
+bool BloomControllerImpl::HasInteraction() const {
+  // TODO(jeroendh): Implement
+  return true;
+}
+
+void BloomControllerImpl::StopInteraction(
+    BloomInteractionResolution resolution) {
+  // TODO(jeroendh): Implement
+  last_interaction_resolution_ = resolution;
+}
+
+BloomInteractionResolution BloomControllerImpl::GetLastInteractionResolution()
+    const {
+  return last_interaction_resolution_;
+}
+
+}  // namespace bloom
+}  // namespace chromeos
diff --git a/chromeos/components/bloom/bloom_controller_impl.h b/chromeos/components/bloom/bloom_controller_impl.h
new file mode 100644
index 0000000..6a8900b
--- /dev/null
+++ b/chromeos/components/bloom/bloom_controller_impl.h
@@ -0,0 +1,56 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROMEOS_COMPONENTS_BLOOM_BLOOM_CONTROLLER_IMPL_H_
+#define CHROMEOS_COMPONENTS_BLOOM_BLOOM_CONTROLLER_IMPL_H_
+
+#include "chromeos/components/bloom/public/cpp/bloom_controller.h"
+
+namespace ash {
+class AssistantInteractionController;
+}  // namespace ash
+
+namespace signin {
+class IdentityManager;
+}  // namespace signin
+
+namespace chromeos {
+namespace bloom {
+
+class BloomInteraction;
+class ScreenshotGrabber;
+
+class BloomControllerImpl : public BloomController {
+ public:
+  BloomControllerImpl(
+      signin::IdentityManager* identity_manager,
+      ash::AssistantInteractionController* assistant_interaction_controller);
+  BloomControllerImpl(const BloomControllerImpl&) = delete;
+  BloomControllerImpl& operator=(const BloomControllerImpl&) = delete;
+  ~BloomControllerImpl() override;
+
+  // BloomController implementation:
+  void StartInteraction() override;
+  BloomInteractionResolution GetLastInteractionResolution() const override;
+
+  bool HasInteraction() const;
+  void StopInteraction(BloomInteractionResolution resolution);
+
+  signin::IdentityManager* identity_manager() { return identity_manager_; }
+  ash::AssistantInteractionController* assistant_interaction_controller() {
+    return assistant_interaction_controller_;
+  }
+
+ private:
+  signin::IdentityManager* const identity_manager_;
+  ash::AssistantInteractionController* const assistant_interaction_controller_;
+
+  BloomInteractionResolution last_interaction_resolution_ =
+      BloomInteractionResolution::kNormal;
+};
+
+}  // namespace bloom
+}  // namespace chromeos
+
+#endif  // CHROMEOS_COMPONENTS_BLOOM_BLOOM_CONTROLLER_IMPL_H_
diff --git a/chromeos/components/bloom/public/cpp/BUILD.gn b/chromeos/components/bloom/public/cpp/BUILD.gn
new file mode 100644
index 0000000..1a21e67
--- /dev/null
+++ b/chromeos/components/bloom/public/cpp/BUILD.gn
@@ -0,0 +1,35 @@
+# Copyright 2020 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+assert(is_chromeos, "Non-Chrome-OS builds must not depend on //chromeos")
+
+component("cpp") {
+  sources = [
+    "bloom_controller.cc",
+    "bloom_controller.h",
+    "bloom_interaction_resolution.cc",
+    "bloom_interaction_resolution.h",
+  ]
+
+  deps = [ "//base" ]
+
+  defines = [ "IS_BLOOM_IMPL" ]
+
+  # Prevent conflict with other targets called 'cpp'.
+  output_name = "bloom_public"
+}
+
+component("bloom_controller_factory") {
+  sources = [
+    "bloom_controller_factory.cc",
+    "bloom_controller_factory.h",
+  ]
+
+  deps = [
+    "//base",
+    "//chromeos/components/bloom",
+  ]
+
+  defines = [ "IS_BLOOM_IMPL" ]
+}
diff --git a/chromeos/components/bloom/public/cpp/bloom_controller.cc b/chromeos/components/bloom/public/cpp/bloom_controller.cc
new file mode 100644
index 0000000..ad7981d
--- /dev/null
+++ b/chromeos/components/bloom/public/cpp/bloom_controller.cc
@@ -0,0 +1,33 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chromeos/components/bloom/public/cpp/bloom_controller.h"
+#include "base/check_op.h"
+
+namespace chromeos {
+namespace bloom {
+
+namespace {
+
+BloomController* g_instance = nullptr;
+
+}  // namespace
+
+// static
+BloomController* BloomController::Get() {
+  return g_instance;
+}
+
+BloomController::BloomController() {
+  DCHECK_EQ(g_instance, nullptr);
+  g_instance = this;
+}
+
+BloomController::~BloomController() {
+  DCHECK_EQ(g_instance, this);
+  g_instance = nullptr;
+}
+
+}  // namespace bloom
+}  // namespace chromeos
diff --git a/chromeos/components/bloom/public/cpp/bloom_controller.h b/chromeos/components/bloom/public/cpp/bloom_controller.h
new file mode 100644
index 0000000..692c161
--- /dev/null
+++ b/chromeos/components/bloom/public/cpp/bloom_controller.h
@@ -0,0 +1,38 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROMEOS_COMPONENTS_BLOOM_PUBLIC_CPP_BLOOM_CONTROLLER_H_
+#define CHROMEOS_COMPONENTS_BLOOM_PUBLIC_CPP_BLOOM_CONTROLLER_H_
+
+#include <memory>
+
+#include "base/component_export.h"
+#include "chromeos/components/bloom/public/cpp/bloom_interaction_resolution.h"
+
+namespace chromeos {
+namespace bloom {
+
+// Main controller for the Bloom integration.
+class COMPONENT_EXPORT(BLOOM) BloomController {
+ public:
+  BloomController();
+  BloomController(const BloomController&) = delete;
+  BloomController& operator=(const BloomController&) = delete;
+  virtual ~BloomController();
+
+  // Access the existing Bloom controller.
+  static BloomController* Get();
+
+  // Starts an interaction. This will ask the user for a screenshot, analyze the
+  // content and display the result.
+  virtual void StartInteraction() = 0;
+
+  // Returns the result of the last interaction.
+  virtual BloomInteractionResolution GetLastInteractionResolution() const = 0;
+};
+
+}  // namespace bloom
+}  // namespace chromeos
+
+#endif  // CHROMEOS_COMPONENTS_BLOOM_PUBLIC_CPP_BLOOM_CONTROLLER_H_
diff --git a/chromeos/components/bloom/public/cpp/bloom_controller_factory.cc b/chromeos/components/bloom/public/cpp/bloom_controller_factory.cc
new file mode 100644
index 0000000..52acc0e8
--- /dev/null
+++ b/chromeos/components/bloom/public/cpp/bloom_controller_factory.cc
@@ -0,0 +1,23 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chromeos/components/bloom/public/cpp/bloom_controller_factory.h"
+
+#include "base/callback.h"
+#include "chromeos/components/bloom/bloom_controller_impl.h"
+
+namespace chromeos {
+namespace bloom {
+
+// static
+std::unique_ptr<BloomController> BloomControllerFactory::Create(
+    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+    signin::IdentityManager* identity_manager,
+    ash::AssistantInteractionController* assistant_interaction_controller) {
+  return std::make_unique<BloomControllerImpl>(
+      identity_manager, assistant_interaction_controller);
+}
+
+}  // namespace bloom
+}  // namespace chromeos
diff --git a/chromeos/components/bloom/public/cpp/bloom_controller_factory.h b/chromeos/components/bloom/public/cpp/bloom_controller_factory.h
new file mode 100644
index 0000000..659bfa9
--- /dev/null
+++ b/chromeos/components/bloom/public/cpp/bloom_controller_factory.h
@@ -0,0 +1,42 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROMEOS_COMPONENTS_BLOOM_PUBLIC_CPP_BLOOM_CONTROLLER_FACTORY_H_
+#define CHROMEOS_COMPONENTS_BLOOM_PUBLIC_CPP_BLOOM_CONTROLLER_FACTORY_H_
+
+#include <memory>
+
+#include "base/component_export.h"
+#include "base/memory/scoped_refptr.h"
+
+namespace ash {
+class AssistantInteractionController;
+}  // namespace ash
+
+namespace network {
+class SharedURLLoaderFactory;
+}  // namespace network
+
+namespace signin {
+class IdentityManager;
+}  // namespace signin
+
+namespace chromeos {
+namespace bloom {
+
+class BloomController;
+
+class COMPONENT_EXPORT(BLOOM) BloomControllerFactory {
+ public:
+  // Create the Bloom controller. Can only be invoked once.
+  static std::unique_ptr<BloomController> Create(
+      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
+      signin::IdentityManager* identity_manager,
+      ash::AssistantInteractionController* assistant_interaction_controller);
+};
+
+}  // namespace bloom
+}  // namespace chromeos
+
+#endif  // CHROMEOS_COMPONENTS_BLOOM_PUBLIC_CPP_BLOOM_CONTROLLER_FACTORY_H_
diff --git a/chromeos/components/bloom/public/cpp/bloom_interaction_resolution.cc b/chromeos/components/bloom/public/cpp/bloom_interaction_resolution.cc
new file mode 100644
index 0000000..60ca91f
--- /dev/null
+++ b/chromeos/components/bloom/public/cpp/bloom_interaction_resolution.cc
@@ -0,0 +1,23 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chromeos/components/bloom/public/cpp/bloom_interaction_resolution.h"
+
+namespace chromeos {
+namespace bloom {
+
+#define CASE(name)                       \
+  case BloomInteractionResolution::name: \
+    return #name;
+
+std::string ToString(BloomInteractionResolution resolution) {
+  switch (resolution) {
+    CASE(kNormal);
+    CASE(kNoAccessToken);
+    CASE(kNoScreenshot);
+  }
+}
+
+}  // namespace bloom
+}  // namespace chromeos
diff --git a/chromeos/components/bloom/public/cpp/bloom_interaction_resolution.h b/chromeos/components/bloom/public/cpp/bloom_interaction_resolution.h
new file mode 100644
index 0000000..295250f
--- /dev/null
+++ b/chromeos/components/bloom/public/cpp/bloom_interaction_resolution.h
@@ -0,0 +1,28 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROMEOS_COMPONENTS_BLOOM_PUBLIC_CPP_BLOOM_INTERACTION_RESOLUTION_H_
+#define CHROMEOS_COMPONENTS_BLOOM_PUBLIC_CPP_BLOOM_INTERACTION_RESOLUTION_H_
+
+#include <string>
+
+namespace chromeos {
+namespace bloom {
+
+enum class BloomInteractionResolution {
+  // Bloom interaction completed normally.
+  kNormal = 0,
+  // Bloom interaction failed to fetch an access token.
+  kNoAccessToken = 1,
+  // Bloom interaction failed to take a screenshot
+  // (or the user aborted while taking a screenshot).
+  kNoScreenshot = 2,
+};
+
+std::string ToString(BloomInteractionResolution resolution);
+
+}  // namespace bloom
+}  // namespace chromeos
+
+#endif  // CHROMEOS_COMPONENTS_BLOOM_PUBLIC_CPP_BLOOM_INTERACTION_RESOLUTION_H_
diff --git a/chromeos/components/phonehub/notification_access_setup_operation.h b/chromeos/components/phonehub/notification_access_setup_operation.h
index 93833b57..d211fff 100644
--- a/chromeos/components/phonehub/notification_access_setup_operation.h
+++ b/chromeos/components/phonehub/notification_access_setup_operation.h
@@ -27,7 +27,7 @@
 class NotificationAccessSetupOperation {
  public:
   // Note: Numerical values should not be changed because they must stay in
-  // sync with values declared in JS.
+  // sync with multidevice_notification_access_setup_dialog.js.
   enum class Status {
     // Connecting to the phone in order to set up notification access.
     kConnecting = 0,
diff --git a/chromeos/components/scanning/mojom/BUILD.gn b/chromeos/components/scanning/mojom/BUILD.gn
new file mode 100644
index 0000000..6085366
--- /dev/null
+++ b/chromeos/components/scanning/mojom/BUILD.gn
@@ -0,0 +1,11 @@
+# Copyright 2020 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//mojo/public/tools/bindings/mojom.gni")
+
+mojom("mojom") {
+  sources = [ "scanning.mojom" ]
+
+  public_deps = [ "//mojo/public/mojom/base" ]
+}
diff --git a/chromeos/components/scanning/mojom/OWNERS b/chromeos/components/scanning/mojom/OWNERS
new file mode 100644
index 0000000..08850f4
--- /dev/null
+++ b/chromeos/components/scanning/mojom/OWNERS
@@ -0,0 +1,2 @@
+per-file *.mojom=set noparent
+per-file *.mojom=file://ipc/SECURITY_OWNERS
diff --git a/chromeos/components/scanning/mojom/scanning.mojom b/chromeos/components/scanning/mojom/scanning.mojom
new file mode 100644
index 0000000..358ce95
--- /dev/null
+++ b/chromeos/components/scanning/mojom/scanning.mojom
@@ -0,0 +1,26 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module chromeos.scanning.mojom;
+
+import "mojo/public/mojom/base/string16.mojom";
+import "mojo/public/mojom/base/unguessable_token.mojom";
+
+// Represents a connected scanner.
+struct Scanner {
+  // The scanner's unique identifier.
+  mojo_base.mojom.UnguessableToken id;
+  // The scanner's display name.
+  mojo_base.mojom.String16 display_name;
+};
+
+// Interface used to obtain information about and interact with connected
+// scanners. It is implemented in the browser and exposed to the Scan app
+// (chrome://scanning).
+interface ScanService {
+  // Returns the connected scanners. Obtaining a scanner's capabilities is
+  // implemented in a separate method to minimize the amount of time clients
+  // must wait before receiving the scanners and displaying their display names.
+  GetScanners() => (array<Scanner> scanners);
+};
diff --git a/components/autofill/android/BUILD.gn b/components/autofill/android/BUILD.gn
index 219ef4c2..b2f3237 100644
--- a/components/autofill/android/BUILD.gn
+++ b/components/autofill/android/BUILD.gn
@@ -40,10 +40,10 @@
     ":autofill_java_resources",
     "//base:base_java",
     "//content/public/android:content_java",
-    "//third_party/android_deps:android_support_v7_appcompat_java",
     "//third_party/android_deps:androidx_annotation_annotation_java",
     "//third_party/android_deps:androidx_appcompat_appcompat_resources_java",
-    "//ui/android:ui_java",
+    "//third_party/android_deps:androidx_core_core_java",
+    "//ui/android:ui_no_recycler_view_java",
   ]
   sources = [
     "java/src/org/chromium/components/autofill/AutofillDelegate.java",
diff --git a/components/autofill/android/provider/BUILD.gn b/components/autofill/android/provider/BUILD.gn
index 263b88a..bff17102 100644
--- a/components/autofill/android/provider/BUILD.gn
+++ b/components/autofill/android/provider/BUILD.gn
@@ -14,7 +14,7 @@
     "//components/version_info/android:version_constants_java",
     "//content/public/android:content_java",
     "//third_party/android_deps:androidx_annotation_annotation_java",
-    "//ui/android:ui_java",
+    "//ui/android:ui_no_recycler_view_java",
   ]
   annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ]
   sources = [
diff --git a/components/autofill/ios/form_util/resources/fill.js b/components/autofill/ios/form_util/resources/fill.js
index 8c33cbc..6117e8b 100644
--- a/components/autofill/ios/form_util/resources/fill.js
+++ b/components/autofill/ios/form_util/resources/fill.js
@@ -2320,7 +2320,10 @@
 __gCrWeb.fill.setUniqueIDIfNeeded = function(element) {
   try {
     const uniqueID = Symbol.for(__gCrWeb.fill.UNIQUE_ID_SYMBOL_NAME);
-    if (typeof element[uniqueID] === 'undefined') {
+    // Do not assign element id value if the base value for the document
+    // is not set.
+    if (typeof document[uniqueID] !== 'undefined' &&
+        typeof element[uniqueID] === 'undefined') {
       element[uniqueID] = document[uniqueID]++;
     }
   } catch (e) {
diff --git a/components/autofill_assistant/browser/actions/action_delegate.h b/components/autofill_assistant/browser/actions/action_delegate.h
index 544a947..736ea0c8 100644
--- a/components/autofill_assistant/browser/actions/action_delegate.h
+++ b/components/autofill_assistant/browser/actions/action_delegate.h
@@ -130,7 +130,8 @@
       std::unique_ptr<std::vector<UserAction>> user_actions,
       bool disable_force_expand_sheet,
       base::OnceCallback<void()> end_on_navigation_callback = base::DoNothing(),
-      bool browse_mode = false) = 0;
+      bool browse_mode = false,
+      bool browse_mode_invisible = false) = 0;
 
   // Have the UI leave the prompt state and go back to its previous state.
   virtual void CleanUpAfterPrompt() = 0;
diff --git a/components/autofill_assistant/browser/actions/mock_action_delegate.h b/components/autofill_assistant/browser/actions/mock_action_delegate.h
index 2aeff07..27169c74 100644
--- a/components/autofill_assistant/browser/actions/mock_action_delegate.h
+++ b/components/autofill_assistant/browser/actions/mock_action_delegate.h
@@ -77,11 +77,12 @@
                void(const ElementFinder::Result& element,
                     base::OnceCallback<void(const ClientStatus&)> callback));
 
-  MOCK_METHOD4(Prompt,
+  MOCK_METHOD5(Prompt,
                void(std::unique_ptr<std::vector<UserAction>> user_actions,
                     bool disable_force_expand_sheet,
                     base::OnceCallback<void()> end_on_navigation_callback,
-                    bool browse_mode));
+                    bool browse_mode,
+                    bool browse_mode_invisible));
   MOCK_METHOD0(CleanUpAfterPrompt, void());
   MOCK_METHOD1(SetBrowseDomainsWhitelist,
                void(std::vector<std::string> domains));
diff --git a/components/autofill_assistant/browser/actions/prompt_action.cc b/components/autofill_assistant/browser/actions/prompt_action.cc
index 9f1cd9c..d58874a 100644
--- a/components/autofill_assistant/browser/actions/prompt_action.cc
+++ b/components/autofill_assistant/browser/actions/prompt_action.cc
@@ -162,7 +162,8 @@
   }
   delegate_->Prompt(
       std::move(user_actions), proto_.prompt().disable_force_expand_sheet(),
-      std::move(end_on_navigation_callback), proto_.prompt().browse_mode());
+      std::move(end_on_navigation_callback), proto_.prompt().browse_mode(),
+      proto_.prompt().browse_mode_invisible());
   precondition_changed_ = false;
 }
 
diff --git a/components/autofill_assistant/browser/actions/prompt_action_unittest.cc b/components/autofill_assistant/browser/actions/prompt_action_unittest.cc
index afb7d7a6..3e66b265 100644
--- a/components/autofill_assistant/browser/actions/prompt_action_unittest.cc
+++ b/components/autofill_assistant/browser/actions/prompt_action_unittest.cc
@@ -45,13 +45,14 @@
 
     EXPECT_CALL(mock_action_delegate_, OnWaitForDom(_, _, _, _))
         .WillRepeatedly(Invoke(this, &PromptActionTest::FakeWaitForDom));
-    ON_CALL(mock_action_delegate_, Prompt(_, _, _, _))
-        .WillByDefault(Invoke(
+    ON_CALL(mock_action_delegate_, Prompt(_, _, _, _, _))
+        .WillByDefault(
             [this](std::unique_ptr<std::vector<UserAction>> user_actions,
                    bool disable_force_expand_sheet,
-                   base::OnceCallback<void()> callback, bool browse_mode) {
+                   base::OnceCallback<void()> callback, bool browse_mode,
+                   bool browse_mode_invisible) {
               user_actions_ = std::move(user_actions);
-            }));
+            });
     prompt_proto_ = proto_.mutable_prompt();
   }
 
@@ -370,7 +371,7 @@
   ok_proto->mutable_chip()->set_type(HIGHLIGHTED_ACTION);
   ok_proto->set_server_payload("ok");
 
-  EXPECT_CALL(mock_action_delegate_, Prompt(_, Eq(false), _, Eq(false)));
+  EXPECT_CALL(mock_action_delegate_, Prompt(_, false, _, false, false));
   PromptAction action(&mock_action_delegate_, proto_);
   action.ProcessAction(callback_.Get());
 }
@@ -382,7 +383,7 @@
   ok_proto->set_server_payload("ok");
 
   prompt_proto_->set_disable_force_expand_sheet(true);
-  EXPECT_CALL(mock_action_delegate_, Prompt(_, Eq(true), _, Eq(false)));
+  EXPECT_CALL(mock_action_delegate_, Prompt(_, true, _, false, false));
   PromptAction action(&mock_action_delegate_, proto_);
   action.ProcessAction(callback_.Get());
 }
@@ -394,7 +395,20 @@
   ok_proto->set_server_payload("ok");
 
   prompt_proto_->set_browse_mode(true);
-  EXPECT_CALL(mock_action_delegate_, Prompt(_, Eq(false), _, Eq(true)));
+  EXPECT_CALL(mock_action_delegate_, Prompt(_, false, _, true, false));
+  PromptAction action(&mock_action_delegate_, proto_);
+  action.ProcessAction(callback_.Get());
+}
+
+TEST_F(PromptActionTest, RunPromptInInvisibleBrowseMode) {
+  auto* ok_proto = prompt_proto_->add_choices();
+  ok_proto->mutable_chip()->set_text("Ok");
+  ok_proto->mutable_chip()->set_type(HIGHLIGHTED_ACTION);
+  ok_proto->set_server_payload("ok");
+
+  prompt_proto_->set_browse_mode(true);
+  prompt_proto_->set_browse_mode_invisible(true);
+  EXPECT_CALL(mock_action_delegate_, Prompt(_, false, _, true, true));
   PromptAction action(&mock_action_delegate_, proto_);
   action.ProcessAction(callback_.Get());
 }
@@ -427,14 +441,14 @@
 }
 
 TEST_F(PromptActionTest, EndActionOnNavigation) {
-  EXPECT_CALL(mock_action_delegate_, Prompt(_, _, _, _))
-      .WillOnce(
-          Invoke([this](std::unique_ptr<std::vector<UserAction>> user_actions,
-                        bool disable_force_expand_sheet,
-                        base::OnceCallback<void()> callback, bool browse_mode) {
-            user_actions_ = std::move(user_actions);
-            std::move(callback).Run();
-          }));
+  EXPECT_CALL(mock_action_delegate_, Prompt(_, _, _, _, _))
+      .WillOnce([this](std::unique_ptr<std::vector<UserAction>> user_actions,
+                       bool disable_force_expand_sheet,
+                       base::OnceCallback<void()> callback, bool browse_mode,
+                       bool browse_mode_invisible) {
+        user_actions_ = std::move(user_actions);
+        std::move(callback).Run();
+      });
 
   prompt_proto_->set_end_on_navigation(true);
   prompt_proto_->add_choices()->mutable_chip()->set_text("ok");
diff --git a/components/autofill_assistant/browser/actions/show_generic_ui_action_unittest.cc b/components/autofill_assistant/browser/actions/show_generic_ui_action_unittest.cc
index e5129d0..469beb5 100644
--- a/components/autofill_assistant/browser/actions/show_generic_ui_action_unittest.cc
+++ b/components/autofill_assistant/browser/actions/show_generic_ui_action_unittest.cc
@@ -116,7 +116,7 @@
 
 TEST_F(ShowGenericUiActionTest, GoesIntoPromptState) {
   InSequence seq;
-  EXPECT_CALL(mock_action_delegate_, Prompt(_, _, _, _)).Times(1);
+  EXPECT_CALL(mock_action_delegate_, Prompt(_, _, _, _, _)).Times(1);
   EXPECT_CALL(mock_action_delegate_, OnSetGenericUi(_, _, _)).Times(1);
   EXPECT_CALL(mock_action_delegate_, ClearGenericUi()).Times(1);
   EXPECT_CALL(mock_action_delegate_, CleanUpAfterPrompt()).Times(1);
@@ -469,12 +469,13 @@
             std::move(view_inflation_finished_callback)
                 .Run(ClientStatus(ACTION_APPLIED));
           }));
-  EXPECT_CALL(mock_action_delegate_, Prompt(_, _, _, _))
-      .WillOnce(Invoke(
-          [](std::unique_ptr<std::vector<UserAction>> user_actions,
-             bool disable_force_expand_sheet,
-             base::OnceCallback<void()> end_navigation_callback,
-             bool browse_mode) { std::move(end_navigation_callback).Run(); }));
+  EXPECT_CALL(mock_action_delegate_, Prompt(_, _, _, _, _))
+      .WillOnce([](std::unique_ptr<std::vector<UserAction>> user_actions,
+                   bool disable_force_expand_sheet,
+                   base::OnceCallback<void()> end_navigation_callback,
+                   bool browse_mode, bool browse_mode_invisible) {
+        std::move(end_navigation_callback).Run();
+      });
   EXPECT_CALL(mock_action_delegate_, CleanUpAfterPrompt()).Times(1);
   EXPECT_CALL(
       callback_,
@@ -490,12 +491,13 @@
 
 TEST_F(ShowGenericUiActionTest, BreakingNavigationBeforeUiIsSet) {
   // End action immediately with ACTION_APPLIED after it goes into prompt.
-  EXPECT_CALL(mock_action_delegate_, Prompt(_, _, _, _))
-      .WillOnce(Invoke(
-          [](std::unique_ptr<std::vector<UserAction>> user_actions,
-             bool disable_force_expand_sheet,
-             base::OnceCallback<void()> end_navigation_callback,
-             bool browse_mode) { std::move(end_navigation_callback).Run(); }));
+  EXPECT_CALL(mock_action_delegate_, Prompt(_, _, _, _, _))
+      .WillOnce([](std::unique_ptr<std::vector<UserAction>> user_actions,
+                   bool disable_force_expand_sheet,
+                   base::OnceCallback<void()> end_navigation_callback,
+                   bool browse_mode, bool browse_mode_invisible) {
+        std::move(end_navigation_callback).Run();
+      });
   ON_CALL(mock_action_delegate_, OnSetGenericUi(_, _, _))
       .WillByDefault(
           Invoke([&](std::unique_ptr<GenericUserInterfaceProto> generic_ui,
diff --git a/components/autofill_assistant/browser/controller.cc b/components/autofill_assistant/browser/controller.cc
index eb6faea7..ced45b0a 100644
--- a/components/autofill_assistant/browser/controller.cc
+++ b/components/autofill_assistant/browser/controller.cc
@@ -49,13 +49,13 @@
 //
 // Note that the UI might be shown in RUNNING state, even if it doesn't require
 // it.
-bool StateNeedsUiInRegularScript(AutofillAssistantState state) {
+bool StateNeedsUiInRegularScript(AutofillAssistantState state,
+                                 bool browse_mode_invisible) {
   switch (state) {
     case AutofillAssistantState::PROMPT:
     case AutofillAssistantState::AUTOSTART_FALLBACK_PROMPT:
     case AutofillAssistantState::MODAL_DIALOG:
     case AutofillAssistantState::STARTING:
-    case AutofillAssistantState::BROWSE:
       return true;
 
     case AutofillAssistantState::INACTIVE:
@@ -63,11 +63,14 @@
     case AutofillAssistantState::STOPPED:
     case AutofillAssistantState::RUNNING:
       return false;
+
+    case AutofillAssistantState::BROWSE:
+      return browse_mode_invisible;
   }
 }
 
-// Same as |StateNeedsUiInRegularScript|, but does not show UI in STARTING or
-// BROWSE state.
+// Same as |StateNeedsUiInRegularScript|, but does not show UI in STARTING
+// state.
 bool StateNeedsUiInLiteScript(AutofillAssistantState state) {
   switch (state) {
     case AutofillAssistantState::PROMPT:
@@ -382,6 +385,10 @@
   }
 }
 
+void Controller::SetBrowseModeInvisible(bool invisible) {
+  browse_mode_invisible_ = invisible;
+}
+
 void Controller::AddNavigationListener(
     ScriptExecutorDelegate::NavigationListener* listener) {
   navigation_listeners_.AddObserver(listener);
@@ -703,6 +710,10 @@
     RequireUI();
   } else if (needs_ui_ && state == AutofillAssistantState::TRACKING) {
     needs_ui_ = false;
+  } else if (browse_mode_invisible_ && needs_ui_ &&
+             state == AutofillAssistantState::BROWSE) {
+    client_->DestroyUI();
+    needs_ui_ = false;
   }
 
   if (ShouldCheckScripts()) {
@@ -1917,7 +1928,7 @@
 
 bool Controller::StateNeedsUI(AutofillAssistantState state) {
   if (!trigger_context_ || !trigger_context_->is_lite_script()) {
-    return StateNeedsUiInRegularScript(state);
+    return StateNeedsUiInRegularScript(state, browse_mode_invisible_);
   }
   return StateNeedsUiInLiteScript(state);
 }
diff --git a/components/autofill_assistant/browser/controller.h b/components/autofill_assistant/browser/controller.h
index dd32747..f6006aa 100644
--- a/components/autofill_assistant/browser/controller.h
+++ b/components/autofill_assistant/browser/controller.h
@@ -143,6 +143,7 @@
       base::OnceCallback<void(const ClientStatus&)>
           view_inflation_finished_callback) override;
   void ClearGenericUi() override;
+  void SetBrowseModeInvisible(bool invisible) override;
 
   // Show the UI if it's not already shown. This is only meaningful while in
   // states where showing the UI is optional, such as RUNNING, in tracking mode.
@@ -497,6 +498,7 @@
 
   bool expand_sheet_for_prompt_action_ = true;
   std::vector<std::string> browse_domains_whitelist_;
+  bool browse_mode_invisible_ = false;
 
   // Only set during a ShowGenericUiAction.
   std::unique_ptr<GenericUserInterfaceProto> generic_user_interface_;
diff --git a/components/autofill_assistant/browser/fake_script_executor_delegate.cc b/components/autofill_assistant/browser/fake_script_executor_delegate.cc
index 27ab678d..8fab50b3 100644
--- a/components/autofill_assistant/browser/fake_script_executor_delegate.cc
+++ b/components/autofill_assistant/browser/fake_script_executor_delegate.cc
@@ -237,4 +237,6 @@
 void FakeScriptExecutorDelegate::SetOverlayBehavior(
     ConfigureUiStateProto::OverlayBehavior overaly_behavior) {}
 
+void FakeScriptExecutorDelegate::SetBrowseModeInvisible(bool invisible) {}
+
 }  // namespace autofill_assistant
diff --git a/components/autofill_assistant/browser/fake_script_executor_delegate.h b/components/autofill_assistant/browser/fake_script_executor_delegate.h
index ff38b8f..1680d6a 100644
--- a/components/autofill_assistant/browser/fake_script_executor_delegate.h
+++ b/components/autofill_assistant/browser/fake_script_executor_delegate.h
@@ -92,6 +92,7 @@
   void ClearGenericUi() override;
   void SetOverlayBehavior(
       ConfigureUiStateProto::OverlayBehavior overlay_behavior) override;
+  void SetBrowseModeInvisible(bool invisible) override;
 
   ClientSettings* GetMutableSettings() { return &client_settings_; }
 
diff --git a/components/autofill_assistant/browser/lite_service.h b/components/autofill_assistant/browser/lite_service.h
index 3e90af7..e1a6911 100644
--- a/components/autofill_assistant/browser/lite_service.h
+++ b/components/autofill_assistant/browser/lite_service.h
@@ -68,9 +68,9 @@
                     bool result,
                     const std::string& response);
 
-  // Stops the script and closes autobot without showing an error message.
-  // This is done by running an explicit stop action, followed by an empty
-  // response in |GetNextActions|.
+  // Stops the script and closes autofill assistant without showing an error
+  // message. This is done by running an explicit stop action, followed by an
+  // empty response in |GetNextActions|.
   void StopWithoutErrorMessage(ResponseCallback callback,
                                Metrics::LiteScriptFinishedState state);
 
diff --git a/components/autofill_assistant/browser/script_executor.cc b/components/autofill_assistant/browser/script_executor.cc
index b9ef1dc..961593a 100644
--- a/components/autofill_assistant/browser/script_executor.cc
+++ b/components/autofill_assistant/browser/script_executor.cc
@@ -427,10 +427,12 @@
     std::unique_ptr<std::vector<UserAction>> user_actions,
     bool disable_force_expand_sheet,
     base::OnceCallback<void()> end_on_navigation_callback,
-    bool browse_mode) {
+    bool browse_mode,
+    bool browse_mode_invisible) {
   // First communicate to the delegate that prompt actions should or should not
   // expand the sheet intitially.
   delegate_->SetExpandSheetForPromptAction(!disable_force_expand_sheet);
+  delegate_->SetBrowseModeInvisible(browse_mode_invisible);
   if (browse_mode) {
     delegate_->EnterState(AutofillAssistantState::BROWSE);
   } else if (delegate_->EnterState(AutofillAssistantState::PROMPT)) {
@@ -471,6 +473,7 @@
 
   delegate_->ClearTouchableElementArea();
   delegate_->SetExpandSheetForPromptAction(true);
+  delegate_->SetBrowseModeInvisible(false);
   delegate_->EnterState(AutofillAssistantState::RUNNING);
 }
 
diff --git a/components/autofill_assistant/browser/script_executor.h b/components/autofill_assistant/browser/script_executor.h
index b62fa73..4af332f8 100644
--- a/components/autofill_assistant/browser/script_executor.h
+++ b/components/autofill_assistant/browser/script_executor.h
@@ -147,7 +147,8 @@
   void Prompt(std::unique_ptr<std::vector<UserAction>> user_actions,
               bool disable_force_expand_sheet,
               base::OnceCallback<void()> end_on_navigation_callback,
-              bool browse_mode) override;
+              bool browse_mode,
+              bool browse_mode_invisible) override;
   void CleanUpAfterPrompt() override;
   void SetBrowseDomainsWhitelist(std::vector<std::string> domains) override;
   void FillAddressForm(
diff --git a/components/autofill_assistant/browser/script_executor_delegate.h b/components/autofill_assistant/browser/script_executor_delegate.h
index 4809e69e..6170d75 100644
--- a/components/autofill_assistant/browser/script_executor_delegate.h
+++ b/components/autofill_assistant/browser/script_executor_delegate.h
@@ -180,6 +180,10 @@
   // Clears the generic UI.
   virtual void ClearGenericUi() = 0;
 
+  // Sets whether browse mode should be invisible or not. Must be set before
+  // calling |EnterState(BROWSE)| to take effect.
+  virtual void SetBrowseModeInvisible(bool invisible) = 0;
+
  protected:
   virtual ~ScriptExecutorDelegate() {}
 };
diff --git a/components/autofill_assistant/browser/service.proto b/components/autofill_assistant/browser/service.proto
index a6c3a70a..57986f6 100644
--- a/components/autofill_assistant/browser/service.proto
+++ b/components/autofill_assistant/browser/service.proto
@@ -1449,6 +1449,16 @@
   // TODO(marianfe): Consider introducing a BrowseAction instead.
   optional bool browse_mode = 7;
 
+  // EXPERIMENTAL.
+  // Only relevant if |browse_mode| is true. If set, the bottom sheet will
+  // completely disappear when the action starts, and re-appear when the action
+  // ends.
+  //
+  // Note: invisible prompts can't show chips to the user. This flag is intended
+  // to be used with prompts that exclusively use choices which are
+  // auto-selected based on DOM state, i.e., |auto_select_when|.
+  optional bool browse_mode_invisible = 10;
+
   // When set to true, end prompt on navigation events happening during a prompt
   // action. The result sent back to the server in
   // ProcessedActionProto.prompt_choice will have |navigation_ended| set to
diff --git a/components/browser_ui/modaldialog/android/BUILD.gn b/components/browser_ui/modaldialog/android/BUILD.gn
index e43c6a6..f3dc112 100644
--- a/components/browser_ui/modaldialog/android/BUILD.gn
+++ b/components/browser_ui/modaldialog/android/BUILD.gn
@@ -75,13 +75,10 @@
   testonly = true
 
   create_srcjar = false
-  sources = [
-    "test/java/res/drawable/ic_add.xml",
-    "test/java/res/values/ids.xml",
-    "test/java/res/values/strings.xml",
-  ]
+  sources = [ "test/java/res/values/ids.xml" ]
   deps = [
     ":java_resources",
+    "//components/browser_ui/strings/android:browser_ui_strings_grd",
     "//ui/android:ui_java_resources",
   ]
 }
diff --git a/components/browser_ui/modaldialog/android/java/src/org/chromium/components/browser_ui/modaldialog/ModalDialogViewTest.java b/components/browser_ui/modaldialog/android/java/src/org/chromium/components/browser_ui/modaldialog/ModalDialogViewTest.java
index 225470cf..5017137 100644
--- a/components/browser_ui/modaldialog/android/java/src/org/chromium/components/browser_ui/modaldialog/ModalDialogViewTest.java
+++ b/components/browser_ui/modaldialog/android/java/src/org/chromium/components/browser_ui/modaldialog/ModalDialogViewTest.java
@@ -159,7 +159,7 @@
     public void testTitleIcon() {
         // Verify that the icon set from builder is displayed.
         PropertyModel model = createModel(mModelBuilder.with(
-                ModalDialogProperties.TITLE_ICON, getActivity(), R.drawable.ic_add));
+                ModalDialogProperties.TITLE_ICON, getActivity(), R.drawable.ic_business));
         onView(allOf(withId(R.id.title), withParent(withId(R.id.title_container))))
                 .check(matches(not(isDisplayed())));
         onView(allOf(withId(R.id.title_icon), withParent(withId(R.id.title_container))))
diff --git a/components/browser_ui/modaldialog/android/test/java/res/drawable/ic_add.xml b/components/browser_ui/modaldialog/android/test/java/res/drawable/ic_add.xml
deleted file mode 100644
index f652b82d..0000000
--- a/components/browser_ui/modaldialog/android/test/java/res/drawable/ic_add.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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. -->
-
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:tools="http://schemas.android.com/tools"
-    tools:targetApi="21"
-    android:width="24dp"
-    android:height="24dp"
-    android:viewportWidth="24.0"
-    android:viewportHeight="24.0">
-
-    <path
-        android:pathData="M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"
-        android:fillColor="@android:color/black" />
-</vector>
diff --git a/components/browser_ui/modaldialog/android/test/java/res/values/strings.xml b/components/browser_ui/modaldialog/android/test/java/res/values/strings.xml
deleted file mode 100644
index 4285811..0000000
--- a/components/browser_ui/modaldialog/android/test/java/res/values/strings.xml
+++ /dev/null
@@ -1,10 +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. -->
-<resources>
-    <string name="title" translatable="false">Title</string>
-    <string name="more" translatable="false">More</string>
-    <string name="cancel" translatable="false">Cancel</string>
-    <string name="ok" translatable="false">OK</string>
-</resources>
diff --git a/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/FourStateCookieSettingsPreference.java b/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/FourStateCookieSettingsPreference.java
index f2353a6ba2..888d39bd 100644
--- a/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/FourStateCookieSettingsPreference.java
+++ b/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/FourStateCookieSettingsPreference.java
@@ -47,7 +47,7 @@
         // Whether the cookies content setting is enforced.
         public boolean cookiesContentSettingEnforced;
         //  Whether third-party blocking is enforced.
-        public boolean thirdPartyBlockingEnforced;
+        public boolean cookieControlsModeEnforced;
     }
 
     // Keeps the params that are applied to the UI if the params are set before the UI is ready.
@@ -166,7 +166,7 @@
             button.setEnabled(false);
         }
         mManagedView.setVisibility(
-                (params.cookiesContentSettingEnforced || params.thirdPartyBlockingEnforced)
+                (params.cookiesContentSettingEnforced || params.cookieControlsModeEnforced)
                         ? View.VISIBLE
                         : View.GONE);
 
@@ -208,10 +208,10 @@
      *         policy restrictions.
      */
     private RadioButtonWithDescription[] getEnforcedButtons(Params params) {
-        if (!params.cookiesContentSettingEnforced && !params.thirdPartyBlockingEnforced) {
+        if (!params.cookiesContentSettingEnforced && !params.cookieControlsModeEnforced) {
             return buttons();
         }
-        if (params.cookiesContentSettingEnforced && params.thirdPartyBlockingEnforced) {
+        if (params.cookiesContentSettingEnforced && params.cookieControlsModeEnforced) {
             return buttons(mAllowButton, mBlockThirdPartyIncognitoButton, mBlockThirdPartyButton,
                     mBlockButton);
         }
@@ -223,7 +223,8 @@
                         mBlockThirdPartyButton, mBlockButton);
             }
         }
-        if (params.blockThirdPartyCookies) {
+        if (params.blockThirdPartyCookies
+                || params.cookieControlsMode == CookieControlsMode.BLOCK_THIRD_PARTY) {
             return buttons(mAllowButton, mBlockThirdPartyIncognitoButton);
         } else {
             return buttons(mBlockThirdPartyIncognitoButton, mBlockThirdPartyButton);
diff --git a/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/SingleCategorySettings.java b/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/SingleCategorySettings.java
index f66cb86..22e6219 100644
--- a/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/SingleCategorySettings.java
+++ b/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/SingleCategorySettings.java
@@ -1084,8 +1084,7 @@
         params.blockThirdPartyCookies = prefService.getBoolean(BLOCK_THIRD_PARTY_COOKIES);
         params.cookieControlsMode = prefService.getInteger(COOKIE_CONTROLS_MODE);
         params.cookiesContentSettingEnforced = mCategory.isManaged();
-        params.thirdPartyBlockingEnforced =
-                prefService.isManagedPreference(BLOCK_THIRD_PARTY_COOKIES);
+        params.cookieControlsModeEnforced = prefService.isManagedPreference(COOKIE_CONTROLS_MODE);
         fourStateCookieToggle.setState(params);
     }
 
diff --git a/components/content_settings/android/cookie_controls_bridge.cc b/components/content_settings/android/cookie_controls_bridge.cc
index f310f08d..de8f12f 100644
--- a/components/content_settings/android/cookie_controls_bridge.cc
+++ b/components/content_settings/android/cookie_controls_bridge.cc
@@ -94,7 +94,7 @@
       browser_context::BrowserContextFromJavaHandle(jbrowser_context_handle);
   return permissions::PermissionsClient::Get()
       ->GetCookieSettings(context)
-      ->IsCookieControlsEnabled();
+      ->ShouldBlockThirdPartyCookies();
 }
 
 static jlong JNI_CookieControlsBridge_Init(
diff --git a/components/content_settings/browser/ui/cookie_controls_controller.cc b/components/content_settings/browser/ui/cookie_controls_controller.cc
index 69b81482..d0f3a2ed 100644
--- a/components/content_settings/browser/ui/cookie_controls_controller.cc
+++ b/components/content_settings/browser/ui/cookie_controls_controller.cc
@@ -59,7 +59,7 @@
 
 std::pair<CookieControlsStatus, CookieControlsEnforcement>
 CookieControlsController::GetStatus(content::WebContents* web_contents) {
-  if (!cookie_settings_->IsCookieControlsEnabled()) {
+  if (!cookie_settings_->ShouldBlockThirdPartyCookies()) {
     return {CookieControlsStatus::kDisabled,
             CookieControlsEnforcement::kNoEnforcement};
   }
diff --git a/components/content_settings/browser/ui/cookie_controls_controller.h b/components/content_settings/browser/ui/cookie_controls_controller.h
index bdb1a78..d9f21eef 100644
--- a/components/content_settings/browser/ui/cookie_controls_controller.h
+++ b/components/content_settings/browser/ui/cookie_controls_controller.h
@@ -12,7 +12,6 @@
 #include "components/content_settings/core/common/cookie_controls_enforcement.h"
 #include "components/content_settings/core/common/cookie_controls_status.h"
 #include "components/prefs/pref_change_registrar.h"
-#include "content/public/browser/web_contents.h"
 
 namespace content {
 class WebContents;
diff --git a/components/content_settings/core/browser/cookie_settings.cc b/components/content_settings/core/browser/cookie_settings.cc
index b92a1d8..49764076 100644
--- a/components/content_settings/core/browser/cookie_settings.cc
+++ b/components/content_settings/core/browser/cookie_settings.cc
@@ -40,10 +40,6 @@
   content_settings_observer_.Add(host_content_settings_map_.get());
   pref_change_registrar_.Init(prefs);
   pref_change_registrar_.Add(
-      prefs::kBlockThirdPartyCookies,
-      base::BindRepeating(&CookieSettings::OnCookiePreferencesChanged,
-                          base::Unretained(this)));
-  pref_change_registrar_.Add(
       prefs::kCookieControlsMode,
       base::BindRepeating(&CookieSettings::OnCookiePreferencesChanged,
                           base::Unretained(this)));
@@ -245,12 +241,6 @@
 CookieSettings::~CookieSettings() = default;
 
 bool CookieSettings::IsCookieControlsEnabled() {
-#if !defined(OS_IOS)
-  if (pref_change_registrar_.prefs()->GetBoolean(
-          prefs::kBlockThirdPartyCookies)) {
-    return true;
-  }
-#endif
 #if defined(OS_IOS)
   if (!base::FeatureList::IsEnabled(kImprovedCookieControls))
     return false;
@@ -285,8 +275,6 @@
   DCHECK(thread_checker_.CalledOnValidThread());
 
   bool new_block_third_party_cookies =
-      pref_change_registrar_.prefs()->GetBoolean(
-          prefs::kBlockThirdPartyCookies) ||
       IsCookieControlsEnabled();
 
   // Safe to read |block_third_party_cookies_| without locking here because the
diff --git a/components/content_settings/core/browser/cookie_settings_policy_handler.cc b/components/content_settings/core/browser/cookie_settings_policy_handler.cc
index 7d61c9bb..22cbf96 100644
--- a/components/content_settings/core/browser/cookie_settings_policy_handler.cc
+++ b/components/content_settings/core/browser/cookie_settings_policy_handler.cc
@@ -6,29 +6,24 @@
 
 #include "components/content_settings/core/browser/cookie_settings.h"
 #include "components/content_settings/core/common/pref_names.h"
+#include "components/policy/core/browser/configuration_policy_handler.h"
 #include "components/policy/core/common/policy_map.h"
 #include "components/policy/policy_constants.h"
 #include "components/prefs/pref_value_map.h"
 
 namespace content_settings {
 
-CookieSettingsPolicyHandler::CookieSettingsPolicyHandler() = default;
+CookieSettingsPolicyHandler::CookieSettingsPolicyHandler()
+    : policy::TypeCheckingPolicyHandler(policy::key::kBlockThirdPartyCookies,
+                                        base::Value::Type::BOOLEAN) {}
 
 CookieSettingsPolicyHandler::~CookieSettingsPolicyHandler() = default;
 
-bool CookieSettingsPolicyHandler::CheckPolicySettings(
-    const policy::PolicyMap& policies,
-    policy::PolicyErrorMap* errors) {
-  return true;
-}
-
 void CookieSettingsPolicyHandler::ApplyPolicySettings(
     const policy::PolicyMap& policies,
     PrefValueMap* prefs) {
-  // When third party cookie blocking is set by policy, the cookie controls UI
-  // can't be enabled.
   const base::Value* third_party_cookie_blocking =
-      policies.GetValue(policy::key::kBlockThirdPartyCookies);
+      policies.GetValue(policy_name());
   if (third_party_cookie_blocking) {
     prefs->SetInteger(
         prefs::kCookieControlsMode,
diff --git a/components/content_settings/core/browser/cookie_settings_policy_handler.h b/components/content_settings/core/browser/cookie_settings_policy_handler.h
index 391e97b..d391c28e 100644
--- a/components/content_settings/core/browser/cookie_settings_policy_handler.h
+++ b/components/content_settings/core/browser/cookie_settings_policy_handler.h
@@ -12,16 +12,14 @@
 
 namespace content_settings {
 
-// A ConfigurationPolicyHandler which forces kCookieControlsEnabled to false
-// when BlockThirdPartyCookies is set by policy.
-class CookieSettingsPolicyHandler : public policy::ConfigurationPolicyHandler {
+// A ConfigurationPolicyHandler which sets kCookieControlsEnabled to
+// kOff/kBlockThirdParty based on the BlockThirdPartyCookies policy.
+class CookieSettingsPolicyHandler : public policy::TypeCheckingPolicyHandler {
  public:
   CookieSettingsPolicyHandler();
   ~CookieSettingsPolicyHandler() override;
 
-  // ConfigurationPolicyHandler:
-  bool CheckPolicySettings(const policy::PolicyMap& policies,
-                           policy::PolicyErrorMap* errors) override;
+  // TypeCheckingPolicyHandler:
   void ApplyPolicySettings(const policy::PolicyMap& policies,
                            PrefValueMap* prefs) override;
 
diff --git a/components/content_settings/core/browser/cookie_settings_unittest.cc b/components/content_settings/core/browser/cookie_settings_unittest.cc
index f029297..ac5fa75 100644
--- a/components/content_settings/core/browser/cookie_settings_unittest.cc
+++ b/components/content_settings/core/browser/cookie_settings_unittest.cc
@@ -85,8 +85,13 @@
         kHttpsSubdomainSite("https://www.example.com"),
         kHttpsSite8080("https://example.com:8080"),
         kAllHttpsSitesPattern(ContentSettingsPattern::FromString("https://*")) {
-    feature_list_.InitAndDisableFeature(
-        net::features::kSameSiteByDefaultCookies);
+    feature_list_.InitWithFeatures(
+        {
+#ifdef OS_IOS
+            kImprovedCookieControls,
+#endif
+        },
+        {net::features::kSameSiteByDefaultCookies});
   }
 
   ~CookieSettingsTest() override { settings_map_->ShutdownOnUIThread(); }
@@ -168,33 +173,21 @@
 }
 
 TEST_F(CookieSettingsTest, CookiesBlockThirdParty) {
-  prefs_.SetBoolean(prefs::kBlockThirdPartyCookies, true);
+  prefs_.SetInteger(prefs::kCookieControlsMode,
+                    static_cast<int>(CookieControlsMode::kBlockThirdParty));
   EXPECT_FALSE(
       cookie_settings_->IsCookieAccessAllowed(kBlockedSite, kFirstPartySite));
   EXPECT_FALSE(cookie_settings_->IsCookieSessionOnly(kBlockedSite));
 }
 
-// Test fixture with ImprovedCookieControls enabled.
-class ImprovedCookieControlsCookieSettingsTest : public CookieSettingsTest {
- public:
-  ImprovedCookieControlsCookieSettingsTest() {
-#if defined(OS_IOS)
-    feature_list_.InitAndEnableFeature(kImprovedCookieControls);
-#endif
-  }
-
- private:
-  base::test::ScopedFeatureList feature_list_;
-};
-
-TEST_F(ImprovedCookieControlsCookieSettingsTest, CookiesControlsDefault) {
+TEST_F(CookieSettingsTest, CookiesControlsDefault) {
   EXPECT_TRUE(
       cookie_settings_->IsCookieAccessAllowed(kBlockedSite, kFirstPartySite));
   EXPECT_FALSE(cookie_settings_incognito_->IsCookieAccessAllowed(
       kBlockedSite, kFirstPartySite));
 }
 
-TEST_F(ImprovedCookieControlsCookieSettingsTest, CookiesControlsEnabled) {
+TEST_F(CookieSettingsTest, CookiesControlsEnabled) {
   prefs_.SetInteger(prefs::kCookieControlsMode,
                     static_cast<int>(CookieControlsMode::kBlockThirdParty));
   EXPECT_FALSE(
@@ -203,7 +196,7 @@
       kBlockedSite, kFirstPartySite));
 }
 
-TEST_F(ImprovedCookieControlsCookieSettingsTest, CookiesControlsDisabled) {
+TEST_F(CookieSettingsTest, CookiesControlsDisabled) {
   prefs_.SetInteger(prefs::kCookieControlsMode,
                     static_cast<int>(CookieControlsMode::kOff));
   EXPECT_TRUE(
@@ -212,8 +205,7 @@
       kBlockedSite, kFirstPartySite));
 }
 
-TEST_F(ImprovedCookieControlsCookieSettingsTest,
-       CookiesControlsEnabledForIncognito) {
+TEST_F(CookieSettingsTest, CookiesControlsEnabledForIncognito) {
   prefs_.SetInteger(prefs::kCookieControlsMode,
                     static_cast<int>(CookieControlsMode::kIncognitoOnly));
   EXPECT_TRUE(
@@ -271,7 +263,8 @@
       cookie_settings_->IsCookieAccessAllowed(kBlockedSite, kFirstPartySite));
   EXPECT_TRUE(cookie_settings_->IsCookieSessionOnly(kBlockedSite));
 
-  prefs_.SetBoolean(prefs::kBlockThirdPartyCookies, true);
+  prefs_.SetInteger(prefs::kCookieControlsMode,
+                    static_cast<int>(CookieControlsMode::kBlockThirdParty));
   EXPECT_TRUE(
       cookie_settings_->IsCookieAccessAllowed(kBlockedSite, kFirstPartySite));
   EXPECT_TRUE(cookie_settings_->IsCookieSessionOnly(kBlockedSite));
@@ -388,7 +381,8 @@
 
 TEST_F(CookieSettingsTest, CookiesThirdPartyBlockedExplicitAllow) {
   cookie_settings_->SetCookieSetting(kAllowedSite, CONTENT_SETTING_ALLOW);
-  prefs_.SetBoolean(prefs::kBlockThirdPartyCookies, true);
+  prefs_.SetInteger(prefs::kCookieControlsMode,
+                    static_cast<int>(CookieControlsMode::kBlockThirdParty));
   EXPECT_TRUE(
       cookie_settings_->IsCookieAccessAllowed(kAllowedSite, kFirstPartySite));
   EXPECT_FALSE(cookie_settings_->IsCookieSessionOnly(kAllowedSite));
@@ -400,7 +394,8 @@
 
 TEST_F(CookieSettingsTest, CookiesThirdPartyBlockedAllSitesAllowed) {
   cookie_settings_->SetCookieSetting(kAllowedSite, CONTENT_SETTING_ALLOW);
-  prefs_.SetBoolean(prefs::kBlockThirdPartyCookies, true);
+  prefs_.SetInteger(prefs::kCookieControlsMode,
+                    static_cast<int>(CookieControlsMode::kBlockThirdParty));
   // As an example for a url that matches all hosts but not all origins,
   // match all HTTPS sites.
   settings_map_->SetContentSettingCustomScope(
@@ -455,7 +450,8 @@
   const GURL top_level_url = GURL(kFirstPartySite);
   const GURL url = GURL(kAllowedSite);
 
-  prefs_.SetBoolean(prefs::kBlockThirdPartyCookies, false);
+  prefs_.SetInteger(prefs::kCookieControlsMode,
+                    static_cast<int>(CookieControlsMode::kOff));
 
   base::HistogramTester histogram_tester;
   histogram_tester.ExpectTotalCount(kAllowedRequestsHistogram, 0);
@@ -479,7 +475,8 @@
   const GURL top_level_url = GURL(kFirstPartySite);
   const GURL url = GURL(kAllowedSite);
 
-  prefs_.SetBoolean(prefs::kBlockThirdPartyCookies, true);
+  prefs_.SetInteger(prefs::kCookieControlsMode,
+                    static_cast<int>(CookieControlsMode::kBlockThirdParty));
 
   settings_map_->SetContentSettingCustomScope(
       ContentSettingsPattern::FromURLNoWildcard(url),
@@ -501,7 +498,8 @@
   base::HistogramTester histogram_tester;
   histogram_tester.ExpectTotalCount(kAllowedRequestsHistogram, 0);
 
-  prefs_.SetBoolean(prefs::kBlockThirdPartyCookies, true);
+  prefs_.SetInteger(prefs::kCookieControlsMode,
+                    static_cast<int>(CookieControlsMode::kBlockThirdParty));
 
   settings_map_->SetContentSettingCustomScope(
       ContentSettingsPattern::FromURLNoWildcard(url),
@@ -532,7 +530,8 @@
   base::HistogramTester histogram_tester;
   histogram_tester.ExpectTotalCount(kAllowedRequestsHistogram, 0);
 
-  prefs_.SetBoolean(prefs::kBlockThirdPartyCookies, true);
+  prefs_.SetInteger(prefs::kCookieControlsMode,
+                    static_cast<int>(CookieControlsMode::kBlockThirdParty));
 
   settings_map_->SetContentSettingCustomScope(
       ContentSettingsPattern::FromURLNoWildcard(url),
@@ -575,7 +574,8 @@
   const GURL top_level_url = GURL(kFirstPartySite);
   const GURL url = GURL(kHttpSite);
 
-  prefs_.SetBoolean(prefs::kBlockThirdPartyCookies, true);
+  prefs_.SetInteger(prefs::kCookieControlsMode,
+                    static_cast<int>(CookieControlsMode::kBlockThirdParty));
 
   settings_map_->SetContentSettingCustomScope(
       ContentSettingsPattern::FromURLNoWildcard(url),
@@ -601,7 +601,8 @@
   const GURL top_level_url = GURL(kHttpSite);
   const GURL url = GURL(kFirstPartySite);
 
-  prefs_.SetBoolean(prefs::kBlockThirdPartyCookies, true);
+  prefs_.SetInteger(prefs::kCookieControlsMode,
+                    static_cast<int>(CookieControlsMode::kBlockThirdParty));
 
   settings_map_->SetContentSettingCustomScope(
       ContentSettingsPattern::FromURLNoWildcard(url),
@@ -648,7 +649,8 @@
   const GURL top_level_url = GURL(kFirstPartySite);
   const GURL url = GURL(kAllowedSite);
 
-  prefs_.SetBoolean(prefs::kBlockThirdPartyCookies, true);
+  prefs_.SetInteger(prefs::kCookieControlsMode,
+                    static_cast<int>(CookieControlsMode::kBlockThirdParty));
 
   settings_map_->SetContentSettingCustomScope(
       ContentSettingsPattern::FromURLNoWildcard(url),
@@ -697,7 +699,8 @@
 }
 
 TEST_F(CookieSettingsTest, ExtensionsThirdParty) {
-  prefs_.SetBoolean(prefs::kBlockThirdPartyCookies, true);
+  prefs_.SetInteger(prefs::kCookieControlsMode,
+                    static_cast<int>(CookieControlsMode::kBlockThirdParty));
 
   // XHRs stemming from extensions are exempt from third-party cookie blocking
   // rules (as the first party is always the extension's security origin).
@@ -711,7 +714,8 @@
   EXPECT_TRUE(
       cookie_settings_->IsCookieAccessAllowed(kHttpsSite, kFirstPartySite));
 
-  prefs_.SetBoolean(prefs::kBlockThirdPartyCookies, true);
+  prefs_.SetInteger(prefs::kCookieControlsMode,
+                    static_cast<int>(CookieControlsMode::kBlockThirdParty));
   EXPECT_FALSE(
       cookie_settings_->IsThirdPartyAccessAllowed(kFirstPartySite, nullptr));
   EXPECT_FALSE(
@@ -757,7 +761,8 @@
 TEST_F(CookieSettingsTest, ThirdPartySettingObserver) {
   CookieSettingsObserver observer(cookie_settings_.get());
   EXPECT_FALSE(observer.last_value());
-  prefs_.SetBoolean(prefs::kBlockThirdPartyCookies, true);
+  prefs_.SetInteger(prefs::kCookieControlsMode,
+                    static_cast<int>(CookieControlsMode::kBlockThirdParty));
   EXPECT_TRUE(observer.last_value());
 }
 
@@ -844,7 +849,7 @@
 // Test fixture with SameSiteByDefaultCookies enabled.
 class SameSiteByDefaultCookieSettingsTest : public CookieSettingsTest {
  public:
-  SameSiteByDefaultCookieSettingsTest() : CookieSettingsTest() {
+  SameSiteByDefaultCookieSettingsTest() {
     feature_list_.InitAndEnableFeature(
         net::features::kSameSiteByDefaultCookies);
   }
diff --git a/components/crash/content/browser/crash_metrics_reporter_android.cc b/components/crash/content/browser/crash_metrics_reporter_android.cc
index 7cce720..55bd51cb 100644
--- a/components/crash/content/browser/crash_metrics_reporter_android.cc
+++ b/components/crash/content/browser/crash_metrics_reporter_android.cc
@@ -117,9 +117,9 @@
           base::android::APPLICATION_STATE_HAS_RUNNING_ACTIVITIES ||
       info.app_state == base::android::APPLICATION_STATE_HAS_PAUSED_ACTIVITIES;
   const bool intentional_kill = info.was_killed_intentionally_by_browser;
-  // TODO(crbug.com/1115517): Take into account renderer_shutdown_requested.
   const bool android_oom_kill = !info.was_killed_intentionally_by_browser &&
-                                !crashed && !info.normal_termination;
+                                !crashed && !info.normal_termination &&
+                                !info.renderer_shutdown_requested;
   const bool renderer_visible = info.renderer_has_visible_clients;
   const bool renderer_subframe = info.renderer_was_subframe;
   const bool renderer_allocation_failed =
@@ -171,7 +171,7 @@
         ReportCrashCount(ProcessedCrashCounts::
                              kRendererForegroundVisibleNormalTermNoMinidump,
                          &reported_counts);
-      } else {
+      } else if (!info.renderer_shutdown_requested) {
         DCHECK(android_oom_kill);
         if (renderer_subframe) {
           ReportCrashCount(
diff --git a/components/discardable_memory/common/discardable_shared_memory_heap.cc b/components/discardable_memory/common/discardable_shared_memory_heap.cc
index d7f83bfb..53030c9 100644
--- a/components/discardable_memory/common/discardable_shared_memory_heap.cc
+++ b/components/discardable_memory/common/discardable_shared_memory_heap.cc
@@ -259,28 +259,32 @@
   auto* total_dump = pmd->CreateAllocatorDump(base::StringPrintf(
       "discardable/child_0x%" PRIXPTR, reinterpret_cast<uintptr_t>(this)));
   const size_t freelist_size = GetSizeOfFreeLists();
-  const size_t total_size = GetSize();
   total_dump->AddScalar("freelist_size",
                         base::trace_event::MemoryAllocatorDump::kUnitsBytes,
                         freelist_size);
-  total_dump->AddScalar("virtual_size",
-                        base::trace_event::MemoryAllocatorDump::kUnitsBytes,
-                        total_size);
   if (args.level_of_detail ==
       base::trace_event::MemoryDumpLevelOfDetail::BACKGROUND) {
+    // These metrics (size and virtual size) are also reported by each
+    // individual segment. If we report both, then the counts are artificially
+    // inflated in detailed dumps, depending on aggregation (for instance, in
+    // about:tracing's UI).
+    const size_t total_size = GetSize();
     total_dump->AddScalar(base::trace_event::MemoryAllocatorDump::kNameSize,
                           base::trace_event::MemoryAllocatorDump::kUnitsBytes,
                           total_size - freelist_size);
-    return true;
+    total_dump->AddScalar("virtual_size",
+                          base::trace_event::MemoryAllocatorDump::kUnitsBytes,
+                          total_size);
+  } else {
+    // This iterates over all the memory allocated by the heap, and calls
+    // |OnMemoryDump| for each. It does not contain any information about the
+    // DiscardableSharedMemoryHeap itself.
+    std::for_each(memory_segments_.begin(), memory_segments_.end(),
+                  [pmd](const std::unique_ptr<ScopedMemorySegment>& segment) {
+                    segment->OnMemoryDump(pmd);
+                  });
   }
 
-  // This iterates over all the memory allocated by the heap, and calls
-  // |OnMemoryDump| for each. It does not contain any information about the
-  // DiscardableSharedMemoryHeap itself.
-  std::for_each(memory_segments_.begin(), memory_segments_.end(),
-                [pmd](const std::unique_ptr<ScopedMemorySegment>& segment) {
-                  segment->OnMemoryDump(pmd);
-                });
   return true;
 }
 
diff --git a/components/discardable_memory/common/discardable_shared_memory_heap_unittest.cc b/components/discardable_memory/common/discardable_shared_memory_heap_unittest.cc
index 71dcbb6..5d3a784 100644
--- a/components/discardable_memory/common/discardable_shared_memory_heap_unittest.cc
+++ b/components/discardable_memory/common/discardable_shared_memory_heap_unittest.cc
@@ -9,6 +9,7 @@
 #include <utility>
 
 #include "base/bind.h"
+#include "base/bind_helpers.h"
 #include "base/memory/discardable_shared_memory.h"
 #include "base/process/process_metrics.h"
 #include "base/strings/stringprintf.h"
@@ -407,5 +408,38 @@
   }
 }
 
+TEST(DiscardableSharedMemoryHeapTest, DetailedDumpsDontContainRedundantData) {
+  using testing::ByRef;
+  using testing::Contains;
+  using testing::Eq;
+  using testing::Not;
+  DiscardableSharedMemoryHeap heap;
+
+  base::trace_event::MemoryDumpArgs args = {
+      base::trace_event::MemoryDumpLevelOfDetail::DETAILED};
+  size_t block_size = base::GetPageSize();
+
+  auto memory = std::make_unique<base::DiscardableSharedMemory>();
+  ASSERT_TRUE(memory->CreateAndMap(block_size));
+  auto span = heap.Grow(std::move(memory), block_size, 1, base::DoNothing());
+
+  base::trace_event::ProcessMemoryDump pmd(args);
+  heap.OnMemoryDump(args, &pmd);
+  auto* dump = pmd.GetAllocatorDump(base::StringPrintf(
+      "discardable/child_0x%" PRIXPTR, reinterpret_cast<uintptr_t>(&heap)));
+  ASSERT_NE(nullptr, dump);
+
+  base::trace_event::MemoryAllocatorDump::Entry freelist("freelist_size",
+                                                         "bytes", 0);
+  EXPECT_THAT(dump->entries(), Contains(Eq(ByRef(freelist))));
+
+  // Detailed dumps do not contain virtual size.
+  base::trace_event::MemoryAllocatorDump::Entry virtual_size(
+      "virtual_size", "bytes", block_size);
+  EXPECT_THAT(dump->entries(), Not(Contains(Eq(ByRef(virtual_size)))));
+
+  heap.MergeIntoFreeLists(std::move(span));
+}
+
 }  // namespace
 }  // namespace discardable_memory
diff --git a/components/embedder_support/android/BUILD.gn b/components/embedder_support/android/BUILD.gn
index 5740262..3ebad27 100644
--- a/components/embedder_support/android/BUILD.gn
+++ b/components/embedder_support/android/BUILD.gn
@@ -105,7 +105,7 @@
     "//base:base_java",
     "//content/public/android:content_java",
     "//third_party/android_deps:androidx_annotation_annotation_java",
-    "//ui/android:ui_java",
+    "//ui/android:ui_no_recycler_view_java",
   ]
   sources = [
     "java/src/org/chromium/components/embedder_support/view/ContentView.java",
@@ -117,7 +117,7 @@
     "//base:base_java",
     "//base:jni_java",
     "//content/public/android:content_java",
-    "//ui/android:ui_java",
+    "//ui/android:ui_no_recycler_view_java",
   ]
   annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ]
   sources = [ "java/src/org/chromium/components/embedder_support/view/ContentViewRenderView.java" ]
@@ -181,7 +181,7 @@
     "//content/public/android:content_java",
     "//third_party/android_deps:androidx_annotation_annotation_java",
     "//third_party/android_deps:androidx_core_core_java",
-    "//ui/android:ui_java",
+    "//ui/android:ui_no_recycler_view_java",
   ]
   resources_package = "org.chromium.components.embedder_support.delegate"
   annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ]
@@ -229,7 +229,7 @@
     "//content/public/android:content_java",
     "//third_party/android_deps:androidx_annotation_annotation_java",
     "//third_party/blink/public:blink_headers_java",
-    "//ui/android:ui_java",
+    "//ui/android:ui_no_recycler_view_java",
   ]
   annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ]
   sources = [ "java/src/org/chromium/components/embedder_support/contextmenu/ContextMenuParams.java" ]
diff --git a/components/location/android/BUILD.gn b/components/location/android/BUILD.gn
index 871738e..3d1ad4c 100644
--- a/components/location/android/BUILD.gn
+++ b/components/location/android/BUILD.gn
@@ -15,7 +15,7 @@
   deps = [
     "//base:base_java",
     "//third_party/android_deps:androidx_annotation_annotation_java",
-    "//ui/android:ui_java",
+    "//ui/android:ui_no_recycler_view_java",
   ]
   sources = [ "java/src/org/chromium/components/location/LocationUtils.java" ]
   srcjar_deps = [ ":location_settings_dialog_enums_java" ]
@@ -26,7 +26,7 @@
     ":location_java",
     "//base:base_java",
     "//base:jni_java",
-    "//ui/android:ui_java",
+    "//ui/android:ui_no_recycler_view_java",
   ]
   sources =
       [ "java/src/org/chromium/components/location/LocationSettings.java" ]
diff --git a/components/metrics/BUILD.gn b/components/metrics/BUILD.gn
index 797f5583..1aad8f4 100644
--- a/components/metrics/BUILD.gn
+++ b/components/metrics/BUILD.gn
@@ -217,6 +217,9 @@
 if (!is_ios) {
   static_library("content") {
     sources = [
+      "content/content_stability_metrics_provider.cc",
+      "content/content_stability_metrics_provider.h",
+      "content/extensions_helper.h",
       "content/gpu_metrics_provider.cc",
       "content/gpu_metrics_provider.h",
       "content/rendering_perf_metrics_provider.cc",
@@ -228,8 +231,12 @@
     deps = [
       "//base",
       "//content/public/browser",
+      "//extensions/buildflags",
       "//gpu/config",
     ]
+    if (is_android) {
+      deps += [ "//components/crash/content/browser" ]
+    }
   }
 }
 
diff --git a/components/metrics/content/DEPS b/components/metrics/content/DEPS
index 9c560c67..c8b2783 100644
--- a/components/metrics/content/DEPS
+++ b/components/metrics/content/DEPS
@@ -1,6 +1,9 @@
 include_rules = [
+  "+components/crash",
   "+content/public/browser",
+  "+content/public/common",
   "+gpu/config",
+  "+ppapi/buildflags/buildflags.h",
 ]
 
 specific_include_rules = {
diff --git a/chrome/browser/metrics/chrome_stability_metrics_provider.cc b/components/metrics/content/content_stability_metrics_provider.cc
similarity index 68%
rename from chrome/browser/metrics/chrome_stability_metrics_provider.cc
rename to components/metrics/content/content_stability_metrics_provider.cc
index 194e837..9a3deef 100644
--- a/chrome/browser/metrics/chrome_stability_metrics_provider.cc
+++ b/components/metrics/content/content_stability_metrics_provider.cc
@@ -2,13 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/metrics/chrome_stability_metrics_provider.h"
+#include "components/metrics/content/content_stability_metrics_provider.h"
 
 #include <vector>
 
 #include "base/check.h"
 #include "base/notreached.h"
 #include "build/build_config.h"
+#include "components/metrics/content/extensions_helper.h"
 #include "content/public/browser/browser_child_process_observer.h"
 #include "content/public/browser/child_process_data.h"
 #include "content/public/browser/child_process_termination_info.h"
@@ -16,28 +17,23 @@
 #include "content/public/browser/notification_types.h"
 #include "content/public/browser/render_process_host.h"
 #include "content/public/common/process_type.h"
-#include "extensions/buildflags/buildflags.h"
 #include "ppapi/buildflags/buildflags.h"
 
 #if defined(OS_ANDROID)
 #include "components/crash/content/browser/crash_metrics_reporter_android.h"
 #endif
 
-#if BUILDFLAG(ENABLE_EXTENSIONS)
-#include "extensions/browser/process_map.h"
-#endif
+namespace metrics {
 
-#if BUILDFLAG(ENABLE_PLUGINS)
-#include "chrome/browser/metrics/plugin_metrics_provider.h"
-#endif
-
-ChromeStabilityMetricsProvider::ChromeStabilityMetricsProvider(
-    PrefService* local_state)
+ContentStabilityMetricsProvider::ContentStabilityMetricsProvider(
+    PrefService* local_state,
+    std::unique_ptr<ExtensionsHelper> extensions_helper)
     :
 #if defined(OS_ANDROID)
       scoped_observer_(this),
 #endif  // defined(OS_ANDROID)
-      helper_(local_state) {
+      helper_(local_state),
+      extensions_helper_(std::move(extensions_helper)) {
   BrowserChildProcessObserver::Add(this);
 
   registrar_.Add(this, content::NOTIFICATION_LOAD_START,
@@ -56,27 +52,25 @@
 #endif  // defined(OS_ANDROID)
 }
 
-ChromeStabilityMetricsProvider::~ChromeStabilityMetricsProvider() {
+ContentStabilityMetricsProvider::~ContentStabilityMetricsProvider() {
   registrar_.RemoveAll();
   BrowserChildProcessObserver::Remove(this);
 }
 
-void ChromeStabilityMetricsProvider::OnRecordingEnabled() {
-}
+void ContentStabilityMetricsProvider::OnRecordingEnabled() {}
 
-void ChromeStabilityMetricsProvider::OnRecordingDisabled() {
-}
+void ContentStabilityMetricsProvider::OnRecordingDisabled() {}
 
-void ChromeStabilityMetricsProvider::ProvideStabilityMetrics(
-    metrics::SystemProfileProto* system_profile_proto) {
+void ContentStabilityMetricsProvider::ProvideStabilityMetrics(
+    SystemProfileProto* system_profile_proto) {
   helper_.ProvideStabilityMetrics(system_profile_proto);
 }
 
-void ChromeStabilityMetricsProvider::ClearSavedStabilityMetrics() {
+void ContentStabilityMetricsProvider::ClearSavedStabilityMetrics() {
   helper_.ClearSavedStabilityMetrics();
 }
 
-void ChromeStabilityMetricsProvider::Observe(
+void ContentStabilityMetricsProvider::Observe(
     int type,
     const content::NotificationSource& source,
     const content::NotificationDetails& details) {
@@ -88,15 +82,10 @@
     case content::NOTIFICATION_RENDERER_PROCESS_CLOSED: {
       content::ChildProcessTerminationInfo* process_info =
           content::Details<content::ChildProcessTerminationInfo>(details).ptr();
-      bool was_extension_process = false;
-#if BUILDFLAG(ENABLE_EXTENSIONS)
-      content::RenderProcessHost* host =
-          content::Source<content::RenderProcessHost>(source).ptr();
-      if (extensions::ProcessMap::Get(host->GetBrowserContext())
-              ->Contains(host->GetID())) {
-        was_extension_process = true;
-      }
-#endif
+      bool was_extension_process =
+          extensions_helper_ &&
+          extensions_helper_->IsExtensionProcess(
+              content::Source<content::RenderProcessHost>(source).ptr());
       helper_.LogRendererCrash(was_extension_process, process_info->status,
                                process_info->exit_code);
       break;
@@ -107,15 +96,10 @@
       break;
 
     case content::NOTIFICATION_RENDERER_PROCESS_CREATED: {
-      bool was_extension_process = false;
-#if BUILDFLAG(ENABLE_EXTENSIONS)
-      content::RenderProcessHost* host =
-          content::Source<content::RenderProcessHost>(source).ptr();
-      if (extensions::ProcessMap::Get(host->GetBrowserContext())
-              ->Contains(host->GetID())) {
-        was_extension_process = true;
-      }
-#endif
+      bool was_extension_process =
+          extensions_helper_ &&
+          extensions_helper_->IsExtensionProcess(
+              content::Source<content::RenderProcessHost>(source).ptr());
       helper_.LogRendererLaunched(was_extension_process);
       break;
     }
@@ -126,15 +110,17 @@
   }
 }
 
-void ChromeStabilityMetricsProvider::BrowserChildProcessCrashed(
+void ContentStabilityMetricsProvider::BrowserChildProcessCrashed(
     const content::ChildProcessData& data,
     const content::ChildProcessTerminationInfo& info) {
   DCHECK(!data.metrics_name.empty());
 #if BUILDFLAG(ENABLE_PLUGINS)
   // Exclude plugin crashes from the count below because we report them via
   // a separate UMA metric.
-  if (PluginMetricsProvider::IsPluginProcess(data.process_type))
+  if (data.process_type == content::PROCESS_TYPE_PPAPI_PLUGIN ||
+      data.process_type == content::PROCESS_TYPE_PPAPI_BROKER) {
     return;
+  }
 #endif
 
   if (data.process_type == content::PROCESS_TYPE_UTILITY)
@@ -142,7 +128,7 @@
   helper_.BrowserChildProcessCrashed();
 }
 
-void ChromeStabilityMetricsProvider::BrowserChildProcessLaunchedAndConnected(
+void ContentStabilityMetricsProvider::BrowserChildProcessLaunchedAndConnected(
     const content::ChildProcessData& data) {
   DCHECK(!data.metrics_name.empty());
   if (data.process_type == content::PROCESS_TYPE_UTILITY)
@@ -150,7 +136,7 @@
 }
 
 #if defined(OS_ANDROID)
-void ChromeStabilityMetricsProvider::OnCrashDumpProcessed(
+void ContentStabilityMetricsProvider::OnCrashDumpProcessed(
     int rph_id,
     const crash_reporter::CrashMetricsReporter::ReportedCrashTypeSet&
         reported_counts) {
@@ -163,5 +149,6 @@
     helper_.IncreaseGpuCrashCount();
   }
 }
-
 #endif  // defined(OS_ANDROID)
+
+}  // namespace metrics
diff --git a/components/metrics/content/content_stability_metrics_provider.h b/components/metrics/content/content_stability_metrics_provider.h
new file mode 100644
index 0000000..3f34e64
--- /dev/null
+++ b/components/metrics/content/content_stability_metrics_provider.h
@@ -0,0 +1,106 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_METRICS_CONTENT_CONTENT_STABILITY_METRICS_PROVIDER_H_
+#define COMPONENTS_METRICS_CONTENT_CONTENT_STABILITY_METRICS_PROVIDER_H_
+
+#include <memory>
+
+#include "base/gtest_prod_util.h"
+#include "base/scoped_observer.h"
+#include "build/build_config.h"
+#include "components/metrics/metrics_provider.h"
+#include "components/metrics/stability_metrics_helper.h"
+#include "content/public/browser/browser_child_process_observer.h"
+#include "content/public/browser/notification_observer.h"
+#include "content/public/browser/notification_registrar.h"
+
+#if defined(OS_ANDROID)
+#include "components/crash/content/browser/crash_metrics_reporter_android.h"
+#endif  // defined(OS_ANDROID)
+
+class PrefService;
+
+FORWARD_DECLARE_TEST(ChromeStabilityMetricsProviderTest,
+                     BrowserChildProcessObserverGpu);
+FORWARD_DECLARE_TEST(ChromeStabilityMetricsProviderTest,
+                     BrowserChildProcessObserverUtility);
+FORWARD_DECLARE_TEST(ChromeStabilityMetricsProviderTest, NotificationObserver);
+
+namespace metrics {
+
+class ExtensionsHelper;
+
+// ContentStabilityMetricsProvider gathers and logs Chrome-specific stability-
+// related metrics.
+class ContentStabilityMetricsProvider
+    : public MetricsProvider,
+      public content::BrowserChildProcessObserver,
+#if defined(OS_ANDROID)
+      public crash_reporter::CrashMetricsReporter::Observer,
+#endif
+      public content::NotificationObserver {
+ public:
+  // |extensions_helper| is used to determine if a process corresponds to an
+  // extension and is optional. If an ExtensionsHelper is not supplied it is
+  // assumed the process does not correspond to an extension.
+  ContentStabilityMetricsProvider(
+      PrefService* local_state,
+      std::unique_ptr<ExtensionsHelper> extensions_helper);
+  ContentStabilityMetricsProvider(const ContentStabilityMetricsProvider&) =
+      delete;
+  ContentStabilityMetricsProvider& operator=(
+      const ContentStabilityMetricsProvider&) = delete;
+  ~ContentStabilityMetricsProvider() override;
+
+  // MetricsDataProvider:
+  void OnRecordingEnabled() override;
+  void OnRecordingDisabled() override;
+  void ProvideStabilityMetrics(
+      SystemProfileProto* system_profile_proto) override;
+  void ClearSavedStabilityMetrics() override;
+
+ private:
+  FRIEND_TEST_ALL_PREFIXES(::ChromeStabilityMetricsProviderTest,
+                           BrowserChildProcessObserverGpu);
+  FRIEND_TEST_ALL_PREFIXES(::ChromeStabilityMetricsProviderTest,
+                           BrowserChildProcessObserverUtility);
+  FRIEND_TEST_ALL_PREFIXES(::ChromeStabilityMetricsProviderTest,
+                           NotificationObserver);
+
+  // content::NotificationObserver:
+  void Observe(int type,
+               const content::NotificationSource& source,
+               const content::NotificationDetails& details) override;
+
+  // content::BrowserChildProcessObserver:
+  void BrowserChildProcessCrashed(
+      const content::ChildProcessData& data,
+      const content::ChildProcessTerminationInfo& info) override;
+  void BrowserChildProcessLaunchedAndConnected(
+      const content::ChildProcessData& data) override;
+
+#if defined(OS_ANDROID)
+  // crash_reporter::CrashMetricsReporter::Observer:
+  void OnCrashDumpProcessed(
+      int rph_id,
+      const crash_reporter::CrashMetricsReporter::ReportedCrashTypeSet&
+          reported_counts) override;
+
+  ScopedObserver<crash_reporter::CrashMetricsReporter,
+                 crash_reporter::CrashMetricsReporter::Observer>
+      scoped_observer_;
+#endif  // defined(OS_ANDROID)
+
+  StabilityMetricsHelper helper_;
+
+  // Registrar for receiving stability-related notifications.
+  content::NotificationRegistrar registrar_;
+
+  std::unique_ptr<ExtensionsHelper> extensions_helper_;
+};
+
+}  // namespace metrics
+
+#endif  // COMPONENTS_METRICS_CONTENT_CONTENT_STABILITY_METRICS_PROVIDER_H_
diff --git a/components/metrics/content/extensions_helper.h b/components/metrics/content/extensions_helper.h
new file mode 100644
index 0000000..20c67bc8
--- /dev/null
+++ b/components/metrics/content/extensions_helper.h
@@ -0,0 +1,29 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_METRICS_CONTENT_EXTENSIONS_HELPER_H_
+#define COMPONENTS_METRICS_CONTENT_EXTENSIONS_HELPER_H_
+
+namespace content {
+class RenderProcessHost;
+}
+
+namespace metrics {
+
+// ExtensionsHelper is used by ContentStabilityMetricsProvider to determine
+// if a RenderProcessHost hosts an extension. This is separate from
+// ContentStabilityMetricsProvider to avoid this code depending directly on
+// extensions.
+class ExtensionsHelper {
+ public:
+  virtual ~ExtensionsHelper() = default;
+
+  // Returns true if |render_process_host| is hosting an extension.
+  virtual bool IsExtensionProcess(
+      content::RenderProcessHost* render_process_host) = 0;
+};
+
+}  // namespace metrics
+
+#endif  // COMPONENTS_METRICS_CONTENT_EXTENSIONS_HELPER_H_
diff --git a/components/paint_preview/common/paint_preview_tracker.cc b/components/paint_preview/common/paint_preview_tracker.cc
index 25ea7cba..66bbe4c 100644
--- a/components/paint_preview/common/paint_preview_tracker.cc
+++ b/components/paint_preview/common/paint_preview_tracker.cc
@@ -44,8 +44,7 @@
     bool is_main_frame)
     : guid_(guid),
       embedding_token_(embedding_token),
-      is_main_frame_(is_main_frame),
-      scroll_(SkISize::Make(0, 0)) {}
+      is_main_frame_(is_main_frame) {}
 
 PaintPreviewTracker::~PaintPreviewTracker() {
   DCHECK(states_.empty());
@@ -96,8 +95,7 @@
     const gfx::Rect& rect,
     const base::UnguessableToken& embedding_token) {
   sk_sp<SkPicture> pic = SkPicture::MakePlaceholder(
-      SkRect::MakeXYWH(rect.x() + scroll_.width(), rect.y() + scroll_.height(),
-                       rect.width(), rect.height()));
+      SkRect::MakeXYWH(rect.x(), rect.y(), rect.width(), rect.height()));
   const uint32_t content_id = pic->uniqueID();
   DCHECK(!base::Contains(picture_context_.content_id_to_embedding_token,
                          content_id));
@@ -106,10 +104,6 @@
   return content_id;
 }
 
-void PaintPreviewTracker::SetScrollForFrame(const SkISize& scroll) {
-  scroll_ = scroll;
-}
-
 void PaintPreviewTracker::AddGlyphs(const SkTextBlob* blob) {
   if (!blob)
     return;
@@ -170,8 +164,8 @@
   DCHECK(it != subframe_pics_.end());
 
   SkRect rect = it->second->cullRect();
-  SkMatrix matrix = SkMatrix::Translate(rect.x(), rect.y());
-  canvas->drawPicture(it->second, &matrix, nullptr);
+  SkMatrix subframe_offset = SkMatrix::Translate(rect.x(), rect.y());
+  canvas->drawPicture(it->second, &subframe_offset, nullptr);
 }
 
 void PaintPreviewTracker::MoveLinks(std::vector<mojom::LinkDataPtr>* out) {
diff --git a/components/paint_preview/common/paint_preview_tracker.h b/components/paint_preview/common/paint_preview_tracker.h
index 9a9f176..4828a9b 100644
--- a/components/paint_preview/common/paint_preview_tracker.h
+++ b/components/paint_preview/common/paint_preview_tracker.h
@@ -62,9 +62,6 @@
       const gfx::Rect& rect,
       const base::UnguessableToken& embedding_token);
 
-  // Sets the scroll position for the top layer frame.
-  void SetScrollForFrame(const SkISize& scroll);
-
   // Adds the glyphs in |blob| to the glyph usage tracker for the |blob|'s
   // associated typface.
   void AddGlyphs(const SkTextBlob* blob);
@@ -85,7 +82,8 @@
   // be considered immutable.
 
   // Inserts the OOP subframe placeholder associated with |content_id| into
-  // |canvas|.
+  // |canvas|. The cull rect of the placeholder will encode the position and
+  // size of the the subframe in its parent's coordinate system.
   void CustomDataToSkPictureCallback(SkCanvas* canvas, uint32_t content_id);
 
   // Expose internal maps for use in MakeSerialProcs().
@@ -111,7 +109,6 @@
   const base::Optional<base::UnguessableToken> embedding_token_;
   const bool is_main_frame_;
 
-  SkISize scroll_;
   SkMatrix matrix_;
   std::vector<SkMatrix> states_;
 
diff --git a/components/paint_preview/common/paint_preview_tracker_unittest.cc b/components/paint_preview/common/paint_preview_tracker_unittest.cc
index 8b62b00..f84d7e1 100644
--- a/components/paint_preview/common/paint_preview_tracker_unittest.cc
+++ b/components/paint_preview/common/paint_preview_tracker_unittest.cc
@@ -4,12 +4,14 @@
 
 #include "components/paint_preview/common/paint_preview_tracker.h"
 
+#include "base/containers/flat_map.h"
 #include "base/unguessable_token.h"
 #include "components/paint_preview/common/serial_utils.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/skia/include/core/SkCanvas.h"
 #include "third_party/skia/include/core/SkPicture.h"
 #include "third_party/skia/include/core/SkPictureRecorder.h"
+#include "third_party/skia/include/core/SkRect.h"
 #include "third_party/skia/include/core/SkRefCnt.h"
 #include "third_party/skia/include/core/SkTextBlob.h"
 #include "ui/gfx/geometry/rect.h"
@@ -23,6 +25,36 @@
   bool was_called;
 };
 
+// A test canvas for checking that the pictures drawn to it have the cull rect
+// we expect them to.
+class ExpectSubframeCanvas : public SkCanvas {
+ public:
+  void onDrawPicture(const SkPicture* picture,
+                     const SkMatrix*,
+                     const SkPaint*) override {
+    drawn_pictures_.insert({picture->uniqueID(), picture->cullRect()});
+  }
+
+  void ExpectHasPicture(uint32_t expected_picture_id,
+                        const gfx::Rect& expected_bounds) {
+    auto it = drawn_pictures_.find(expected_picture_id);
+    if (it == drawn_pictures_.end()) {
+      ADD_FAILURE() << "Picture ID was not recorded.";
+      return;
+    }
+
+    SkIRect rect = it->second.round();
+    EXPECT_EQ(rect.x(), expected_bounds.x());
+    EXPECT_EQ(rect.y(), expected_bounds.y());
+    EXPECT_EQ(rect.width(), expected_bounds.width());
+    EXPECT_EQ(rect.height(), expected_bounds.height());
+  }
+
+ private:
+  // Map of picture id to expected bounds of pictures drawn into this canvas.
+  base::flat_map<uint32_t, SkRect> drawn_pictures_;
+};
+
 }  // namespace
 
 TEST(PaintPreviewTrackerTest, TestGetters) {
@@ -52,42 +84,10 @@
   EXPECT_EQ(context->content_id_to_embedding_token[content_id],
             kEmbeddingTokenChild);
 
-  SkPictureRecorder recorder;
-  SkCanvas* canvas = recorder.beginRecording(100, 100);
-  tracker.CustomDataToSkPictureCallback(canvas, content_id);
-  sk_sp<SkPicture> pic = recorder.finishRecordingAsPicture();
+  ExpectSubframeCanvas canvas;
+  tracker.CustomDataToSkPictureCallback(&canvas, content_id);
 
-  // TODO(crbug/1009552): find a good way to check that a filler picture was
-  // actually inserted into |pic|. This is difficult without using the
-  // underlying private picture record.
-}
-
-TEST(PaintPreviewTrackerTest, TestRemoteFramePlaceholderPictureWithScroll) {
-  const base::UnguessableToken kDocToken = base::UnguessableToken::Create();
-  const base::UnguessableToken kEmbeddingToken =
-      base::UnguessableToken::Create();
-  PaintPreviewTracker tracker(kDocToken, kEmbeddingToken, true);
-  tracker.SetScrollForFrame(SkISize::Make(10, 20));
-
-  const base::UnguessableToken kEmbeddingTokenChild =
-      base::UnguessableToken::Create();
-  gfx::Rect rect(50, 40, 30, 20);
-  uint32_t content_id =
-      tracker.CreateContentForRemoteFrame(rect, kEmbeddingTokenChild);
-  PictureSerializationContext* context =
-      tracker.GetPictureSerializationContext();
-  EXPECT_TRUE(context->content_id_to_embedding_token.count(content_id));
-  EXPECT_EQ(context->content_id_to_embedding_token[content_id],
-            kEmbeddingTokenChild);
-
-  SkPictureRecorder recorder;
-  SkCanvas* canvas = recorder.beginRecording(100, 100);
-  tracker.CustomDataToSkPictureCallback(canvas, content_id);
-  sk_sp<SkPicture> pic = recorder.finishRecordingAsPicture();
-
-  // TODO(crbug/1009552): find a good way to check that a filler picture was
-  // actually inserted into |pic|. This is difficult without using the
-  // underlying private picture record.
+  canvas.ExpectHasPicture(content_id, rect);
 }
 
 TEST(PaintPreviewTrackerTest, TestGlyphRunList) {
diff --git a/components/paint_preview/common/serial_utils.cc b/components/paint_preview/common/serial_utils.cc
index 800f12b..ff06e2f4 100644
--- a/components/paint_preview/common/serial_utils.cc
+++ b/components/paint_preview/common/serial_utils.cc
@@ -18,10 +18,12 @@
 #pragma pack(push, 1)
 struct SerializedRectData {
   uint32_t content_id;
-  int64_t x;
-  int64_t y;
-  int64_t width;
-  int64_t height;
+
+  // The size of the subframe in the local coordinates when it was drawn.
+  int64_t subframe_width;
+  int64_t subframe_height;
+
+  // The rect of the subframe in its parent frame's root coordinate system.
   int64_t transformed_x;
   int64_t transformed_y;
   int64_t transformed_width;
@@ -39,10 +41,10 @@
   if (it == context->content_id_to_transformed_clip.end())
     return nullptr;
 
+  // This data originates from |PaintPreviewTracker|.
   const SkRect& transformed_cull_rect = it->second;
   SerializedRectData rect_data = {
-      picture->uniqueID(),           picture->cullRect().x(),
-      picture->cullRect().y(),       picture->cullRect().width(),
+      picture->uniqueID(),           picture->cullRect().width(),
       picture->cullRect().height(),  transformed_cull_rect.x(),
       transformed_cull_rect.y(),     transformed_cull_rect.width(),
       transformed_cull_rect.height()};
@@ -71,7 +73,7 @@
   return subset_data;
 }
 
-// Deserializies a clip rect for a subframe within the main SkPicture. These
+// Deserializes a clip rect for a subframe within the main SkPicture. These
 // represent subframes and require special decoding as they are custom data
 // rather than a valid SkPicture.
 // Precondition: the version of the SkPicture should be checked prior to
@@ -106,21 +108,19 @@
   memcpy(&rect_data, data, sizeof(rect_data));
   auto* context = reinterpret_cast<LoadedFramesDeserialContext*>(ctx);
 
-  auto it = context->subframes.find(rect_data.content_id);
-  if (it == context->subframes.end())
+  auto it = context->find(rect_data.content_id);
+  if (it == context->end())
     return MakeEmptyPicture();
 
   // Scroll and clip the subframe manually since the picture in |ctx| does not
   // encode this information.
-  SkRect subframe_bounds = SkRect::MakeWH(rect_data.width, rect_data.height);
-  subframe_bounds.offset(-context->scroll_offsets.width(),
-                         -context->scroll_offsets.height());
+  SkRect subframe_bounds =
+      SkRect::MakeWH(rect_data.subframe_width, rect_data.subframe_height);
   SkPictureRecorder recorder;
   SkCanvas* canvas = recorder.beginRecording(subframe_bounds);
   canvas->clipRect(subframe_bounds);
   SkMatrix apply_scroll_offsets = SkMatrix::Translate(
-      -context->scroll_offsets.width() - it->second.scroll_offsets.width(),
-      -context->scroll_offsets.height() - it->second.scroll_offsets.height());
+      -it->second.scroll_offsets.width(), -it->second.scroll_offsets.height());
   canvas->drawPicture(it->second.picture, &apply_scroll_offsets, nullptr);
   return recorder.finishRecordingAsPicture();
 }
@@ -147,9 +147,6 @@
 FrameAndScrollOffsets& FrameAndScrollOffsets::operator=(
     const FrameAndScrollOffsets&) = default;
 
-LoadedFramesDeserialContext::LoadedFramesDeserialContext() = default;
-LoadedFramesDeserialContext::~LoadedFramesDeserialContext() = default;
-
 sk_sp<SkPicture> MakeEmptyPicture() {
   // Effectively a no-op.
   SkPictureRecorder rec;
diff --git a/components/paint_preview/common/serial_utils.h b/components/paint_preview/common/serial_utils.h
index 619ce16a..e2fddc7 100644
--- a/components/paint_preview/common/serial_utils.h
+++ b/components/paint_preview/common/serial_utils.h
@@ -69,20 +69,11 @@
   gfx::Size scroll_offsets;
 };
 
-// Deserialization context for |MakeDeserialProcs| that contains the information
-// needed to embed a subframe within its parent frame.
-struct LoadedFramesDeserialContext {
-  LoadedFramesDeserialContext();
-  ~LoadedFramesDeserialContext();
-
-  // The scroll offsets for the current frame.
-  gfx::Size scroll_offsets;
-
-  // Maps a content ID to a frame's picture. A frame's subframes should be
-  // loaded into this context before |MakeDeserialProcs| is called to ensure
-  // that the resulting |SkPicture| contains all subframes.
-  base::flat_map<uint32_t, FrameAndScrollOffsets> subframes;
-};
+// Maps a content ID to a frame's picture. A frame's subframes should be
+// loaded into this context before |MakeDeserialProcs| is called to ensure
+// that the resulting |SkPicture| contains all subframes.
+using LoadedFramesDeserialContext =
+    base::flat_map<uint32_t, FrameAndScrollOffsets>;
 
 // Creates a no-op SkPicture.
 sk_sp<SkPicture> MakeEmptyPicture();
diff --git a/components/paint_preview/player/BUILD.gn b/components/paint_preview/player/BUILD.gn
index 052be10..c3237358 100644
--- a/components/paint_preview/player/BUILD.gn
+++ b/components/paint_preview/player/BUILD.gn
@@ -4,6 +4,7 @@
 
 source_set("player") {
   sources = [
+    "compositor_status.h",
     "player_compositor_delegate.cc",
     "player_compositor_delegate.h",
   ]
diff --git a/components/paint_preview/player/android/BUILD.gn b/components/paint_preview/player/android/BUILD.gn
index 10353812..d96e8b2 100644
--- a/components/paint_preview/player/android/BUILD.gn
+++ b/components/paint_preview/player/android/BUILD.gn
@@ -4,6 +4,10 @@
 
 import("//build/config/android/rules.gni")
 
+java_cpp_enum("compositor_status_generated_enum") {
+  sources = [ "//components/paint_preview/player/compositor_status.h" ]
+}
+
 source_set("android") {
   sources = [
     "player_compositor_delegate_android.cc",
@@ -74,6 +78,8 @@
     "java/src/org/chromium/components/paintpreview/player/frame/PlayerFrameViewport.java",
   ]
 
+  srcjar_deps = [ ":compositor_status_generated_enum" ]
+
   deps = [
     "//base:base_java",
     "//base:jni_java",
diff --git a/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/PlayerCompositorDelegateImpl.java b/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/PlayerCompositorDelegateImpl.java
index faa5eb6..d9a5647 100644
--- a/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/PlayerCompositorDelegateImpl.java
+++ b/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/PlayerCompositorDelegateImpl.java
@@ -36,7 +36,7 @@
 
     PlayerCompositorDelegateImpl(NativePaintPreviewServiceProvider service, GURL url,
             String directoryKey, @NonNull CompositorListener compositorListener,
-            Runnable compositorErrorCallback) {
+            Callback<Integer> compositorErrorCallback) {
         mCompositorListener = compositorListener;
         if (service != null && service.getNativeService() != 0) {
             mNativePlayerCompositorDelegate = PlayerCompositorDelegateImplJni.get().initialize(this,
@@ -130,7 +130,7 @@
     @NativeMethods
     interface Natives {
         long initialize(PlayerCompositorDelegateImpl caller, long nativePaintPreviewBaseService,
-                String urlSpec, String directoryKey, Runnable compositorErrorCallback);
+                String urlSpec, String directoryKey, Callback<Integer> compositorErrorCallback);
         void destroy(long nativePlayerCompositorDelegateAndroid);
         void requestBitmap(long nativePlayerCompositorDelegateAndroid, UnguessableToken frameGuid,
                 Callback<Bitmap> bitmapCallback, Runnable errorCallback, float scaleFactor,
diff --git a/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/PlayerManager.java b/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/PlayerManager.java
index 637b718e..d1390df 100644
--- a/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/PlayerManager.java
+++ b/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/PlayerManager.java
@@ -15,6 +15,7 @@
 import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
 
+import org.chromium.base.Callback;
 import org.chromium.base.TraceEvent;
 import org.chromium.base.UnguessableToken;
 import org.chromium.components.paintpreview.browser.NativePaintPreviewServiceProvider;
@@ -63,7 +64,7 @@
             String directoryKey, @NonNull LinkClickHandler linkClickHandler,
             @Nullable Runnable refreshCallback, Runnable viewReadyCallback,
             @Nullable Runnable firstPaintListener, Runnable userInteractionCallback,
-            int backgroundColor, Runnable compositorErrorCallback,
+            int backgroundColor, Callback<Integer> compositorErrorCallback,
             boolean ignoreInitialScrollOffset) {
         TraceEvent.startAsync(sInitEvent, hashCode());
         mContext = context;
diff --git a/components/paint_preview/player/android/javatests/src/org/chromium/components/paintpreview/player/PaintPreviewPlayerTest.java b/components/paint_preview/player/android/javatests/src/org/chromium/components/paintpreview/player/PaintPreviewPlayerTest.java
index ac411ca7..b4d2268 100644
--- a/components/paint_preview/player/android/javatests/src/org/chromium/components/paintpreview/player/PaintPreviewPlayerTest.java
+++ b/components/paint_preview/player/android/javatests/src/org/chromium/components/paintpreview/player/PaintPreviewPlayerTest.java
@@ -206,7 +206,7 @@
                         Assert.fail("View Ready callback occurred, but expected a failure.");
                     },
                     null, null, 0xffffffff,
-                    () -> { compositorErrorCallback.notifyCalled(); }, false);
+                    (status) -> { compositorErrorCallback.notifyCalled(); }, false);
             mPlayerManager.setCompressOnClose(false);
         });
         compositorErrorCallback.waitForFirst();
@@ -358,7 +358,7 @@
             mPlayerManager = new PlayerManager(new GURL(TEST_URL), getActivity(), service,
                     TEST_DIRECTORY_KEY, mLinkClickHandler, mRefreshedCallback::notifyCalled,
                     viewReady::notifyCalled, null, null, 0xffffffff,
-                    () -> { mInitializationFailed = true; }, false);
+                    (status) -> { mInitializationFailed = true; }, false);
             mPlayerManager.setCompressOnClose(false);
             getActivity().setContentView(mPlayerManager.getView());
         });
diff --git a/components/paint_preview/player/android/player_compositor_delegate_android.cc b/components/paint_preview/player/android/player_compositor_delegate_android.cc
index 6ee6ced..1b2dced 100644
--- a/components/paint_preview/player/android/player_compositor_delegate_android.cc
+++ b/components/paint_preview/player/android/player_compositor_delegate_android.cc
@@ -84,7 +84,7 @@
           DirectoryKey{
               base::android::ConvertJavaStringToUTF8(env, j_directory_key)},
           base::BindOnce(
-              &base::android::RunRunnableAndroid,
+              &base::android::RunIntCallbackAndroid,
               ScopedJavaGlobalRef<jobject>(j_compositor_error_callback))),
       request_id_(0),
       startup_timestamp_(base::TimeTicks::Now()) {
@@ -92,23 +92,23 @@
 }
 
 void PlayerCompositorDelegateAndroid::OnCompositorReady(
-    mojom::PaintPreviewCompositor::BeginCompositeStatus status,
+    CompositorStatus compositor_status,
     mojom::PaintPreviewBeginCompositeResponsePtr composite_response) {
-  bool compositor_started =
-      status == mojom::PaintPreviewCompositor::BeginCompositeStatus::kSuccess ||
-      status ==
-          mojom::PaintPreviewCompositor::BeginCompositeStatus::kPartialSuccess;
+  bool compositor_started = CompositorStatus::OK == compositor_status;
   base::UmaHistogramBoolean(
       "Browser.PaintPreview.Player.CompositorProcessStartedCorrectly",
       compositor_started);
   if (!compositor_started && compositor_error_) {
-    LOG(ERROR) << "Compositor process failed to begin with code: " << status;
-    std::move(compositor_error_).Run();
+    LOG(ERROR) << "Compositor process failed to begin with code: "
+               << static_cast<int>(compositor_status);
+    std::move(compositor_error_).Run(static_cast<int>(compositor_status));
     return;
   }
-  base::UmaHistogramTimes(
-      "Browser.PaintPreview.Player.CompositorProcessStartupTime",
-      base::TimeTicks::Now() - startup_timestamp_);
+  auto delta = base::TimeTicks::Now() - startup_timestamp_;
+  if (delta.InMicroseconds() >= 0) {
+    base::UmaHistogramTimes(
+        "Browser.PaintPreview.Player.CompositorProcessStartupTime", delta);
+  }
   JNIEnv* env = base::android::AttachCurrentThread();
 
   std::vector<base::UnguessableToken> all_guids;
@@ -243,13 +243,16 @@
                                                       j_bitmap);
             },
             j_bitmap_callback, j_error_callback)));
+    if (request_id == 0) {
+      auto delta = base::TimeTicks::Now() - startup_timestamp_;
+      if (delta.InMicroseconds() >= 0) {
+        base::UmaHistogramTimes("Browser.PaintPreview.Player.TimeToFirstBitmap",
+                                delta);
+      }
+    }
   } else {
     base::android::RunRunnableAndroid(j_error_callback);
   }
-  if (request_id == 0) {
-    base::UmaHistogramTimes("Browser.PaintPreview.Player.TimeToFirstBitmap",
-                            base::TimeTicks::Now() - startup_timestamp_);
-  }
 }
 
 ScopedJavaLocalRef<jstring> PlayerCompositorDelegateAndroid::OnClick(
diff --git a/components/paint_preview/player/android/player_compositor_delegate_android.h b/components/paint_preview/player/android/player_compositor_delegate_android.h
index ad20969..7952151 100644
--- a/components/paint_preview/player/android/player_compositor_delegate_android.h
+++ b/components/paint_preview/player/android/player_compositor_delegate_android.h
@@ -26,7 +26,7 @@
       const base::android::JavaParamRef<jobject>& j_compositor_error_callback);
 
   void OnCompositorReady(
-      mojom::PaintPreviewCompositor::BeginCompositeStatus status,
+      CompositorStatus compositor_status,
       mojom::PaintPreviewBeginCompositeResponsePtr composite_response) override;
 
   // Called from Java when there is a request for a new bitmap. When the bitmap
diff --git a/components/paint_preview/player/compositor_status.h b/components/paint_preview/player/compositor_status.h
new file mode 100644
index 0000000..0ed7597
--- /dev/null
+++ b/components/paint_preview/player/compositor_status.h
@@ -0,0 +1,26 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_PAINT_PREVIEW_PLAYER_COMPOSITOR_STATUS_H_
+#define COMPONENTS_PAINT_PREVIEW_PLAYER_COMPOSITOR_STATUS_H_
+
+namespace paint_preview {
+
+// GENERATED_JAVA_ENUM_PACKAGE: (
+//   org.chromium.components.paintpreview.player)
+enum class CompositorStatus : int {
+  OK,
+  URL_MISMATCH,
+  COMPOSITOR_SERVICE_DISCONNECT,
+  COMPOSITOR_CLIENT_DISCONNECT,
+  PROTOBUF_DESERIALIZATION_ERROR,
+  COMPOSITOR_DESERIALIZATION_ERROR,
+  INVALID_ROOT_FRAME_SKP,
+  INVALID_REQUEST,
+  COUNT,
+};
+
+}  // namespace paint_preview
+
+#endif  // COMPONENTS_PAINT_PREVIEW_PLAYER_COMPOSITOR_STATUS_H_
diff --git a/components/paint_preview/player/player_compositor_delegate.cc b/components/paint_preview/player/player_compositor_delegate.cc
index f37540d..097348d 100644
--- a/components/paint_preview/player/player_compositor_delegate.cc
+++ b/components/paint_preview/player/player_compositor_delegate.cc
@@ -12,6 +12,7 @@
 #include "base/files/file_path.h"
 #include "base/memory/read_only_shared_memory_region.h"
 #include "base/memory/weak_ptr.h"
+#include "base/notreached.h"
 #include "base/optional.h"
 #include "base/strings/string_piece.h"
 #include "base/strings/utf_string_conversions.h"
@@ -98,7 +99,7 @@
     PaintPreviewBaseService* paint_preview_service,
     const GURL& expected_url,
     const DirectoryKey& key,
-    base::OnceClosure compositor_error,
+    base::OnceCallback<void(int)> compositor_error,
     bool skip_service_launch)
     : compositor_error_(std::move(compositor_error)),
       paint_preview_service_(paint_preview_service),
@@ -139,10 +140,35 @@
   }
 }
 
+void PlayerCompositorDelegate::OnCompositorReadyStatusAdapter(
+    mojom::PaintPreviewCompositor::BeginCompositeStatus status,
+    mojom::PaintPreviewBeginCompositeResponsePtr composite_response) {
+  CompositorStatus new_status;
+  switch (status) {
+    // falltrhough
+    case mojom::PaintPreviewCompositor::BeginCompositeStatus::kSuccess:
+    case mojom::PaintPreviewCompositor::BeginCompositeStatus::kPartialSuccess:
+      new_status = CompositorStatus::OK;
+      break;
+    case mojom::PaintPreviewCompositor::BeginCompositeStatus::
+        kDeserializingFailure:
+      new_status = CompositorStatus::COMPOSITOR_DESERIALIZATION_ERROR;
+      break;
+    case mojom::PaintPreviewCompositor::BeginCompositeStatus::
+        kCompositingFailure:
+      new_status = CompositorStatus::INVALID_ROOT_FRAME_SKP;
+      break;
+    default:
+      NOTREACHED();
+  }
+  OnCompositorReady(new_status, std::move(composite_response));
+}
+
 void PlayerCompositorDelegate::OnCompositorServiceDisconnected() {
   LOG(ERROR) << "Compositor service disconnected.";
   if (compositor_error_)
-    std::move(compositor_error_).Run();
+    std::move(compositor_error_)
+        .Run(static_cast<int>(CompositorStatus::COMPOSITOR_SERVICE_DISCONNECT));
 }
 
 void PlayerCompositorDelegate::OnCompositorClientCreated(
@@ -161,26 +187,21 @@
     std::unique_ptr<PaintPreviewProto> proto) {
   if (!proto || !proto->IsInitialized()) {
     // TODO(crbug.com/1021590): Handle initialization errors.
-    OnCompositorReady(mojom::PaintPreviewCompositor::BeginCompositeStatus::
-                          kCompositingFailure,
+    OnCompositorReady(CompositorStatus::PROTOBUF_DESERIALIZATION_ERROR,
                       nullptr);
     return;
   }
 
   auto proto_url = GURL(proto->metadata().url());
   if (expected_url != proto_url) {
-    OnCompositorReady(mojom::PaintPreviewCompositor::BeginCompositeStatus::
-                          kDeserializingFailure,
-                      nullptr);
+    OnCompositorReady(CompositorStatus::URL_MISMATCH, nullptr);
     return;
   }
 
   hit_testers_ = BuildHitTesters(*proto);
 
   if (!paint_preview_compositor_client_) {
-    OnCompositorReady(mojom::PaintPreviewCompositor::BeginCompositeStatus::
-                          kCompositingFailure,
-                      nullptr);
+    OnCompositorReady(CompositorStatus::COMPOSITOR_CLIENT_DISCONNECT, nullptr);
     return;
   }
 
@@ -197,22 +218,21 @@
     mojom::PaintPreviewBeginCompositeRequestPtr begin_composite_request) {
   // TODO(crbug.com/1021590): Handle initialization errors.
   if (!begin_composite_request) {
-    OnCompositorReady(mojom::PaintPreviewCompositor::BeginCompositeStatus::
-                          kCompositingFailure,
-                      nullptr);
+    OnCompositorReady(CompositorStatus::INVALID_REQUEST, nullptr);
     return;
   }
 
   paint_preview_compositor_client_->BeginSeparatedFrameComposite(
       std::move(begin_composite_request),
-      base::BindOnce(&PlayerCompositorDelegate::OnCompositorReady,
+      base::BindOnce(&PlayerCompositorDelegate::OnCompositorReadyStatusAdapter,
                      weak_factory_.GetWeakPtr()));
 }
 
 void PlayerCompositorDelegate::OnCompositorClientDisconnected() {
   LOG(ERROR) << "Compositor client disconnected.";
   if (compositor_error_)
-    std::move(compositor_error_).Run();
+    std::move(compositor_error_)
+        .Run(static_cast<int>(CompositorStatus::COMPOSITOR_CLIENT_DISCONNECT));
 }
 
 void PlayerCompositorDelegate::RequestBitmap(
diff --git a/components/paint_preview/player/player_compositor_delegate.h b/components/paint_preview/player/player_compositor_delegate.h
index 2c4936f..636bc5ba 100644
--- a/components/paint_preview/player/player_compositor_delegate.h
+++ b/components/paint_preview/player/player_compositor_delegate.h
@@ -12,6 +12,7 @@
 #include "base/unguessable_token.h"
 #include "components/paint_preview/browser/hit_tester.h"
 #include "components/paint_preview/browser/paint_preview_base_service.h"
+#include "components/paint_preview/player/compositor_status.h"
 #include "components/paint_preview/public/paint_preview_compositor_client.h"
 #include "components/paint_preview/public/paint_preview_compositor_service.h"
 #include "components/services/paint_preview_compositor/public/mojom/paint_preview_compositor.mojom.h"
@@ -32,7 +33,7 @@
   PlayerCompositorDelegate(PaintPreviewBaseService* paint_preview_service,
                            const GURL& url,
                            const DirectoryKey& key,
-                           base::OnceClosure compositor_error,
+                           base::OnceCallback<void(int)> compositor_error,
                            bool skip_service_launch = false);
   virtual ~PlayerCompositorDelegate();
 
@@ -42,7 +43,7 @@
   void SetCompressOnClose(bool compress) { compress_on_close_ = compress; }
 
   virtual void OnCompositorReady(
-      mojom::PaintPreviewCompositor::BeginCompositeStatus status,
+      CompositorStatus compositor_status,
       mojom::PaintPreviewBeginCompositeResponsePtr composite_response) {}
 
   // Called when there is a request for a new bitmap. When the bitmap
@@ -59,9 +60,13 @@
                                    const gfx::Rect& rect);
 
  protected:
-  base::OnceClosure compositor_error_;
+  base::OnceCallback<void(int)> compositor_error_;
 
  private:
+  void OnCompositorReadyStatusAdapter(
+      mojom::PaintPreviewCompositor::BeginCompositeStatus status,
+      mojom::PaintPreviewBeginCompositeResponsePtr composite_response);
+
   void OnCompositorServiceDisconnected();
 
   void OnCompositorClientCreated(const GURL& expected_url,
diff --git a/components/paint_preview/renderer/paint_preview_recorder_impl.cc b/components/paint_preview/renderer/paint_preview_recorder_impl.cc
index a828cb19..b7898ceb 100644
--- a/components/paint_preview/renderer/paint_preview_recorder_impl.cc
+++ b/components/paint_preview/renderer/paint_preview_recorder_impl.cc
@@ -187,7 +187,6 @@
   auto tracker = std::make_unique<PaintPreviewTracker>(
       params->guid, frame->GetEmbeddingToken(), is_main_frame_);
   auto size = frame->GetScrollOffset();
-  tracker->SetScrollForFrame(SkISize::Make(size.width, size.height));
   response->scroll_offsets = gfx::Size(size.width, size.height);
 
   cc::PaintRecorder recorder;
diff --git a/components/password_manager/core/browser/ui/compromised_credentials_manager.cc b/components/password_manager/core/browser/ui/compromised_credentials_manager.cc
index 115556c..e79ed75f 100644
--- a/components/password_manager/core/browser/ui/compromised_credentials_manager.cc
+++ b/components/password_manager/core/browser/ui/compromised_credentials_manager.cc
@@ -34,22 +34,24 @@
 // Transparent comparator that can compare CompromisedCredentials and
 // autofill::PasswordForm.
 struct CredentialWithoutPasswordLess {
-  static std::tuple<const std::string&, const base::string16&>
-  CredentialOriginAndUsername(const autofill::PasswordForm& form) {
-    return std::tie(form.signon_realm, form.username_value);
-  }
-
-  static std::tuple<const std::string&, const base::string16&>
-  CredentialOriginAndUsername(const CompromisedCredentials& c) {
-    return std::tie(c.signon_realm, c.username);
-  }
-
   template <typename T, typename U>
   bool operator()(const T& lhs, const U& rhs) const {
-    return CredentialOriginAndUsername(lhs) < CredentialOriginAndUsername(rhs);
+    return CredentialOriginAndUsernameAndStore(lhs) <
+           CredentialOriginAndUsernameAndStore(rhs);
   }
 
   using is_transparent = void;
+
+ private:
+  static auto CredentialOriginAndUsernameAndStore(
+      const autofill::PasswordForm& form) {
+    return std::tie(form.signon_realm, form.username_value, form.in_store);
+  }
+
+  static auto CredentialOriginAndUsernameAndStore(
+      const CompromisedCredentials& c) {
+    return std::tie(c.signon_realm, c.username, c.in_store);
+  }
 };
 
 CompromiseTypeFlags ConvertCompromiseType(CompromiseType type) {
@@ -184,11 +186,14 @@
     CredentialWithPassword&& other) = default;
 
 CompromisedCredentialsManager::CompromisedCredentialsManager(
-    scoped_refptr<PasswordStore> store,
-    SavedPasswordsPresenter* presenter)
-    : store_(std::move(store)),
-      presenter_(presenter),
-      compromised_credentials_reader_(store_.get()) {
+    SavedPasswordsPresenter* presenter,
+    scoped_refptr<PasswordStore> profile_store,
+    scoped_refptr<PasswordStore> account_store)
+    : presenter_(presenter),
+      profile_store_(std::move(profile_store)),
+      account_store_(std::move(account_store)),
+      compromised_credentials_reader_(profile_store_.get(),
+                                      account_store_.get()) {
   observed_compromised_credentials_reader_.Add(
       &compromised_credentials_reader_);
   observed_saved_password_presenter_.Add(presenter_);
@@ -211,7 +216,10 @@
     if (saved_password.password_value == credential.password() &&
         CanonicalizeUsername(saved_password.username_value) ==
             canonicalized_username) {
-      store_->AddCompromisedCredentials({
+      PasswordStore& store = saved_password.IsUsingAccountStore()
+                                 ? *account_store_
+                                 : *profile_store_;
+      store.AddCompromisedCredentials({
           .signon_realm = saved_password.signon_realm,
           .username = saved_password.username_value,
           .create_time = base::Time::Now(),
@@ -235,7 +243,7 @@
     return false;
 
   for (size_t i = 1; i < forms.size(); ++i)
-    store_->RemoveLogin(forms[i]);
+    profile_store_->RemoveLogin(forms[i]);
 
   // Note: We Invoke EditPassword on the presenter rather than UpdateLogin() on
   // the store, so that observers of the presenter get notified of this event.
@@ -252,7 +260,7 @@
   // credentials were deleted.
   const auto& saved_passwords = it->second.forms;
   for (const autofill::PasswordForm& saved_password : saved_passwords)
-    store_->RemoveLogin(saved_password);
+    profile_store_->RemoveLogin(saved_password);
 
   return !saved_passwords.empty();
 }
diff --git a/components/password_manager/core/browser/ui/compromised_credentials_manager.h b/components/password_manager/core/browser/ui/compromised_credentials_manager.h
index 8393a9c..ac58a19 100644
--- a/components/password_manager/core/browser/ui/compromised_credentials_manager.h
+++ b/components/password_manager/core/browser/ui/compromised_credentials_manager.h
@@ -112,8 +112,10 @@
         CredentialsView credentials) = 0;
   };
 
-  explicit CompromisedCredentialsManager(scoped_refptr<PasswordStore> store,
-                                         SavedPasswordsPresenter* presenter);
+  CompromisedCredentialsManager(
+      SavedPasswordsPresenter* presenter,
+      scoped_refptr<PasswordStore> profile_store,
+      scoped_refptr<PasswordStore> account_store = nullptr);
   ~CompromisedCredentialsManager() override;
 
   void Init();
@@ -160,15 +162,16 @@
   void UpdateCachedDataAndNotifyObservers(
       SavedPasswordsPresenter::SavedPasswordsView saved_passwords);
 
-  // The password store containing the compromised credentials.
-  scoped_refptr<PasswordStore> store_;
-
   // A weak handle to the presenter used to join the list of compromised
   // credentials with saved passwords. Needs to outlive this instance.
   SavedPasswordsPresenter* presenter_ = nullptr;
 
+  // The password stores containing the compromised credentials.
+  scoped_refptr<PasswordStore> profile_store_;
+  scoped_refptr<PasswordStore> account_store_;
+
   // The reader used to read the compromised credentials from the password
-  // store.
+  // stores.
   CompromisedCredentialsReader compromised_credentials_reader_;
 
   // Cache of the most recently obtained compromised credentials.
diff --git a/components/password_manager/core/browser/ui/compromised_credentials_manager_unittest.cc b/components/password_manager/core/browser/ui/compromised_credentials_manager_unittest.cc
index 114cb18..4d22e109 100644
--- a/components/password_manager/core/browser/ui/compromised_credentials_manager_unittest.cc
+++ b/components/password_manager/core/browser/ui/compromised_credentials_manager_unittest.cc
@@ -27,6 +27,7 @@
 
 constexpr char kPassword1[] = "f00b4r";
 constexpr char kPassword2[] = "s3cr3t";
+constexpr char kPassword3[] = "484her";
 
 using autofill::PasswordForm;
 using ::testing::ElementsAre;
@@ -71,7 +72,7 @@
                              base::ASCIIToUTF16(password));
 }
 
-CredentialWithPassword MakeComprmisedCredential(
+CredentialWithPassword MakeCompromisedCredential(
     PasswordForm form,
     CompromisedCredentials credential) {
   CredentialWithPassword credential_with_password((CredentialView(form)));
@@ -103,7 +104,7 @@
   scoped_refptr<TestPasswordStore> store_ =
       base::MakeRefCounted<TestPasswordStore>();
   SavedPasswordsPresenter presenter_{store_};
-  CompromisedCredentialsManager provider_{store_, &presenter_};
+  CompromisedCredentialsManager provider_{&presenter_, store_};
 };
 
 }  // namespace
@@ -249,7 +250,7 @@
   RunUntilIdle();
 
   CredentialWithPassword expected =
-      MakeComprmisedCredential(password, credential);
+      MakeCompromisedCredential(password, credential);
   expected.password = password.password_value;
   EXPECT_THAT(provider().GetCompromisedCredentials(), ElementsAre(expected));
 }
@@ -269,7 +270,7 @@
   store().AddCompromisedCredentials(phished);
   RunUntilIdle();
 
-  CredentialWithPassword expected = MakeComprmisedCredential(password, leaked);
+  CredentialWithPassword expected = MakeCompromisedCredential(password, leaked);
   expected.password = password.password_value;
   expected.compromise_type = (CompromiseTypeFlags::kCredentialLeaked |
                               CompromiseTypeFlags::kCredentialPhished);
@@ -288,9 +289,9 @@
       MakeCompromised(kExampleCom, kUsername1),
       MakeCompromised(kExampleCom, kUsername2)};
 
-  std::vector<CredentialWithPassword> expected;
-  expected.push_back(MakeComprmisedCredential(passwords[0], credentials[0]));
-  expected.push_back(MakeComprmisedCredential(passwords[1], credentials[1]));
+  std::vector<CredentialWithPassword> expected = {
+      MakeCompromisedCredential(passwords[0], credentials[0]),
+      MakeCompromisedCredential(passwords[1], credentials[1])};
 
   store().AddLogin(passwords[0]);
   RunUntilIdle();
@@ -336,9 +337,9 @@
   RunUntilIdle();
 
   CredentialWithPassword expected1 =
-      MakeComprmisedCredential(passwords[0], credentials[0]);
+      MakeCompromisedCredential(passwords[0], credentials[0]);
   CredentialWithPassword expected2 =
-      MakeComprmisedCredential(passwords[1], credentials[1]);
+      MakeCompromisedCredential(passwords[1], credentials[1]);
 
   EXPECT_THAT(provider().GetCompromisedCredentials(),
               ElementsAre(expected1, expected2));
@@ -394,9 +395,9 @@
   RunUntilIdle();
 
   CredentialWithPassword expected1 =
-      MakeComprmisedCredential(passwords[0], credential);
+      MakeCompromisedCredential(passwords[0], credential);
   CredentialWithPassword expected2 =
-      MakeComprmisedCredential(passwords[1], credential);
+      MakeCompromisedCredential(passwords[1], credential);
 
   EXPECT_THAT(provider().GetCompromisedCredentials(),
               ElementsAre(expected1, expected2));
@@ -417,7 +418,7 @@
   RunUntilIdle();
 
   CredentialWithPassword expected =
-      MakeComprmisedCredential(passwords[0], credential);
+      MakeCompromisedCredential(passwords[0], credential);
 
   EXPECT_THAT(provider().GetCompromisedCredentials(), ElementsAre(expected));
 }
@@ -434,13 +435,10 @@
       MakeCompromised(kExampleCom, kUsername1),
       MakeCompromised(kExampleOrg, kUsername2)};
 
-  std::vector<CredentialWithPassword> credentuals_with_password;
-  credentuals_with_password.push_back(
-      MakeComprmisedCredential(passwords[0], credentials[0]));
-  credentuals_with_password.push_back(
-      MakeComprmisedCredential(passwords[1], credentials[0]));
-  credentuals_with_password.push_back(
-      MakeComprmisedCredential(passwords[2], credentials[1]));
+  std::vector<CredentialWithPassword> credentials_with_password = {
+      MakeCompromisedCredential(passwords[0], credentials[0]),
+      MakeCompromisedCredential(passwords[1], credentials[0]),
+      MakeCompromisedCredential(passwords[2], credentials[1])};
 
   store().AddLogin(passwords[0]);
   store().AddLogin(passwords[1]);
@@ -449,13 +447,13 @@
   store().AddCompromisedCredentials(credentials[1]);
 
   RunUntilIdle();
-  EXPECT_THAT(provider().GetSavedPasswordsFor(credentuals_with_password[0]),
+  EXPECT_THAT(provider().GetSavedPasswordsFor(credentials_with_password[0]),
               ElementsAreArray(store().stored_passwords().at(kExampleCom)));
 
-  EXPECT_THAT(provider().GetSavedPasswordsFor(credentuals_with_password[1]),
+  EXPECT_THAT(provider().GetSavedPasswordsFor(credentials_with_password[1]),
               ElementsAreArray(store().stored_passwords().at(kExampleCom)));
 
-  EXPECT_THAT(provider().GetSavedPasswordsFor(credentuals_with_password[2]),
+  EXPECT_THAT(provider().GetSavedPasswordsFor(credentials_with_password[2]),
               ElementsAreArray(store().stored_passwords().at(kExampleOrg)));
 }
 
@@ -472,7 +470,7 @@
   RunUntilIdle();
 
   CredentialWithPassword expected =
-      MakeComprmisedCredential(password_form, compromised_credential);
+      MakeCompromisedCredential(password_form, compromised_credential);
   expected.create_time = base::Time::Now();
 
   provider().SaveCompromisedCredential(credential);
@@ -493,7 +491,7 @@
 
   RunUntilIdle();
   CredentialWithPassword expected =
-      MakeComprmisedCredential(password_form, credential);
+      MakeCompromisedCredential(password_form, credential);
 
   provider().UpdateCompromisedCredentials(expected, kPassword2);
   RunUntilIdle();
@@ -512,7 +510,7 @@
   RunUntilIdle();
 
   CredentialWithPassword expected =
-      MakeComprmisedCredential(password, credential);
+      MakeCompromisedCredential(password, credential);
   expected.password = password.password_value;
 
   EXPECT_THAT(provider().GetCompromisedCredentials(), ElementsAre(expected));
@@ -522,4 +520,126 @@
   EXPECT_THAT(provider().GetCompromisedCredentials(), IsEmpty());
 }
 
+namespace {
+class CompromisedCredentialsManagerWithTwoStoresTest : public ::testing::Test {
+ protected:
+  CompromisedCredentialsManagerWithTwoStoresTest() {
+    profile_store_->Init(/*prefs=*/nullptr);
+    account_store_->Init(/*prefs=*/nullptr);
+  }
+
+  ~CompromisedCredentialsManagerWithTwoStoresTest() override {
+    account_store_->ShutdownOnUIThread();
+    profile_store_->ShutdownOnUIThread();
+    task_env_.RunUntilIdle();
+  }
+
+  TestPasswordStore& profile_store() { return *profile_store_; }
+  TestPasswordStore& account_store() { return *account_store_; }
+  CompromisedCredentialsManager& provider() { return provider_; }
+
+  void RunUntilIdle() { task_env_.RunUntilIdle(); }
+
+ private:
+  base::test::SingleThreadTaskEnvironment task_env_;
+  scoped_refptr<TestPasswordStore> profile_store_ =
+      base::MakeRefCounted<TestPasswordStore>(/*is_account_store=*/false);
+  scoped_refptr<TestPasswordStore> account_store_ =
+      base::MakeRefCounted<TestPasswordStore>(/*is_account_store=*/true);
+  SavedPasswordsPresenter presenter_{profile_store_, account_store_};
+  CompromisedCredentialsManager provider_{&presenter_, profile_store_,
+                                          account_store_};
+};
+}  // namespace
+
+// Tests that verifies mapping compromised credentials to passwords works
+// correctly.
+TEST_F(CompromisedCredentialsManagerWithTwoStoresTest,
+       MapCompromisedPasswordsToPasswords) {
+  // Add credentials for both `kExampleCom` and `kExampleOrg` in both stores
+  // with the same username and difference passwords. For `kUsername1`, the
+  // `kPassword1` are `kPassword2` are compromised while
+  // `kPassword3` is safe.
+  std::vector<PasswordForm> profile_store_passwords = {
+      MakeSavedPassword(kExampleCom, kUsername1, kPassword1),
+      MakeSavedPassword(kExampleOrg, kUsername1, kPassword3)};
+  for (const PasswordForm& profile_password : profile_store_passwords)
+    profile_store().AddLogin(profile_password);
+
+  std::vector<PasswordForm> account_store_passwords = {
+      MakeSavedPassword(kExampleCom, kUsername1, kPassword3),
+      MakeSavedPassword(kExampleOrg, kUsername1, kPassword2)};
+  for (const PasswordForm& account_password : account_store_passwords)
+    account_store().AddLogin(account_password);
+
+  // Mark `kPassword1` to be compromised in the profile store, and `kPassword2`
+  // to be compromised in the account store.
+  profile_store().AddCompromisedCredentials(
+      MakeCompromised(kExampleCom, kUsername1));
+  account_store().AddCompromisedCredentials(
+      MakeCompromised(kExampleOrg, kUsername1));
+
+  RunUntilIdle();
+
+  // Each password should be joined only with compromised credential from
+  // their store.
+  EXPECT_THAT(
+      provider().GetSavedPasswordsFor(
+          CredentialView(kExampleCom, GURL(), base::ASCIIToUTF16(kUsername1),
+                         base::ASCIIToUTF16(kPassword1))),
+      ElementsAreArray(profile_store().stored_passwords().at(kExampleCom)));
+
+  EXPECT_THAT(provider().GetSavedPasswordsFor(CredentialView(
+                  kExampleOrg, GURL(), base::ASCIIToUTF16(kUsername1),
+                  base::ASCIIToUTF16(kPassword3))),
+              IsEmpty());
+
+  EXPECT_THAT(provider().GetSavedPasswordsFor(CredentialView(
+                  kExampleCom, GURL(), base::ASCIIToUTF16(kUsername1),
+                  base::ASCIIToUTF16(kPassword3))),
+              IsEmpty());
+
+  EXPECT_THAT(
+      provider().GetSavedPasswordsFor(
+          CredentialView(kExampleOrg, GURL(), base::ASCIIToUTF16(kUsername1),
+                         base::ASCIIToUTF16(kPassword2))),
+      ElementsAreArray(account_store().stored_passwords().at(kExampleOrg)));
+}
+
+// Test verifies that saving LeakCheckCredential via provider adds expected
+// compromised credential to the correct store.
+TEST_F(CompromisedCredentialsManagerWithTwoStoresTest,
+       SaveCompromisedPassword) {
+  ASSERT_TRUE(profile_store().compromised_credentials().empty());
+  ASSERT_TRUE(account_store().compromised_credentials().empty());
+  // Add `kUsername1`,`kPassword1` to both stores.
+  // And add `kUsername1`,`kPassword2` to the account store only.
+  profile_store().AddLogin(
+      MakeSavedPassword(kExampleCom, kUsername1, kPassword1));
+
+  account_store().AddLogin(
+      MakeSavedPassword(kExampleOrg, kUsername1, kPassword1));
+  account_store().AddLogin(
+      MakeSavedPassword(kExampleCom, kUsername1, kPassword2));
+
+  RunUntilIdle();
+
+  // Mark `kUsername1`, `kPassword1` as compromised, a new entry should be
+  // added to both stores.
+  provider().SaveCompromisedCredential(
+      MakeLeakCredential(kUsername1, kPassword1));
+  RunUntilIdle();
+
+  EXPECT_EQ(1U, profile_store().compromised_credentials().size());
+  EXPECT_EQ(1U, account_store().compromised_credentials().size());
+
+  // Now, mark `kUsername1`, `kPassword2` as compromised, a new entry should be
+  // added only to the account store.
+  provider().SaveCompromisedCredential(
+      MakeLeakCredential(kUsername1, kPassword2));
+  RunUntilIdle();
+
+  EXPECT_EQ(1U, profile_store().compromised_credentials().size());
+  EXPECT_EQ(2U, account_store().compromised_credentials().size());
+}
 }  // namespace password_manager
diff --git a/components/password_manager/core/browser/well_known_change_password_util.h b/components/password_manager/core/browser/well_known_change_password_util.h
index 14196147..fd296a8e 100644
--- a/components/password_manager/core/browser/well_known_change_password_util.h
+++ b/components/password_manager/core/browser/well_known_change_password_util.h
@@ -11,6 +11,15 @@
 
 namespace password_manager {
 
+// Used to report UKMs about the support for .well-known/change-password.
+// These values are persisted to logs. Entries should not be renumbered and
+// numeric values should never be reused.
+enum class WellKnownChangePasswordResult {
+  kFallbackToOriginUrl = 0,
+  kFallbackToOverrideUrl = 1,
+  kUsedWellKnownChangePassword = 2,
+};
+
 // Path for Well-Known change password url
 // Spec: https://wicg.github.io/change-password-url/
 extern const char kWellKnownChangePasswordPath[];
diff --git a/components/renderer_context_menu/render_view_context_menu_base.cc b/components/renderer_context_menu/render_view_context_menu_base.cc
index b5fac6e..8c079b6 100644
--- a/components/renderer_context_menu/render_view_context_menu_base.cc
+++ b/components/renderer_context_menu/render_view_context_menu_base.cc
@@ -209,17 +209,8 @@
 void RenderViewContextMenuBase::AddMenuItemWithIcon(
     int command_id,
     const base::string16& title,
-    const gfx::ImageSkia& image) {
-  menu_model_.AddItemWithIcon(command_id, title,
-                              ui::ImageModel::FromImageSkia(image));
-}
-
-void RenderViewContextMenuBase::AddMenuItemWithIcon(
-    int command_id,
-    const base::string16& title,
-    const gfx::VectorIcon& icon) {
-  menu_model_.AddItemWithIcon(command_id, title,
-                              ui::ImageModel::FromVectorIcon(icon));
+    const ui::ImageModel& icon) {
+  menu_model_.AddItemWithIcon(command_id, title, icon);
 }
 
 void RenderViewContextMenuBase::AddCheckItem(int command_id,
@@ -241,18 +232,9 @@
     int command_id,
     int message_id,
     ui::MenuModel* model,
-    const gfx::ImageSkia& image) {
-  menu_model_.AddSubMenuWithStringIdAndIcon(
-      command_id, message_id, model, ui::ImageModel::FromImageSkia(image));
-}
-
-void RenderViewContextMenuBase::AddSubMenuWithStringIdAndIcon(
-    int command_id,
-    int message_id,
-    ui::MenuModel* model,
-    const gfx::VectorIcon& icon) {
-  menu_model_.AddSubMenuWithStringIdAndIcon(
-      command_id, message_id, model, ui::ImageModel::FromVectorIcon(icon));
+    const ui::ImageModel& icon) {
+  menu_model_.AddSubMenuWithStringIdAndIcon(command_id, message_id, model,
+                                            icon);
 }
 
 void RenderViewContextMenuBase::UpdateMenuItem(int command_id,
@@ -271,12 +253,12 @@
 }
 
 void RenderViewContextMenuBase::UpdateMenuIcon(int command_id,
-                                               const gfx::Image& image) {
+                                               const ui::ImageModel& icon) {
   int index = menu_model_.GetIndexOfCommandId(command_id);
   if (index == -1)
     return;
 
-  menu_model_.SetIcon(index, ui::ImageModel::FromImage(image));
+  menu_model_.SetIcon(index, icon);
 #if defined(OS_CHROMEOS)
   if (toolkit_delegate_)
     toolkit_delegate_->RebuildMenu();
diff --git a/components/renderer_context_menu/render_view_context_menu_base.h b/components/renderer_context_menu/render_view_context_menu_base.h
index 2fc1837..4fe4c282 100644
--- a/components/renderer_context_menu/render_view_context_menu_base.h
+++ b/components/renderer_context_menu/render_view_context_menu_base.h
@@ -97,10 +97,7 @@
   void AddMenuItem(int command_id, const base::string16& title) override;
   void AddMenuItemWithIcon(int command_id,
                            const base::string16& title,
-                           const gfx::ImageSkia& image) override;
-  void AddMenuItemWithIcon(int command_id,
-                           const base::string16& title,
-                           const gfx::VectorIcon& icon) override;
+                           const ui::ImageModel& icon) override;
   void AddCheckItem(int command_id, const base::string16& title) override;
   void AddSeparator() override;
   void AddSubMenu(int command_id,
@@ -109,16 +106,12 @@
   void AddSubMenuWithStringIdAndIcon(int command_id,
                                      int message_id,
                                      ui::MenuModel* model,
-                                     const gfx::ImageSkia& image) override;
-  void AddSubMenuWithStringIdAndIcon(int command_id,
-                                     int message_id,
-                                     ui::MenuModel* model,
-                                     const gfx::VectorIcon& icon) override;
+                                     const ui::ImageModel& icon) override;
   void UpdateMenuItem(int command_id,
                       bool enabled,
                       bool hidden,
                       const base::string16& title) override;
-  void UpdateMenuIcon(int command_id, const gfx::Image& image) override;
+  void UpdateMenuIcon(int command_id, const ui::ImageModel& icon) override;
   void RemoveMenuItem(int command_id) override;
   void RemoveAdjacentSeparators() override;
   content::RenderViewHost* GetRenderViewHost() const override;
diff --git a/components/renderer_context_menu/render_view_context_menu_proxy.h b/components/renderer_context_menu/render_view_context_menu_proxy.h
index 33b34387..1f8a9ea 100644
--- a/components/renderer_context_menu/render_view_context_menu_proxy.h
+++ b/components/renderer_context_menu/render_view_context_menu_proxy.h
@@ -13,13 +13,8 @@
 class WebContents;
 }
 
-namespace gfx {
-class Image;
-class ImageSkia;
-struct VectorIcon;
-}
-
 namespace ui {
+class ImageModel;
 class MenuModel;
 }
 
@@ -84,10 +79,7 @@
   virtual void AddMenuItem(int command_id, const base::string16& title) = 0;
   virtual void AddMenuItemWithIcon(int command_id,
                                    const base::string16& title,
-                                   const gfx::ImageSkia& image) = 0;
-  virtual void AddMenuItemWithIcon(int command_id,
-                                   const base::string16& title,
-                                   const gfx::VectorIcon& image) = 0;
+                                   const ui::ImageModel& icon) = 0;
   virtual void AddCheckItem(int command_id, const base::string16& title) = 0;
   virtual void AddSeparator() = 0;
 
@@ -98,11 +90,7 @@
   virtual void AddSubMenuWithStringIdAndIcon(int command_id,
                                              int message_id,
                                              ui::MenuModel* model,
-                                             const gfx::ImageSkia& image) = 0;
-  virtual void AddSubMenuWithStringIdAndIcon(int command_id,
-                                             int message_id,
-                                             ui::MenuModel* model,
-                                             const gfx::VectorIcon& image) = 0;
+                                             const ui::ImageModel& icon) = 0;
 
   // Update the status and text of the specified context-menu item.
   virtual void UpdateMenuItem(int command_id,
@@ -111,7 +99,7 @@
                               const base::string16& title) = 0;
 
   // Update the icon of the specified context-menu item.
-  virtual void UpdateMenuIcon(int command_id, const gfx::Image& image) = 0;
+  virtual void UpdateMenuIcon(int command_id, const ui::ImageModel& icon) = 0;
 
   // Remove the specified context-menu item.
   virtual void RemoveMenuItem(int command_id) = 0;
diff --git a/components/services/paint_preview_compositor/paint_preview_compositor_impl.cc b/components/services/paint_preview_compositor/paint_preview_compositor_impl.cc
index a05b029c..85bfdc78 100644
--- a/components/services/paint_preview_compositor/paint_preview_compositor_impl.cc
+++ b/components/services/paint_preview_compositor/paint_preview_compositor_impl.cc
@@ -337,9 +337,6 @@
   // before the current frame is loaded to ensure the order of loading is
   // topologically sorted.
   LoadedFramesDeserialContext deserial_context;
-  deserial_context.scroll_offsets = gfx::Size(
-      frame_proto.has_scroll_offset_x() ? frame_proto.scroll_offset_x() : 0,
-      frame_proto.has_scroll_offset_y() ? frame_proto.scroll_offset_y() : 0);
 
   *subframe_failed = false;
   for (const auto& id_pair : frame_proto.content_id_to_embedding_tokens()) {
@@ -396,7 +393,7 @@
                                      subframe_proto_it->has_scroll_offset_y()
                                          ? subframe_proto_it->scroll_offset_y()
                                          : 0);
-    deserial_context.subframes.insert({id_pair.content_id(), frame});
+    deserial_context.insert({id_pair.content_id(), frame});
   }
 
   auto recording_it = recording_map->find(frame_guid);
diff --git a/components/services/paint_preview_compositor/paint_preview_compositor_impl_unittest.cc b/components/services/paint_preview_compositor/paint_preview_compositor_impl_unittest.cc
index 9354210..2796be83 100644
--- a/components/services/paint_preview_compositor/paint_preview_compositor_impl_unittest.cc
+++ b/components/services/paint_preview_compositor/paint_preview_compositor_impl_unittest.cc
@@ -208,10 +208,6 @@
   // observing it.
   PaintPreviewTracker tracker(base::UnguessableToken::Create(), guid,
                               set_is_main_frame);
-  if (set_is_main_frame) {
-    tracker.SetScrollForFrame(
-        SkISize::Make(scroll_offsets.width(), scroll_offsets.height()));
-  }
   mojom::FrameDataPtr expected_frame_data = mojom::FrameData::New();
   expected_frame_data->scroll_extents = scroll_extents;
   expected_frame_data->scroll_offsets = scroll_offsets;
diff --git a/components/signin/core/browser/android/BUILD.gn b/components/signin/core/browser/android/BUILD.gn
index 8e50995..4b08d8a 100644
--- a/components/signin/core/browser/android/BUILD.gn
+++ b/components/signin/core/browser/android/BUILD.gn
@@ -39,7 +39,6 @@
     "java/src/org/chromium/components/signin/AuthException.java",
     "java/src/org/chromium/components/signin/ChildAccountInfoFetcher.java",
     "java/src/org/chromium/components/signin/ChildAccountStatus.java",
-    "java/src/org/chromium/components/signin/ChromeSigninController.java",
     "java/src/org/chromium/components/signin/GmsAvailabilityException.java",
     "java/src/org/chromium/components/signin/GmsJustUpdatedException.java",
     "java/src/org/chromium/components/signin/MutableObservableValue.java",
diff --git a/components/signin/core/browser/android/java/src/org/chromium/components/signin/ChromeSigninController.java b/components/signin/core/browser/android/java/src/org/chromium/components/signin/ChromeSigninController.java
deleted file mode 100644
index d55b7a3..0000000
--- a/components/signin/core/browser/android/java/src/org/chromium/components/signin/ChromeSigninController.java
+++ /dev/null
@@ -1,43 +0,0 @@
-// Copyright 2013 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.components.signin;
-
-import org.chromium.base.ContextUtils;
-
-/**
- * Caches the signed-in username in the app prefs.
- */
-@Deprecated
-public class ChromeSigninController {
-    public static final String TAG = "ChromeSigninController";
-
-    private static final String SIGNED_IN_ACCOUNT_KEY = "google.services.username";
-
-    private static final Object LOCK = new Object();
-
-    private static ChromeSigninController sChromeSigninController;
-
-    private ChromeSigninController() {}
-
-    /**
-     * A factory method for the ChromeSigninController.
-     *
-     * @return a singleton instance of the ChromeSigninController
-     */
-    public static ChromeSigninController get() {
-        synchronized (LOCK) {
-            if (sChromeSigninController == null) {
-                sChromeSigninController = new ChromeSigninController();
-            }
-        }
-        return sChromeSigninController;
-    }
-
-    // TODO(https://crbug.com/1046412): Remove after migrating downstream usages to
-    //                                  SigninPreferencesManager.
-    public String getSignedInAccountName() {
-        return ContextUtils.getAppSharedPreferences().getString(SIGNED_IN_ACCOUNT_KEY, null);
-    }
-}
diff --git a/components/sync/trusted_vault/standalone_trusted_vault_backend.cc b/components/sync/trusted_vault/standalone_trusted_vault_backend.cc
index 6c859c7..5c569c3 100644
--- a/components/sync/trusted_vault/standalone_trusted_vault_backend.cc
+++ b/components/sync/trusted_vault/standalone_trusted_vault_backend.cc
@@ -73,12 +73,12 @@
       FindUserVault(account_info.gaia);
 
   // TODO(crbug.com/1094326): currently there is no guarantee that
-  // |syncing_account_| is set before FetchKeys() call and this may cause
+  // |primary_account_| is set before FetchKeys() call and this may cause
   // redundant sync error in the UI (for key retrieval), especially during the
   // browser startup. Try to find a way to avoid this issue.
   if (!base::FeatureList::IsEnabled(switches::kFollowTrustedVaultKeyRotation) ||
-      !syncing_account_.has_value() ||
-      syncing_account_->gaia != account_info.gaia || !per_user_vault ||
+      !primary_account_.has_value() ||
+      primary_account_->gaia != account_info.gaia || !per_user_vault ||
       !per_user_vault->keys_are_stale() ||
       !per_user_vault->local_device_registration_info().device_registered()) {
     // Keys download attempt is not needed or not possible.
@@ -87,7 +87,7 @@
   }
 
   // Current state guarantees there is no ongoing requests to the server:
-  // 1. Current |syncing_account_| is |account_info|, so there is no ongoing
+  // 1. Current |primary_account_| is |account_info|, so there is no ongoing
   // request for other accounts.
   // 2. Device is already registered, so there is no device registration for
   // |account_info|.
@@ -120,7 +120,7 @@
                              .key_material();
   std::vector<uint8_t> last_key_bytes(last_key.begin(), last_key.end());
   connection_->DownloadKeys(
-      *syncing_account_, last_key_bytes,
+      *primary_account_, last_key_bytes,
       per_user_vault->last_vault_key_version(), std::move(key_pair),
       base::BindOnce(&StandaloneTrustedVaultBackend::OnKeysDownloaded,
                      weak_factory_for_connection_.GetWeakPtr(),
@@ -156,15 +156,15 @@
   AbandonConnectionRequest();
 }
 
-void StandaloneTrustedVaultBackend::SetSyncingAccount(
-    const base::Optional<CoreAccountInfo>& syncing_account) {
-  if (syncing_account == syncing_account_) {
+void StandaloneTrustedVaultBackend::SetPrimaryAccount(
+    const base::Optional<CoreAccountInfo>& primary_account) {
+  if (primary_account == primary_account_) {
     return;
   }
-  syncing_account_ = syncing_account;
+  primary_account_ = primary_account;
   AbandonConnectionRequest();
-  if (syncing_account_.has_value()) {
-    MaybeRegisterDevice(syncing_account_->gaia);
+  if (primary_account_.has_value()) {
+    MaybeRegisterDevice(primary_account_->gaia);
   }
 }
 
@@ -182,6 +182,11 @@
   return true;
 }
 
+base::Optional<CoreAccountInfo>
+StandaloneTrustedVaultBackend::GetPrimaryAccountForTesting() const {
+  return primary_account_;
+}
+
 sync_pb::LocalDeviceRegistrationInfo
 StandaloneTrustedVaultBackend::GetDeviceRegistrationInfoForTesting(
     const std::string& gaia_id) {
@@ -197,8 +202,8 @@
   if (!base::FeatureList::IsEnabled(switches::kFollowTrustedVaultKeyRotation)) {
     return;
   }
-  if (!syncing_account_.has_value() || syncing_account_->gaia != gaia_id) {
-    // Device registration is supported only for |syncing_account_|.
+  if (!primary_account_.has_value() || primary_account_->gaia != gaia_id) {
+    // Device registration is supported only for |primary_account_|.
     return;
   }
   sync_pb::LocalTrustedVaultPerUser* per_user_vault = FindUserVault(gaia_id);
@@ -247,7 +252,7 @@
   // one ongoing request.
   AbandonConnectionRequest();
   connection_->RegisterDevice(
-      *syncing_account_, last_key_bytes,
+      *primary_account_, last_key_bytes,
       per_user_vault->last_vault_key_version(), key_pair->public_key(),
       base::BindOnce(&StandaloneTrustedVaultBackend::OnDeviceRegistered,
                      weak_factory_for_connection_.GetWeakPtr(), gaia_id));
@@ -256,9 +261,9 @@
 void StandaloneTrustedVaultBackend::OnDeviceRegistered(
     const std::string& gaia_id,
     TrustedVaultRequestStatus status) {
-  // If |syncing_account_| was changed meanwhile, this callback must be
+  // If |primary_account_| was changed meanwhile, this callback must be
   // cancelled.
-  DCHECK(syncing_account_ && syncing_account_->gaia == gaia_id);
+  DCHECK(primary_account_ && primary_account_->gaia == gaia_id);
 
   sync_pb::LocalTrustedVaultPerUser* per_user_vault = FindUserVault(gaia_id);
   DCHECK(per_user_vault);
@@ -285,7 +290,7 @@
     TrustedVaultRequestStatus status,
     const std::vector<std::vector<uint8_t>>& vault_keys,
     int last_vault_key_version) {
-  DCHECK(syncing_account_ && syncing_account_->gaia == gaia_id);
+  DCHECK(primary_account_ && primary_account_->gaia == gaia_id);
   DCHECK(!ongoing_fetch_keys_callback_.is_null());
   DCHECK(ongoing_fetch_keys_gaia_id_ == gaia_id);
 
diff --git a/components/sync/trusted_vault/standalone_trusted_vault_backend.h b/components/sync/trusted_vault/standalone_trusted_vault_backend.h
index ff709c6..b05a074 100644
--- a/components/sync/trusted_vault/standalone_trusted_vault_backend.h
+++ b/components/sync/trusted_vault/standalone_trusted_vault_backend.h
@@ -64,9 +64,11 @@
   // Removes all keys for all accounts from both memory and |file_path_|.
   void RemoveAllStoredKeys();
 
-  // Sets/resets |syncing_account_|.
-  void SetSyncingAccount(
-      const base::Optional<CoreAccountInfo>& syncing_account);
+  // Sets/resets |primary_account_|.
+  void SetPrimaryAccount(
+      const base::Optional<CoreAccountInfo>& primary_account);
+
+  base::Optional<CoreAccountInfo> GetPrimaryAccountForTesting() const;
 
   sync_pb::LocalDeviceRegistrationInfo GetDeviceRegistrationInfoForTesting(
       const std::string& gaia_id);
@@ -102,9 +104,9 @@
 
   sync_pb::LocalTrustedVault data_;
 
-  // Only current |syncing_account_| can be used for communication with trusted
+  // Only current |primary_account_| can be used for communication with trusted
   // vault server.
-  base::Optional<CoreAccountInfo> syncing_account_;
+  base::Optional<CoreAccountInfo> primary_account_;
 
   // Used for communication with trusted vault server.
   std::unique_ptr<TrustedVaultConnection> connection_;
diff --git a/components/sync/trusted_vault/standalone_trusted_vault_backend_unittest.cc b/components/sync/trusted_vault/standalone_trusted_vault_backend_unittest.cc
index 39341e5..d6bc767 100644
--- a/components/sync/trusted_vault/standalone_trusted_vault_backend_unittest.cc
+++ b/components/sync/trusted_vault/standalone_trusted_vault_backend_unittest.cc
@@ -96,16 +96,16 @@
                       TrustedVaultConnection::RegisterDeviceCallback callback) {
           device_registration_callback = std::move(callback);
         });
-    // Setting the syncing account will trigger device registration.
-    backend()->SetSyncingAccount(account_info);
+    // Setting the primary account will trigger device registration.
+    backend()->SetPrimaryAccount(account_info);
     EXPECT_FALSE(device_registration_callback.is_null());
 
     // Pretend that the registration completed successfully.
     std::move(device_registration_callback)
         .Run(TrustedVaultRequestStatus::kSuccess);
 
-    // Reset syncing account.
-    backend()->SetSyncingAccount(base::nullopt);
+    // Reset primary account.
+    backend()->SetPrimaryAccount(base::nullopt);
 
     std::string device_private_key_material =
         backend_->GetDeviceRegistrationInfoForTesting(account_info.gaia)
@@ -143,8 +143,8 @@
         device_registration_callback = std::move(callback);
       });
 
-  // Setting the syncing account will trigger device registration.
-  backend()->SetSyncingAccount(account_info);
+  // Setting the primary account will trigger device registration.
+  backend()->SetPrimaryAccount(account_info);
   ASSERT_FALSE(device_registration_callback.is_null());
 
   // Pretend that the registration completed successfully.
@@ -176,7 +176,7 @@
   // Make keys downloading theoretically possible.
   StoreKeysAndMimicDeviceRegistration(kVaultKeys, kLastKeyVersion,
                                       account_info);
-  backend()->SetSyncingAccount(account_info);
+  backend()->SetPrimaryAccount(account_info);
 
   EXPECT_CALL(*connection(), DownloadKeys(_, _, _, _, _)).Times(0);
 
@@ -202,7 +202,7 @@
       StoreKeysAndMimicDeviceRegistration({kInitialVaultKey},
                                           kInitialLastKeyVersion, account_info);
   EXPECT_TRUE(backend()->MarkKeysAsStale(account_info));
-  backend()->SetSyncingAccount(account_info);
+  backend()->SetPrimaryAccount(account_info);
 
   const std::vector<std::vector<uint8_t>> kNewVaultKeys = {kInitialVaultKey,
                                                            {1, 3, 2}};
diff --git a/components/sync/trusted_vault/standalone_trusted_vault_client.cc b/components/sync/trusted_vault/standalone_trusted_vault_client.cc
index 66f46c1..ace680a 100644
--- a/components/sync/trusted_vault/standalone_trusted_vault_client.cc
+++ b/components/sync/trusted_vault/standalone_trusted_vault_client.cc
@@ -27,13 +27,100 @@
     base::MayBlock(), base::TaskPriority::USER_VISIBLE,
     base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN};
 
+class PrimaryAccountObserver : public signin::IdentityManager::Observer {
+ public:
+  PrimaryAccountObserver(
+      scoped_refptr<base::SequencedTaskRunner> backend_task_runner,
+      scoped_refptr<StandaloneTrustedVaultBackend> backend,
+      signin::IdentityManager* identity_manager);
+  PrimaryAccountObserver(const PrimaryAccountObserver& other) = delete;
+  PrimaryAccountObserver& operator=(const PrimaryAccountObserver& other) =
+      delete;
+  ~PrimaryAccountObserver() override;
+
+  // signin::IdentityManager::Observer implementation.
+  void OnPrimaryAccountSet(
+      const CoreAccountInfo& primary_account_info) override;
+  void OnPrimaryAccountCleared(
+      const CoreAccountInfo& previous_primary_account_info) override;
+  void OnUnconsentedPrimaryAccountChanged(
+      const CoreAccountInfo& unconsented_primary_account_info) override;
+
+ private:
+  void UpdatePrimaryAccountIfNeeded();
+
+  const scoped_refptr<base::SequencedTaskRunner> backend_task_runner_;
+  const scoped_refptr<StandaloneTrustedVaultBackend> backend_;
+  signin::IdentityManager* const identity_manager_;
+  CoreAccountInfo primary_account_;
+};
+
+PrimaryAccountObserver::PrimaryAccountObserver(
+    scoped_refptr<base::SequencedTaskRunner> backend_task_runner,
+    scoped_refptr<StandaloneTrustedVaultBackend> backend,
+    signin::IdentityManager* identity_manager)
+    : backend_task_runner_(backend_task_runner),
+      backend_(backend),
+      identity_manager_(identity_manager) {
+  DCHECK(backend_task_runner_);
+  DCHECK(backend_);
+  DCHECK(identity_manager_);
+
+  identity_manager_->AddObserver(this);
+  UpdatePrimaryAccountIfNeeded();
+}
+
+PrimaryAccountObserver::~PrimaryAccountObserver() {
+  identity_manager_->RemoveObserver(this);
+}
+
+void PrimaryAccountObserver::OnPrimaryAccountSet(
+    const CoreAccountInfo& primary_account_info) {
+  UpdatePrimaryAccountIfNeeded();
+}
+
+void PrimaryAccountObserver::OnPrimaryAccountCleared(
+    const CoreAccountInfo& previous_primary_account_info) {
+  UpdatePrimaryAccountIfNeeded();
+}
+
+void PrimaryAccountObserver::OnUnconsentedPrimaryAccountChanged(
+    const CoreAccountInfo& unconsented_primary_account_info) {
+  UpdatePrimaryAccountIfNeeded();
+}
+
+void PrimaryAccountObserver::UpdatePrimaryAccountIfNeeded() {
+  CoreAccountInfo primary_account = identity_manager_->GetPrimaryAccountInfo(
+      signin::ConsentLevel::kNotRequired);
+  if (primary_account == primary_account_) {
+    return;
+  }
+  primary_account_ = primary_account;
+
+  // IdentityManager returns empty CoreAccountInfo if there is no primary
+  // account.
+  base::Optional<CoreAccountInfo> optional_primary_account;
+  if (!primary_account_.IsEmpty()) {
+    optional_primary_account = primary_account_;
+  }
+
+  backend_task_runner_->PostTask(
+      FROM_HERE,
+      base::BindOnce(&StandaloneTrustedVaultBackend::SetPrimaryAccount,
+                     backend_, optional_primary_account));
+}
+
 }  // namespace
 
 StandaloneTrustedVaultClient::StandaloneTrustedVaultClient(
-    const base::FilePath& file_path)
-    : file_path_(file_path),
+    const base::FilePath& file_path,
+    signin::IdentityManager* identity_manager)
+    : identity_manager_(identity_manager),
+      file_path_(file_path),
       backend_task_runner_(
-          base::ThreadPool::CreateSequencedTaskRunner(kBackendTaskTraits)) {}
+          base::ThreadPool::CreateSequencedTaskRunner(kBackendTaskTraits)) {
+  DCHECK(identity_manager_);
+}
 
 StandaloneTrustedVaultClient::~StandaloneTrustedVaultClient() = default;
 
@@ -98,6 +185,16 @@
                                          std::move(cb));
 }
 
+void StandaloneTrustedVaultClient::FetchBackendPrimaryAccountForTesting(
+    base::OnceCallback<void(const base::Optional<CoreAccountInfo>&)> cb) const {
+  base::PostTaskAndReplyWithResult(
+      backend_task_runner_.get(), FROM_HERE,
+      base::BindOnce(
+          &StandaloneTrustedVaultBackend::GetPrimaryAccountForTesting,
+          backend_),
+      std::move(cb));
+}
+
 void StandaloneTrustedVaultClient::TriggerLazyInitializationIfNeeded() {
   if (backend_) {
     return;
@@ -117,8 +214,12 @@
       FROM_HERE,
       base::BindOnce(&StandaloneTrustedVaultBackend::ReadDataFromDisk,
                      backend_));
-  // TODO(crbug.com/1113597): populate current syncing account to |backend_|
-  // here and upon syncing account change.
+
+  // |primary_account_observer_| will populate primary account to |backend_|
+  // whenever it changes and upon construction, if there is a primary account
+  // already.
+  primary_account_observer_ = std::make_unique<PrimaryAccountObserver>(
+      backend_task_runner_, backend_, identity_manager_);
 }
 
 bool StandaloneTrustedVaultClient::IsInitializationTriggeredForTesting() const {
diff --git a/components/sync/trusted_vault/standalone_trusted_vault_client.h b/components/sync/trusted_vault/standalone_trusted_vault_client.h
index 78ffa18..ca93faa 100644
--- a/components/sync/trusted_vault/standalone_trusted_vault_client.h
+++ b/components/sync/trusted_vault/standalone_trusted_vault_client.h
@@ -13,6 +13,7 @@
 #include "base/files/file_path.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/sequenced_task_runner.h"
+#include "components/signin/public/identity_manager/identity_manager.h"
 #include "components/sync/driver/trusted_vault_client.h"
 
 struct CoreAccountInfo;
@@ -28,8 +29,9 @@
 // Reading of the file is done lazily.
 class StandaloneTrustedVaultClient : public TrustedVaultClient {
  public:
-  // TODO(crbug.com/1113597): plumb IdentityManager into ctor.
-  explicit StandaloneTrustedVaultClient(const base::FilePath& file_path);
+  // |identity_manager| must not be null and must outlive this object.
+  StandaloneTrustedVaultClient(const base::FilePath& file_path,
+                               signin::IdentityManager* identity_manager);
   StandaloneTrustedVaultClient(const StandaloneTrustedVaultClient& other) =
       delete;
   StandaloneTrustedVaultClient& operator=(
@@ -54,12 +56,18 @@
 
   // Runs |cb| when all requests have completed.
   void WaitForFlushForTesting(base::OnceClosure cb) const;
+  void FetchBackendPrimaryAccountForTesting(
+      base::OnceCallback<void(const base::Optional<CoreAccountInfo>&)> callback)
+      const;
   bool IsInitializationTriggeredForTesting() const;
   void SetRecoverabilityDegradedForTesting();
 
  private:
   void TriggerLazyInitializationIfNeeded();
 
+  // Never null and must outlive this object.
+  signin::IdentityManager* identity_manager_;
+
   const base::FilePath file_path_;
   const scoped_refptr<base::SequencedTaskRunner> backend_task_runner_;
 
@@ -69,6 +77,11 @@
   // |backend_task_runner_| and destroyed (refcounted) on any thread.
   scoped_refptr<StandaloneTrustedVaultBackend> backend_;
 
+  // Observes changes of primary account and populates them into |backend_|.
+  // Created lazily together with |backend_| and holds reference to it and
+  // |backend_task_runner_|.
+  std::unique_ptr<signin::IdentityManager::Observer> primary_account_observer_;
+
   bool is_recoverability_degraded_for_testing_ = false;
 };
 
diff --git a/components/sync/trusted_vault/standalone_trusted_vault_client_unittest.cc b/components/sync/trusted_vault/standalone_trusted_vault_client_unittest.cc
index 7608eb8..50cd48c 100644
--- a/components/sync/trusted_vault/standalone_trusted_vault_client_unittest.cc
+++ b/components/sync/trusted_vault/standalone_trusted_vault_client_unittest.cc
@@ -13,6 +13,7 @@
 #include "components/os_crypt/os_crypt.h"
 #include "components/os_crypt/os_crypt_mocker.h"
 #include "components/signin/public/identity_manager/account_info.h"
+#include "components/signin/public/identity_manager/identity_test_environment.h"
 #include "components/sync/protocol/local_trusted_vault.pb.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -65,7 +66,7 @@
   StandaloneTrustedVaultClientTest()
       : file_path_(CreateUniqueTempDir(&temp_dir_)
                        .Append(base::FilePath(FILE_PATH_LITERAL("some_file")))),
-        client_(file_path_) {}
+        client_(file_path_, identity_env_.identity_manager()) {}
 
   ~StandaloneTrustedVaultClientTest() override = default;
 
@@ -84,9 +85,23 @@
     loop.Run();
   }
 
+  base::Optional<CoreAccountInfo> FetchBackendPrimaryAccountAndWait() {
+    base::RunLoop loop;
+    base::Optional<CoreAccountInfo> fetched_primary_account;
+    client_.FetchBackendPrimaryAccountForTesting(base::BindLambdaForTesting(
+        [&](const base::Optional<CoreAccountInfo>& primary_account) {
+          fetched_primary_account = primary_account;
+          loop.Quit();
+        }));
+    loop.Run();
+    return fetched_primary_account;
+  }
+
   base::test::TaskEnvironment task_environment_;
   base::ScopedTempDir temp_dir_;
   const base::FilePath file_path_;
+  // |identity_env_| should outlive |client_|.
+  signin::IdentityTestEnvironment identity_env_;
   StandaloneTrustedVaultClient client_;
 };
 
@@ -175,7 +190,8 @@
   WaitForFlush();
 
   // Instantiate a second client to read the file.
-  StandaloneTrustedVaultClient other_client(file_path_);
+  StandaloneTrustedVaultClient other_client(file_path_,
+                                            identity_env_.identity_manager());
   EXPECT_THAT(FetchKeysAndWaitForClient(kGaiaId1, &other_client),
               ElementsAre(kKey1));
   EXPECT_THAT(FetchKeysAndWaitForClient(kGaiaId2, &other_client),
@@ -205,6 +221,47 @@
   EXPECT_FALSE(base::PathExists(file_path_));
 }
 
+// ChromeOS doesn't support sign-out.
+#if !defined(OS_CHROMEOS)
+TEST_F(StandaloneTrustedVaultClientTest, ShouldPopulatePrimaryAccountChanges) {
+  ASSERT_FALSE(client_.IsInitializationTriggeredForTesting());
+
+  // Set initial primary account before backend initialization.
+  const std::string email1 = "user1@gmail.com";
+  identity_env_.SetPrimaryAccount(email1);
+
+  // Trigger backend initialization.
+  ASSERT_THAT(FetchKeysAndWait("user1"), IsEmpty());
+  ASSERT_TRUE(client_.IsInitializationTriggeredForTesting());
+
+  // Verify that current primary account corresponds to |email1|.
+  base::Optional<CoreAccountInfo> current_primary_account =
+      FetchBackendPrimaryAccountAndWait();
+  ASSERT_THAT(current_primary_account, Ne(base::nullopt));
+  EXPECT_THAT(current_primary_account->email, Eq(email1));
+
+  // Remove primary account.
+  identity_env_.ClearPrimaryAccount();
+  current_primary_account = FetchBackendPrimaryAccountAndWait();
+  EXPECT_THAT(current_primary_account, Eq(base::nullopt));
+
+  // Set new primary account.
+  const std::string email2 = "user2@gmail.com";
+  identity_env_.SetPrimaryAccount(email2);
+  current_primary_account = FetchBackendPrimaryAccountAndWait();
+  ASSERT_THAT(current_primary_account, Ne(base::nullopt));
+  EXPECT_THAT(current_primary_account->email, Eq(email2));
+
+  // Set unconsented primary account.
+  const std::string email3 = "user3@gmail.com";
+  identity_env_.ClearPrimaryAccount();
+  identity_env_.SetUnconsentedPrimaryAccount(email3);
+  current_primary_account = FetchBackendPrimaryAccountAndWait();
+  ASSERT_THAT(current_primary_account, Ne(base::nullopt));
+  EXPECT_THAT(current_primary_account->email, Eq(email3));
+}
+#endif
+
 }  // namespace
 
 }  // namespace syncer
diff --git a/components/viz/service/BUILD.gn b/components/viz/service/BUILD.gn
index e11f416..a9d641d 100644
--- a/components/viz/service/BUILD.gn
+++ b/components/viz/service/BUILD.gn
@@ -642,7 +642,7 @@
     deps = [
       "//base:base_java",
       "//base:jni_java",
-      "//ui/android:ui_full_java",
+      "//ui/android:ui_no_recycler_view_java",
     ]
     annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ]
     sources = [
diff --git a/content/browser/accessibility/browser_accessibility_android.cc b/content/browser/accessibility/browser_accessibility_android.cc
index 2aae62b..0bc6363 100644
--- a/content/browser/accessibility/browser_accessibility_android.cc
+++ b/content/browser/accessibility/browser_accessibility_android.cc
@@ -992,7 +992,6 @@
     case ax::mojom::Role::kMenuBar:
       message_id = IDS_AX_ROLE_MENU_BAR;
       break;
-    case ax::mojom::Role::kMenuButton:
     case ax::mojom::Role::kMenuItem:
       message_id = IDS_AX_ROLE_MENU_ITEM;
       break;
diff --git a/content/browser/accessibility/browser_accessibility_com_win.cc b/content/browser/accessibility/browser_accessibility_com_win.cc
index ce8fb53..ce8253a 100644
--- a/content/browser/accessibility/browser_accessibility_com_win.cc
+++ b/content/browser/accessibility/browser_accessibility_com_win.cc
@@ -1630,26 +1630,6 @@
   return ToBrowserAccessibilityComWin(Manager()->GetFromID(id));
 }
 
-bool BrowserAccessibilityComWin::IsListBoxOptionOrMenuListOption() {
-  if (!owner()->PlatformGetParent())
-    return false;
-
-  ax::mojom::Role role = owner()->GetRole();
-  ax::mojom::Role parent_role = owner()->PlatformGetParent()->GetRole();
-
-  if (role == ax::mojom::Role::kListBoxOption &&
-      parent_role == ax::mojom::Role::kListBox) {
-    return true;
-  }
-
-  if (role == ax::mojom::Role::kMenuListOption &&
-      parent_role == ax::mojom::Role::kMenuListPopup) {
-    return true;
-  }
-
-  return false;
-}
-
 void BrowserAccessibilityComWin::FireNativeEvent(LONG win_event_type) const {
   // We only allow events on descendants of a platform leaf when that platform
   // leaf is a popup button parent of a menu list popup. On Windows, the menu
diff --git a/content/browser/accessibility/browser_accessibility_com_win.h b/content/browser/accessibility/browser_accessibility_com_win.h
index f839748..46b96f4 100644
--- a/content/browser/accessibility/browser_accessibility_com_win.h
+++ b/content/browser/accessibility/browser_accessibility_com_win.h
@@ -400,10 +400,6 @@
   // TODO(nektar): Rename this function to GetFromNodeID.
   BrowserAccessibilityComWin* GetFromID(int32_t id) const;
 
-  // Returns true if this is a list box option with a parent of type list box,
-  // or a menu list option with a parent of type menu list popup.
-  bool IsListBoxOptionOrMenuListOption();
-
   // Fire a Windows-specific accessibility event notification on this node.
   void FireNativeEvent(LONG win_event_type) const;
   struct WinAttributes {
diff --git a/content/browser/device/device_service.cc b/content/browser/device/device_service.cc
index 6f9144c2..6a37826 100644
--- a/content/browser/device/device_service.cc
+++ b/content/browser/device/device_service.cc
@@ -49,12 +49,14 @@
       mojo::PendingRemote<network::mojom::URLLoaderClient> client,
       const net::MutableNetworkTrafficAnnotationTag& traffic_annotation)
       override {
-    GetContentClient()
-        ->browser()
-        ->GetSystemSharedURLLoaderFactory()
-        ->CreateLoaderAndStart(std::move(receiver), routing_id, request_id,
-                               options, url_request, std::move(client),
-                               traffic_annotation);
+    auto factory =
+        GetContentClient()->browser()->GetSystemSharedURLLoaderFactory();
+    if (!factory)
+      return;
+
+    factory->CreateLoaderAndStart(std::move(receiver), routing_id, request_id,
+                                  options, url_request, std::move(client),
+                                  traffic_annotation);
   }
 
   // SharedURLLoaderFactory implementation:
diff --git a/content/browser/frame_host/render_frame_host_feature_policy_unittest.cc b/content/browser/frame_host/render_frame_host_feature_policy_unittest.cc
index 913ce4a..b3f2e2e75 100644
--- a/content/browser/frame_host/render_frame_host_feature_policy_unittest.cc
+++ b/content/browser/frame_host/render_frame_host_feature_policy_unittest.cc
@@ -29,7 +29,7 @@
   static constexpr const char* kOrigin4 = "https://test.com";
 
   static const blink::mojom::FeaturePolicyFeature kDefaultEnabledFeature =
-      blink::mojom::FeaturePolicyFeature::kDocumentWrite;
+      blink::mojom::FeaturePolicyFeature::kSyncXHR;
   static const blink::mojom::FeaturePolicyFeature kDefaultSelfFeature =
       blink::mojom::FeaturePolicyFeature::kGeolocation;
 
diff --git a/content/browser/loader/navigation_url_loader_impl.cc b/content/browser/loader/navigation_url_loader_impl.cc
index 2b94b8b2..6c6c7b5 100644
--- a/content/browser/loader/navigation_url_loader_impl.cc
+++ b/content/browser/loader/navigation_url_loader_impl.cc
@@ -49,7 +49,6 @@
 #include "content/browser/web_package/web_bundle_utils.h"
 #include "content/browser/webui/url_data_manager_backend.h"
 #include "content/browser/webui/web_ui_url_loader_factory_internal.h"
-#include "content/common/net/record_load_histograms.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
@@ -89,6 +88,7 @@
 #include "services/network/public/mojom/url_loader_factory.mojom.h"
 #include "third_party/blink/public/common/features.h"
 #include "third_party/blink/public/common/loader/mime_sniffing_throttle.h"
+#include "third_party/blink/public/common/loader/record_load_histograms.h"
 #include "third_party/blink/public/common/loader/throttling_url_loader.h"
 #include "third_party/blink/public/common/mime_util/mime_util.h"
 
@@ -309,9 +309,9 @@
   // request. The net::OK check may not be necessary - the case where OK is
   // received without receiving any headers looks broken, anyways.
   if (!received_response_ && (!status_ || status_->error_code != net::OK)) {
-    RecordLoadHistograms(url::Origin::Create(url_),
-                         resource_request_->destination,
-                         status_ ? status_->error_code : net::ERR_ABORTED);
+    blink::RecordLoadHistograms(
+        url::Origin::Create(url_), resource_request_->destination,
+        status_ ? status_->error_code : net::ERR_ABORTED);
   }
 }
 
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index 0a3af064..802b728 100644
--- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc
@@ -5093,6 +5093,7 @@
     observers_.ForEachObserver([&](WebContentsObserver* observer) {
       observer->DidChangeThemeColor();
     });
+    last_sent_theme_color_ = source->theme_color();
   }
 }
 
diff --git a/content/child/runtime_features.cc b/content/child/runtime_features.cc
index 0e961bb..be1e157 100644
--- a/content/child/runtime_features.cc
+++ b/content/child/runtime_features.cc
@@ -18,7 +18,6 @@
 #include "content/common/content_switches_internal.h"
 #include "content/public/common/content_features.h"
 #include "content/public/common/content_switches.h"
-#include "content/public/common/referrer.h"
 #include "device/fido/features.h"
 #include "device/gamepad/public/cpp/gamepad_features.h"
 #include "gpu/config/gpu_switches.h"
@@ -28,6 +27,7 @@
 #include "services/network/public/cpp/features.h"
 #include "services/network/public/cpp/network_switches.h"
 #include "third_party/blink/public/common/features.h"
+#include "third_party/blink/public/common/loader/referrer_utils.h"
 #include "third_party/blink/public/common/switches.h"
 #include "third_party/blink/public/platform/web_runtime_features.h"
 #include "ui/accessibility/accessibility_features.h"
@@ -341,6 +341,7 @@
     {wf::EnableMediaFeeds, media::kMediaFeeds, kUseFeatureState},
     {wf::EnableRestrictGamepadAccess, features::kRestrictGamepadAccess,
      kEnableOnly},
+    {wf::EnableCompositeSVG, blink::features::kCompositeSVG, kUseFeatureState},
     {wf::EnableCompositingOptimizations,
      blink::features::kCompositingOptimizations, kUseFeatureState},
     {wf::EnableConversionMeasurementInfraSupport,
@@ -625,7 +626,7 @@
   WebRuntimeFeatures::EnableReducedReferrerGranularity(
       base::FeatureList::IsEnabled(
           blink::features::kReducedReferrerGranularity) &&
-      !content::Referrer::ShouldForceLegacyDefaultReferrerPolicy());
+      !blink::ReferrerUtils::ShouldForceLegacyDefaultReferrerPolicy());
 
   if (base::FeatureList::IsEnabled(
           blink::features::kAppCacheRequireOriginTrial)) {
diff --git a/content/common/BUILD.gn b/content/common/BUILD.gn
index 262eeee..8355177 100644
--- a/content/common/BUILD.gn
+++ b/content/common/BUILD.gn
@@ -157,8 +157,6 @@
     "navigation_params.h",
     "navigation_params_mojom_traits.h",
     "navigation_params_utils.h",
-    "net/record_load_histograms.cc",
-    "net/record_load_histograms.h",
     "origin_util.cc",
     "page_messages.h",
     "page_state_serialization.cc",
diff --git a/content/common/net/OWNERS b/content/common/net/OWNERS
deleted file mode 100644
index b772929..0000000
--- a/content/common/net/OWNERS
+++ /dev/null
@@ -1,4 +0,0 @@
-file://net/OWNERS
-
-# COMPONENT: Internals>Network
-# TEAM: net-dev@chromium.org
diff --git a/content/common/net/record_load_histograms.h b/content/common/net/record_load_histograms.h
deleted file mode 100644
index e3eafa54..0000000
--- a/content/common/net/record_load_histograms.h
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CONTENT_COMMON_NET_RECORD_LOAD_HISTOGRAMS_H_
-#define CONTENT_COMMON_NET_RECORD_LOAD_HISTOGRAMS_H_
-
-#include "services/network/public/mojom/fetch_api.mojom-shared.h"
-#include "url/origin.h"
-
-namespace content {
-
-// Logs histograms when a resource destined for a renderer (One with a
-// network::mojom::RequestDestination) finishes loading, or when a load is
-// aborted. Not used for internal network requests initiated by the browser
-// itself.
-void RecordLoadHistograms(const url::Origin& origin,
-                          network::mojom::RequestDestination destination,
-                          int net_error);
-
-}  // namespace content
-
-#endif  // CONTENT_COMMON_NET_RECORD_LOAD_HISTOGRAMS_H_
diff --git a/content/public/android/BUILD.gn b/content/public/android/BUILD.gn
index 4589c1b..cb42ffd 100644
--- a/content/public/android/BUILD.gn
+++ b/content/public/android/BUILD.gn
@@ -109,7 +109,8 @@
     "//third_party/blink/public/mojom:android_mojo_bindings_java",
     "//third_party/blink/public/mojom:mojom_core_java",
     "//third_party/blink/public/mojom:mojom_platform_java",
-    "//ui/android:ui_java",
+    "//ui/android:ui_no_recycler_view_java",
+    "//ui/android:ui_utils_java",
     "//ui/gfx/geometry/mojom:mojom_java",
     "//url:gurl_java",
     "//url:origin_java",
diff --git a/content/public/common/referrer.cc b/content/public/common/referrer.cc
index 22e7ffb..c1d9d1bb 100644
--- a/content/public/common/referrer.cc
+++ b/content/public/common/referrer.cc
@@ -72,17 +72,6 @@
 }
 
 // static
-void Referrer::SetForceLegacyDefaultReferrerPolicy(bool force) {
-  blink::ReferrerUtils::ReadModifyWriteForceLegacyPolicyFlag(force);
-}
-
-// static
-bool Referrer::ShouldForceLegacyDefaultReferrerPolicy() {
-  return blink::ReferrerUtils::ReadModifyWriteForceLegacyPolicyFlag(
-      base::nullopt);
-}
-
-// static
 network::mojom::ReferrerPolicy Referrer::ConvertToPolicy(int32_t policy) {
   return mojo::ConvertIntToMojoEnum<network::mojom::ReferrerPolicy>(policy)
       .value_or(network::mojom::ReferrerPolicy::kDefault);
diff --git a/content/public/test/referrer_unittest.cc b/content/public/test/referrer_unittest.cc
index 1ae04b42..c3ff9a0 100644
--- a/content/public/test/referrer_unittest.cc
+++ b/content/public/test/referrer_unittest.cc
@@ -110,24 +110,4 @@
   }
 }
 
-TEST(DefaultReferrerPolicyTest, SetAndGetForceLegacy) {
-  EXPECT_FALSE(content::Referrer::ShouldForceLegacyDefaultReferrerPolicy());
-  content::Referrer::SetForceLegacyDefaultReferrerPolicy(true);
-  EXPECT_TRUE(content::Referrer::ShouldForceLegacyDefaultReferrerPolicy());
-}
-
-TEST(DefaultReferrerPolicyTest, ForceLegacyOnly) {
-  content::Referrer::SetForceLegacyDefaultReferrerPolicy(true);
-  EXPECT_EQ(blink::ReferrerUtils::GetDefaultNetReferrerPolicy(),
-            net::ReferrerPolicy::CLEAR_ON_TRANSITION_FROM_SECURE_TO_INSECURE);
-}
-
-TEST(DefaultReferrerPolicyTest, FeatureAndForceLegacy) {
-  base::test::ScopedFeatureList f;
-  f.InitAndEnableFeature(blink::features::kReducedReferrerGranularity);
-  content::Referrer::SetForceLegacyDefaultReferrerPolicy(true);
-  EXPECT_EQ(blink::ReferrerUtils::GetDefaultNetReferrerPolicy(),
-            net::ReferrerPolicy::CLEAR_ON_TRANSITION_FROM_SECURE_TO_INSECURE);
-}
-
 }  // namespace content
diff --git a/content/renderer/BUILD.gn b/content/renderer/BUILD.gn
index ebb030a..21c03c04 100644
--- a/content/renderer/BUILD.gn
+++ b/content/renderer/BUILD.gn
@@ -137,8 +137,6 @@
     "media/audio/audio_device_factory.h",
     "media/audio/audio_input_ipc_factory.cc",
     "media/audio/audio_input_ipc_factory.h",
-    "media/audio/audio_output_ipc_factory.cc",
-    "media/audio/audio_output_ipc_factory.h",
     "media/audio/audio_renderer_mixer_manager.cc",
     "media/audio/audio_renderer_mixer_manager.h",
     "media/audio/audio_renderer_sink_cache.h",
@@ -146,8 +144,6 @@
     "media/audio/audio_renderer_sink_cache_impl.h",
     "media/audio/mojo_audio_input_ipc.cc",
     "media/audio/mojo_audio_input_ipc.h",
-    "media/audio/mojo_audio_output_ipc.cc",
-    "media/audio/mojo_audio_output_ipc.h",
     "media/audio_decoder.cc",
     "media/audio_decoder.h",
     "media/batching_media_log.cc",
diff --git a/content/renderer/loader/resource_load_stats.cc b/content/renderer/loader/resource_load_stats.cc
index ce2bc5b0..4e286d5c 100644
--- a/content/renderer/loader/resource_load_stats.cc
+++ b/content/renderer/loader/resource_load_stats.cc
@@ -6,7 +6,6 @@
 
 #include "base/bind.h"
 #include "base/metrics/histogram_macros.h"
-#include "content/common/net/record_load_histograms.h"
 #include "content/renderer/render_frame_impl.h"
 #include "content/renderer/render_thread_impl.h"
 #include "net/base/ip_endpoint.h"
@@ -15,6 +14,7 @@
 #include "services/network/public/mojom/fetch_api.mojom.h"
 #include "services/network/public/mojom/url_response_head.mojom.h"
 #include "third_party/blink/public/common/loader/network_utils.h"
+#include "third_party/blink/public/common/loader/record_load_histograms.h"
 #include "third_party/blink/public/common/loader/resource_type_util.h"
 #include "third_party/blink/public/mojom/loader/resource_load_info.mojom.h"
 
@@ -204,9 +204,9 @@
     int render_frame_id,
     blink::mojom::ResourceLoadInfoPtr resource_load_info,
     const network::URLLoaderCompletionStatus& status) {
-  RecordLoadHistograms(url::Origin::Create(resource_load_info->final_url),
-                       resource_load_info->request_destination,
-                       status.error_code);
+  blink::RecordLoadHistograms(
+      url::Origin::Create(resource_load_info->final_url),
+      resource_load_info->request_destination, status.error_code);
 
   resource_load_info->was_cached = status.exists_in_cache;
   resource_load_info->net_error = status.error_code;
@@ -230,8 +230,9 @@
     int render_frame_id,
     blink::mojom::ResourceLoadInfoPtr resource_load_info,
     int net_error) {
-  RecordLoadHistograms(url::Origin::Create(resource_load_info->final_url),
-                       resource_load_info->request_destination, net_error);
+  blink::RecordLoadHistograms(
+      url::Origin::Create(resource_load_info->final_url),
+      resource_load_info->request_destination, net_error);
 
   auto task_runner = RenderThreadImpl::DeprecatedGetMainTaskRunner();
   if (!task_runner)
diff --git a/content/renderer/media/audio/audio_device_factory.cc b/content/renderer/media/audio/audio_device_factory.cc
index 8b80a66..98fc2d6 100644
--- a/content/renderer/media/audio/audio_device_factory.cc
+++ b/content/renderer/media/audio/audio_device_factory.cc
@@ -17,7 +17,6 @@
 #include "build/build_config.h"
 #include "content/common/content_constants_internal.h"
 #include "content/renderer/media/audio/audio_input_ipc_factory.h"
-#include "content/renderer/media/audio/audio_output_ipc_factory.h"
 #include "content/renderer/media/audio/audio_renderer_mixer_manager.h"
 #include "content/renderer/media/audio/audio_renderer_sink_cache_impl.h"
 #include "content/renderer/media/audio/mojo_audio_input_ipc.h"
@@ -28,6 +27,7 @@
 #include "media/base/audio_renderer_mixer_input.h"
 #include "media/base/media_switches.h"
 #include "third_party/blink/public/mojom/media/renderer_audio_input_stream_factory.mojom.h"
+#include "third_party/blink/public/web/modules/media/audio/web_audio_output_ipc_factory.h"
 
 namespace content {
 
@@ -59,10 +59,11 @@
     const base::UnguessableToken& frame_token,
     const media::AudioSinkParameters& params,
     base::TimeDelta auth_timeout) {
-  CHECK(AudioOutputIPCFactory::get());
+  CHECK(blink::WebAudioOutputIPCFactory::get());
   auto device = base::MakeRefCounted<media::AudioOutputDevice>(
-      AudioOutputIPCFactory::get()->CreateAudioOutputIPC(frame_token),
-      AudioOutputIPCFactory::get()->io_task_runner(), params, auth_timeout);
+      blink::WebAudioOutputIPCFactory::get()->CreateAudioOutputIPC(frame_token),
+      blink::WebAudioOutputIPCFactory::get()->io_task_runner(), params,
+      auth_timeout);
   device->RequestDeviceAuthorization();
   return device;
 }
diff --git a/content/renderer/media/audio/audio_output_ipc_factory.cc b/content/renderer/media/audio/audio_output_ipc_factory.cc
deleted file mode 100644
index 6651543..0000000
--- a/content/renderer/media/audio/audio_output_ipc_factory.cc
+++ /dev/null
@@ -1,109 +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 "content/renderer/media/audio/audio_output_ipc_factory.h"
-
-#include <utility>
-
-#include "base/bind.h"
-#include "base/check_op.h"
-#include "base/single_thread_task_runner.h"
-#include "content/renderer/media/audio/mojo_audio_output_ipc.h"
-#include "third_party/blink/public/common/browser_interface_broker_proxy.h"
-
-namespace content {
-
-AudioOutputIPCFactory* AudioOutputIPCFactory::instance_ = nullptr;
-
-AudioOutputIPCFactory::AudioOutputIPCFactory(
-    scoped_refptr<base::SingleThreadTaskRunner> io_task_runner)
-    : io_task_runner_(std::move(io_task_runner)) {
-  DCHECK(!instance_);
-  instance_ = this;
-}
-
-AudioOutputIPCFactory::~AudioOutputIPCFactory() {
-  // Allow destruction in tests.
-  DCHECK(factory_remotes_.empty());
-  DCHECK_EQ(instance_, this);
-  instance_ = nullptr;
-}
-
-std::unique_ptr<media::AudioOutputIPC>
-AudioOutputIPCFactory::CreateAudioOutputIPC(
-    const base::UnguessableToken& frame_token) const {
-  // Unretained is safe due to the contract at the top of the header file.
-  return std::make_unique<MojoAudioOutputIPC>(
-      base::BindRepeating(&AudioOutputIPCFactory::GetRemoteFactory,
-                          base::Unretained(this), frame_token),
-      io_task_runner_);
-}
-
-void AudioOutputIPCFactory::RegisterRemoteFactory(
-    const base::UnguessableToken& frame_token,
-    blink::BrowserInterfaceBrokerProxy* interface_broker) {
-  mojo::PendingRemote<blink::mojom::RendererAudioOutputStreamFactory>
-      factory_remote;
-  interface_broker->GetInterface(
-      factory_remote.InitWithNewPipeAndPassReceiver());
-  // Unretained is safe due to the contract at the top of the header file.
-  // It's safe to pass the |factory_remote| PendingRemote between threads.
-  io_task_runner_->PostTask(
-      FROM_HERE,
-      base::BindOnce(&AudioOutputIPCFactory::RegisterRemoteFactoryOnIOThread,
-                     base::Unretained(this), frame_token,
-                     std::move(factory_remote)));
-}
-
-void AudioOutputIPCFactory::MaybeDeregisterRemoteFactory(
-    const base::UnguessableToken& frame_token) {
-  io_task_runner_->PostTask(
-      FROM_HERE,
-      base::BindOnce(
-          &AudioOutputIPCFactory::MaybeDeregisterRemoteFactoryOnIOThread,
-          base::Unretained(this), frame_token));
-}
-
-blink::mojom::RendererAudioOutputStreamFactory*
-AudioOutputIPCFactory::GetRemoteFactory(
-    const base::UnguessableToken& frame_token) const {
-  DCHECK(io_task_runner_->BelongsToCurrentThread());
-  auto it = factory_remotes_.find(frame_token);
-  return it == factory_remotes_.end() ? nullptr : it->second.get();
-}
-
-void AudioOutputIPCFactory::RegisterRemoteFactoryOnIOThread(
-    const base::UnguessableToken& frame_token,
-    mojo::PendingRemote<blink::mojom::RendererAudioOutputStreamFactory>
-        factory_pending_remote) {
-  DCHECK(io_task_runner_->BelongsToCurrentThread());
-  std::pair<StreamFactoryMap::iterator, bool> emplace_result =
-      factory_remotes_.emplace(frame_token, std::move(factory_pending_remote));
-
-  DCHECK(emplace_result.second) << "Attempt to register a factory for a "
-                                   "frame which already has a factory "
-                                   "registered.";
-
-  auto& emplaced_factory = emplace_result.first->second;
-  DCHECK(emplaced_factory.is_bound())
-      << "Factory is not bound to a remote implementation.";
-
-  // Unretained is safe because |this| owns the remote, so a connection error
-  // cannot trigger after destruction.
-  emplaced_factory.set_disconnect_handler(base::BindOnce(
-      &AudioOutputIPCFactory::MaybeDeregisterRemoteFactoryOnIOThread,
-      base::Unretained(this), frame_token));
-}
-
-void AudioOutputIPCFactory::MaybeDeregisterRemoteFactoryOnIOThread(
-    const base::UnguessableToken& frame_token) {
-  DCHECK(io_task_runner_->BelongsToCurrentThread());
-  // This function can be called both by the frame and the connection error
-  // handler of the factory remote. Calling erase multiple times even though
-  // there is nothing to erase is safe, so we don't have to handle this in any
-  // particular way.
-  factory_remotes_.erase(frame_token);
-}
-
-}  // namespace content
diff --git a/content/renderer/media/audio/audio_output_ipc_factory.h b/content/renderer/media/audio/audio_output_ipc_factory.h
deleted file mode 100644
index d950e66e..0000000
--- a/content/renderer/media/audio/audio_output_ipc_factory.h
+++ /dev/null
@@ -1,98 +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 CONTENT_RENDERER_MEDIA_AUDIO_AUDIO_OUTPUT_IPC_FACTORY_H_
-#define CONTENT_RENDERER_MEDIA_AUDIO_AUDIO_OUTPUT_IPC_FACTORY_H_
-
-#include <memory>
-
-#include "base/containers/flat_map.h"
-#include "base/memory/ref_counted.h"
-#include "content/common/content_export.h"
-#include "mojo/public/cpp/bindings/pending_remote.h"
-#include "mojo/public/cpp/bindings/remote.h"
-#include "third_party/blink/public/mojom/media/renderer_audio_output_stream_factory.mojom.h"
-
-namespace base {
-class SingleThreadTaskRunner;
-}
-
-namespace media {
-class AudioOutputIPC;
-}
-
-namespace blink {
-class BrowserInterfaceBrokerProxy;
-}
-
-namespace content {
-
-// This is a factory for AudioOutputIPC objects. It is threadsafe. This class
-// is designed to be leaked at shutdown, as it posts tasks to itself using
-// base::Unretained and also hands out references to itself in the
-// AudioOutputIPCs it creates, but in the case where the owner is sure that
-// there are no outstanding references (such as in a unit test), the class can
-// be destructed.
-// TODO(maxmorin): Registering the factories for each frame will become
-// unnecessary when https://crbug.com/668275 is fixed. When that is done, this
-// class can be greatly simplified.
-class CONTENT_EXPORT AudioOutputIPCFactory {
- public:
-  AudioOutputIPCFactory(
-      scoped_refptr<base::SingleThreadTaskRunner> io_task_runner);
-  ~AudioOutputIPCFactory();
-
-  static AudioOutputIPCFactory* get() { return instance_; }
-
-  const scoped_refptr<base::SingleThreadTaskRunner>& io_task_runner() const {
-    return io_task_runner_;
-  }
-
-  // Enables |this| to create MojoAudioOutputIPCs for the specified frame.
-  // Does nothing if not using mojo factories.
-  void RegisterRemoteFactory(
-      const base::UnguessableToken& frame_token,
-      blink::BrowserInterfaceBrokerProxy* interface_broker);
-
-  // Every call to the above method must be matched by a call to this one when
-  // the frame is destroyed. Does nothing if not using mojo factories.
-  void MaybeDeregisterRemoteFactory(const base::UnguessableToken& frame_token);
-
-  // The returned object may only be used on |io_task_runner()|.
-  std::unique_ptr<media::AudioOutputIPC> CreateAudioOutputIPC(
-      const base::UnguessableToken& frame_token) const;
-
- private:
-  using StreamFactoryMap = base::flat_map<
-      base::UnguessableToken,
-      mojo::Remote<blink::mojom::RendererAudioOutputStreamFactory>>;
-
-  blink::mojom::RendererAudioOutputStreamFactory* GetRemoteFactory(
-      const base::UnguessableToken& frame_token) const;
-
-  void RegisterRemoteFactoryOnIOThread(
-      const base::UnguessableToken& frame_token,
-      mojo::PendingRemote<blink::mojom::RendererAudioOutputStreamFactory>
-          factory_pending_remote);
-
-  void MaybeDeregisterRemoteFactoryOnIOThread(
-      const base::UnguessableToken& frame_token);
-
-  // Indicates whether mojo factories are used.
-  bool UsingMojoFactories() const;
-
-  // Maps frame id to the corresponding factory.
-  StreamFactoryMap factory_remotes_;
-
-  const scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_;
-
-  // Global instance, set in constructor and unset in destructor.
-  static AudioOutputIPCFactory* instance_;
-
-  DISALLOW_COPY_AND_ASSIGN(AudioOutputIPCFactory);
-};
-
-}  // namespace content
-
-#endif  // CONTENT_RENDERER_MEDIA_AUDIO_AUDIO_OUTPUT_IPC_FACTORY_H_
diff --git a/content/renderer/pepper/pepper_platform_audio_output.cc b/content/renderer/pepper/pepper_platform_audio_output.cc
index e1f5a45d..b6160a8a 100644
--- a/content/renderer/pepper/pepper_platform_audio_output.cc
+++ b/content/renderer/pepper/pepper_platform_audio_output.cc
@@ -12,9 +12,9 @@
 #include "base/threading/thread_task_runner_handle.h"
 #include "build/build_config.h"
 #include "content/child/child_process.h"
-#include "content/renderer/media/audio/audio_output_ipc_factory.h"
 #include "content/renderer/pepper/audio_helper.h"
 #include "ppapi/shared_impl/ppb_audio_config_shared.h"
+#include "third_party/blink/public/web/modules/media/audio/web_audio_output_ipc_factory.h"
 
 namespace content {
 
@@ -136,7 +136,8 @@
   DCHECK(client);
   client_ = client;
 
-  ipc_ = AudioOutputIPCFactory::get()->CreateAudioOutputIPC(source_frame_token);
+  ipc_ = blink::WebAudioOutputIPCFactory::get()->CreateAudioOutputIPC(
+      source_frame_token);
   CHECK(ipc_);
 
   media::AudioParameters params(media::AudioParameters::AUDIO_PCM_LOW_LATENCY,
diff --git a/content/renderer/pepper/pepper_platform_audio_output_dev.cc b/content/renderer/pepper/pepper_platform_audio_output_dev.cc
index 08c620e..9d1b5ff 100644
--- a/content/renderer/pepper/pepper_platform_audio_output_dev.cc
+++ b/content/renderer/pepper/pepper_platform_audio_output_dev.cc
@@ -14,13 +14,13 @@
 #include "build/build_config.h"
 #include "content/child/child_process.h"
 #include "content/common/content_constants_internal.h"
-#include "content/renderer/media/audio/audio_output_ipc_factory.h"
 #include "content/renderer/pepper/audio_helper.h"
 #include "content/renderer/pepper/pepper_audio_output_host.h"
 #include "content/renderer/pepper/pepper_media_device_manager.h"
 #include "content/renderer/render_frame_impl.h"
 #include "media/audio/audio_device_description.h"
 #include "ppapi/shared_impl/ppb_audio_config_shared.h"
+#include "third_party/blink/public/web/modules/media/audio/web_audio_output_ipc_factory.h"
 #include "third_party/blink/public/web/web_local_frame.h"
 
 namespace {
@@ -260,7 +260,7 @@
 
   client_ = client;
 
-  ipc_ = AudioOutputIPCFactory::get()->CreateAudioOutputIPC(
+  ipc_ = blink::WebAudioOutputIPCFactory::get()->CreateAudioOutputIPC(
       render_frame->GetWebFrame()->GetFrameToken());
   CHECK(ipc_);
 
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc
index 64135cd..2b9f3b0b 100644
--- a/content/renderer/render_frame_impl.cc
+++ b/content/renderer/render_frame_impl.cc
@@ -60,7 +60,6 @@
 #include "content/common/navigation_params.h"
 #include "content/common/navigation_params_mojom_traits.h"
 #include "content/common/navigation_params_utils.h"
-#include "content/common/net/record_load_histograms.h"
 #include "content/common/page_messages.h"
 #include "content/common/render_accessibility.mojom.h"
 #include "content/common/renderer_host.mojom.h"
@@ -110,7 +109,6 @@
 #include "content/renderer/loader/web_url_request_util.h"
 #include "content/renderer/loader/web_worker_fetch_context_impl.h"
 #include "content/renderer/media/audio/audio_device_factory.h"
-#include "content/renderer/media/audio/audio_output_ipc_factory.h"
 #include "content/renderer/media/audio/audio_renderer_sink_cache.h"
 #include "content/renderer/media/media_permission_dispatcher.h"
 #include "content/renderer/mhtml_handle_writer.h"
@@ -158,6 +156,7 @@
 #include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
 #include "third_party/blink/public/common/features.h"
 #include "third_party/blink/public/common/input/web_keyboard_event.h"
+#include "third_party/blink/public/common/loader/record_load_histograms.h"
 #include "third_party/blink/public/common/loader/url_loader_throttle.h"
 #include "third_party/blink/public/common/logging/logging_utils.h"
 #include "third_party/blink/public/common/service_worker/service_worker_utils.h"
@@ -188,6 +187,7 @@
 #include "third_party/blink/public/platform/web_url_response.h"
 #include "third_party/blink/public/platform/web_vector.h"
 #include "third_party/blink/public/web/blink.h"
+#include "third_party/blink/public/web/modules/media/audio/web_audio_output_ipc_factory.h"
 #include "third_party/blink/public/web/modules/media/webmediaplayer_util.h"
 #include "third_party/blink/public/web/modules/mediastream/web_media_stream_device_observer.h"
 #include "third_party/blink/public/web/web_autofill_client.h"
@@ -2040,8 +2040,8 @@
   // embedder can call GetWebFrame on any RenderFrame.
   GetContentClient()->renderer()->RenderFrameCreated(this);
 
-  // AudioOutputIPCFactory may be null in tests.
-  if (auto* factory = AudioOutputIPCFactory::get())
+  // blink::WebAudioOutputIPCFactory may be null in tests.
+  if (auto* factory = blink::WebAudioOutputIPCFactory::get())
     factory->RegisterRemoteFactory(GetWebFrame()->GetFrameToken(),
                                    GetBrowserInterfaceBroker());
 
@@ -3063,9 +3063,9 @@
 void RenderFrameImpl::NotifyResourceLoadCompleted(
     blink::mojom::ResourceLoadInfoPtr resource_load_info,
     const network::URLLoaderCompletionStatus& status) {
-  RecordLoadHistograms(url::Origin::Create(resource_load_info->final_url),
-                       resource_load_info->request_destination,
-                       status.error_code);
+  blink::RecordLoadHistograms(
+      url::Origin::Create(resource_load_info->final_url),
+      resource_load_info->request_destination, status.error_code);
   DidCompleteResponse(resource_load_info->request_id, status);
   GetFrameHost()->ResourceLoadComplete(std::move(resource_load_info));
 }
@@ -4160,7 +4160,7 @@
   for (auto& observer : observers_)
     observer.WillDetach();
 
-  if (auto* factory = AudioOutputIPCFactory::get())
+  if (auto* factory = blink::WebAudioOutputIPCFactory::get())
     factory->MaybeDeregisterRemoteFactory(GetWebFrame()->GetFrameToken());
 
   // Send a state update before the frame is detached.
@@ -4417,8 +4417,8 @@
     // RenderFrameHostImpl.
     browser_interface_broker_receiver = browser_interface_broker_proxy_.Reset();
 
-    // AudioOutputIPCFactory may be null in tests.
-    if (auto* factory = AudioOutputIPCFactory::get()) {
+    // blink::WebAudioOutputIPCFactory may be null in tests.
+    if (auto* factory = blink::WebAudioOutputIPCFactory::get()) {
       // The RendererAudioOutputStreamFactory must be readily accessible on the
       // IO thread when it's needed, because the main thread may block while
       // waiting for the factory call to finish on the IO thread, so if we tried
diff --git a/content/renderer/render_thread_impl.h b/content/renderer/render_thread_impl.h
index 6721d865..51f5c9d 100644
--- a/content/renderer/render_thread_impl.h
+++ b/content/renderer/render_thread_impl.h
@@ -41,7 +41,6 @@
 #include "content/renderer/compositor/compositor_dependencies.h"
 #include "content/renderer/discardable_memory_utils.h"
 #include "content/renderer/media/audio/audio_input_ipc_factory.h"
-#include "content/renderer/media/audio/audio_output_ipc_factory.h"
 #include "gpu/ipc/client/gpu_channel_host.h"
 #include "ipc/ipc_sync_channel.h"
 #include "media/media_buildflags.h"
@@ -61,6 +60,7 @@
 #include "third_party/blink/public/platform/scheduler/web_rail_mode_observer.h"
 #include "third_party/blink/public/platform/scheduler/web_thread_scheduler.h"
 #include "third_party/blink/public/platform/web_connection_type.h"
+#include "third_party/blink/public/web/modules/media/audio/web_audio_output_ipc_factory.h"
 #include "third_party/blink/public/web/web_memory_statistics.h"
 #include "ui/gfx/native_widget_types.h"
 
@@ -523,7 +523,7 @@
   base::Optional<AudioInputIPCFactory> audio_input_ipc_factory_;
   // Provides AudioOutputIPC objects for audio output devices. Initialized in
   // Init.
-  base::Optional<AudioOutputIPCFactory> audio_output_ipc_factory_;
+  base::Optional<blink::WebAudioOutputIPCFactory> audio_output_ipc_factory_;
 
   // Used on the render thread.
   std::unique_ptr<blink::WebVideoCaptureImplManager> vc_manager_;
diff --git a/content/shell/renderer/web_test/web_ax_object_proxy.cc b/content/shell/renderer/web_test/web_ax_object_proxy.cc
index 1cc6896..ceaccee 100644
--- a/content/shell/renderer/web_test/web_ax_object_proxy.cc
+++ b/content/shell/renderer/web_test/web_ax_object_proxy.cc
@@ -261,8 +261,6 @@
       return result.append("Math");
     case ax::mojom::Role::kMenuBar:
       return result.append("MenuBar");
-    case ax::mojom::Role::kMenuButton:
-      return result.append("MenuButton");
     case ax::mojom::Role::kMenuItem:
       return result.append("MenuItem");
     case ax::mojom::Role::kMenuItemCheckBox:
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index 0635d10..15bad59 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -2044,13 +2044,11 @@
     "../renderer/loader/test_request_peer.h",
     "../renderer/loader/url_loader_client_impl_unittest.cc",
     "../renderer/loader/web_url_loader_impl_unittest.cc",
-    "../renderer/media/audio/audio_output_ipc_factory_unittest.cc",
     "../renderer/media/audio/audio_renderer_mixer_manager_unittest.cc",
     "../renderer/media/audio/audio_renderer_sink_cache_unittest.cc",
     "../renderer/media/audio/mock_audio_device_factory.cc",
     "../renderer/media/audio/mock_audio_device_factory.h",
     "../renderer/media/audio/mojo_audio_input_ipc_unittest.cc",
-    "../renderer/media/audio/mojo_audio_output_ipc_unittest.cc",
     "../renderer/media/batching_media_log_unittest.cc",
     "../renderer/media/inspector_media_event_handler_unittest.cc",
     "../renderer/media/power_status_helper_impl_unittest.cc",
diff --git a/content/test/data/accessibility/aria/aria-menuitem-in-group-expected-auralinux.txt b/content/test/data/accessibility/aria/aria-menuitem-in-group-expected-auralinux.txt
index 4de62c6..25423dc9 100644
--- a/content/test/data/accessibility/aria/aria-menuitem-in-group-expected-auralinux.txt
+++ b/content/test/data/accessibility/aria/aria-menuitem-in-group-expected-auralinux.txt
@@ -1,3 +1,3 @@
 [document web]
 ++[panel]
-++++[menu item] name='Menu button'
\ No newline at end of file
+++++[menu item] name='Menu item'
diff --git a/content/test/data/accessibility/aria/aria-menuitem-in-group-expected-blink.txt b/content/test/data/accessibility/aria/aria-menuitem-in-group-expected-blink.txt
index 0632a3f..61558b9 100644
--- a/content/test/data/accessibility/aria/aria-menuitem-in-group-expected-blink.txt
+++ b/content/test/data/accessibility/aria/aria-menuitem-in-group-expected-blink.txt
@@ -2,4 +2,4 @@
 ++genericContainer ignored
 ++++genericContainer ignored
 ++++++group
-++++++++menuButton name='Menu button'
+++++++++menuItem name='Menu item'
diff --git a/content/test/data/accessibility/aria/aria-menuitem-in-group-expected-mac.txt b/content/test/data/accessibility/aria/aria-menuitem-in-group-expected-mac.txt
index e7643190..fa05943 100644
--- a/content/test/data/accessibility/aria/aria-menuitem-in-group-expected-mac.txt
+++ b/content/test/data/accessibility/aria/aria-menuitem-in-group-expected-mac.txt
@@ -1,3 +1,3 @@
 AXWebArea
 ++AXGroup
-++++AXMenuButton AXTitle='Menu button'
+++++AXMenuItem AXTitle='Menu item'
diff --git a/content/test/data/accessibility/aria/aria-menuitem-in-group-expected-uia-win.txt b/content/test/data/accessibility/aria/aria-menuitem-in-group-expected-uia-win.txt
index 4f3320c..40276bc3 100644
--- a/content/test/data/accessibility/aria/aria-menuitem-in-group-expected-uia-win.txt
+++ b/content/test/data/accessibility/aria/aria-menuitem-in-group-expected-uia-win.txt
@@ -1,3 +1,3 @@
 Document
 ++Group
-++++MenuItem Name='Menu button'
\ No newline at end of file
+++++MenuItem Name='Menu item'
diff --git a/content/test/data/accessibility/aria/aria-menuitem-in-group-expected-win.txt b/content/test/data/accessibility/aria/aria-menuitem-in-group-expected-win.txt
index fb03bc8..38ee260d 100644
--- a/content/test/data/accessibility/aria/aria-menuitem-in-group-expected-win.txt
+++ b/content/test/data/accessibility/aria/aria-menuitem-in-group-expected-win.txt
@@ -1,3 +1,3 @@
 ROLE_SYSTEM_DOCUMENT READONLY FOCUSABLE
 ++ROLE_SYSTEM_GROUPING xml-roles:group
-++++ROLE_SYSTEM_MENUITEM name='Menu button' FOCUSABLE xml-roles:menuitem
+++++ROLE_SYSTEM_MENUITEM name='Menu item' FOCUSABLE xml-roles:menuitem
diff --git a/content/test/data/accessibility/aria/aria-menuitem-in-group.html b/content/test/data/accessibility/aria/aria-menuitem-in-group.html
index b064bcd..170ef6b1 100644
--- a/content/test/data/accessibility/aria/aria-menuitem-in-group.html
+++ b/content/test/data/accessibility/aria/aria-menuitem-in-group.html
@@ -4,7 +4,7 @@
 <html>
   <body>
     <div role="group">
-      <div tabindex="0" role="menuitem">Menu button</div>
+      <div tabindex="0" role="menuitem">Menu item</div>
     </div>
   </body>
 </html>
diff --git a/content/test/data/accessibility/html/modal-dialog-opened-expected-uia-win.txt b/content/test/data/accessibility/html/modal-dialog-opened-expected-uia-win.txt
index d0b1c1a..8ef172d7 100644
--- a/content/test/data/accessibility/html/modal-dialog-opened-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/modal-dialog-opened-expected-uia-win.txt
@@ -3,4 +3,4 @@
 ++++Text Name='The dialog subtree should be the only text content in the accessibility tree. '
 ++++Hyperlink Name='Link inside the dialog.'
 ++++++Text Name='Link inside the dialog.' IsControlElement=false
-++Menu IsControlElement=false Selection.CanSelectMultiple=false Selection.IsSelectionRequired=false
\ No newline at end of file
+++List IsControlElement=false Selection.CanSelectMultiple=false Selection.IsSelectionRequired=false
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/modal-dialog-opened-expected-win.txt b/content/test/data/accessibility/html/modal-dialog-opened-expected-win.txt
index da520f6..5d9fd4d 100644
--- a/content/test/data/accessibility/html/modal-dialog-opened-expected-win.txt
+++ b/content/test/data/accessibility/html/modal-dialog-opened-expected-win.txt
@@ -3,4 +3,4 @@
 ++++ROLE_SYSTEM_STATICTEXT name='The dialog subtree should be the only text content in the accessibility tree. '
 ++++ROLE_SYSTEM_LINK name='Link inside the dialog.' FOCUSABLE
 ++++++ROLE_SYSTEM_STATICTEXT name='Link inside the dialog.'
-++ROLE_SYSTEM_MENUPOPUP INVISIBLE
\ No newline at end of file
+++ROLE_SYSTEM_LIST INVISIBLE
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/modal-dialog-stack-expected-uia-win.txt b/content/test/data/accessibility/html/modal-dialog-stack-expected-uia-win.txt
index 8e3f560c..64e0b93 100644
--- a/content/test/data/accessibility/html/modal-dialog-stack-expected-uia-win.txt
+++ b/content/test/data/accessibility/html/modal-dialog-stack-expected-uia-win.txt
@@ -1,5 +1,5 @@
 Document
-++Menu IsControlElement=false Selection.CanSelectMultiple=false Selection.IsSelectionRequired=false
+++List IsControlElement=false Selection.CanSelectMultiple=false Selection.IsSelectionRequired=false
 ++Pane IsControlElement=false Window.IsModal=true
 ++++Text Name='This is the now active dialog. Of course it should be in the tree. '
 ++++Button Name='This is in the active dialog and should be in the tree.'
\ No newline at end of file
diff --git a/content/test/data/accessibility/html/modal-dialog-stack-expected-win.txt b/content/test/data/accessibility/html/modal-dialog-stack-expected-win.txt
index f49562a..fdedce904 100644
--- a/content/test/data/accessibility/html/modal-dialog-stack-expected-win.txt
+++ b/content/test/data/accessibility/html/modal-dialog-stack-expected-win.txt
@@ -1,5 +1,5 @@
 ROLE_SYSTEM_DOCUMENT READONLY FOCUSABLE
-++ROLE_SYSTEM_MENUPOPUP INVISIBLE
+++ROLE_SYSTEM_LIST INVISIBLE
 ++ROLE_SYSTEM_DIALOG IA2_STATE_MODAL
 ++++ROLE_SYSTEM_STATICTEXT name='This is the now active dialog. Of course it should be in the tree. '
 ++++ROLE_SYSTEM_PUSHBUTTON name='This is in the active dialog and should be in the tree.' FOCUSABLE
\ No newline at end of file
diff --git a/docs/README.md b/docs/README.md
index 90753e4..9d2af07 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -154,6 +154,8 @@
     chrome:// URLs).
 *   [Watchlists](infra/watchlists.md) - Use watchlists to get notified of CLs
     you are interested in.
+*   [Shutdown](shutdown.md) - Explains the steps of Chrome shutdown, to make it
+    easier to determine where to add a new shutdown operation.
 
 ### Testing
 *   [Running and Debugging Web Tests](testing/web_tests.md)
diff --git a/docs/shutdown.md b/docs/shutdown.md
new file mode 100644
index 0000000..4165def
--- /dev/null
+++ b/docs/shutdown.md
@@ -0,0 +1,96 @@
+# Chrome Shutdown
+
+[TOC]
+
+This documents shutdown steps on Windows, Mac and Linux.
+
+On Android, the system can terminate the Chrome app at any point without running
+any shutdown step.
+
+TODO: Document ChromeOS shutdown.
+
+## Step 1: Exiting the main loop
+
+Shutdown starts when nothing keeps Chrome alive. Typically, this happens when
+all browser windows are closed, but other things can [keep Chrome
+alive](https://source.chromium.org/chromium/chromium/src/+/master:components/keep_alive_registry/keep_alive_types.h).
+
+When nothing keeps Chrome alive, `BrowserProcessImpl::Unpin` asks the main
+thread's message loop to quit as soon as it no longer has tasks ready to run
+immediately.
+
+```
+base::RunLoop::QuitWhenIdle
+…
+BrowserProcessImpl::Unpin
+BrowserProcessImpl::OnKeepAliveStateChanged
+KeepAliveRegistry::OnKeepAliveStateChanged
+KeepAliveRegistry::Unregister
+ScopedKeepAlive::~ScopedKeepAlive
+...
+Browser::UnregisterKeepAlive
+BrowserList::RemoveBrowser
+Browser::~Browser
+```
+
+Following this request, `ChromeBrowserMainParts::MainMessageLoopRun` exits. Tasks
+posted to the main thread without a delay prior to this point are guaranteed to
+have run; tasks posted to the main thread after this point will never run.
+
+## Step 2: Cleaning up, after main loop exit
+
+`BrowserMainRunnerImpl::Shutdown` is called on the main thread. Within that
+method, `BrowserMainLoop::ShutdownThreadsAndCleanUp` orchestrates the main
+shutdown steps.
+
+`ChromeBrowserMainParts::PostMainMessageLoopRun` is invoked. It invokes the
+`PostMainMessageLoopRun` method of each `ChromeBrowserMainExtraParts` instance.
+This is a good place to perform shutdown steps of a component that require the
+IO thread, the `ThreadPool` or the `Profile` to still be available.
+
+`ChromeBrowserMainParts::PostMainMessageLoopRun` also invokes
+`BrowserProcessImpl::StartTearDown` which deletes many services owned by
+`BrowserProcessImpl` (aka `g_browser_process`). One of these services is the
+`ProfileManager`. Deleting the `ProfileManager` deletes `Profiles`. As part of
+deleting a `Profile`, its `KeyedServices` are deleted, including:
+
+* Sync Service
+* History Service
+
+## Step 3: Joining other threads
+
+The IO thread is joined. No IPC or Mojo can be received after this.
+
+`ThreadPool` shutdown starts. At this point, no new `SKIP_ON_SHUTDOWN` or
+`CONTINUE_ON_SHUTDOWN` task can start running (they are deleted without
+running). The main thread blocks until all `SKIP_ON_SHUTDOWN` tasks that started
+running prior to `ThreadPool` shutdown start are complete, and all
+`BLOCK_SHUTDOWN` tasks are complete (irrespective of whether they were posted
+before or after `ThreadPool` shutdown start). When no more `SKIP_ON_SHUTDOWN` is
+running and no more `BLOCK_SHUTDOWN` task is queued or running, the main thread
+is unblocked and `ThreadPool` shutdown is considered complete. Note:
+`CONTINUE_ON_SHUTDOWN` tasks that started before `ThreadPool` shutdown may still
+be running.
+
+At this point, new tasks posted to the IO thread or to the `ThreadPool` cannot
+run. It is illegal to post a `BLOCK_SHUTDOWN` task to the `ThreadPool` (enforced
+by a `DCHECK`).
+
+## Step 4: Cleaning up, after joining other threads
+
+`ChromeBrowserMainParts::PostDestroyThreads` is invoked. It invokes
+`BrowserProcessImpl::PostDestroyThreads`. Since it is guaranteed that no
+`SKIP_ON_SHUTDOWN` or `BLOCK_SHUTDOWN` task is running at this point, it is a
+good place to delete objects accessed directly from these tasks.
+
+Then, if a new Chrome executable, it is swapped with the current one
+(Windows-only).
+
+```
+upgrade_util::SwapNewChromeExeIfPresent
+browser_shutdown::ShutdownPostThreadsStop
+ChromeBrowserMainParts::PostDestroyThreads
+content::BrowserMainLoop::ShutdownThreadsAndCleanUp
+content::BrowserMainLoop::ShutdownThreadsAndCleanUp
+content::BrowserMainRunnerImpl::Shutdown
+```
diff --git a/docs/speed/metrics_changelog/2020_06_cls.md b/docs/speed/metrics_changelog/2020_06_cls.md
new file mode 100644
index 0000000..52813fa
--- /dev/null
+++ b/docs/speed/metrics_changelog/2020_06_cls.md
@@ -0,0 +1,21 @@
+# Cumulative Layout Shift Changes in M85
+
+## Changes in Chrome 85
+Prior to Chrome 85, there was a [bug](https://bugs.chromium.org/p/chromium/issues/detail?id=1088311)
+in Cumulative Layout Shift on pages with video elements. Hovering over the video
+element so that the thumb slider was visible would result in layout shifts when
+it moved. The bug was fixed in Chrome 85. The source code of the change can be
+seen [here](https://chromium-review.googlesource.com/c/chromium/src/+/2233310).
+
+## How does this affect a site's metrics?
+
+This change only affects metrics for a very small amount of sites. Desktop sites
+with video elements that users hover their mouse over for an extended period of
+time will have lower CLS values starting in Chrome 85.
+
+We do not see an impact from this change in our overall metrics, so we believe
+the effect on most sites will be minimal.
+
+## When were users affected?
+
+Most users were updated to Chrome 85 the week of August 24, 2020.
diff --git a/docs/speed/metrics_changelog/2020_06_fcp.md b/docs/speed/metrics_changelog/2020_06_fcp.md
new file mode 100644
index 0000000..3ec1798
--- /dev/null
+++ b/docs/speed/metrics_changelog/2020_06_fcp.md
@@ -0,0 +1,21 @@
+# First Contentful Paint Changes in M84
+
+## Changes in Chrome 84
+Starting in Chrome 84, content with opacity:0 is no longer counted as the
+first contentful paint. This brings behavior in line with the
+[specification](https://www.w3.org/TR/paint-timing/).
+The source code of the change can be seen
+[here](https://chromium-review.googlesource.com/c/chromium/src/+/2145134).
+
+## How does this affect a site's metrics?
+
+This change affects sites whose content is all opacity:0 during the first paint.
+After this change, the first contentful paint will be reported the next time
+visible content paints.
+
+We do not see an impact from this change in our overall metrics, so we believe
+the effect on most sites will be minimal.
+
+## When were users affected?
+
+Most users were updated to Chrome 84 the week of July 13, 2020.
diff --git a/docs/speed/metrics_changelog/2020_07_fcp.md b/docs/speed/metrics_changelog/2020_07_fcp.md
new file mode 100644
index 0000000..94bdbaff
--- /dev/null
+++ b/docs/speed/metrics_changelog/2020_07_fcp.md
@@ -0,0 +1,24 @@
+# First Contentful Paint Changes in M86
+
+## Changes in Chrome 86
+In Chrome 86, some small changes were made to First Contentful Paint to bring
+its implementation in line with the [specification](https://www.w3.org/TR/paint-timing/).
+
+The changes are:
+ * Video elements now trigger FCP when painted. [Source code for this change](https://chromium-review.googlesource.com/c/chromium/src/+/2276244).
+ * Only SVG elements with content now trigger FCP. Previously empty SVG paints triggered SVG. [Source code for this change](https://chromium-review.googlesource.com/c/chromium/src/+/2285532).
+ * WebGL2 canvases now trigger FCP when painted. [Source code for this change](https://chromium-review.googlesource.com/c/chromium/src/+/2348694).
+
+## How does this affect a site's metrics?
+
+This change affects a small set of sites:
+ * Sites with a first contentful paint which is only a video element without a `poster` attribute will have an earlier FCP time.
+ * Sites with a first contentful paint which is only an empty SVG element will have a later FCP time.
+ * Sites with a first contentful paint which is only a WebGL canvas will have an earlier FCP time.
+
+We do not see an impact from this change in our overall metrics, so we believe
+the effect on most sites will be minimal.
+
+## When were users affected?
+
+Most users were updated to Chrome 86 the week of October 5, 2020.
diff --git a/docs/speed/metrics_changelog/2020_08_lcp.md b/docs/speed/metrics_changelog/2020_08_lcp.md
new file mode 100644
index 0000000..b9170b5
--- /dev/null
+++ b/docs/speed/metrics_changelog/2020_08_lcp.md
@@ -0,0 +1,23 @@
+# Largest Contentful Paint Changes in M86
+
+## Changes in Chrome 86
+Prior to Chrome 86, there was a [bug](https://bugs.chromium.org/p/chromium/issues/detail?id=1092473)
+in Largest Contentful Paint on some pages where the largest contentful element
+initially has opacity:0. The bug was fixed in Chrome 86. The source code of the
+change can be seen [here](https://chromium-review.googlesource.com/c/chromium/src/+/2316788).
+
+## How does this affect a site's metrics?
+
+This change only affects metrics for a very small amount of sites. Generally
+sites whose largest contentful elements are opacity:0 initially are using A/B
+testing frameworks which initially clear the page before displaying the correct
+content. Sites using this technique will now see a longer LCP, reflecting the
+time until the largest contentful paint is visible to the user, instead of the
+time it is loaded in the DOM but invisible.
+
+We do not see an impact from this change in our overall metrics, so we believe
+the effect on most sites will be minimal.
+
+## When were users affected?
+
+Most users were updated to Chrome 86 the week of October 5, 2020.
diff --git a/docs/speed/metrics_changelog/cls.md b/docs/speed/metrics_changelog/cls.md
index b2fd866..a2d8fa1 100644
--- a/docs/speed/metrics_changelog/cls.md
+++ b/docs/speed/metrics_changelog/cls.md
@@ -8,6 +8,8 @@
     * an element and its descendants if they move together, and
     * inline elements and texts in a block after a shifted text.
   These changes will affect layout instability score for the specific cases.
+* Chrome 85
+  * Metric definition improvement: [Cumulative Layout Shift ignores layout shifts from video slider thumb](2020_06_cls.md)
 * Chrome 79
   * Metric is elevated to stable; changes in metric definition will be reported in this log.
 * Chrome 77
diff --git a/docs/speed/metrics_changelog/fcp.md b/docs/speed/metrics_changelog/fcp.md
index 1d8545c46..ef2c9f4 100644
--- a/docs/speed/metrics_changelog/fcp.md
+++ b/docs/speed/metrics_changelog/fcp.md
@@ -2,8 +2,12 @@
 
 This is a list of changes to [First Contentful Paint](https://web.dev/fcp).
 
+* Chrome 86
+  * Metric definition improvements: [First Contentful Paint reported for videos, webgl2 canvases, and non-contentful SVG](2020_07_fcp.md)
+* Chrome 84
+  * Metric definition improvement: [First Contentful Paint not reported for opacity:0](2020_06_fcp.md)
 * Chrome 77
   * Metric definition improvement: [First Contentful Paint ending switches from swap time to presentation time](2019_12_fcp.md)
   * Chrome performance regression: [First Contentful Paint regression (recovered in Chrome 78)](2019_12_fcp.md)
 * Chrome 60
-  * Metric exposed via API: [First Contentful Paint](https://web.dev/first-contentful-paint/) available via [Paint Timing API](https://w3c.github.io/paint-timing/#first-contentful-paint)
\ No newline at end of file
+  * Metric exposed via API: [First Contentful Paint](https://web.dev/first-contentful-paint/) available via [Paint Timing API](https://w3c.github.io/paint-timing/#first-contentful-paint)
diff --git a/docs/speed/metrics_changelog/lcp.md b/docs/speed/metrics_changelog/lcp.md
index 80aabcf..5d114ddf4 100644
--- a/docs/speed/metrics_changelog/lcp.md
+++ b/docs/speed/metrics_changelog/lcp.md
@@ -2,6 +2,8 @@
 
 This is a list of changes to [Largest Contentful Paint](https://web.dev/lcp).
 
+* Chrome 86
+  * Metric definition improvement: [Largest Contentful Paint ignores paints with opacity 0](2020_08_lcp.md)
 * Chrome 83
   * Metric definition improvement: [Largest Contentful Paint measurement stops at first input or scroll](2020_05_lcp.md)
   * Metric definition improvement: [Largest Contentful Paint properly accounts for visual size of background images](2020_05_lcp.md)
diff --git a/docs/updating_clang.md b/docs/updating_clang.md
index e32b7ca..05737095 100644
--- a/docs/updating_clang.md
+++ b/docs/updating_clang.md
@@ -7,7 +7,9 @@
 An archive of all packages built so far is at https://is.gd/chromeclang
 
 1.  Check that https://ci.chromium.org/p/chromium/g/chromium.clang/console
-    looks reasonably green.
+    looks reasonably green. Red bots with seemingly normal test failures are
+    usually ok, that likely means the test is broken with the stable Clang as
+    well.
 1.  Sync your Chromium tree to the latest revision to pick up any plugin
     changes.
 1.  Run [go/chrome-push-clang-to-goma](https://goto.google.com/chrome-push-clang-to-goma).
@@ -18,6 +20,12 @@
     https://crbug.com/1034081). Then it will push the packages to goma. If you
     do not have the necessary credentials to do the upload, ask
     clang@chromium.org to find someone who does.
+    *   Alternatively, to create your own roll CL, you can manually run
+	`tools/clang/scripts/upload_revision.py` with a recent upstream LLVM
+	commit hash as the argument. After the `*_upload_clang` trybots are
+	successfully finished, run
+	[go/chrome-promote-clang](https://goto.google.com/chrome-promote-clang)
+	on the new Clang package name.
 1.  Run an exhaustive set of try jobs to test the new compiler. The CL
     description created previously by upload_revision.py includes
     `Cq-Include-Trybots:` lines for all needed bots, so it's sufficient to just
diff --git a/extensions/common/api/automation.idl b/extensions/common/api/automation.idl
index c6ef2e9c..938092ad 100644
--- a/extensions/common/api/automation.idl
+++ b/extensions/common/api/automation.idl
@@ -211,7 +211,6 @@
     math,
     menu,
     menuBar,
-    menuButton,
     menuItem,
     menuItemCheckBox,
     menuItemRadio,
diff --git a/gpu/command_buffer/common/gpu_memory_buffer_support.cc b/gpu/command_buffer/common/gpu_memory_buffer_support.cc
index c268bc1..388de9f4 100644
--- a/gpu/command_buffer/common/gpu_memory_buffer_support.cc
+++ b/gpu/command_buffer/common/gpu_memory_buffer_support.cc
@@ -55,7 +55,7 @@
 uint32_t GetPlatformSpecificTextureTarget() {
 #if defined(OS_MAC)
   return macos_specific_texture_target;
-#elif defined(OS_ANDROID) || defined(OS_LINUX)
+#elif defined(OS_ANDROID) || defined(OS_LINUX) || defined(OS_CHROMEOS)
   return GL_TEXTURE_EXTERNAL_OES;
 #elif defined(OS_WIN) || defined(OS_FUCHSIA)
   return GL_TEXTURE_2D;
@@ -85,7 +85,7 @@
 
 GPU_EXPORT bool NativeBufferNeedsPlatformSpecificTextureTarget(
     gfx::BufferFormat format) {
-#if defined(USE_OZONE) || defined(OS_LINUX)
+#if defined(USE_OZONE) || defined(OS_LINUX) || defined(OS_CHROMEOS)
   // Always use GL_TEXTURE_2D as the target for RGB textures.
   // https://crbug.com/916728
   if (format == gfx::BufferFormat::R_8 || format == gfx::BufferFormat::RG_88 ||
diff --git a/gpu/command_buffer/tests/gl_gpu_memory_buffer_unittest.cc b/gpu/command_buffer/tests/gl_gpu_memory_buffer_unittest.cc
index 8f9eb62b..4dd9c92 100644
--- a/gpu/command_buffer/tests/gl_gpu_memory_buffer_unittest.cc
+++ b/gpu/command_buffer/tests/gl_gpu_memory_buffer_unittest.cc
@@ -29,7 +29,7 @@
 #include "ui/gl/gl_image.h"
 #include "ui/gl/test/gl_image_test_support.h"
 
-#if defined(OS_LINUX)
+#if defined(OS_LINUX) || defined(OS_CHROMEOS)
 #include "gpu/ipc/common/gpu_memory_buffer_impl_native_pixmap.h"
 #include "ui/gfx/linux/client_native_pixmap_factory_dmabuf.h"
 #endif
@@ -72,7 +72,7 @@
   GLManager gl_;
 };
 
-#if defined(OS_LINUX)
+#if defined(OS_LINUX) || defined(OS_CHROMEOS)
 class GpuMemoryBufferTestEGL : public testing::Test,
                                public gpu::GpuCommandBufferTestEGL {
  public:
@@ -90,7 +90,7 @@
   bool egl_initialized_{false};
   std::unique_ptr<gfx::ClientNativePixmapFactory> native_pixmap_factory_;
 };
-#endif  // defined(OS_LINUX)
+#endif  // defined(OS_LINUX) || defined(OS_CHROMEOS)
 
 namespace {
 
@@ -361,7 +361,7 @@
   glDeleteTextures(1, &texture_id);
 }
 
-#if defined(OS_LINUX)
+#if defined(OS_LINUX) || defined(OS_CHROMEOS)
 // Test glCreateImageCHROMIUM with gfx::NATIVE_PIXMAP. Basically the test
 // reproduces the situation where some dmabuf fds are available outside the
 // gpu process and the user wants to import them using glCreateImageCHROMIUM.
@@ -475,7 +475,7 @@
   glDestroyImageCHROMIUM(image_id);
   glDeleteTextures(1, &texture_id);
 }
-#endif  // defined(OS_LINUX)
+#endif  // defined(OS_LINUX) || defined(OS_CHROMEOS)
 
 INSTANTIATE_TEST_SUITE_P(
     GpuMemoryBufferTests,
diff --git a/gpu/ipc/service/gpu_memory_buffer_factory.cc b/gpu/ipc/service/gpu_memory_buffer_factory.cc
index 57af72e..be28596 100644
--- a/gpu/ipc/service/gpu_memory_buffer_factory.cc
+++ b/gpu/ipc/service/gpu_memory_buffer_factory.cc
@@ -12,7 +12,7 @@
 #include "gpu/ipc/service/gpu_memory_buffer_factory_io_surface.h"
 #endif
 
-#if defined(OS_LINUX) || defined(OS_FUCHSIA)
+#if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_FUCHSIA)
 #include "gpu/ipc/service/gpu_memory_buffer_factory_native_pixmap.h"
 #endif
 
@@ -34,7 +34,7 @@
   return std::make_unique<GpuMemoryBufferFactoryIOSurface>();
 #elif defined(OS_ANDROID)
   return std::make_unique<GpuMemoryBufferFactoryAndroidHardwareBuffer>();
-#elif defined(OS_LINUX) || defined(OS_FUCHSIA)
+#elif defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_FUCHSIA)
   return std::make_unique<GpuMemoryBufferFactoryNativePixmap>(
       vulkan_context_provider);
 #elif defined(OS_WIN)
diff --git a/ios/chrome/app/main_controller.mm b/ios/chrome/app/main_controller.mm
index 07d2951..89cdcdd5 100644
--- a/ios/chrome/app/main_controller.mm
+++ b/ios/chrome/app/main_controller.mm
@@ -652,7 +652,10 @@
   if (self.appState.foregroundActiveScene) {
     return self.appState.foregroundActiveScene.interfaceProvider;
   }
-  return self.appState.connectedScenes[0].interfaceProvider;
+  NSArray<SceneState*>* connectedScenes = self.appState.connectedScenes;
+
+  return connectedScenes.count == 0 ? nil
+                                    : connectedScenes[0].interfaceProvider;
 }
 
 - (BOOL)isFirstLaunchAfterUpgrade {
diff --git a/ios/chrome/browser/passwords/BUILD.gn b/ios/chrome/browser/passwords/BUILD.gn
index 525e3c9..e8c655e 100644
--- a/ios/chrome/browser/passwords/BUILD.gn
+++ b/ios/chrome/browser/passwords/BUILD.gn
@@ -197,6 +197,7 @@
     "//components/prefs:test_support",
     "//components/safe_browsing/core/common:safe_browsing_prefs",
     "//components/security_state/ios",
+    "//components/ukm:test_support",
     "//google_apis",
     "//ios/chrome/browser/autofill",
     "//ios/chrome/browser/browser_state:test_support",
diff --git a/ios/chrome/browser/passwords/ios_chrome_password_check_manager.mm b/ios/chrome/browser/passwords/ios_chrome_password_check_manager.mm
index 371f141..c050e0d 100644
--- a/ios/chrome/browser/passwords/ios_chrome_password_check_manager.mm
+++ b/ios/chrome/browser/passwords/ios_chrome_password_check_manager.mm
@@ -95,8 +95,8 @@
           browser_state,
           ServiceAccessType::EXPLICIT_ACCESS)),
       saved_passwords_presenter_(password_store_),
-      compromised_credentials_manager_(password_store_,
-                                       &saved_passwords_presenter_),
+      compromised_credentials_manager_(&saved_passwords_presenter_,
+                                       password_store_),
       bulk_leak_check_service_adapter_(
           &saved_passwords_presenter_,
           IOSChromeBulkLeakCheckServiceFactory::GetForBrowserState(
diff --git a/ios/chrome/browser/passwords/well_known_change_password_tab_helper.h b/ios/chrome/browser/passwords/well_known_change_password_tab_helper.h
index 9a862d58..700b898f 100644
--- a/ios/chrome/browser/passwords/well_known_change_password_tab_helper.h
+++ b/ios/chrome/browser/passwords/well_known_change_password_tab_helper.h
@@ -6,6 +6,7 @@
 
 #include "components/password_manager/core/browser/change_password_url_service.h"
 #include "components/password_manager/core/browser/well_known_change_password_state.h"
+#include "components/password_manager/core/browser/well_known_change_password_util.h"
 #include "ios/web/public/navigation/web_state_policy_decider.h"
 #include "ios/web/public/web_state_observer.h"
 #import "ios/web/public/web_state_user_data.h"
@@ -67,6 +68,8 @@
   void OnProcessingFinished(bool is_supported) override;
   // Redirects to a given URL in the same tab.
   void Redirect(const GURL& url);
+  // Records the given UKM metric.
+  void RecordMetric(WellKnownChangePasswordResult result);
 
   web::WebState* web_state_ = nullptr;
   ProcessingState processing_state_ = kWaitingForFirstRequest;
diff --git a/ios/chrome/browser/passwords/well_known_change_password_tab_helper.mm b/ios/chrome/browser/passwords/well_known_change_password_tab_helper.mm
index 6df6b59..1c6a5ed 100644
--- a/ios/chrome/browser/passwords/well_known_change_password_tab_helper.mm
+++ b/ios/chrome/browser/passwords/well_known_change_password_tab_helper.mm
@@ -7,12 +7,14 @@
 #import <Foundation/Foundation.h>
 
 #include "base/logging.h"
-#include "components/password_manager/core/browser/well_known_change_password_util.h"
 #include "components/password_manager/core/common/password_manager_features.h"
+#import "components/ukm/ios/ukm_url_recorder.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
 #include "ios/chrome/browser/passwords/ios_chrome_change_password_url_service_factory.h"
 #import "ios/web/public/navigation/navigation_context.h"
 #import "net/base/mac/url_conversions.h"
+#include "services/metrics/public/cpp/ukm_builders.h"
+#include "services/metrics/public/cpp/ukm_recorder.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
@@ -124,12 +126,19 @@
   if (is_supported) {
     std::move(response_policy_callback_)
         .Run(web::WebStatePolicyDecider::PolicyDecision::Allow());
+    RecordMetric(WellKnownChangePasswordResult::kUsedWellKnownChangePassword);
   } else {
     std::move(response_policy_callback_)
         .Run(web::WebStatePolicyDecider::PolicyDecision::Cancel());
     GURL redirect_url =
         change_password_url_service_->GetChangePasswordUrl(request_url_);
-    Redirect(redirect_url.is_valid() ? redirect_url : request_url_.GetOrigin());
+    if (redirect_url.is_valid()) {
+      RecordMetric(WellKnownChangePasswordResult::kFallbackToOverrideUrl);
+      Redirect(redirect_url);
+    } else {
+      RecordMetric(WellKnownChangePasswordResult::kFallbackToOriginUrl);
+      Redirect(request_url_.GetOrigin());
+    }
   }
 }
 
@@ -143,4 +152,12 @@
   }
 }
 
+void WellKnownChangePasswordTabHelper::RecordMetric(
+    WellKnownChangePasswordResult result) {
+  ukm::SourceId source_id = ukm::GetSourceIdForWebStateDocument(web_state_);
+  ukm::builders::PasswordManager_WellKnownChangePasswordResult(source_id)
+      .SetWellKnownChangePasswordResult(static_cast<int64_t>(result))
+      .Record(ukm::UkmRecorder::Get());
+}
+
 WEB_STATE_USER_DATA_KEY_IMPL(WellKnownChangePasswordTabHelper)
diff --git a/ios/chrome/browser/passwords/well_known_change_password_tab_helper_unittest.mm b/ios/chrome/browser/passwords/well_known_change_password_tab_helper_unittest.mm
index b3bb727..5930ea4 100644
--- a/ios/chrome/browser/passwords/well_known_change_password_tab_helper_unittest.mm
+++ b/ios/chrome/browser/passwords/well_known_change_password_tab_helper_unittest.mm
@@ -10,6 +10,7 @@
 #include "base/test/scoped_feature_list.h"
 #include "components/password_manager/core/browser/well_known_change_password_util.h"
 #include "components/password_manager/core/common/password_manager_features.h"
+#include "components/ukm/test_ukm_recorder.h"
 #include "ios/chrome/browser/passwords/ios_chrome_change_password_url_service_factory.h"
 #import "ios/web/public/navigation/navigation_manager.h"
 #import "ios/web/public/test/fakes/test_web_client.h"
@@ -24,6 +25,7 @@
 #include "net/test/embedded_test_server/embedded_test_server.h"
 #include "net/test/embedded_test_server/http_request.h"
 #include "net/test/embedded_test_server/http_response.h"
+#include "services/metrics/public/cpp/ukm_builders.h"
 #include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
 #include "services/network/test/test_url_loader_factory.h"
 
@@ -41,6 +43,7 @@
 using net::test_server::HttpResponse;
 using password_manager::kWellKnownChangePasswordPath;
 using password_manager::kWellKnownNotExistingResourcePath;
+using password_manager::WellKnownChangePasswordResult;
 
 // ServerResponse describes how a server should respond to a given path.
 struct ServerResponse {
@@ -77,6 +80,8 @@
 class WellKnownChangePasswordTabHelperTest : public web::TestWebClient,
                                              public web::WebTestWithWebState {
  public:
+  using UkmBuilder =
+      ukm::builders::PasswordManager_WellKnownChangePasswordResult;
   WellKnownChangePasswordTabHelperTest() {
     feature_list_.InitAndEnableFeature(
         password_manager::features::kWellKnownChangePassword);
@@ -104,6 +109,7 @@
     SetSharedURLLoaderFactory(
         base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
             &test_url_loader_factory_));
+    test_recorder_ = std::make_unique<ukm::TestAutoSetUkmRecorder>();
   }
 
   // Sets a response for the |test_url_loader_factory_| with the |test_server_|
@@ -113,6 +119,15 @@
     test_url_loader_factory_.AddResponse(test_server_->GetURL(path).spec(), "",
                                          status_code);
   }
+
+  void ExpectUkmMetric(WellKnownChangePasswordResult expected) {
+    auto entries = test_recorder_->GetEntriesByName(UkmBuilder::kEntryName);
+    // Expect one recorded metric.
+    ASSERT_EQ(1, static_cast<int>(entries.size()));
+    test_recorder_->ExpectEntryMetric(
+        entries[0], UkmBuilder::kWellKnownChangePasswordResultName,
+        static_cast<int64_t>(expected));
+  }
   // Waits until the navigation is complete and waits for backgroundtasks to
   // complete. Returns false when timed out.
   bool WaitUntilLoaded();
@@ -124,6 +139,7 @@
   std::unique_ptr<EmbeddedTestServer> test_server_ =
       std::make_unique<EmbeddedTestServer>();
   TestChangePasswordUrlService* url_service_ = nullptr;
+  std::unique_ptr<ukm::TestAutoSetUkmRecorder> test_recorder_;
 
  private:
   // Returns a response for the given request. Uses |path_response_map_| to
@@ -185,6 +201,7 @@
                      test_server_->GetURL(kWellKnownChangePasswordPath));
   ASSERT_TRUE(WaitUntilLoaded());
   EXPECT_EQ(GetNavigatedUrl().path(), kWellKnownChangePasswordPath);
+  ExpectUkmMetric(WellKnownChangePasswordResult::kUsedWellKnownChangePassword);
 }
 
 TEST_F(WellKnownChangePasswordTabHelperTest,
@@ -200,6 +217,7 @@
                      test_server_->GetURL(kWellKnownChangePasswordPath));
   ASSERT_TRUE(WaitUntilLoaded());
   EXPECT_EQ(GetNavigatedUrl().path(), "/change-password");
+  ExpectUkmMetric(WellKnownChangePasswordResult::kUsedWellKnownChangePassword);
 }
 
 TEST_F(WellKnownChangePasswordTabHelperTest,
@@ -212,6 +230,7 @@
                      test_server_->GetURL(kWellKnownChangePasswordPath));
   ASSERT_TRUE(WaitUntilLoaded());
   EXPECT_EQ(GetNavigatedUrl().path(), "/");
+  ExpectUkmMetric(WellKnownChangePasswordResult::kFallbackToOriginUrl);
 }
 
 TEST_F(WellKnownChangePasswordTabHelperTest, NoSupportForChangePassword_Ok) {
@@ -223,6 +242,7 @@
                      test_server_->GetURL(kWellKnownChangePasswordPath));
   ASSERT_TRUE(WaitUntilLoaded());
   EXPECT_EQ(GetNavigatedUrl().path(), "/");
+  ExpectUkmMetric(WellKnownChangePasswordResult::kFallbackToOriginUrl);
 }
 
 TEST_F(WellKnownChangePasswordTabHelperTest,
@@ -235,6 +255,7 @@
                      test_server_->GetURL(kWellKnownChangePasswordPath));
   ASSERT_TRUE(WaitUntilLoaded());
   EXPECT_EQ(GetNavigatedUrl().path(), "/");
+  ExpectUkmMetric(WellKnownChangePasswordResult::kFallbackToOriginUrl);
 }
 
 TEST_F(WellKnownChangePasswordTabHelperTest,
@@ -248,4 +269,5 @@
                      test_server_->GetURL(kWellKnownChangePasswordPath));
   ASSERT_TRUE(WaitUntilLoaded());
   EXPECT_EQ(GetNavigatedUrl().path(), kMockChangePasswordPath);
+  ExpectUkmMetric(WellKnownChangePasswordResult::kFallbackToOverrideUrl);
 }
diff --git a/ios/chrome/browser/snapshots/snapshot_browser_agent.h b/ios/chrome/browser/snapshots/snapshot_browser_agent.h
index 0eee04e9..09adf24 100644
--- a/ios/chrome/browser/snapshots/snapshot_browser_agent.h
+++ b/ios/chrome/browser/snapshots/snapshot_browser_agent.h
@@ -34,6 +34,9 @@
   // performing any necessary migrations.
   void PerformStorageMaintenance();
 
+  // Permanently removes all snapshots.
+  void RemoveAllSnapshots();
+
   SnapshotCache* snapshot_cache() { return snapshot_cache_; }
 
  private:
diff --git a/ios/chrome/browser/snapshots/snapshot_browser_agent.mm b/ios/chrome/browser/snapshots/snapshot_browser_agent.mm
index 2cc3014..9dd59ef 100644
--- a/ios/chrome/browser/snapshots/snapshot_browser_agent.mm
+++ b/ios/chrome/browser/snapshots/snapshot_browser_agent.mm
@@ -97,6 +97,10 @@
   PurgeUnusedSnapshots();
 }
 
+void SnapshotBrowserAgent::RemoveAllSnapshots() {
+  [snapshot_cache_ removeAllImages];
+}
+
 void SnapshotBrowserAgent::MigrateStorageIfNecessary() {
   DCHECK(snapshot_cache_);
   base::FilePath legacy_directory;
diff --git a/ios/chrome/browser/snapshots/snapshot_cache.h b/ios/chrome/browser/snapshots/snapshot_cache.h
index 1304760..48597def 100644
--- a/ios/chrome/browser/snapshots/snapshot_cache.h
+++ b/ios/chrome/browser/snapshots/snapshot_cache.h
@@ -50,22 +50,11 @@
 
 - (void)setImage:(UIImage*)image withSnapshotID:(NSString*)snapshotID;
 
-// Removes the image from both the LRU and disk cache, unless it is marked for
-// deferred deletion. Images marked for deferred deletion can only be removed by
-// calling |-removeMarkedImages|.
+// Removes the image from both the LRU and disk.
 - (void)removeImageWithSnapshotID:(NSString*)snapshotID;
 
-// Marks an image for deferred deletion. The image will not be immediately
-// deleted when |-removeImageWithSnapshotID:| is called. Images marked for
-// deferred deletion can only be removed by calling |-removeMarkedImages|.
-- (void)markImageWithSnapshotID:(NSString*)snapshotID;
-
-// Removes all marked images from both the LRU and disk cache.
-- (void)removeMarkedImages;
-
-// Unmarks all images, so they remain in the cache. They are no longer marked
-// for deferred deletion.
-- (void)unmarkAllImages;
+// Removes all images from the LRU and disk.
+- (void)removeAllImages;
 
 // Moves all images for |snapshotIDs| from |sourcePath| to the current storage
 // path of this snapshot cache. Deletes the folder |sourcePath| after migration,
@@ -78,6 +67,7 @@
 // background thread.
 - (void)purgeCacheOlderThan:(const base::Time&)date
                     keeping:(NSSet*)liveSnapshotIDs;
+
 // Hint that the snapshot for |snapshotID| will likely be saved to disk when the
 // application is backgrounded.  The snapshot is then saved in memory, so it
 // does not need to be read off disk.
diff --git a/ios/chrome/browser/snapshots/snapshot_cache.mm b/ios/chrome/browser/snapshots/snapshot_cache.mm
index 40b48a8..a434ced 100644
--- a/ios/chrome/browser/snapshots/snapshot_cache.mm
+++ b/ios/chrome/browser/snapshots/snapshot_cache.mm
@@ -48,8 +48,6 @@
 @interface SnapshotCache ()
 // List of observers to be notified of changes to the snapshot cache.
 @property(nonatomic, strong) SnapshotCacheObservers* observers;
-// Marked set of identifiers for which images should not be immediately deleted.
-@property(nonatomic, strong) NSMutableSet<NSString*>* markedIDs;
 @end
 
 namespace {
@@ -252,7 +250,6 @@
     [self createStorageIfNecessary];
 
     _observers = [SnapshotCacheObservers observers];
-    _markedIDs = [[NSMutableSet alloc] init];
 
     [[NSNotificationCenter defaultCenter]
         addObserver:self
@@ -351,9 +348,6 @@
 
 - (void)removeImageWithSnapshotID:(NSString*)snapshotID {
   DCHECK_CALLED_ON_VALID_SEQUENCE(_sequenceChecker);
-  // Do not immediately delete if the ID is marked.
-  if ([self.markedIDs containsObject:snapshotID])
-    return;
 
   [_lruCache removeObjectForKey:snapshotID];
 
@@ -375,20 +369,29 @@
       }));
 }
 
-- (void)markImageWithSnapshotID:(NSString*)snapshotID {
-  [self.markedIDs addObject:snapshotID];
-}
+- (void)removeAllImages {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(_sequenceChecker);
 
-- (void)removeMarkedImages {
-  while (self.markedIDs.count > 0) {
-    NSString* snapshotID = [self.markedIDs anyObject];
-    [self.markedIDs removeObject:snapshotID];
-    [self removeImageWithSnapshotID:snapshotID];
-  }
-}
+  [_lruCache removeAllObjects];
 
-- (void)unmarkAllImages {
-  [self.markedIDs removeAllObjects];
+  if (!_taskRunner)
+    return;
+  // Copy ivars used by the block so that it does not reference |self|.
+  const base::FilePath cacheDirectory = _cacheDirectory;
+  _taskRunner->PostTask(
+      FROM_HERE, base::BindOnce(^{
+        if (cacheDirectory.empty() || !base::DirectoryExists(cacheDirectory)) {
+          return;
+        }
+        if (!base::DeletePathRecursively(cacheDirectory)) {
+          DLOG(ERROR) << "Error deleting snapshots storage. "
+                      << cacheDirectory.AsUTF8Unsafe();
+        }
+        if (!base::CreateDirectory(cacheDirectory)) {
+          DLOG(ERROR) << "Error creating snapshot storage "
+                      << cacheDirectory.AsUTF8Unsafe();
+        }
+      }));
 }
 
 - (base::FilePath)imagePathForSnapshotID:(NSString*)snapshotID {
diff --git a/ios/chrome/browser/snapshots/snapshot_cache_unittest.mm b/ios/chrome/browser/snapshots/snapshot_cache_unittest.mm
index b8465de..a6e80a1 100644
--- a/ios/chrome/browser/snapshots/snapshot_cache_unittest.mm
+++ b/ios/chrome/browser/snapshots/snapshot_cache_unittest.mm
@@ -651,39 +651,34 @@
   EXPECT_FALSE(base::PathExists(retinaFile));
 }
 
-// Tests that a marked image does not immediately delete when calling
-// |-removeImageWithSnapshotID:|. Calling |-removeMarkedImages| immediately
-// deletes the marked image.
-TEST_F(SnapshotCacheTest, MarkedImageNotImmediatelyDeleted) {
+// Tests that image immediately deletes when calling
+// |-removeImageWithSnapshotID:|.
+TEST_F(SnapshotCacheTest, ImageDeleted) {
   SnapshotCache* cache = GetSnapshotCache();
   UIImage* image =
       GenerateRandomImage(CGSizeMake(kSnapshotPixelSize, kSnapshotPixelSize));
   [cache setImage:image withSnapshotID:@"snapshotID"];
   base::FilePath image_path = [cache imagePathForSnapshotID:@"snapshotID"];
-  [cache markImageWithSnapshotID:@"snapshotID"];
   [cache removeImageWithSnapshotID:@"snapshotID"];
   // Give enough time for deletion.
   FlushRunLoops();
-  EXPECT_TRUE(base::PathExists(image_path));
-  [cache removeMarkedImages];
-  FlushRunLoops();
   EXPECT_FALSE(base::PathExists(image_path));
 }
 
-// Tests that unmarked images are not deleted when calling
-// |-removeMarkedImages|.
-TEST_F(SnapshotCacheTest, UnmarkedImageNotDeleted) {
+// Tests that all images are deleted when calling |-removeAllImages|.
+TEST_F(SnapshotCacheTest, AllImagesDeleted) {
   SnapshotCache* cache = GetSnapshotCache();
   UIImage* image =
       GenerateRandomImage(CGSizeMake(kSnapshotPixelSize, kSnapshotPixelSize));
-  [cache setImage:image withSnapshotID:@"snapshotID"];
-  base::FilePath image_path = [cache imagePathForSnapshotID:@"snapshotID"];
-  [cache markImageWithSnapshotID:@"snapshotID"];
-  [cache unmarkAllImages];
-  [cache removeMarkedImages];
+  [cache setImage:image withSnapshotID:@"snapshotID-1"];
+  [cache setImage:image withSnapshotID:@"snapshotID-2"];
+  base::FilePath image_1_path = [cache imagePathForSnapshotID:@"snapshotID-1"];
+  base::FilePath image_2_path = [cache imagePathForSnapshotID:@"snapshotID-2"];
+  [cache removeAllImages];
   // Give enough time for deletion.
   FlushRunLoops();
-  EXPECT_TRUE(base::PathExists(image_path));
+  EXPECT_FALSE(base::PathExists(image_1_path));
+  EXPECT_FALSE(base::PathExists(image_2_path));
 }
 
 // Tests that observers are notified when a snapshot is cached and removed.
diff --git a/ios/chrome/browser/snapshots/snapshot_tab_helper.h b/ios/chrome/browser/snapshots/snapshot_tab_helper.h
index 2f2ee6c..0639b792 100644
--- a/ios/chrome/browser/snapshots/snapshot_tab_helper.h
+++ b/ios/chrome/browser/snapshots/snapshot_tab_helper.h
@@ -78,6 +78,15 @@
   // Instructs the helper not to snapshot content for the next page load event.
   void IgnoreNextLoad();
 
+  // Hint that the snapshot will likely be saved to disk when the application is
+  // backgrounded.  The snapshot is then saved in memory, so it does not need to
+  // be read off disk.
+  void WillBeSavedGreyWhenBackgrounding();
+
+  // Write a grey copy of the snapshot to disk, but if and only if a color
+  // version of the snapshot already exists in memory or on disk.
+  void SaveGreyInBackground();
+
  private:
   friend class web::WebStateUserData<SnapshotTabHelper>;
 
@@ -97,6 +106,7 @@
   void OnManagerShuttingDown(infobars::InfoBarManager* manager) override;
 
   web::WebState* web_state_ = nullptr;
+  NSString* tab_id_ = nil;
   SnapshotGenerator* snapshot_generator_ = nil;
   infobars::InfoBarManager* infobar_manager_ = nullptr;
 
diff --git a/ios/chrome/browser/snapshots/snapshot_tab_helper.mm b/ios/chrome/browser/snapshots/snapshot_tab_helper.mm
index 300e7c3..38d93f80 100644
--- a/ios/chrome/browser/snapshots/snapshot_tab_helper.mm
+++ b/ios/chrome/browser/snapshots/snapshot_tab_helper.mm
@@ -11,6 +11,7 @@
 #include "components/infobars/core/infobar.h"
 #include "components/infobars/core/infobar_delegate.h"
 #include "ios/chrome/browser/infobars/infobar_manager_impl.h"
+#import "ios/chrome/browser/snapshots/snapshot_cache.h"
 #import "ios/chrome/browser/snapshots/snapshot_generator.h"
 #import "ios/chrome/browser/ui/infobars/infobar_feature.h"
 #include "ios/chrome/browser/ui/util/ui_util.h"
@@ -101,7 +102,6 @@
 }
 
 void SnapshotTabHelper::RemoveSnapshot() {
-  DCHECK(web_state_);
   [snapshot_generator_ removeSnapshot];
 }
 
@@ -109,13 +109,24 @@
   ignore_next_load_ = true;
 }
 
+void SnapshotTabHelper::WillBeSavedGreyWhenBackgrounding() {
+  [snapshot_generator_.snapshotCache willBeSavedGreyWhenBackgrounding:tab_id_];
+}
+
+void SnapshotTabHelper::SaveGreyInBackground() {
+  [snapshot_generator_.snapshotCache saveGreyInBackgroundForSnapshotID:tab_id_];
+}
+
 SnapshotTabHelper::SnapshotTabHelper(web::WebState* web_state, NSString* tab_id)
     : web_state_(web_state),
+      tab_id_([tab_id copy]),
       web_state_observer_(this),
       infobar_observer_(this),
       weak_ptr_factory_(this) {
+  DCHECK(web_state_);
+  DCHECK(tab_id_.length > 0);
   snapshot_generator_ = [[SnapshotGenerator alloc] initWithWebState:web_state_
-                                                              tabID:tab_id];
+                                                              tabID:tab_id_];
   web_state_observer_.Add(web_state_);
 
   // Supports missing InfoBarManager to make testing easier.
@@ -180,6 +191,7 @@
   DCHECK_EQ(web_state_, web_state);
   web_state_observer_.Remove(web_state);
   web_state_ = nullptr;
+  tab_id_ = nil;
 }
 
 void SnapshotTabHelper::OnInfoBarAdded(infobars::InfoBar* infobar) {
diff --git a/ios/chrome/browser/tabs/tab_model.mm b/ios/chrome/browser/tabs/tab_model.mm
index 1b0c3e7..dba66bf 100644
--- a/ios/chrome/browser/tabs/tab_model.mm
+++ b/ios/chrome/browser/tabs/tab_model.mm
@@ -35,10 +35,8 @@
 #import "ios/chrome/browser/sessions/session_restoration_browser_agent.h"
 #import "ios/chrome/browser/sessions/session_service_ios.h"
 #import "ios/chrome/browser/sessions/session_window_ios.h"
-#import "ios/chrome/browser/snapshots/snapshot_browser_agent.h"
-#import "ios/chrome/browser/snapshots/snapshot_cache.h"
+#import "ios/chrome/browser/snapshots/snapshot_tab_helper.h"
 #import "ios/chrome/browser/tabs/tab_parenting_observer.h"
-#import "ios/chrome/browser/web/tab_id_tab_helper.h"
 #import "ios/chrome/browser/web_state_list/web_state_list.h"
 #import "ios/chrome/browser/web_state_list/web_state_list_observer.h"
 #import "ios/chrome/browser/web_state_list/web_usage_enabler/web_usage_enabler_browser_agent.h"
@@ -133,9 +131,6 @@
   // Weak reference to the session restoration agent.
   SessionRestorationBrowserAgent* _sessionRestorationBrowserAgent;
 
-  // Used for saving gray images.
-  SnapshotBrowserAgent* _snapshotBrowserAgent;
-
   // Used to ensure thread-safety of the certificate policy management code.
   base::CancelableTaskTracker _clearPoliciesTaskTracker;
 
@@ -180,8 +175,6 @@
         SessionRestorationBrowserAgent::FromBrowser(browser);
     _webEnabler = WebUsageEnablerBrowserAgent::FromBrowser(browser);
 
-    _snapshotBrowserAgent = SnapshotBrowserAgent::FromBrowser(browser);
-
     _webStateListObservers.push_back(std::make_unique<TabParentingObserver>());
 
     for (const auto& webStateListObserver : _webStateListObservers)
@@ -239,12 +232,8 @@
 // TODO(crbug.com/1115611): Move to SceneController.
 - (void)willResignActive:(NSNotification*)notify {
   if (_webEnabler->IsWebUsageEnabled() && _webStateList->GetActiveWebState()) {
-    NSString* tabId =
-        TabIdTabHelper::FromWebState(_webStateList->GetActiveWebState())
-            ->tab_id();
-
-    [_snapshotBrowserAgent->snapshot_cache()
-        willBeSavedGreyWhenBackgrounding:tabId];
+    SnapshotTabHelper::FromWebState(_webStateList->GetActiveWebState())
+        ->WillBeSavedGreyWhenBackgrounding();
   }
 }
 
@@ -268,12 +257,8 @@
 
   // Write out a grey version of the current website to disk.
   if (_webEnabler->IsWebUsageEnabled() && _webStateList->GetActiveWebState()) {
-    NSString* tabId =
-        TabIdTabHelper::FromWebState(_webStateList->GetActiveWebState())
-            ->tab_id();
-
-    [_snapshotBrowserAgent->snapshot_cache()
-        saveGreyInBackgroundForSnapshotID:tabId];
+    SnapshotTabHelper::FromWebState(_webStateList->GetActiveWebState())
+        ->SaveGreyInBackground();
   }
 }
 
diff --git a/ios/chrome/browser/ui/authentication/BUILD.gn b/ios/chrome/browser/ui/authentication/BUILD.gn
index 03f0169..027b52e 100644
--- a/ios/chrome/browser/ui/authentication/BUILD.gn
+++ b/ios/chrome/browser/ui/authentication/BUILD.gn
@@ -170,6 +170,21 @@
   ]
 }
 
+source_set("internal+eg2") {
+  defines = [ "CHROME_EARL_GREY_2" ]
+  configs += [
+    "//build/config/compiler:enable_arc",
+    "//build/config/ios:xctest_config",
+  ]
+  testonly = true
+  visibility = [ ":*" ]
+  sources = [
+    "signin_earl_grey_app_interface.h",
+    "signin_earl_grey_app_interface_stub.mm",
+  ]
+  deps = [ "//ios/third_party/earl_grey2:test_lib" ]
+}
+
 source_set("eg_test_support+eg2") {
   defines = [ "CHROME_EARL_GREY_2" ]
   configs += [
@@ -180,12 +195,11 @@
   sources = [
     "signin_earl_grey.h",
     "signin_earl_grey.mm",
-    "signin_earl_grey_app_interface.h",
-    "signin_earl_grey_app_interface_stub.mm",
     "signin_earl_grey_ui.h",
     "signin_earl_grey_ui.mm",
   ]
   deps = [
+    ":internal+eg2",
     "//base",
     "//base/test:test_support",
     "//ios/chrome/app/strings",
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller.mm b/ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller.mm
index f3e2cec4..a9849fa 100644
--- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller.mm
+++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller.mm
@@ -477,6 +477,11 @@
             @"%@%li",
             kContentSuggestionsMostVisitedAccessibilityIdentifierPrefix,
             indexPath.row];
+    // Apple doesn't handle the transparency of the background during animations
+    // linked to context menus. To prevent the cell from turning black during
+    // animations, its background is set to be the same as the NTP background.
+    // See: crbug.com/1120321.
+    cell.backgroundColor = ntp_home::kNTPBackgroundColor();
     [self.collectionViewModel itemAtIndexPath:indexPath]
         .accessibilityIdentifier = cell.accessibilityIdentifier;
   }
diff --git a/ios/chrome/browser/ui/location_bar/location_bar_view_controller.mm b/ios/chrome/browser/ui/location_bar/location_bar_view_controller.mm
index 5652580..900a33f228 100644
--- a/ios/chrome/browser/ui/location_bar/location_bar_view_controller.mm
+++ b/ios/chrome/browser/ui/location_bar/location_bar_view_controller.mm
@@ -87,6 +87,9 @@
 // Stores the current content type in the clipboard. This is only valid if
 // |hasCopiedContent| is YES.
 @property(nonatomic, assign) ClipboardContentType copiedContentType;
+// Stores whether the cached clipboard state is currently being updated. See
+// |-updateCachedClipboardState| for more information.
+@property(nonatomic, assign) BOOL isUpdatingCachedClipboardState;
 
 // Starts voice search, updating the NamedGuide to be constrained to the
 // trailing button.
@@ -574,6 +577,14 @@
 }
 
 - (void)updateCachedClipboardState {
+  // Sometimes, checking the clipboard state itself causes the clipboard to
+  // emit a UIPasteboardChangedNotification, leading to an infinite loop. For
+  // now, just prevent re-checking the clipboard state, but hopefully this will
+  // be fixed in a future iOS version (see crbug.com/1049053 for crash details).
+  if (self.isUpdatingCachedClipboardState) {
+    return;
+  }
+  self.isUpdatingCachedClipboardState = YES;
   self.hasCopiedContent = NO;
   ClipboardRecentContent* clipboardRecentContent =
       ClipboardRecentContent::GetInstance();
@@ -597,6 +608,7 @@
                    matched_types.end()) {
           weakSelf.copiedContentType = ClipboardContentType::Text;
         }
+        weakSelf.isUpdatingCachedClipboardState = NO;
       }));
 }
 
diff --git a/ios/chrome/browser/ui/main/scene_controller.mm b/ios/chrome/browser/ui/main/scene_controller.mm
index a88d946..7a6c83c 100644
--- a/ios/chrome/browser/ui/main/scene_controller.mm
+++ b/ios/chrome/browser/ui/main/scene_controller.mm
@@ -216,7 +216,6 @@
   if (self) {
     _sceneState = sceneState;
     [_sceneState addObserver:self];
-    [_sceneState.appState addObserver:self];
     // The window is necessary very early in the app/scene lifecycle, so it
     // should be created right away.
     // When multiwindow is supported, the window is created by SceneDelegate,
@@ -503,6 +502,8 @@
     [self startUpChromeUI];
   }
 
+  [self.sceneState.appState addObserver:self];
+
   self.hasInitializedUI = YES;
 }
 
@@ -676,6 +677,8 @@
   self.browserViewWrangler = nil;
 
   self.hasInitializedUI = NO;
+
+  [self.sceneState.appState removeObserver:self];
 }
 
 #pragma mark - First Run
diff --git a/ios/chrome/browser/ui/omnibox/omnibox_view_controller.mm b/ios/chrome/browser/ui/omnibox/omnibox_view_controller.mm
index 0bfb48d..a5791c9 100644
--- a/ios/chrome/browser/ui/omnibox/omnibox_view_controller.mm
+++ b/ios/chrome/browser/ui/omnibox/omnibox_view_controller.mm
@@ -89,6 +89,9 @@
 // Stores the current content type in the clipboard. This is only valid if
 // |hasCopiedContent| is YES.
 @property(nonatomic, assign) ClipboardContentType copiedContentType;
+// Stores whether the cached clipboard state is currently being updated. See
+// |-updateCachedClipboardState| for more information.
+@property(nonatomic, assign) BOOL isUpdatingCachedClipboardState;
 
 @end
 
@@ -414,6 +417,14 @@
 }
 
 - (void)updateCachedClipboardState {
+  // Sometimes, checking the clipboard state itself causes the clipboard to
+  // emit a UIPasteboardChangedNotification, leading to an infinite loop. For
+  // now, just prevent re-checking the clipboard state, but hopefully this will
+  // be fixed in a future iOS version (see crbug.com/1049053 for crash details).
+  if (self.isUpdatingCachedClipboardState) {
+    return;
+  }
+  self.isUpdatingCachedClipboardState = YES;
   self.hasCopiedContent = NO;
   ClipboardRecentContent* clipboardRecentContent =
       ClipboardRecentContent::GetInstance();
@@ -437,6 +448,7 @@
                    matched_types.end()) {
           weakSelf.copiedContentType = ClipboardContentType::Text;
         }
+        self.isUpdatingCachedClipboardState = NO;
       }));
 }
 
diff --git a/ios/chrome/browser/ui/settings/safety_check/OWNERS b/ios/chrome/browser/ui/settings/safety_check/OWNERS
new file mode 100644
index 0000000..046d8a7
--- /dev/null
+++ b/ios/chrome/browser/ui/settings/safety_check/OWNERS
@@ -0,0 +1,5 @@
+harrisonsean@chromium.org
+javierrobles@chromium.org
+
+# TEAM: ios-directory-owners@chromium.org
+# OS: iOS
diff --git a/ios/chrome/browser/ui/settings/sync/utils/sync_util.mm b/ios/chrome/browser/ui/settings/sync/utils/sync_util.mm
index ac75145c..acfc431e 100644
--- a/ios/chrome/browser/ui/settings/sync/utils/sync_util.mm
+++ b/ios/chrome/browser/ui/settings/sync/utils/sync_util.mm
@@ -26,14 +26,17 @@
 // to the user. This was added for crbug/265352 to quantify how often this
 // bug shows up in the wild. The logged histogram count should be interpreted
 // as a ratio of the number of active sync users.
-enum ErrorState {
+// These values are persisted to logs. Entries should not be renumbered and
+// numeric values should never be reused.
+enum InfobarSyncError {
   SYNC_SIGN_IN_NEEDS_UPDATE = 1,
-  SYNC_SERVICE_UNAVAILABLE,
-  SYNC_NEEDS_PASSPHRASE,
-  SYNC_UNRECOVERABLE_ERROR,
-  SYNC_SYNC_SETTINGS_NOT_CONFIRMED,
-  SYNC_NEEDS_TRUSTED_VAULT_KEY,
-  SYNC_ERROR_COUNT
+  // DEPRECATED. No longer recorded.
+  // SYNC_SERVICE_UNAVAILABLE = 2
+  SYNC_NEEDS_PASSPHRASE = 3,
+  SYNC_UNRECOVERABLE_ERROR = 4,
+  SYNC_SYNC_SETTINGS_NOT_CONFIRMED = 5,
+  SYNC_NEEDS_TRUSTED_VAULT_KEY = 6,
+  kMaxValue = SYNC_NEEDS_TRUSTED_VAULT_KEY,
 };
 
 }  // namespace
@@ -179,11 +182,12 @@
     return false;
 
   // Logs when an infobar is shown to user. See crbug/265352.
-  ErrorState loggedErrorState = SYNC_ERROR_COUNT;
+  InfobarSyncError loggedErrorState;
   switch (errorState) {
     case SyncSetupService::kNoSyncServiceError:
     case SyncSetupService::kSyncServiceCouldNotConnect:
     case SyncSetupService::kSyncServiceServiceUnavailable:
+      loggedErrorState = kMaxValue;
       NOTREACHED();
       break;
     case SyncSetupService::kSyncServiceSignInNeedsUpdate:
@@ -202,8 +206,7 @@
       loggedErrorState = SYNC_SYNC_SETTINGS_NOT_CONFIRMED;
       break;
   }
-  UMA_HISTOGRAM_ENUMERATION("Sync.SyncErrorInfobarDisplayed", loggedErrorState,
-                            SYNC_ERROR_COUNT);
+  UMA_HISTOGRAM_ENUMERATION("Sync.SyncErrorInfobarDisplayed", loggedErrorState);
 
   DCHECK(web_state);
   infobars::InfoBarManager* infoBarManager =
diff --git a/ios/chrome/browser/ui/tab_grid/tab_grid_mediator.mm b/ios/chrome/browser/ui/tab_grid/tab_grid_mediator.mm
index 1d2183f..82f098e 100644
--- a/ios/chrome/browser/ui/tab_grid/tab_grid_mediator.mm
+++ b/ios/chrome/browser/ui/tab_grid/tab_grid_mediator.mm
@@ -298,18 +298,12 @@
 - (void)closeAllItems {
   // This is a no-op if |webStateList| is already empty.
   self.webStateList->CloseAllWebStates(WebStateList::CLOSE_USER_ACTION);
+  SnapshotBrowserAgent::FromBrowser(self.browser)->RemoveAllSnapshots();
 }
 
 - (void)saveAndCloseAllItems {
   if (self.webStateList->empty())
     return;
-  // Tell the cache to mark these images for deletion, rather than immediately
-  // deleting them.
-  for (int i = 0; i < self.webStateList->count(); i++) {
-    web::WebState* webState = self.webStateList->GetWebStateAt(i);
-    TabIdTabHelper* tabHelper = TabIdTabHelper::FromWebState(webState);
-    [self.snapshotCache markImageWithSnapshotID:tabHelper->tab_id()];
-  }
   self.closedSessionWindow = SerializeWebStateList(self.webStateList);
   int old_size =
       self.tabRestoreService ? self.tabRestoreService->entries().size() : 0;
@@ -328,8 +322,6 @@
   self.closedSessionWindow = nil;
   [self removeEntriesFromTabRestoreService];
   self.syncedClosedTabsCount = 0;
-  // Unmark all images for deletion since they are now active tabs again.
-  [self.snapshotCache unmarkAllImages];
 }
 
 - (void)discardSavedClosedItems {
@@ -337,8 +329,7 @@
     return;
   self.syncedClosedTabsCount = 0;
   self.closedSessionWindow = nil;
-  // Delete all marked images from the cache.
-  [self.snapshotCache removeMarkedImages];
+  SnapshotBrowserAgent::FromBrowser(self.browser)->RemoveAllSnapshots();
 }
 
 #pragma mark GridCommands helpers
diff --git a/ios/web/browsing_data/browsing_data_remover_unittest.mm b/ios/web/browsing_data/browsing_data_remover_unittest.mm
index 2210783..abe56a1 100644
--- a/ios/web/browsing_data/browsing_data_remover_unittest.mm
+++ b/ios/web/browsing_data/browsing_data_remover_unittest.mm
@@ -134,6 +134,10 @@
 
 // Tests that removing the cookies remove them from the cookie store.
 TEST_F(BrowsingDataRemoverTest, RemoveCookie) {
+  // TODO(crbug.com/1121425): Currently failing on iOS14.
+  if (@available(iOS 14, *)) {
+    return;
+  }
   ASSERT_TRUE(AddCookie());
   ASSERT_TRUE(HasCookies(true));
 
diff --git a/media/capture/video/fake_video_capture_device_factory.cc b/media/capture/video/fake_video_capture_device_factory.cc
index 9a28f8a..f45dafa 100644
--- a/media/capture/video/fake_video_capture_device_factory.cc
+++ b/media/capture/video/fake_video_capture_device_factory.cc
@@ -208,7 +208,7 @@
   int entry_index = 0;
   for (const auto& entry : devices_config_) {
     VideoCaptureApi api =
-#if defined(OS_LINUX)
+#if defined(OS_LINUX) || defined(OS_CHROMEOS)
         VideoCaptureApi::LINUX_V4L2_SINGLE_PLANE;
 #elif defined(OS_MAC)
         VideoCaptureApi::MACOSX_AVFOUNDATION;
diff --git a/media/capture/video/file_video_capture_device_factory.cc b/media/capture/video/file_video_capture_device_factory.cc
index 7957082..d899c50 100644
--- a/media/capture/video/file_video_capture_device_factory.cc
+++ b/media/capture/video/file_video_capture_device_factory.cc
@@ -52,7 +52,7 @@
       VideoCaptureApi::WIN_DIRECT_SHOW;
 #elif defined(OS_MAC)
       VideoCaptureApi::MACOSX_AVFOUNDATION;
-#elif defined(OS_LINUX)
+#elif defined(OS_LINUX) || defined(OS_CHROMEOS)
       VideoCaptureApi::LINUX_V4L2_SINGLE_PLANE;
 #else
       VideoCaptureApi::UNKNOWN;
diff --git a/media/capture/video/video_capture_buffer_pool_impl.cc b/media/capture/video/video_capture_buffer_pool_impl.cc
index 4a260cc4..1a4b2ca 100644
--- a/media/capture/video/video_capture_buffer_pool_impl.cc
+++ b/media/capture/video/video_capture_buffer_pool_impl.cc
@@ -58,7 +58,7 @@
     int buffer_id) {
 // This requires platforms where base::SharedMemoryHandle is backed by a
 // file descriptor.
-#if defined(OS_LINUX)
+#if defined(OS_LINUX) || defined(OS_CHROMEOS)
   base::AutoLock lock(lock_);
 
   VideoCaptureBufferTracker* tracker = GetTracker(buffer_id);
diff --git a/media/capture/video/video_capture_device_client.cc b/media/capture/video/video_capture_device_client.cc
index 43a88b7..4d933062 100644
--- a/media/capture/video/video_capture_device_client.cc
+++ b/media/capture/video/video_capture_device_client.cc
@@ -301,7 +301,7 @@
 // see http://linuxtv.org/downloads/v4l-dvb-apis/packed-rgb.html.
 // Windows RGB24 defines blue at lowest byte,
 // see https://msdn.microsoft.com/en-us/library/windows/desktop/dd407253
-#if defined(OS_LINUX)
+#if defined(OS_LINUX) || defined(OS_CHROMEOS)
       fourcc_format = libyuv::FOURCC_RAW;
 #elif defined(OS_WIN)
       fourcc_format = libyuv::FOURCC_24BG;
diff --git a/media/capture/video/video_capture_device_client_unittest.cc b/media/capture/video/video_capture_device_client_unittest.cc
index 9bd1d329..5e3dfa8 100644
--- a/media/capture/video/video_capture_device_client_unittest.cc
+++ b/media/capture/video/video_capture_device_client_unittest.cc
@@ -227,7 +227,7 @@
     PIXEL_FORMAT_NV21,
     PIXEL_FORMAT_YUY2,
     PIXEL_FORMAT_UYVY,
-#if defined(OS_WIN) || defined(OS_LINUX)
+#if defined(OS_WIN) || defined(OS_LINUX) || defined(OS_CHROMEOS)
     PIXEL_FORMAT_RGB24,
 #endif
     PIXEL_FORMAT_ARGB,
diff --git a/media/capture/video/win/video_capture_device_factory_win.cc b/media/capture/video/win/video_capture_device_factory_win.cc
index 4ae6beb..769b0f6d 100644
--- a/media/capture/video/win/video_capture_device_factory_win.cc
+++ b/media/capture/video/win/video_capture_device_factory_win.cc
@@ -50,6 +50,18 @@
 const char kPidPrefix[] = "pid_";  // Also contains '\0'.
 const size_t kVidPidSize = 4;
 
+// AQS device selector string to filter enumerated DeviceInformation objects to
+// KSCATEGORY_SENSOR_CAMERA (Class GUID 24e552d7-6523-47F7-a647-d3465bf1f5ca)
+// OR KSCATEGORY_VIDEO_CAMERA (Class GUID e5323777-f976-4f5b-9b55-b94699c46e44).
+const wchar_t* kVideoAndSensorCamerasAqsString =
+    L"(System.Devices.InterfaceClassGuid:="
+    L"\"{e5323777-f976-4f5b-9b55-b94699c46e44}\" AND "
+    L"(System.Devices.WinPhone8CameraFlags:=[] OR "
+    L"System.Devices.WinPhone8CameraFlags:<4096)) OR "
+    L"System.Devices.InterfaceClassGuid:="
+    L"\"{24e552d7-6523-47f7-a647-d3465bf1f5ca}\" AND "
+    L"System.Devices.InterfaceEnabled:=System.StructuredQueryType.Boolean#True";
+
 // Avoid enumerating and/or using certain devices due to they provoking crashes
 // or any other reason (http://crbug.com/378494). This enum is defined for the
 // purposes of UMA collection. Existing entries cannot be removed.
@@ -542,8 +554,9 @@
   }
 
   IAsyncOperation<DeviceInformationCollection*>* async_op;
-  hr = dev_info_statics->FindAllAsyncDeviceClass(
-      ABI::Windows::Devices::Enumeration::DeviceClass_VideoCapture, &async_op);
+  ScopedHString aqs_filter =
+      ScopedHString::Create(kVideoAndSensorCamerasAqsString);
+  hr = dev_info_statics->FindAllAsyncAqsFilter(aqs_filter.get(), &async_op);
   if (FAILED(hr)) {
     UWP_ENUM_ERROR_HANDLER(hr, "Find all devices asynchronously failed: ");
     return;
diff --git a/media/gpu/test/video_encoder/video_encoder_client.h b/media/gpu/test/video_encoder/video_encoder_client.h
index 4811c23..9461ab66 100644
--- a/media/gpu/test/video_encoder/video_encoder_client.h
+++ b/media/gpu/test/video_encoder/video_encoder_client.h
@@ -37,7 +37,7 @@
   static constexpr uint32_t kDefaultBitrate = 200000;
   VideoEncoderClientConfig(const Video* video,
                            VideoCodecProfile output_profile,
-                           uint32_t bitrate = kDefaultBitrate);
+                           uint32_t bitrate);
   VideoEncoderClientConfig(const VideoEncoderClientConfig&);
 
   // The output output profile to be used.
diff --git a/media/gpu/video_encode_accelerator_tests.cc b/media/gpu/video_encode_accelerator_tests.cc
index 857710f9..7e05457 100644
--- a/media/gpu/video_encode_accelerator_tests.cc
+++ b/media/gpu/video_encode_accelerator_tests.cc
@@ -252,8 +252,9 @@
 
 // Encode video from start to end. Multiple buffer encodes will be queued in the
 // encoder, without waiting for the result of the previous encode requests.
-TEST_F(VideoEncoderTest, FlushAtEndOfStream_MultipleOutstandingDecodes) {
-  VideoEncoderClientConfig config(g_env->Video(), g_env->Profile());
+TEST_F(VideoEncoderTest, FlushAtEndOfStream_MultipleOutstandingEncodes) {
+  VideoEncoderClientConfig config(g_env->Video(), g_env->Profile(),
+                                  g_env->Bitrate());
   config.max_outstanding_encode_requests = 4;
   auto encoder = CreateVideoEncoder(g_env->Video(), config);
 
diff --git a/media/video/fake_gpu_memory_buffer.cc b/media/video/fake_gpu_memory_buffer.cc
index 6fbdb059..ef2b49d 100644
--- a/media/video/fake_gpu_memory_buffer.cc
+++ b/media/video/fake_gpu_memory_buffer.cc
@@ -10,7 +10,7 @@
 #include "media/base/format_utils.h"
 #include "media/base/video_frame.h"
 
-#if defined(OS_LINUX)
+#if defined(OS_LINUX) || defined(OS_CHROMEOS)
 #include <fcntl.h>
 #include <sys/stat.h>
 #include <sys/types.h>
@@ -48,7 +48,7 @@
 
 }  // namespace
 
-#if defined(OS_LINUX)
+#if defined(OS_LINUX) || defined(OS_CHROMEOS)
 base::ScopedFD GetDummyFD() {
   base::ScopedFD fd(open("/dev/zero", O_RDWR));
   DCHECK(fd.is_valid());
@@ -73,7 +73,7 @@
   static base::NoDestructor<base::AtomicSequenceNumber> buffer_id_generator;
   handle_.id = gfx::GpuMemoryBufferId(buffer_id_generator->GetNext());
 
-#if defined(OS_LINUX)
+#if defined(OS_LINUX) || defined(OS_CHROMEOS)
   for (size_t i = 0; i < VideoFrame::NumPlanes(video_pixel_format_); i++) {
     const gfx::Size plane_size_in_bytes =
         VideoFrame::PlaneSize(video_pixel_format_, i, size_);
@@ -81,7 +81,7 @@
         plane_size_in_bytes.width(), 0, plane_size_in_bytes.GetArea(),
         GetDummyFD());
   }
-#endif  // defined(OS_LINUX)
+#endif  // defined(OS_LINUX) || defined(OS_CHROMEOS)
 }
 
 FakeGpuMemoryBuffer::~FakeGpuMemoryBuffer() = default;
@@ -129,7 +129,7 @@
   gfx::GpuMemoryBufferHandle handle;
   handle.type = gfx::NATIVE_PIXMAP;
   handle.id = handle_.id;
-#if defined(OS_LINUX)
+#if defined(OS_LINUX) || defined(OS_CHROMEOS)
   handle.native_pixmap_handle =
       gfx::CloneHandleForIPC(handle_.native_pixmap_handle);
 #endif
diff --git a/media/video/gpu_memory_buffer_video_frame_pool.cc b/media/video/gpu_memory_buffer_video_frame_pool.cc
index cb443c90..cff6651 100644
--- a/media/video/gpu_memory_buffer_video_frame_pool.cc
+++ b/media/video/gpu_memory_buffer_video_frame_pool.cc
@@ -573,7 +573,7 @@
   }
 
   bool is_software_backed_video_frame = !video_frame->HasTextures();
-#if defined(OS_LINUX)
+#if defined(OS_LINUX) || defined(OS_CHROMEOS)
   is_software_backed_video_frame &= !video_frame->HasDmaBufs();
 #endif
 
diff --git a/services/device/screen_orientation/BUILD.gn b/services/device/screen_orientation/BUILD.gn
index ceec11a..de70dc03 100644
--- a/services/device/screen_orientation/BUILD.gn
+++ b/services/device/screen_orientation/BUILD.gn
@@ -42,7 +42,7 @@
     sources = [ "android/java/src/org/chromium/device/screen_orientation/ScreenOrientationListener.java" ]
     deps = [
       "//base:base_java",
-      "//ui/android:ui_java",
+      "//ui/android:ui_no_recycler_view_java",
     ]
   }
 }
diff --git a/services/network/public/cpp/request_destination.cc b/services/network/public/cpp/request_destination.cc
index 4cc1933..38884cc 100644
--- a/services/network/public/cpp/request_destination.cc
+++ b/services/network/public/cpp/request_destination.cc
@@ -10,7 +10,7 @@
     network::mojom::RequestDestination dest) {
   switch (dest) {
     case network::mojom::RequestDestination::kEmpty:
-      return "empty";
+      return "";
     case network::mojom::RequestDestination::kAudio:
       return "audio";
     case network::mojom::RequestDestination::kAudioWorklet:
diff --git a/services/network/sec_header_helpers.cc b/services/network/sec_header_helpers.cc
index 7e64d9cc..dc1dc70e 100644
--- a/services/network/sec_header_helpers.cc
+++ b/services/network/sec_header_helpers.cc
@@ -138,7 +138,12 @@
 // Sec-Fetch-Dest
 void SetSecFetchDestHeader(net::URLRequest* request,
                            network::mojom::RequestDestination dest) {
-  std::string header_value = RequestDestinationToString(dest);
+  // https://w3c.github.io/webappsec-fetch-metadata/#abstract-opdef-set-dest
+  // If r's destination is the empty string, set header's value to the string
+  // "empty". Otherwise, set header's value to r's destination.
+  std::string header_value = dest == mojom::RequestDestination::kEmpty
+                                 ? "empty"
+                                 : RequestDestinationToString(dest);
   request->SetExtraRequestHeaderByName(kSecFetchDest, header_value, true);
 }
 
diff --git a/testing/buildbot/filters/android.emulator_m.content_browsertests.filter b/testing/buildbot/filters/android.emulator_m.content_browsertests.filter
index a21ad8f..9bc9a4a 100644
--- a/testing/buildbot/filters/android.emulator_m.content_browsertests.filter
+++ b/testing/buildbot/filters/android.emulator_m.content_browsertests.filter
@@ -56,4 +56,4 @@
 -RenderFrameHostImplBrowserTest.VisibilityScrolledOutOfView
 
 # crbug.com/1120813
--P/CompositorImplBrowserTestRefreshRate.VideoPreference
+-P/CompositorImplBrowserTestRefreshRate.VideoPreference/*
diff --git a/third_party/blink/common/BUILD.gn b/third_party/blink/common/BUILD.gn
index befabe9f..f52b74a6c 100644
--- a/third_party/blink/common/BUILD.gn
+++ b/third_party/blink/common/BUILD.gn
@@ -108,6 +108,7 @@
     "loader/mime_sniffing_throttle.cc",
     "loader/mime_sniffing_url_loader.cc",
     "loader/network_utils.cc",
+    "loader/record_load_histograms.cc",
     "loader/referrer_utils.cc",
     "loader/resource_type_util.cc",
     "loader/throttling_url_loader.cc",
diff --git a/third_party/blink/common/features.cc b/third_party/blink/common/features.cc
index 35268351..959556f4 100644
--- a/third_party/blink/common/features.cc
+++ b/third_party/blink/common/features.cc
@@ -692,6 +692,11 @@
 const base::Feature kCrOSAutoSelect{"CrOSAutoSelect",
                                     base::FEATURE_DISABLED_BY_DEFAULT};
 
+// Enables compositing of eligible SVG elements to improve animation
+// performance. See crbug.com/1101002.
+const base::Feature kCompositeSVG{"CompositeSVG",
+                                  base::FEATURE_DISABLED_BY_DEFAULT};
+
 const base::Feature kCompositingOptimizations{
     "CompositingOptimizations", base::FEATURE_DISABLED_BY_DEFAULT};
 
diff --git a/content/common/net/record_load_histograms.cc b/third_party/blink/common/loader/record_load_histograms.cc
similarity index 92%
rename from content/common/net/record_load_histograms.cc
rename to third_party/blink/common/loader/record_load_histograms.cc
index a02b49cf..2823576 100644
--- a/content/common/net/record_load_histograms.cc
+++ b/third_party/blink/common/loader/record_load_histograms.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 "content/common/net/record_load_histograms.h"
+#include "third_party/blink/public/common/loader/record_load_histograms.h"
 
 #include "base/metrics/histogram_functions.h"
 #include "base/metrics/histogram_macros.h"
@@ -11,7 +11,7 @@
 #include "net/base/url_util.h"
 #include "url/gurl.h"
 
-namespace content {
+namespace blink {
 
 void RecordLoadHistograms(const url::Origin& origin,
                           network::mojom::RequestDestination destination,
@@ -40,4 +40,4 @@
   }
 }
 
-}  // namespace content
+}  // namespace blink
diff --git a/third_party/blink/common/loader/referrer_utils.cc b/third_party/blink/common/loader/referrer_utils.cc
index c5c7bc8..7b410d9 100644
--- a/third_party/blink/common/loader/referrer_utils.cc
+++ b/third_party/blink/common/loader/referrer_utils.cc
@@ -10,6 +10,28 @@
 #include "third_party/blink/public/common/features.h"
 #include "third_party/blink/public/common/switches.h"
 
+namespace {
+
+// Using an atomic is necessary because this code is called from both the
+// browser and the renderer (so that access is not on a single sequence when in
+// single-process mode), and because it is called from multiple threads within
+// the renderer.
+bool ReadModifyWriteForceLegacyPolicyFlag(
+    base::Optional<bool> maybe_new_value) {
+  // Default to false in the browser process (it is not expected
+  // that the browser will be provided this switch).
+  // The value is propagated to other processes through the command line.
+  DCHECK(base::CommandLine::InitializedForCurrentProcess());
+  static std::atomic<bool> value(
+      base::CommandLine::ForCurrentProcess()->HasSwitch(
+          blink::switches::kForceLegacyDefaultReferrerPolicy));
+  if (!maybe_new_value.has_value())
+    return value;
+  return value.exchange(*maybe_new_value);
+}
+
+}  // namespace
+
 namespace blink {
 
 network::mojom::ReferrerPolicy ReferrerUtils::NetToMojoReferrerPolicy(
@@ -48,30 +70,18 @@
   if (!base::FeatureList::IsEnabled(features::kReducedReferrerGranularity))
     return net::ReferrerPolicy::CLEAR_ON_TRANSITION_FROM_SECURE_TO_INSECURE;
 
-  bool should_force_legacy_default_referrer_policy =
-      ReadModifyWriteForceLegacyPolicyFlag(base::nullopt);
-  return should_force_legacy_default_referrer_policy
+  return ShouldForceLegacyDefaultReferrerPolicy()
              ? net::ReferrerPolicy::CLEAR_ON_TRANSITION_FROM_SECURE_TO_INSECURE
              : net::ReferrerPolicy::
                    REDUCE_GRANULARITY_ON_TRANSITION_CROSS_ORIGIN;
 }
 
-// Using an atomic is necessary because this code is called from both the
-// browser and the renderer (so that access is not on a single sequence when in
-// single-process mode), and because it is called from multiple threads within
-// the renderer.
-bool ReferrerUtils::ReadModifyWriteForceLegacyPolicyFlag(
-    base::Optional<bool> maybe_new_value) {
-  // Default to false in the browser process (it is not expected
-  // that the browser will be provided this switch).
-  // The value is propagated to other processes through the command line.
-  DCHECK(base::CommandLine::InitializedForCurrentProcess());
-  static std::atomic<bool> value(
-      base::CommandLine::ForCurrentProcess()->HasSwitch(
-          switches::kForceLegacyDefaultReferrerPolicy));
-  if (!maybe_new_value.has_value())
-    return value;
-  return value.exchange(*maybe_new_value);
+void ReferrerUtils::SetForceLegacyDefaultReferrerPolicy(bool force) {
+  ReadModifyWriteForceLegacyPolicyFlag(force);
+}
+
+bool ReferrerUtils::ShouldForceLegacyDefaultReferrerPolicy() {
+  return ReadModifyWriteForceLegacyPolicyFlag(base::nullopt);
 }
 
 }  // namespace blink
diff --git a/third_party/blink/common/loader/referrer_utils_unittest.cc b/third_party/blink/common/loader/referrer_utils_unittest.cc
index 435ac5d..6d00d76b 100644
--- a/third_party/blink/common/loader/referrer_utils_unittest.cc
+++ b/third_party/blink/common/loader/referrer_utils_unittest.cc
@@ -22,4 +22,24 @@
             net::ReferrerPolicy::REDUCE_GRANULARITY_ON_TRANSITION_CROSS_ORIGIN);
 }
 
+TEST(DefaultReferrerPolicyTest, SetAndGetForceLegacy) {
+  EXPECT_FALSE(blink::ReferrerUtils::ShouldForceLegacyDefaultReferrerPolicy());
+  blink::ReferrerUtils::SetForceLegacyDefaultReferrerPolicy(true);
+  EXPECT_TRUE(blink::ReferrerUtils::ShouldForceLegacyDefaultReferrerPolicy());
+}
+
+TEST(DefaultReferrerPolicyTest, ForceLegacyOnly) {
+  blink::ReferrerUtils::SetForceLegacyDefaultReferrerPolicy(true);
+  EXPECT_EQ(blink::ReferrerUtils::GetDefaultNetReferrerPolicy(),
+            net::ReferrerPolicy::CLEAR_ON_TRANSITION_FROM_SECURE_TO_INSECURE);
+}
+
+TEST(DefaultReferrerPolicyTest, FeatureAndForceLegacy) {
+  base::test::ScopedFeatureList f;
+  f.InitAndEnableFeature(blink::features::kReducedReferrerGranularity);
+  blink::ReferrerUtils::SetForceLegacyDefaultReferrerPolicy(true);
+  EXPECT_EQ(blink::ReferrerUtils::GetDefaultNetReferrerPolicy(),
+            net::ReferrerPolicy::CLEAR_ON_TRANSITION_FROM_SECURE_TO_INSECURE);
+}
+
 }  // namespace blink
diff --git a/third_party/blink/public/BUILD.gn b/third_party/blink/public/BUILD.gn
index d24b32ac..3bbcce1c 100644
--- a/third_party/blink/public/BUILD.gn
+++ b/third_party/blink/public/BUILD.gn
@@ -278,6 +278,7 @@
     "platform/websocket_handshake_throttle.h",
     "web/blink.h",
     "web/modules/autofill/web_form_element_observer.h",
+    "web/modules/media/audio/web_audio_output_ipc_factory.h",
     "web/modules/media/webmediaplayer_util.h",
     "web/modules/mediastream/encoded_video_frame.h",
     "web/modules/mediastream/media_stream_video_sink.h",
diff --git a/third_party/blink/public/common/BUILD.gn b/third_party/blink/public/common/BUILD.gn
index 56f55a8..4d63d23 100644
--- a/third_party/blink/public/common/BUILD.gn
+++ b/third_party/blink/public/common/BUILD.gn
@@ -116,6 +116,7 @@
     "loader/mime_sniffing_url_loader.h",
     "loader/network_utils.h",
     "loader/previews_state.h",
+    "loader/record_load_histograms.h",
     "loader/referrer_utils.h",
     "loader/resource_type_util.h",
     "loader/url_loader_factory_bundle.h",
diff --git a/third_party/blink/public/common/features.h b/third_party/blink/public/common/features.h
index edba1c2..19471ff 100644
--- a/third_party/blink/public/common/features.h
+++ b/third_party/blink/public/common/features.h
@@ -277,6 +277,7 @@
 
 BLINK_COMMON_EXPORT extern const base::Feature kCrOSAutoSelect;
 
+BLINK_COMMON_EXPORT extern const base::Feature kCompositeSVG;
 BLINK_COMMON_EXPORT extern const base::Feature kCompositingOptimizations;
 
 BLINK_COMMON_EXPORT extern const base::Feature kReducedReferrerGranularity;
diff --git a/third_party/blink/public/common/loader/record_load_histograms.h b/third_party/blink/public/common/loader/record_load_histograms.h
new file mode 100644
index 0000000..2907426
--- /dev/null
+++ b/third_party/blink/public/common/loader/record_load_histograms.h
@@ -0,0 +1,25 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_PUBLIC_COMMON_LOADER_RECORD_LOAD_HISTOGRAMS_H_
+#define THIRD_PARTY_BLINK_PUBLIC_COMMON_LOADER_RECORD_LOAD_HISTOGRAMS_H_
+
+#include "services/network/public/mojom/fetch_api.mojom-shared.h"
+#include "third_party/blink/public/common/common_export.h"
+#include "url/origin.h"
+
+namespace blink {
+
+// Logs histograms when a resource destined for a renderer (One with a
+// network::mojom::RequestDestination) finishes loading, or when a load is
+// aborted. Not used for internal network requests initiated by the browser
+// itself.
+BLINK_COMMON_EXPORT void RecordLoadHistograms(
+    const url::Origin& origin,
+    network::mojom::RequestDestination destination,
+    int net_error);
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_PUBLIC_COMMON_LOADER_RECORD_LOAD_HISTOGRAMS_H_
diff --git a/third_party/blink/public/common/loader/referrer_utils.h b/third_party/blink/public/common/loader/referrer_utils.h
index 466255c..0136839ca 100644
--- a/third_party/blink/public/common/loader/referrer_utils.h
+++ b/third_party/blink/public/common/loader/referrer_utils.h
@@ -19,8 +19,13 @@
 
   static BLINK_COMMON_EXPORT net::ReferrerPolicy GetDefaultNetReferrerPolicy();
 
-  static BLINK_COMMON_EXPORT bool ReadModifyWriteForceLegacyPolicyFlag(
-      base::Optional<bool> maybe_new_value);
+  // Configures retaining the pre-M80 default referrer
+  // policy of no-referrer-when-downgrade.
+  // TODO(crbug.com/1016541): After M88, remove when the corresponding
+  // enterprise policy has been deleted.
+  static BLINK_COMMON_EXPORT void SetForceLegacyDefaultReferrerPolicy(
+      bool force);
+  static BLINK_COMMON_EXPORT bool ShouldForceLegacyDefaultReferrerPolicy();
 };
 
 }  // namespace blink
diff --git a/third_party/blink/public/mojom/feature_policy/document_policy_feature.mojom b/third_party/blink/public/mojom/feature_policy/document_policy_feature.mojom
index 86ee7e7..ed7c671 100644
--- a/third_party/blink/public/mojom/feature_policy/document_policy_feature.mojom
+++ b/third_party/blink/public/mojom/feature_policy/document_policy_feature.mojom
@@ -26,6 +26,10 @@
   kUnsizedMedia = 7,
   // Restricts the usage of layout-causing animations in a document.
   kLayoutAnimations = 8,
+  // Controls the ability of the document to use several dynamic markup API
+  // which interfere with document's input stream (document.write(),
+  // document.close(), etc.).
+  kDocumentWrite = 9,
   // Don't change assigned numbers of any item, and don't reuse removed slots.
   // Add new features at the end of the enum.
   // Also, run update_document_policy_enum.py in
diff --git a/third_party/blink/public/mojom/feature_policy/feature_policy_feature.mojom b/third_party/blink/public/mojom/feature_policy/feature_policy_feature.mojom
index 4a8a505..f44b30c 100644
--- a/third_party/blink/public/mojom/feature_policy/feature_policy_feature.mojom
+++ b/third_party/blink/public/mojom/feature_policy/feature_policy_feature.mojom
@@ -49,10 +49,6 @@
   kPictureInPicture = 26,
   // Controls the ability to block and interfere with vertical scrolling.
   kVerticalScroll = 27,
-  // Controls the ability of the document to use several dynamic markup API
-  // which interfere with document's input stream (document.write(),
-  // document.close(), etc.).
-  kDocumentWrite = 28,
   // Controls access to Screen Wake Lock
   kScreenWakeLock = 31,
   // These are the defined sandbox features implemented as policy-controlled
diff --git a/third_party/blink/public/platform/web_runtime_features.h b/third_party/blink/public/platform/web_runtime_features.h
index 2811c80..3303e3c 100644
--- a/third_party/blink/public/platform/web_runtime_features.h
+++ b/third_party/blink/public/platform/web_runtime_features.h
@@ -253,6 +253,7 @@
   BLINK_PLATFORM_EXPORT static void EnableConversionMeasurementInfraSupport(
       bool);
 
+  BLINK_PLATFORM_EXPORT static void EnableCompositeSVG(bool);
   BLINK_PLATFORM_EXPORT static void EnableCompositingOptimizations(bool);
 
  private:
diff --git a/third_party/blink/public/web/modules/media/audio/web_audio_output_ipc_factory.h b/third_party/blink/public/web/modules/media/audio/web_audio_output_ipc_factory.h
new file mode 100644
index 0000000..adb61e6
--- /dev/null
+++ b/third_party/blink/public/web/modules/media/audio/web_audio_output_ipc_factory.h
@@ -0,0 +1,72 @@
+// 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 THIRD_PARTY_BLINK_PUBLIC_WEB_MODULES_MEDIA_AUDIO_WEB_AUDIO_OUTPUT_IPC_FACTORY_H_
+#define THIRD_PARTY_BLINK_PUBLIC_WEB_MODULES_MEDIA_AUDIO_WEB_AUDIO_OUTPUT_IPC_FACTORY_H_
+
+#include <memory>
+
+#include "base/memory/scoped_refptr.h"
+#include "base/unguessable_token.h"
+#include "third_party/blink/public/platform/web_common.h"
+
+namespace base {
+class SingleThreadTaskRunner;
+}
+
+namespace media {
+class AudioOutputIPC;
+}
+
+namespace blink {
+class BrowserInterfaceBrokerProxy;
+
+// This is a factory for AudioOutputIPC objects. It is threadsafe. This class
+// is designed to be leaked at shutdown, as it posts tasks to itself using
+// base::Unretained and also hands out references to itself in the
+// AudioOutputIPCs it creates, but in the case where the owner is sure that
+// there are no outstanding references (such as in a unit test), the class can
+// be destructed.
+// TODO(maxmorin): Registering the factories for each frame will become
+// unnecessary when https://crbug.com/668275 is fixed. When that is done, this
+// class can be greatly simplified.
+class BLINK_MODULES_EXPORT WebAudioOutputIPCFactory {
+ public:
+  explicit WebAudioOutputIPCFactory(
+      scoped_refptr<base::SingleThreadTaskRunner> io_task_runner);
+  ~WebAudioOutputIPCFactory();
+
+  static WebAudioOutputIPCFactory* get() { return instance_; }
+
+  const scoped_refptr<base::SingleThreadTaskRunner>& io_task_runner() const;
+
+  // Enables |this| to create MojoAudioOutputIPCs for the specified frame.
+  // Does nothing if not using mojo factories.
+  void RegisterRemoteFactory(
+      const base::UnguessableToken& frame_token,
+      blink::BrowserInterfaceBrokerProxy* interface_broker);
+
+  // Every call to the above method must be matched by a call to this one when
+  // the frame is destroyed. Does nothing if not using mojo factories.
+  void MaybeDeregisterRemoteFactory(const base::UnguessableToken& frame_token);
+
+  // The returned object may only be used on |io_task_runner()|.
+  std::unique_ptr<media::AudioOutputIPC> CreateAudioOutputIPC(
+      const base::UnguessableToken& frame_token) const;
+
+ private:
+  // TODO(https://crbug.com/787252): When this header gets moved out of the
+  // Blink public API layer, move this Pimpl class back to its outer class.
+  class Impl;
+  std::unique_ptr<Impl> impl_;
+
+  // Global instance, set in constructor and unset in destructor.
+  static WebAudioOutputIPCFactory* instance_;
+
+  DISALLOW_COPY_AND_ASSIGN(WebAudioOutputIPCFactory);
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_PUBLIC_WEB_MODULES_MEDIA_AUDIO_WEB_AUDIO_OUTPUT_IPC_FACTORY_H_
diff --git a/third_party/blink/renderer/bindings/IDLExtendedAttributes.txt b/third_party/blink/renderer/bindings/IDLExtendedAttributes.txt
index fff0e107..55eca856 100644
--- a/third_party/blink/renderer/bindings/IDLExtendedAttributes.txt
+++ b/third_party/blink/renderer/bindings/IDLExtendedAttributes.txt
@@ -77,6 +77,7 @@
 LogActivity=|GetterOnly|SetterOnly
 LogAllWorlds
 NewObject
+NoAllocDirectCall
 Measure
 MeasureAs=*
 NamedConstructor=*
diff --git a/third_party/blink/renderer/bindings/core/v8/v8_dom_configuration.cc b/third_party/blink/renderer/bindings/core/v8/v8_dom_configuration.cc
index 0e69088..05457977 100644
--- a/third_party/blink/renderer/bindings/core/v8/v8_dom_configuration.cc
+++ b/third_party/blink/renderer/bindings/core/v8/v8_dom_configuration.cc
@@ -463,7 +463,8 @@
                            v8::Local<v8::FunctionTemplate> interface_template,
                            v8::Local<v8::Signature> signature,
                            const Configuration& method,
-                           const DOMWrapperWorld& world) {
+                           const DOMWrapperWorld& world,
+                           const v8::CFunction* v8_c_function = nullptr) {
   if (!WorldConfigurationApplies(method, world))
     return;
 
@@ -488,7 +489,7 @@
     v8::Local<v8::FunctionTemplate> function_template =
         v8::FunctionTemplate::New(
             isolate, callback, v8::Local<v8::Value>(), signature, method.length,
-            v8::ConstructorBehavior::kAllow, side_effect_type);
+            v8::ConstructorBehavior::kAllow, side_effect_type, v8_c_function);
     function_template->RemovePrototype();
     function_template->SetAcceptAnyReceiver(
         method.access_check_configuration ==
@@ -792,6 +793,22 @@
                           interface_template, signature, methods[i], world);
 }
 
+void V8DOMConfiguration::InstallMethods(
+    v8::Isolate* isolate,
+    const DOMWrapperWorld& world,
+    v8::Local<v8::ObjectTemplate> instance_template,
+    v8::Local<v8::ObjectTemplate> prototype_template,
+    v8::Local<v8::FunctionTemplate> interface_template,
+    v8::Local<v8::Signature> signature,
+    const NoAllocDirectCallMethodConfiguration* methods,
+    size_t method_count) {
+  for (size_t i = 0; i < method_count; ++i) {
+    InstallMethodInternal(
+        isolate, instance_template, prototype_template, interface_template,
+        signature, methods[i].method_config, world, &methods[i].v8_c_function);
+  }
+}
+
 void V8DOMConfiguration::InstallMethod(
     v8::Isolate* isolate,
     const DOMWrapperWorld& world,
diff --git a/third_party/blink/renderer/bindings/core/v8/v8_dom_configuration.h b/third_party/blink/renderer/bindings/core/v8/v8_dom_configuration.h
index 5bceb00..a88efd6 100644
--- a/third_party/blink/renderer/bindings/core/v8/v8_dom_configuration.h
+++ b/third_party/blink/renderer/bindings/core/v8/v8_dom_configuration.h
@@ -34,6 +34,7 @@
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/platform/bindings/v8_dom_wrapper.h"
 #include "third_party/blink/renderer/platform/bindings/v8_private_property.h"
+#include "v8/include/v8-fast-api-calls.h"
 #include "v8/include/v8.h"
 
 namespace blink {
@@ -89,9 +90,12 @@
   // AttributeConfiguration translates into calls to SetNativeDataProperty() on
   // either of instance or prototype object (or their object template).
   struct AttributeConfiguration {
-    AttributeConfiguration& operator=(const AttributeConfiguration&) = delete;
     DISALLOW_NEW();
-    const char* const name;
+
+   public:
+    AttributeConfiguration& operator=(const AttributeConfiguration&) = delete;
+
+    const char* name;
     v8::AccessorNameGetterCallback getter;
     v8::AccessorNameSetterCallback setter;
 
@@ -140,9 +144,12 @@
   // either of instance, prototype, or interface object (or their object
   // template).
   struct AccessorConfiguration {
-    AccessorConfiguration& operator=(const AccessorConfiguration&) = delete;
     DISALLOW_NEW();
-    const char* const name;
+
+   public:
+    AccessorConfiguration& operator=(const AccessorConfiguration&) = delete;
+
+    const char* name;
     v8::FunctionCallback getter;
     v8::FunctionCallback setter;
     // V8PrivateProperty::CachedAccessor
@@ -211,6 +218,9 @@
   // object's constants. It sets the constant on both the FunctionTemplate and
   // the ObjectTemplate. PropertyAttributes is always ReadOnly.
   struct ConstantConfiguration {
+    DISALLOW_NEW();
+
+   public:
     constexpr ConstantConfiguration(const char* name,
                                     ConstantType type,
                                     int value)
@@ -220,8 +230,8 @@
                                     double value)
         : name(name), type(type), dvalue(value) {}
     ConstantConfiguration& operator=(const ConstantConfiguration&) = delete;
-    DISALLOW_NEW();
-    const char* const name;
+
+    const char* name;
     ConstantType type;
     union {
       int ivalue;
@@ -243,8 +253,8 @@
     ConstantCallbackConfiguration& operator=(
         const ConstantCallbackConfiguration&) = delete;
 
-    const char* const name;
-    const v8::AccessorNameGetterCallback getter;
+    const char* name;
+    v8::AccessorNameGetterCallback getter;
   };
 
   // Constant installation
@@ -296,13 +306,16 @@
   // object's callbacks. It sets a method on instance, prototype or
   // interface object (or their object tepmplate).
   struct MethodConfiguration {
-    MethodConfiguration& operator=(const MethodConfiguration&) = delete;
     DISALLOW_NEW();
+
+   public:
+    MethodConfiguration& operator=(const MethodConfiguration&) = delete;
+
     v8::Local<v8::String> MethodName(v8::Isolate* isolate) const {
       return V8AtomicString(isolate, name);
     }
 
-    const char* const name;
+    const char* name;
     v8::FunctionCallback callback;
     int length;
     // v8::PropertyAttribute
@@ -320,15 +333,18 @@
   };
 
   struct SymbolKeyedMethodConfiguration {
+    DISALLOW_NEW();
+
+   public:
     SymbolKeyedMethodConfiguration& operator=(
         const SymbolKeyedMethodConfiguration&) = delete;
-    DISALLOW_NEW();
+
     v8::Local<v8::Name> MethodName(v8::Isolate* isolate) const {
       return get_symbol(isolate);
     }
 
     v8::Local<v8::Symbol> (*get_symbol)(v8::Isolate*);
-    const char* const symbol_alias;
+    const char* symbol_alias;
     v8::FunctionCallback callback;
     // SymbolKeyedMethodConfiguration doesn't support per-world bindings.
     int length;
@@ -344,6 +360,17 @@
     unsigned side_effect_type : 1;
   };
 
+  struct NoAllocDirectCallMethodConfiguration {
+    DISALLOW_NEW();
+
+   public:
+    NoAllocDirectCallMethodConfiguration& operator=(
+        const NoAllocDirectCallMethodConfiguration&) = delete;
+
+    MethodConfiguration method_config;
+    v8::CFunction v8_c_function;
+  };
+
   static void InstallMethods(v8::Isolate*,
                              const DOMWrapperWorld&,
                              v8::Local<v8::ObjectTemplate> instance_template,
@@ -352,6 +379,15 @@
                              v8::Local<v8::Signature>,
                              const MethodConfiguration*,
                              size_t method_count);
+  static void InstallMethods(v8::Isolate*,
+                             const DOMWrapperWorld&,
+                             v8::Local<v8::ObjectTemplate> instance_template,
+                             v8::Local<v8::ObjectTemplate> prototype_template,
+                             v8::Local<v8::FunctionTemplate> interface_template,
+                             v8::Local<v8::Signature>,
+                             const NoAllocDirectCallMethodConfiguration*,
+                             size_t method_count);
+
   static void InstallMethod(v8::Isolate*,
                             const DOMWrapperWorld&,
                             v8::Local<v8::ObjectTemplate> instance_template,
diff --git a/third_party/blink/renderer/bindings/generated_in_modules.gni b/third_party/blink/renderer/bindings/generated_in_modules.gni
index a8dbc80..ff90454 100644
--- a/third_party/blink/renderer/bindings/generated_in_modules.gni
+++ b/third_party/blink/renderer/bindings/generated_in_modules.gni
@@ -788,6 +788,8 @@
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_client_type.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_color_gamut.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_color_gamut.h",
+  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_codec_state.cc",
+  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_codec_state.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_connection_type.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_connection_type.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_contact_property.cc",
diff --git a/third_party/blink/renderer/bindings/idl_in_modules.gni b/third_party/blink/renderer/bindings/idl_in_modules.gni
index 2b6010d..d3d26144 100644
--- a/third_party/blink/renderer/bindings/idl_in_modules.gni
+++ b/third_party/blink/renderer/bindings/idl_in_modules.gni
@@ -736,6 +736,7 @@
           "//third_party/blink/renderer/modules/webcodecs/audio_frame.idl",
           "//third_party/blink/renderer/modules/webcodecs/audio_frame_init.idl",
           "//third_party/blink/renderer/modules/webcodecs/audio_frame_output_callback.idl",
+          "//third_party/blink/renderer/modules/webcodecs/codec_state.idl",
           "//third_party/blink/renderer/modules/webcodecs/encoded_audio_chunk.idl",
           "//third_party/blink/renderer/modules/webcodecs/encoded_audio_chunk_init.idl",
           "//third_party/blink/renderer/modules/webcodecs/encoded_audio_config.idl",
diff --git a/third_party/blink/renderer/bindings/scripts/bind_gen/interface.py b/third_party/blink/renderer/bindings/scripts/bind_gen/interface.py
index 4ccb29a..587ec371 100644
--- a/third_party/blink/renderer/bindings/scripts/bind_gen/interface.py
+++ b/third_party/blink/renderer/bindings/scripts/bind_gen/interface.py
@@ -95,7 +95,8 @@
 
 def callback_function_name(cg_context,
                            overload_index=None,
-                           for_cross_origin=False):
+                           for_cross_origin=False,
+                           no_alloc_direct_call=False):
     assert isinstance(cg_context, CodeGenContext)
 
     def _cxx_name(name):
@@ -150,6 +151,8 @@
         suffix = "CrossOrigin"
     elif overload_index is not None:
         suffix = "Overload{}".format(overload_index + 1)
+    elif no_alloc_direct_call:
+        suffix = "NoAllocDirectCallback"
     else:
         suffix = "Callback"
 
@@ -2114,6 +2117,54 @@
     return SequenceNode([named_ctor_def, EmptyNode(), func_def])
 
 
+def make_no_alloc_direct_call_callback_def(cg_context, function_name):
+    assert isinstance(cg_context, CodeGenContext)
+    assert isinstance(function_name, str)
+
+    assert cg_context.operation_group and len(cg_context.operation_group) == 1
+
+    func_like = cg_context.operation_group[0]
+
+    return_type = ("void" if func_like.return_type.is_void else
+                   blink_type_info(func_like.return_type).value_t)
+    arg_type_and_names = [(blink_type_info(arg.idl_type).value_t,
+                           name_style.arg_f("arg{}_{}", index + 1,
+                                            arg.identifier))
+                          for index, arg in enumerate(func_like.arguments)]
+    arg_decls = ["v8::ApiObject arg0_receiver"] + [
+        "{} {}".format(arg_type, arg_name)
+        for arg_type, arg_name in arg_type_and_names
+    ]
+    func_def = CxxFuncDefNode(name=function_name,
+                              arg_decls=arg_decls,
+                              return_type=return_type)
+    body = func_def.body
+
+    pattern = """\
+ThreadState::NoAllocationScope no_alloc_scope(ThreadState::Current());
+v8::Object* v8_receiver = reinterpret_cast<v8::Object*>(&arg0_receiver);
+v8::Isolate* isolate = v8_receiver->GetIsolate();
+v8::Isolate::DisallowJavascriptExecutionScope no_js_exec_scope(
+    isolate,
+    v8::Isolate::DisallowJavascriptExecutionScope::CRASH_ON_FAILURE);
+{blink_class}* blink_receiver =
+    ToScriptWrappable(v8_receiver)->ToImpl<{blink_class}>();
+return blink_receiver->{member_func}({blink_arguments});\
+"""
+    blink_class = blink_class_name(cg_context.interface)
+    member_func = backward_compatible_api_func(cg_context)
+    blink_arguments = ", ".join(
+        [arg_name for arg_type, arg_name in arg_type_and_names])
+    body.append(
+        TextNode(
+            _format(pattern,
+                    blink_class=blink_class,
+                    member_func=member_func,
+                    blink_arguments=blink_arguments)))
+
+    return func_def
+
+
 def make_operation_entry(cg_context):
     assert isinstance(cg_context, CodeGenContext)
 
@@ -2163,7 +2214,9 @@
     return func_def
 
 
-def make_operation_callback_def(cg_context, function_name):
+def make_operation_callback_def(cg_context,
+                                function_name,
+                                no_alloc_direct_callback_name=None):
     assert isinstance(cg_context, CodeGenContext)
     assert isinstance(function_name, str)
 
@@ -2171,6 +2224,19 @@
 
     assert (not ("Custom" in operation_group.extended_attributes)
             or len(operation_group) == 1)
+    assert (not ("NoAllocDirectCall" in operation_group.extended_attributes)
+            or len(operation_group) == 1)
+
+    if "NoAllocDirectCall" in operation_group.extended_attributes:
+        return ListNode([
+            make_operation_function_def(
+                cg_context.make_copy(operation=operation_group[0]),
+                function_name),
+            EmptyNode(),
+            make_no_alloc_direct_call_callback_def(
+                cg_context.make_copy(operation=operation_group[0]),
+                no_alloc_direct_callback_name),
+        ])
 
     if len(operation_group) == 1:
         return make_operation_function_def(
@@ -4278,49 +4344,6 @@
     return "V8PrivateProperty::CachedAccessor::{}".format(value or "kNone")
 
 
-def _make_property_entry_v8_property_attribute(property_):
-    values = []
-    if "NotEnumerable" in property_.extended_attributes:
-        values.append("v8::DontEnum")
-    if "LegacyUnforgeable" in property_.extended_attributes:
-        if not isinstance(property_, web_idl.Attribute):
-            values.append("v8::ReadOnly")
-        values.append("v8::DontDelete")
-    if not values:
-        values.append("v8::None")
-    if len(values) == 1:
-        return values[0]
-    else:
-        return "static_cast<v8::PropertyAttribute>({})".format(
-            " | ".join(values))
-
-
-def _make_property_entry_on_which_object(property_):
-    ON_INSTANCE = "V8DOMConfiguration::kOnInstance"
-    ON_PROTOTYPE = "V8DOMConfiguration::kOnPrototype"
-    ON_INTERFACE = "V8DOMConfiguration::kOnInterface"
-    if isinstance(property_, web_idl.Constant):
-        return ON_INTERFACE
-    if hasattr(property_, "is_static") and property_.is_static:
-        return ON_INTERFACE
-    if "Global" in property_.owner.extended_attributes:
-        return ON_INSTANCE
-    if "LegacyUnforgeable" in property_.extended_attributes:
-        return ON_INSTANCE
-    return ON_PROTOTYPE
-
-
-def _make_property_entry_check_receiver(property_):
-    if ("LenientThis" in property_.extended_attributes
-            or (isinstance(property_, web_idl.Attribute)
-                and property_.idl_type.unwrap().is_promise)
-            or (isinstance(property_, web_idl.OverloadGroup)
-                and property_[0].return_type.unwrap().is_promise)):
-        return "V8DOMConfiguration::kDoNotCheckHolder"
-    else:
-        return "V8DOMConfiguration::kCheckHolder"
-
-
 def _make_property_entry_check_cross_origin_access(property_,
                                                    is_get=False,
                                                    is_set=False):
@@ -4339,21 +4362,15 @@
         return constants[True]
 
 
-def _make_property_entry_has_side_effect(property_):
-    if property_.extended_attributes.value_of("Affects") == "Nothing":
-        return "V8DOMConfiguration::kHasNoSideEffect"
+def _make_property_entry_check_receiver(property_):
+    if ("LenientThis" in property_.extended_attributes
+            or (isinstance(property_, web_idl.Attribute)
+                and property_.idl_type.unwrap().is_promise)
+            or (isinstance(property_, web_idl.OverloadGroup)
+                and property_[0].return_type.unwrap().is_promise)):
+        return "V8DOMConfiguration::kDoNotCheckHolder"
     else:
-        return "V8DOMConfiguration::kHasSideEffect"
-
-
-def _make_property_entry_world(world):
-    if world == CodeGenContext.MAIN_WORLD:
-        return "V8DOMConfiguration::kMainWorld"
-    if world == CodeGenContext.NON_MAIN_WORLDS:
-        return "V8DOMConfiguration::kNonMainWorlds"
-    if world == CodeGenContext.ALL_WORLDS:
-        return "V8DOMConfiguration::kAllWorlds"
-    assert False
+        return "V8DOMConfiguration::kCheckHolder"
 
 
 def _make_property_entry_constant_type_and_value_format(property_):
@@ -4373,6 +4390,62 @@
     assert False, "Unsupported type: {}".format(idl_type.syntactic_form)
 
 
+def _make_property_entry_has_side_effect(property_):
+    if property_.extended_attributes.value_of("Affects") == "Nothing":
+        return "V8DOMConfiguration::kHasNoSideEffect"
+    else:
+        return "V8DOMConfiguration::kHasSideEffect"
+
+
+def _make_property_entry_on_which_object(property_):
+    ON_INSTANCE = "V8DOMConfiguration::kOnInstance"
+    ON_PROTOTYPE = "V8DOMConfiguration::kOnPrototype"
+    ON_INTERFACE = "V8DOMConfiguration::kOnInterface"
+    if isinstance(property_, web_idl.Constant):
+        return ON_INTERFACE
+    if hasattr(property_, "is_static") and property_.is_static:
+        return ON_INTERFACE
+    if "Global" in property_.owner.extended_attributes:
+        return ON_INSTANCE
+    if "LegacyUnforgeable" in property_.extended_attributes:
+        return ON_INSTANCE
+    return ON_PROTOTYPE
+
+
+def _make_property_entry_v8_c_function(entry):
+    if entry.no_alloc_direct_callback_name is None:
+        return None
+    return "v8::CFunction::Make({})".format(
+        entry.no_alloc_direct_callback_name)
+
+
+def _make_property_entry_v8_property_attribute(property_):
+    values = []
+    if "NotEnumerable" in property_.extended_attributes:
+        values.append("v8::DontEnum")
+    if "LegacyUnforgeable" in property_.extended_attributes:
+        if not isinstance(property_, web_idl.Attribute):
+            values.append("v8::ReadOnly")
+        values.append("v8::DontDelete")
+    if not values:
+        values.append("v8::None")
+    if len(values) == 1:
+        return values[0]
+    else:
+        return "static_cast<v8::PropertyAttribute>({})".format(
+            " | ".join(values))
+
+
+def _make_property_entry_world(world):
+    if world == CodeGenContext.MAIN_WORLD:
+        return "V8DOMConfiguration::kMainWorld"
+    if world == CodeGenContext.NON_MAIN_WORLDS:
+        return "V8DOMConfiguration::kNonMainWorlds"
+    if world == CodeGenContext.ALL_WORLDS:
+        return "V8DOMConfiguration::kAllWorlds"
+    assert False
+
+
 def _make_attribute_registration_table(table_name, attribute_entries):
     assert isinstance(table_name, str)
     assert isinstance(attribute_entries, (list, tuple))
@@ -4421,7 +4494,7 @@
         entry_nodes.append(T(text))
 
     return ListNode([
-        T("static constexpr V8DOMConfiguration::AccessorConfiguration " +
+        T("static constexpr const V8DOMConfiguration::AccessorConfiguration " +
           table_name + "[] = {"),
         ListNode(entry_nodes),
         T("};"),
@@ -4448,8 +4521,9 @@
         entry_nodes.append(T(text))
 
     return ListNode([
-        T("static constexpr V8DOMConfiguration::ConstantCallbackConfiguration "
-          + table_name + "[] = {"),
+        T("static constexpr const "
+          "V8DOMConfiguration::ConstantCallbackConfiguration " + table_name +
+          "[] = {"),
         ListNode(entry_nodes),
         T("};"),
     ])
@@ -4484,7 +4558,7 @@
         entry_nodes.append(T(text))
 
     return ListNode([
-        T("static constexpr V8DOMConfiguration::ConstantConfiguration " +
+        T("static constexpr const V8DOMConfiguration::ConstantConfiguration " +
           table_name + "[] = {"),
         ListNode(entry_nodes),
         T("};"),
@@ -4522,8 +4596,8 @@
         entry_nodes.append(T(text))
 
     return ListNode([
-        T("static constexpr V8DOMConfiguration::AttributeConfiguration " +
-          table_name + "[] = {"),
+        T("static constexpr const V8DOMConfiguration::AttributeConfiguration "
+          + table_name + "[] = {"),
         ListNode(entry_nodes),
         T("};"),
     ])
@@ -4538,6 +4612,14 @@
 
     T = TextNode
 
+    no_alloc_direct_call_count = 0
+    for entry in operation_entries:
+        if entry.no_alloc_direct_callback_name:
+            no_alloc_direct_call_count += 1
+    assert (no_alloc_direct_call_count == 0
+            or no_alloc_direct_call_count == len(operation_entries))
+    no_alloc_direct_call_enabled = no_alloc_direct_call_count > 0
+
     entry_nodes = []
     for entry in operation_entries:
         pattern = ("{{"
@@ -4551,6 +4633,8 @@
                    "{has_side_effect}, "
                    "{world}"
                    "}}, ")
+        if no_alloc_direct_call_enabled:
+            pattern = "{{" + pattern + "{v8_c_function}}}, "
         text = _format(
             pattern,
             property_name=entry.property_.identifier,
@@ -4567,12 +4651,17 @@
                     entry.property_)),
             has_side_effect=_make_property_entry_has_side_effect(
                 entry.property_),
-            world=_make_property_entry_world(entry.world))
+            world=_make_property_entry_world(entry.world),
+            v8_c_function=_make_property_entry_v8_c_function(entry))
         entry_nodes.append(T(text))
 
+    table_decl_before_name = (
+        "static constexpr const V8DOMConfiguration::MethodConfiguration")
+    if no_alloc_direct_call_enabled:
+        table_decl_before_name = ("static const V8DOMConfiguration::"
+                                  "NoAllocDirectCallMethodConfiguration")
     return ListNode([
-        T("static constexpr V8DOMConfiguration::MethodConfiguration " +
-          table_name + "[] = {"),
+        T(table_decl_before_name + " " + table_name + "[] = {"),
         ListNode(entry_nodes),
         T("};"),
     ])
@@ -4637,8 +4726,14 @@
 
 
 class _PropEntryOperationGroup(_PropEntryBase):
-    def __init__(self, is_context_dependent, exposure_conditional, world,
-                 operation_group, op_callback_name, op_func_length):
+    def __init__(self,
+                 is_context_dependent,
+                 exposure_conditional,
+                 world,
+                 operation_group,
+                 op_callback_name,
+                 op_func_length,
+                 no_alloc_direct_callback_name=None):
         assert isinstance(op_callback_name, str)
         assert isinstance(op_func_length, (int, long))
 
@@ -4646,6 +4741,7 @@
                                 exposure_conditional, world, operation_group)
         self.op_callback_name = op_callback_name
         self.op_func_length = op_func_length
+        self.no_alloc_direct_callback_name = no_alloc_direct_callback_name
 
 
 def _make_property_entries_and_callback_defs(
@@ -4839,7 +4935,14 @@
         cgc = cg_context.make_copy(
             operation_group=operation_group, for_world=world)
         op_callback_name = callback_function_name(cgc)
-        op_callback_node = make_operation_callback_def(cgc, op_callback_name)
+        no_alloc_direct_callback_name = (
+            callback_function_name(cgc, no_alloc_direct_call=True)
+            if "NoAllocDirectCall" in operation_group.extended_attributes else
+            None)
+        op_callback_node = make_operation_callback_def(
+            cgc,
+            op_callback_name,
+            no_alloc_direct_callback_name=no_alloc_direct_callback_name)
 
         callback_def_nodes.extend([
             op_callback_node,
@@ -4853,7 +4956,8 @@
                 world=world,
                 operation_group=operation_group,
                 op_callback_name=op_callback_name,
-                op_func_length=operation_group.min_num_of_required_arguments))
+                op_func_length=operation_group.min_num_of_required_arguments,
+                no_alloc_direct_callback_name=no_alloc_direct_callback_name))
 
     def process_stringifier(_, is_context_dependent, exposure_conditional,
                             world):
@@ -5538,8 +5642,14 @@
             "${instance_template}, ${prototype_template}, "
             "${interface_template}, ${signature}, "
             "kOperationTable, base::size(kOperationTable));")
-    install_properties(table_name, operation_entries,
-                       _make_operation_registration_table, installer_call_text)
+    entries = filter(lambda entry: not entry.no_alloc_direct_callback_name,
+                     operation_entries)
+    install_properties(table_name, entries, _make_operation_registration_table,
+                       installer_call_text)
+    entries = filter(lambda entry: entry.no_alloc_direct_callback_name,
+                     operation_entries)
+    install_properties(table_name, entries, _make_operation_registration_table,
+                       installer_call_text)
 
     return func_decl, func_def, trampoline_def
 
diff --git a/third_party/blink/renderer/bindings/scripts/web_idl/idl_compiler.py b/third_party/blink/renderer/bindings/scripts/web_idl/idl_compiler.py
index 63bc83f..d0d10a5 100644
--- a/third_party/blink/renderer/bindings/scripts/web_idl/idl_compiler.py
+++ b/third_party/blink/renderer/bindings/scripts/web_idl/idl_compiler.py
@@ -518,8 +518,8 @@
 
     def _propagate_extattrs_to_overload_group(self):
         ANY_OF = ('CrossOrigin', 'Custom', 'LegacyUnforgeable', 'LenientThis',
-                  'NotEnumerable', 'PerWorldBindings', 'SecureContext',
-                  'Unscopable')
+                  'NoAllocDirectCall', 'NotEnumerable', 'PerWorldBindings',
+                  'SecureContext', 'Unscopable')
 
         old_irs = self._ir_map.irs_of_kinds(IRMap.IR.Kind.INTERFACE,
                                             IRMap.IR.Kind.NAMESPACE)
diff --git a/third_party/blink/renderer/core/animation/BUILD.gn b/third_party/blink/renderer/core/animation/BUILD.gn
index ed10902..a06ec21 100644
--- a/third_party/blink/renderer/core/animation/BUILD.gn
+++ b/third_party/blink/renderer/core/animation/BUILD.gn
@@ -60,6 +60,8 @@
     "css/css_animations.h",
     "css/css_keyframe_effect_model.cc",
     "css/css_keyframe_effect_model.h",
+    "css/css_scroll_timeline.cc",
+    "css/css_scroll_timeline.h",
     "css/css_timing_data.cc",
     "css/css_timing_data.h",
     "css/css_transition.cc",
diff --git a/third_party/blink/renderer/core/animation/animation_timeline.h b/third_party/blink/renderer/core/animation/animation_timeline.h
index 2d68a4a..a86e00e 100644
--- a/third_party/blink/renderer/core/animation/animation_timeline.h
+++ b/third_party/blink/renderer/core/animation/animation_timeline.h
@@ -42,6 +42,7 @@
 
   virtual bool IsDocumentTimeline() const { return false; }
   virtual bool IsScrollTimeline() const { return false; }
+  virtual bool IsCSSScrollTimeline() const { return false; }
   virtual bool IsActive() const = 0;
   virtual double ZeroTimeInSeconds() = 0;
   // https://drafts.csswg.org/web-animations/#monotonically-increasing-timeline
diff --git a/third_party/blink/renderer/core/animation/css/css_animations.cc b/third_party/blink/renderer/core/animation/css/css_animations.cc
index dd84051..559fe98 100644
--- a/third_party/blink/renderer/core/animation/css/css_animations.cc
+++ b/third_party/blink/renderer/core/animation/css/css_animations.cc
@@ -40,6 +40,7 @@
 #include "third_party/blink/renderer/core/animation/css/compositor_keyframe_value_factory.h"
 #include "third_party/blink/renderer/core/animation/css/css_animation.h"
 #include "third_party/blink/renderer/core/animation/css/css_keyframe_effect_model.h"
+#include "third_party/blink/renderer/core/animation/css/css_scroll_timeline.h"
 #include "third_party/blink/renderer/core/animation/css/css_transition.h"
 #include "third_party/blink/renderer/core/animation/css_interpolation_types_map.h"
 #include "third_party/blink/renderer/core/animation/document_animations.h"
@@ -51,13 +52,10 @@
 #include "third_party/blink/renderer/core/animation/interpolation_type.h"
 #include "third_party/blink/renderer/core/animation/keyframe_effect.h"
 #include "third_party/blink/renderer/core/animation/keyframe_effect_model.h"
-#include "third_party/blink/renderer/core/animation/scroll_timeline.h"
 #include "third_party/blink/renderer/core/animation/scroll_timeline_offset.h"
 #include "third_party/blink/renderer/core/animation/timing_calculations.h"
 #include "third_party/blink/renderer/core/animation/transition_interpolation.h"
 #include "third_party/blink/renderer/core/animation/worklet_animation_base.h"
-#include "third_party/blink/renderer/core/css/css_id_selector_value.h"
-#include "third_party/blink/renderer/core/css/css_identifier_value.h"
 #include "third_party/blink/renderer/core/css/css_keyframe_rule.h"
 #include "third_party/blink/renderer/core/css/css_property_equality.h"
 #include "third_party/blink/renderer/core/css/css_value_list.h"
@@ -428,109 +426,15 @@
   return iteration_duration * (iteration_boundary - iteration_start);
 }
 
-bool IsIdentifier(const CSSValue* value, CSSValueID value_id) {
-  if (const auto* ident = DynamicTo<CSSIdentifierValue>(value))
-    return ident->GetValueID() == value_id;
-  return false;
-}
-
-bool IsAuto(const CSSValue* value) {
-  return IsIdentifier(value, CSSValueID::kAuto);
-}
-
-bool IsNone(const CSSValue* value) {
-  return IsIdentifier(value, CSSValueID::kNone);
-}
-
-const cssvalue::CSSIdSelectorValue* GetIdSelectorValue(const CSSValue* value) {
-  if (const auto* selector = DynamicTo<CSSFunctionValue>(value)) {
-    if (selector->FunctionType() != CSSValueID::kSelector)
-      return nullptr;
-    DCHECK_EQ(selector->length(), 1u);
-    return DynamicTo<cssvalue::CSSIdSelectorValue>(selector->Item(0));
-  }
-  return nullptr;
-}
-
-Element* ComputeScrollSource(Element* element, const CSSValue* value) {
-  if (const auto* id = GetIdSelectorValue(value))
-    return element->GetDocument().getElementById(id->Id());
-  if (IsNone(value))
-    return nullptr;
-  DCHECK(!value || IsAuto(value));
-  return element->GetDocument().scrollingElement();
-}
-
-ScrollTimeline::ScrollDirection ComputeScrollDirection(const CSSValue* value) {
-  CSSValueID value_id = CSSValueID::kAuto;
-
-  if (const auto* identifier = DynamicTo<CSSIdentifierValue>(value))
-    value_id = identifier->GetValueID();
-
-  switch (value_id) {
-    case CSSValueID::kInline:
-      return ScrollTimeline::Inline;
-    case CSSValueID::kHorizontal:
-      return ScrollTimeline::Horizontal;
-    case CSSValueID::kVertical:
-      return ScrollTimeline::Vertical;
-    case CSSValueID::kAuto:
-    case CSSValueID::kBlock:
-    default:
-      return ScrollTimeline::Block;
-  }
-}
-
-ScrollTimelineOffset* ComputeScrollOffset(const CSSValue* value) {
-  if (auto* primitive_value = DynamicTo<CSSPrimitiveValue>(value))
-    return MakeGarbageCollected<ScrollTimelineOffset>(primitive_value);
-  DCHECK(!value || IsAuto(value));
-  return MakeGarbageCollected<ScrollTimelineOffset>();
-}
-
-HeapVector<Member<ScrollTimelineOffset>>* ComputeScrollOffsets(
-    const CSSValue* start,
-    const CSSValue* end) {
-  auto* offsets =
-      MakeGarbageCollected<HeapVector<Member<ScrollTimelineOffset>>>();
-  offsets->push_back(ComputeScrollOffset(start));
-  offsets->push_back(ComputeScrollOffset(end));
-  return offsets;
-}
-
-base::Optional<double> ComputeTimeRange(const CSSValue* value) {
-  if (auto* primitive = DynamicTo<CSSPrimitiveValue>(value))
-    return primitive->ComputeSeconds() * 1000.0;
-  // TODO(crbug.com/1097041): Support 'auto' value.
-  return base::nullopt;
-}
-
-struct CSSScrollTimelineOptions {
-  STACK_ALLOCATED();
-
- public:
-  CSSScrollTimelineOptions(Element* element, StyleRuleScrollTimeline& rule)
-      : source(ComputeScrollSource(element, rule.GetSource())),
-        direction(ComputeScrollDirection(rule.GetOrientation())),
-        offsets(ComputeScrollOffsets(rule.GetStart(), rule.GetEnd())),
-        time_range(ComputeTimeRange(rule.GetTimeRange())) {}
-
-  Element* source;
-  ScrollTimeline::ScrollDirection direction;
-  HeapVector<Member<ScrollTimelineOffset>>* offsets;
-  base::Optional<double> time_range;
-};
-
-ScrollTimeline* CreateScrollTimeline(Element* element,
-                                     StyleRuleScrollTimeline* rule) {
+CSSScrollTimeline* CreateCSSScrollTimeline(Element* element,
+                                           StyleRuleScrollTimeline* rule) {
   if (!rule)
     return nullptr;
-  CSSScrollTimelineOptions options(element, *rule);
-  if (!options.time_range)
+  CSSScrollTimeline::Options options(element, *rule);
+  if (!options.IsValid())
     return nullptr;
-  auto* scroll_timeline = MakeGarbageCollected<ScrollTimeline>(
-      &element->GetDocument(), options.source, options.direction,
-      options.offsets, *options.time_range);
+  auto* scroll_timeline =
+      MakeGarbageCollected<CSSScrollTimeline>(&element->GetDocument(), options);
   // It's is not allowed for a style resolve to create timelines that
   // needs timing updates (i.e. AnimationTimeline::NeedsAnimationTimingUpdate()
   // must return false). Servicing animations after creation preserves this
@@ -541,12 +445,28 @@
 }
 
 AnimationTimeline* ComputeTimeline(Element* element,
+                                   const StyleNameOrKeyword& timeline_name,
                                    StyleRuleScrollTimeline* rule) {
+  if (timeline_name.IsKeyword()) {
+    if (timeline_name.GetKeyword() == CSSValueID::kAuto)
+      return &element->GetDocument().Timeline();
+    DCHECK_EQ(timeline_name.GetKeyword(), CSSValueID::kNone);
+    return nullptr;
+  }
   if (rule) {
-    if (auto* timeline = CreateScrollTimeline(element, rule))
+    if (auto* timeline = CreateCSSScrollTimeline(element, rule))
       return timeline;
   }
-  return &element->GetDocument().Timeline();
+  return nullptr;
+}
+
+StyleRuleScrollTimeline* FindScrollTimelineRule(
+    Document& document,
+    const StyleNameOrKeyword& timeline_name) {
+  if (timeline_name.IsKeyword())
+    return nullptr;
+  return document.GetStyleEngine().FindScrollTimelineRule(
+      timeline_name.GetName().GetValue());
 }
 
 }  // namespace
@@ -695,17 +615,11 @@
 
       const StyleNameOrKeyword& timeline_name = animation_data->GetTimeline(i);
 
-      StyleRuleScrollTimeline* scroll_timeline_rule = nullptr;
-
-      // TODO(crbug.com/1097046): Support 'none' keyword.
-      if (!timeline_name.IsKeyword()) {
-        scroll_timeline_rule =
-            element.GetDocument().GetStyleEngine().FindScrollTimelineRule(
-                timeline_name.GetName().GetValue());
-      }
+      StyleRuleScrollTimeline* scroll_timeline_rule =
+          FindScrollTimelineRule(element.GetDocument(), timeline_name);
 
       AnimationTimeline* timeline =
-          ComputeTimeline(&element, scroll_timeline_rule);
+          ComputeTimeline(&element, timeline_name, scroll_timeline_rule);
 
       const RunningAnimation* existing_animation = nullptr;
       wtf_size_t existing_animation_index = 0;
@@ -769,10 +683,14 @@
       } else {
         DCHECK(!is_animation_style_change);
         base::Optional<TimelinePhase> inherited_phase;
-        base::Optional<double> inherited_time = 0;
-        if (timeline && timeline->IsScrollTimeline()) {
-          inherited_phase = base::make_optional(timeline->Phase());
-          inherited_time = timeline->CurrentTimeSeconds();
+        base::Optional<double> inherited_time;
+        if (timeline) {
+          if (timeline->IsMonotonicallyIncreasing()) {
+            inherited_time = 0;
+          } else {
+            inherited_phase = base::make_optional(timeline->Phase());
+            inherited_time = timeline->CurrentTimeSeconds();
+          }
         }
         update.StartAnimation(
             name, name_index, i,
@@ -921,15 +839,9 @@
     auto* effect = MakeGarbageCollected<KeyframeEffect>(
         element, inert_animation->Model(), inert_animation->SpecifiedTiming(),
         KeyframeEffect::kDefaultPriority, event_delegate);
-
-    AnimationTimeline* timeline = entry.timeline;
-    // We always fall back to the DocumentTimeline at the moment, so the
-    // timeline can't be nullptr here.
-    // TODO(crbug.com/1097046): Support animation-timeline:none
-    DCHECK(timeline);
     auto* animation = MakeGarbageCollected<CSSAnimation>(
-        element->GetExecutionContext(), timeline, effect, entry.position_index,
-        entry.name);
+        element->GetExecutionContext(), entry.timeline, effect,
+        entry.position_index, entry.name);
     animation->play();
     if (inert_animation->Paused())
       animation->pause();
diff --git a/third_party/blink/renderer/core/animation/css/css_scroll_timeline.cc b/third_party/blink/renderer/core/animation/css/css_scroll_timeline.cc
new file mode 100644
index 0000000..83e50cc
--- /dev/null
+++ b/third_party/blink/renderer/core/animation/css/css_scroll_timeline.cc
@@ -0,0 +1,113 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/core/animation/css/css_scroll_timeline.h"
+
+#include "third_party/blink/renderer/core/css/css_id_selector_value.h"
+#include "third_party/blink/renderer/core/css/css_identifier_value.h"
+#include "third_party/blink/renderer/core/css/css_value_list.h"
+#include "third_party/blink/renderer/core/css/style_rule.h"
+#include "third_party/blink/renderer/core/dom/document.h"
+#include "third_party/blink/renderer/core/dom/element.h"
+
+namespace blink {
+
+namespace {
+
+bool IsIdentifier(const CSSValue* value, CSSValueID value_id) {
+  if (const auto* ident = DynamicTo<CSSIdentifierValue>(value))
+    return ident->GetValueID() == value_id;
+  return false;
+}
+
+bool IsAuto(const CSSValue* value) {
+  return IsIdentifier(value, CSSValueID::kAuto);
+}
+
+bool IsNone(const CSSValue* value) {
+  return IsIdentifier(value, CSSValueID::kNone);
+}
+
+const cssvalue::CSSIdSelectorValue* GetIdSelectorValue(const CSSValue* value) {
+  if (const auto* selector = DynamicTo<CSSFunctionValue>(value)) {
+    if (selector->FunctionType() != CSSValueID::kSelector)
+      return nullptr;
+    DCHECK_EQ(selector->length(), 1u);
+    return DynamicTo<cssvalue::CSSIdSelectorValue>(selector->Item(0));
+  }
+  return nullptr;
+}
+
+Element* ComputeScrollSource(Element* element, const CSSValue* value) {
+  if (const auto* id = GetIdSelectorValue(value))
+    return element->GetDocument().getElementById(id->Id());
+  if (IsNone(value))
+    return nullptr;
+  DCHECK(!value || IsAuto(value));
+  return element->GetDocument().scrollingElement();
+}
+
+ScrollTimeline::ScrollDirection ComputeScrollDirection(const CSSValue* value) {
+  CSSValueID value_id = CSSValueID::kAuto;
+
+  if (const auto* identifier = DynamicTo<CSSIdentifierValue>(value))
+    value_id = identifier->GetValueID();
+
+  switch (value_id) {
+    case CSSValueID::kInline:
+      return ScrollTimeline::Inline;
+    case CSSValueID::kHorizontal:
+      return ScrollTimeline::Horizontal;
+    case CSSValueID::kVertical:
+      return ScrollTimeline::Vertical;
+    case CSSValueID::kAuto:
+    case CSSValueID::kBlock:
+    default:
+      return ScrollTimeline::Block;
+  }
+}
+
+ScrollTimelineOffset* ComputeScrollOffset(const CSSValue* value) {
+  if (auto* primitive_value = DynamicTo<CSSPrimitiveValue>(value))
+    return MakeGarbageCollected<ScrollTimelineOffset>(primitive_value);
+  DCHECK(!value || IsAuto(value));
+  return MakeGarbageCollected<ScrollTimelineOffset>();
+}
+
+HeapVector<Member<ScrollTimelineOffset>>* ComputeScrollOffsets(
+    const CSSValue* start,
+    const CSSValue* end) {
+  auto* offsets =
+      MakeGarbageCollected<HeapVector<Member<ScrollTimelineOffset>>>();
+  offsets->push_back(ComputeScrollOffset(start));
+  offsets->push_back(ComputeScrollOffset(end));
+  return offsets;
+}
+
+base::Optional<double> ComputeTimeRange(const CSSValue* value) {
+  if (auto* primitive = DynamicTo<CSSPrimitiveValue>(value))
+    return primitive->ComputeSeconds() * 1000.0;
+  // TODO(crbug.com/1097041): Support 'auto' value.
+  return base::nullopt;
+}
+
+}  // anonymous namespace
+
+CSSScrollTimeline::Options::Options(Element* element,
+                                    StyleRuleScrollTimeline& rule)
+    : source_(ComputeScrollSource(element, rule.GetSource())),
+      direction_(ComputeScrollDirection(rule.GetOrientation())),
+      offsets_(ComputeScrollOffsets(rule.GetStart(), rule.GetEnd())),
+      time_range_(ComputeTimeRange(rule.GetTimeRange())) {}
+
+CSSScrollTimeline::CSSScrollTimeline(Document* document, const Options& options)
+    : ScrollTimeline(document,
+                     options.source_,
+                     options.direction_,
+                     options.offsets_,
+                     *options.time_range_) {
+  DCHECK(options.IsValid());
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/core/animation/css/css_scroll_timeline.h b/third_party/blink/renderer/core/animation/css/css_scroll_timeline.h
new file mode 100644
index 0000000..2fb0946
--- /dev/null
+++ b/third_party/blink/renderer/core/animation/css/css_scroll_timeline.h
@@ -0,0 +1,54 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_ANIMATION_CSS_CSS_SCROLL_TIMELINE_H_
+#define THIRD_PARTY_BLINK_RENDERER_CORE_ANIMATION_CSS_CSS_SCROLL_TIMELINE_H_
+
+#include "base/optional.h"
+#include "third_party/blink/renderer/core/animation/scroll_timeline.h"
+
+namespace blink {
+
+class Document;
+class Element;
+class StyleRuleScrollTimeline;
+
+// A CSSScrollTimeline is like a ScrollTimeline, except it originates from
+// an @scroll-timeline rule.
+class CORE_EXPORT CSSScrollTimeline : public ScrollTimeline {
+ public:
+  struct Options {
+    STACK_ALLOCATED();
+
+   public:
+    Options(Element*, StyleRuleScrollTimeline&);
+
+    // TODO(crbug.com/1097041): Support 'auto' value.
+    bool IsValid() const { return time_range_.has_value(); }
+
+   private:
+    friend class CSSScrollTimeline;
+
+    Element* source_;
+    ScrollTimeline::ScrollDirection direction_;
+    HeapVector<Member<ScrollTimelineOffset>>* offsets_;
+    base::Optional<double> time_range_;
+  };
+
+  CSSScrollTimeline(Document*, const Options&);
+
+  // AnimationTimeline implementation.
+  bool IsCSSScrollTimeline() const override { return true; }
+};
+
+template <>
+struct DowncastTraits<CSSScrollTimeline> {
+  static bool AllowFrom(const AnimationTimeline& value) {
+    return value.IsCSSScrollTimeline();
+  }
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_CORE_ANIMATION_CSS_CSS_SCROLL_TIMELINE_H_
diff --git a/third_party/blink/renderer/core/dom/document.cc b/third_party/blink/renderer/core/dom/document.cc
index 4b8ab16..a92fb85 100644
--- a/third_party/blink/renderer/core/dom/document.cc
+++ b/third_party/blink/renderer/core/dom/document.cc
@@ -7135,7 +7135,7 @@
     return true;
   }
   if (!GetFrame() || GetExecutionContext()->IsFeatureEnabled(
-                         mojom::blink::FeaturePolicyFeature::kDocumentWrite,
+                         mojom::blink::DocumentPolicyFeature::kDocumentWrite,
                          ReportOptions::kReportOnFailure)) {
     return true;
   }
diff --git a/third_party/blink/renderer/core/feature_policy/document_policy_features.json5 b/third_party/blink/renderer/core/feature_policy/document_policy_features.json5
index 5d0f0936..b531a2bd 100644
--- a/third_party/blink/renderer/core/feature_policy/document_policy_features.json5
+++ b/third_party/blink/renderer/core/feature_policy/document_policy_features.json5
@@ -95,5 +95,12 @@
       default_value: "true",
       depends_on: ["ExperimentalProductivityFeatures"],
     },
+    {
+      name: "DocumentWrite",
+      document_policy_name: "document-write",
+      value_type: "Bool",
+      default_value: "true",
+      depends_on: ["ExperimentalProductivityFeatures"],
+    },
   ],
 }
diff --git a/third_party/blink/renderer/core/feature_policy/feature_policy_features.json5 b/third_party/blink/renderer/core/feature_policy/feature_policy_features.json5
index 50cacd4..5a004c4 100644
--- a/third_party/blink/renderer/core/feature_policy/feature_policy_features.json5
+++ b/third_party/blink/renderer/core/feature_policy/feature_policy_features.json5
@@ -152,12 +152,6 @@
       feature_default: "EnableForAll",
     },
     {
-      name: "DocumentWrite",
-      feature_policy_name: "document-write",
-      feature_default: "EnableForAll",
-      depends_on: ["ExperimentalProductivityFeatures"],
-    },
-    {
       name: "Downloads",
       feature_policy_name: "downloads",
       feature_default: "EnableForAll",
diff --git a/third_party/blink/renderer/core/layout/layout_flexible_box.cc b/third_party/blink/renderer/core/layout/layout_flexible_box.cc
index cf82bf0..d28f0e9 100644
--- a/third_party/blink/renderer/core/layout/layout_flexible_box.cc
+++ b/third_party/blink/renderer/core/layout/layout_flexible_box.cc
@@ -55,7 +55,8 @@
 namespace blink {
 
 static bool HasAspectRatio(const LayoutBox& child) {
-  return child.IsImage() || child.IsCanvas() || IsA<LayoutVideo>(child);
+  return child.IsImage() || child.IsCanvas() || IsA<LayoutVideo>(child) ||
+         child.StyleRef().AspectRatio();
 }
 
 LayoutFlexibleBox::LayoutFlexibleBox(Element* element)
@@ -736,7 +737,7 @@
 bool LayoutFlexibleBox::UseChildAspectRatio(const LayoutBox& child) const {
   if (!HasAspectRatio(child))
     return false;
-  if (child.IntrinsicSize().Height() == 0) {
+  if (!child.StyleRef().AspectRatio() && child.IntrinsicSize().Height() == 0) {
     // We can't compute a ratio in this case.
     return false;
   }
@@ -749,7 +750,6 @@
     const LayoutBox& child,
     const Length& cross_size_length) const {
   DCHECK(HasAspectRatio(child));
-  DCHECK_NE(child.IntrinsicSize().Height(), 0);
 
   LayoutUnit cross_size;
   if (cross_size_length.IsFixed()) {
@@ -762,9 +762,15 @@
                            ValueForLength(cross_size_length, ContentWidth()));
   }
 
-  const LayoutSize& child_intrinsic_size = child.IntrinsicSize();
-  double ratio = child_intrinsic_size.Width().ToFloat() /
-                 child_intrinsic_size.Height().ToFloat();
+  LayoutSize aspect_ratio;
+  if (child.StyleRef().AspectRatio()) {
+    IntSize int_ratio = *child.StyleRef().AspectRatio();
+    aspect_ratio = LayoutSize{int_ratio.Width(), int_ratio.Height()};
+  } else {
+    aspect_ratio = child.IntrinsicSize();
+  }
+  double ratio =
+      aspect_ratio.Width().ToFloat() / aspect_ratio.Height().ToFloat();
   if (IsHorizontalFlow())
     return LayoutUnit(cross_size * ratio);
   return LayoutUnit(cross_size / ratio);
diff --git a/third_party/blink/renderer/core/page/context_menu_controller.cc b/third_party/blink/renderer/core/page/context_menu_controller.cc
index 7c9ab47..34bae4f 100644
--- a/third_party/blink/renderer/core/page/context_menu_controller.cc
+++ b/third_party/blink/renderer/core/page/context_menu_controller.cc
@@ -248,17 +248,6 @@
             PhysicalOffset(FlooredIntPoint(point)));
   }
 
-  // Store text selection when it happens as it might be cleared when the
-  // browser will request |TextFragmentSelectorGenerator| to generator selector.
-  if (!selected_frame->Selection().SelectedText().IsEmpty()) {
-    VisibleSelectionInFlatTree selection =
-        selected_frame->Selection().ComputeVisibleSelectionInFlatTree();
-    EphemeralRangeInFlatTree selection_range(selection.Start(),
-                                             selection.End());
-    page_->GetTextFragmentSelectorGenerator().UpdateSelection(selected_frame,
-                                                              selection_range);
-  }
-
   WebContextMenuData data;
   data.mouse_position = selected_frame->View()->FrameToViewport(
       result.RoundedPointInInnerNodeFrame());
@@ -433,6 +422,11 @@
         << "]\nVisibleSelection: "
         << selected_frame->Selection()
                .ComputeVisibleSelectionInDOMTreeDeprecated();
+
+    // Store text selection when it happens as it might be cleared when the
+    // browser will request |TextFragmentSelectorGenerator| to generate
+    // selector.
+    UpdateTextFragmentSelectorGenerator(selected_frame);
   }
 
   if (result.IsContentEditable()) {
@@ -528,4 +522,13 @@
   return true;
 }
 
+void ContextMenuController::UpdateTextFragmentSelectorGenerator(
+    LocalFrame* selected_frame) {
+  VisibleSelectionInFlatTree selection =
+      selected_frame->Selection().ComputeVisibleSelectionInFlatTree();
+  EphemeralRangeInFlatTree selection_range(selection.Start(), selection.End());
+  page_->GetTextFragmentSelectorGenerator().UpdateSelection(selected_frame,
+                                                            selection_range);
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/page/context_menu_controller.h b/third_party/blink/renderer/core/page/context_menu_controller.h
index a88a26e..7c4c7e8 100644
--- a/third_party/blink/renderer/core/page/context_menu_controller.h
+++ b/third_party/blink/renderer/core/page/context_menu_controller.h
@@ -74,6 +74,8 @@
                        const MouseEvent* mouse_event = nullptr);
   bool ShouldShowContextMenuFromTouch(const WebContextMenuData&);
 
+  void UpdateTextFragmentSelectorGenerator(LocalFrame*);
+
   Member<Page> page_;
   Member<ContextMenuProvider> menu_provider_;
   HitTestResult hit_test_result_;
diff --git a/third_party/blink/renderer/core/timing/profiler.cc b/third_party/blink/renderer/core/timing/profiler.cc
index bd4b3393..f4e347fb 100644
--- a/third_party/blink/renderer/core/timing/profiler.cc
+++ b/third_party/blink/renderer/core/timing/profiler.cc
@@ -7,6 +7,7 @@
 #include "third_party/blink/renderer/core/dom/dom_exception.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
 #include "third_party/blink/renderer/core/timing/profiler_group.h"
+#include "third_party/blink/renderer/platform/bindings/script_forbidden_scope.h"
 
 namespace blink {
 
@@ -32,6 +33,9 @@
   ScriptPromise promise = resolver->Promise();
 
   if (!stopped()) {
+    // Ensure that we don't synchronously invoke script when resolving
+    // (crbug.com/1119865).
+    ScriptForbiddenScope forbid_script;
     DCHECK(profiler_group_);
     profiler_group_->StopProfiler(script_state, this, resolver);
     profiler_group_ = nullptr;
diff --git a/third_party/blink/renderer/core/timing/profiler_group_test.cc b/third_party/blink/renderer/core/timing/profiler_group_test.cc
index bc014b4..d3bd881 100644
--- a/third_party/blink/renderer/core/timing/profiler_group_test.cc
+++ b/third_party/blink/renderer/core/timing/profiler_group_test.cc
@@ -5,6 +5,7 @@
 #include "third_party/blink/renderer/core/timing/profiler_group.h"
 
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/bindings/core/v8/script_function.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_testing.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_profiler_init_options.h"
 #include "third_party/blink/renderer/core/timing/profiler.h"
@@ -174,4 +175,36 @@
   }
 }
 
+TEST(ProfilerGroupTest, Bug1119865) {
+  class ExpectNoCallFunction : public ScriptFunction {
+   public:
+    static v8::Local<v8::Function> Create(ScriptState* state) {
+      return MakeGarbageCollected<ExpectNoCallFunction>(state)
+          ->BindToV8Function();
+    }
+
+    explicit ExpectNoCallFunction(ScriptState* state) : ScriptFunction(state) {}
+
+    ScriptValue Call(ScriptValue) override {
+      EXPECT_FALSE(true)
+          << "Promise should not resolve without dispatching a task";
+      return ScriptValue();
+    }
+  };
+
+  V8TestingScope scope;
+
+  ProfilerGroup* profiler_group = ProfilerGroup::From(scope.GetIsolate());
+
+  ProfilerInitOptions* init_options = ProfilerInitOptions::Create();
+  init_options->setSampleInterval(0);
+
+  auto* profiler = profiler_group->CreateProfiler(
+      scope.GetScriptState(), *init_options, base::TimeTicks(),
+      scope.GetExceptionState());
+
+  auto function = ExpectNoCallFunction::Create(scope.GetScriptState());
+  profiler->stop(scope.GetScriptState()).Then(function);
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/BUILD.gn b/third_party/blink/renderer/modules/BUILD.gn
index f3a094db..5d3b52e 100644
--- a/third_party/blink/renderer/modules/BUILD.gn
+++ b/third_party/blink/renderer/modules/BUILD.gn
@@ -323,6 +323,8 @@
     "manifest/manifest_manager_unittest.cc",
     "manifest/manifest_parser_unittest.cc",
     "manifest/manifest_type_converters_unittest.cc",
+    "media/audio/mojo_audio_output_ipc_test.cc",
+    "media/audio/web_audio_output_ipc_factory_test.cc",
     "media/webmediaplayer_util_unittest.cc",
     "media_capabilities/media_capabilities_test.cc",
     "media_controls/elements/media_control_animated_arrow_container_element_test.cc",
diff --git a/third_party/blink/renderer/modules/accessibility/ax_layout_object.cc b/third_party/blink/renderer/modules/accessibility/ax_layout_object.cc
index 86d91868..a7f4e69c 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_layout_object.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_layout_object.cc
@@ -1768,14 +1768,6 @@
   if (AriaRoleAttribute() == ax::mojom::blink::Role::kMenuBar)
     return AXObjectCache().GetOrCreate(layout_object_->Parent());
 
-  // menuButton and its corresponding menu are DOM siblings, but Accessibility
-  // needs them to be parent/child.
-  if (AriaRoleAttribute() == ax::mojom::blink::Role::kMenu) {
-    AXObject* parent = MenuButtonForMenu();
-    if (parent)
-      return parent;
-  }
-
   if (GetNode())
     return AXNodeObject::ComputeParent();
 
@@ -1799,14 +1791,6 @@
   if (AriaRoleAttribute() == ax::mojom::blink::Role::kMenuBar)
     return AXObjectCache().Get(layout_object_->Parent());
 
-  // menuButton and its corresponding menu are DOM siblings, but Accessibility
-  // needs them to be parent/child.
-  if (AriaRoleAttribute() == ax::mojom::blink::Role::kMenu) {
-    AXObject* parent = MenuButtonForMenuIfExists();
-    if (parent)
-      return parent;
-  }
-
   if (GetNode())
     return AXNodeObject::ComputeParentIfExists();
 
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 09b33e4..cb0bc6a 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
@@ -1090,32 +1090,6 @@
   return false;
 }
 
-AXObject* AXNodeObject::MenuButtonForMenu() const {
-  Element* menu_item = MenuItemElementForMenu();
-
-  if (menu_item) {
-    // ARIA just has generic menu items. AppKit needs to know if this is a top
-    // level items like MenuBarButton or MenuBarItem
-    AXObject* menu_item_ax = AXObjectCache().GetOrCreate(menu_item);
-    if (menu_item_ax && menu_item_ax->IsMenuButton())
-      return menu_item_ax;
-  }
-  return nullptr;
-}
-
-AXObject* AXNodeObject::MenuButtonForMenuIfExists() const {
-  Element* menu_item = MenuItemElementForMenu();
-
-  if (menu_item) {
-    // ARIA just has generic menu items. AppKit needs to know if this is a top
-    // level items like MenuBarButton or MenuBarItem
-    AXObject* menu_item_ax = AXObjectCache().Get(menu_item);
-    if (menu_item_ax && menu_item_ax->IsMenuButton())
-      return menu_item_ax;
-  }
-  return nullptr;
-}
-
 static Element* SiblingWithAriaRole(String role, Node* node) {
   Node* parent = LayoutTreeBuilderTraversal::Parent(*node);
   if (!parent)
@@ -3429,7 +3403,6 @@
     case ax::mojom::blink::Role::kCheckBox:
     case ax::mojom::blink::Role::kImage:
     case ax::mojom::blink::Role::kListBoxOption:
-    case ax::mojom::blink::Role::kMenuButton:
     case ax::mojom::blink::Role::kMenuListOption:
     case ax::mojom::blink::Role::kMenuItem:
     case ax::mojom::blink::Role::kMenuItemCheckBox:
@@ -3459,7 +3432,6 @@
     case ax::mojom::blink::Role::kCheckBox:
     case ax::mojom::blink::Role::kListBoxOption:
     case ax::mojom::blink::Role::kMath:  // role="math" is flat, unlike <math>
-    case ax::mojom::blink::Role::kMenuButton:
     case ax::mojom::blink::Role::kMenuListOption:
     case ax::mojom::blink::Role::kMenuItem:
     case ax::mojom::blink::Role::kMenuItemCheckBox:
diff --git a/third_party/blink/renderer/modules/accessibility/ax_node_object.h b/third_party/blink/renderer/modules/accessibility/ax_node_object.h
index 320f5bd..fdecf9c 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_node_object.h
+++ b/third_party/blink/renderer/modules/accessibility/ax_node_object.h
@@ -74,8 +74,6 @@
 
   bool HasContentEditableAttributeSet() const;
   bool IsTextControl() const override;
-  AXObject* MenuButtonForMenu() const;
-  AXObject* MenuButtonForMenuIfExists() const;
   Element* MenuItemElementForMenu() const;
   Element* MouseButtonListener() const;
   bool IsNativeCheckboxOrRadio() const;
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object.cc b/third_party/blink/renderer/modules/accessibility/ax_object.cc
index e7bfd4a..b890343c 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_object.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_object.cc
@@ -383,7 +383,6 @@
     {ax::mojom::blink::Role::kMarquee, "Marquee"},
     {ax::mojom::blink::Role::kMath, "Math"},
     {ax::mojom::blink::Role::kMenuBar, "MenuBar"},
-    {ax::mojom::blink::Role::kMenuButton, "MenuButton"},
     {ax::mojom::blink::Role::kMenuItem, "MenuItem"},
     {ax::mojom::blink::Role::kMenuItemCheckBox, "MenuItemCheckBox"},
     {ax::mojom::blink::Role::kMenuItemRadio, "MenuItemRadio"},
@@ -459,7 +458,6 @@
     {"button", ax::mojom::blink::Role::kToggleButton},
     {"combobox", ax::mojom::blink::Role::kPopUpButton},
     {"contentinfo", ax::mojom::blink::Role::kFooter},
-    {"menuitem", ax::mojom::blink::Role::kMenuButton},
     {"menuitem", ax::mojom::blink::Role::kMenuListOption},
     {"progressbar", ax::mojom::blink::Role::kMeter},
     {"region", ax::mojom::blink::Role::kSection},
@@ -976,10 +974,6 @@
   return RoleValue() == ax::mojom::blink::Role::kMenu;
 }
 
-bool AXObject::IsMenuButton() const {
-  return RoleValue() == ax::mojom::blink::Role::kMenuButton;
-}
-
 bool AXObject::IsCheckable() const {
   switch (RoleValue()) {
     case ax::mojom::blink::Role::kCheckBox:
@@ -2365,7 +2359,6 @@
     case ax::mojom::blink::Role::kListBox:
     case ax::mojom::blink::Role::kLink:
     case ax::mojom::blink::Role::kPopUpButton:
-    case ax::mojom::blink::Role::kMenuButton:
     case ax::mojom::blink::Role::kMenuItem:
     case ax::mojom::blink::Role::kMenuItemCheckBox:
     case ax::mojom::blink::Role::kMenuItemRadio:
@@ -2621,11 +2614,6 @@
     if (role == ax::mojom::blink::Role::kListBoxOption &&
         parent_aria_role == ax::mojom::blink::Role::kMenu)
       return ax::mojom::blink::Role::kMenuItem;
-    // An aria "menuitem" may map to MenuButton or MenuItem depending on its
-    // parent.
-    if (role == ax::mojom::blink::Role::kMenuItem &&
-        parent_aria_role == ax::mojom::blink::Role::kGroup)
-      return ax::mojom::blink::Role::kMenuButton;
 
     // If the parent had a different role, then we don't need to continue
     // searching up the chain.
@@ -3997,7 +3985,6 @@
     case ax::mojom::blink::Role::kLineBreak:
     case ax::mojom::blink::Role::kLink:
     case ax::mojom::blink::Role::kListBoxOption:
-    case ax::mojom::blink::Role::kMenuButton:
     case ax::mojom::blink::Role::kMenuItem:
     case ax::mojom::blink::Role::kMenuItemCheckBox:
     case ax::mojom::blink::Role::kMenuItemRadio:
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object.h b/third_party/blink/renderer/modules/accessibility/ax_object.h
index 164a3e94..7d10bfa 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_object.h
+++ b/third_party/blink/renderer/modules/accessibility/ax_object.h
@@ -444,7 +444,6 @@
   bool IsLink() const;
   virtual bool IsInPageLinkTarget() const;
   bool IsMenu() const;
-  bool IsMenuButton() const;
   bool IsMenuRelated() const;
   bool IsMeter() const;
   virtual bool IsNativeImage() const;
diff --git a/third_party/blink/renderer/modules/media/BUILD.gn b/third_party/blink/renderer/modules/media/BUILD.gn
index 142e769..8cc957d 100644
--- a/third_party/blink/renderer/modules/media/BUILD.gn
+++ b/third_party/blink/renderer/modules/media/BUILD.gn
@@ -5,5 +5,10 @@
 import("//third_party/blink/renderer/modules/modules.gni")
 
 blink_modules_sources("media") {
-  sources = [ "webmediaplayer_util.cc" ]
+  sources = [
+    "audio/mojo_audio_output_ipc.cc",
+    "audio/mojo_audio_output_ipc.h",
+    "audio/web_audio_output_ipc_factory.cc",
+    "webmediaplayer_util.cc",
+  ]
 }
diff --git a/third_party/blink/renderer/modules/media/audio/DEPS b/third_party/blink/renderer/modules/media/audio/DEPS
new file mode 100644
index 0000000..a5811516
--- /dev/null
+++ b/third_party/blink/renderer/modules/media/audio/DEPS
@@ -0,0 +1,19 @@
+include_rules = [
+    # TODO(https://crbug.com/787252): Remove this include when WTF::HashMap.
+    # is used instead.
+    "+base/containers/flat_map.h",
+
+    "+media/audio",
+    "+media/base/audio_parameters.h",
+    "+media/mojo",
+]
+
+specific_include_rules = {
+    ".*test\.cc" : [
+        "+base/message_loop/message_pump_type.h",
+        "+base/run_loop.h",
+        "+base/test/bind_test_util.h",
+        "+base/test/gtest_util.h",
+        "+base/threading/thread.h",
+    ],
+}
diff --git a/content/renderer/media/audio/mojo_audio_output_ipc.cc b/third_party/blink/renderer/modules/media/audio/mojo_audio_output_ipc.cc
similarity index 87%
rename from content/renderer/media/audio/mojo_audio_output_ipc.cc
rename to third_party/blink/renderer/modules/media/audio/mojo_audio_output_ipc.cc
index 73611d3..ac3302b 100644
--- a/content/renderer/media/audio/mojo_audio_output_ipc.cc
+++ b/third_party/blink/renderer/modules/media/audio/mojo_audio_output_ipc.cc
@@ -2,24 +2,24 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "content/renderer/media/audio/mojo_audio_output_ipc.h"
+#include "third_party/blink/renderer/modules/media/audio/mojo_audio_output_ipc.h"
 
 #include <utility>
 
 #include "base/bind.h"
 #include "base/metrics/histogram_macros.h"
 #include "media/audio/audio_device_description.h"
-#include "media/mojo/mojom/audio_output_stream.mojom.h"
+#include "media/mojo/mojom/audio_output_stream.mojom-blink.h"
 #include "mojo/public/cpp/bindings/callback_helpers.h"
 #include "mojo/public/cpp/system/platform_handle.h"
 
-namespace content {
+namespace blink {
 
 namespace {
 
-void TrivialAuthorizedCallback(media::OutputDeviceStatus,
+void TrivialAuthorizedCallback(media::mojom::blink::OutputDeviceStatus,
                                const media::AudioParameters&,
-                               const std::string&) {}
+                               const String&) {}
 
 }  // namespace
 
@@ -57,8 +57,9 @@
       mojo::WrapCallbackWithDefaultInvokeIfNotRun(
           base::BindOnce(&MojoAudioOutputIPC::ReceivedDeviceAuthorization,
                          weak_factory_.GetWeakPtr(), base::TimeTicks::Now()),
-          media::OutputDeviceStatus::OUTPUT_DEVICE_STATUS_ERROR_INTERNAL,
-          media::AudioParameters::UnavailableDeviceParams(), std::string()));
+          static_cast<media::mojom::blink::OutputDeviceStatus>(
+              media::OutputDeviceStatus::OUTPUT_DEVICE_STATUS_ERROR_INTERNAL),
+          media::AudioParameters::UnavailableDeviceParams(), String()));
 }
 
 void MojoAudioOutputIPC::CreateStream(
@@ -83,7 +84,7 @@
   DCHECK_EQ(delegate_, delegate);
   // Since the creation callback won't fire if the provider receiver is gone
   // and |this| owns |stream_provider_|, unretained is safe.
-  mojo::PendingRemote<media::mojom::AudioOutputStreamProviderClient>
+  mojo::PendingRemote<media::mojom::blink::AudioOutputStreamProviderClient>
       client_remote;
   receiver_.Bind(client_remote.InitWithNewPipeAndPassReceiver());
   // Unretained is safe because |this| owns |receiver_|.
@@ -140,7 +141,7 @@
   DCHECK(io_task_runner_->RunsTasksInCurrentSequence());
   DCHECK(delegate_);
   if (disconnect_reason ==
-      static_cast<uint32_t>(media::mojom::AudioOutputStreamObserver::
+      static_cast<uint32_t>(media::mojom::blink::AudioOutputStreamObserver::
                                 DisconnectReason::kPlatformError)) {
     delegate_->OnError();
   }
@@ -157,7 +158,7 @@
   return receiver_.is_bound();
 }
 
-mojo::PendingReceiver<media::mojom::AudioOutputStreamProvider>
+mojo::PendingReceiver<media::mojom::blink::AudioOutputStreamProvider>
 MojoAudioOutputIPC::MakeProviderReceiver() {
   DCHECK(io_task_runner_->RunsTasksInCurrentSequence());
   DCHECK(!AuthorizationRequested());
@@ -203,14 +204,14 @@
       MakeProviderReceiver(),
       session_id.is_empty() ? base::Optional<base::UnguessableToken>()
                             : session_id,
-      device_id, std::move(callback));
+      String::FromUTF8(device_id), std::move(callback));
 }
 
 void MojoAudioOutputIPC::ReceivedDeviceAuthorization(
     base::TimeTicks auth_start_time,
-    media::OutputDeviceStatus status,
+    media::mojom::blink::OutputDeviceStatus status,
     const media::AudioParameters& params,
-    const std::string& device_id) const {
+    const String& device_id) const {
   DCHECK(io_task_runner_->RunsTasksInCurrentSequence());
   DCHECK(delegate_);
 
@@ -221,12 +222,13 @@
                              base::TimeDelta::FromMilliseconds(1),
                              base::TimeDelta::FromSeconds(15), 100);
 
-  delegate_->OnDeviceAuthorized(status, params, device_id);
+  delegate_->OnDeviceAuthorized(static_cast<media::OutputDeviceStatus>(status),
+                                params, device_id.Utf8());
 }
 
 void MojoAudioOutputIPC::Created(
-    mojo::PendingRemote<media::mojom::AudioOutputStream> pending_stream,
-    media::mojom::ReadWriteAudioDataPipePtr data_pipe) {
+    mojo::PendingRemote<media::mojom::blink::AudioOutputStream> pending_stream,
+    media::mojom::blink::ReadWriteAudioDataPipePtr data_pipe) {
   DCHECK(io_task_runner_->RunsTasksInCurrentSequence());
   DCHECK(delegate_);
 
@@ -250,4 +252,4 @@
     stream_->Play();
 }
 
-}  // namespace content
+}  // namespace blink
diff --git a/content/renderer/media/audio/mojo_audio_output_ipc.h b/third_party/blink/renderer/modules/media/audio/mojo_audio_output_ipc.h
similarity index 68%
rename from content/renderer/media/audio/mojo_audio_output_ipc.h
rename to third_party/blink/renderer/modules/media/audio/mojo_audio_output_ipc.h
index e4295b74..475e75a 100644
--- a/content/renderer/media/audio/mojo_audio_output_ipc.h
+++ b/third_party/blink/renderer/modules/media/audio/mojo_audio_output_ipc.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 CONTENT_RENDERER_MEDIA_AUDIO_MOJO_AUDIO_OUTPUT_IPC_H_
-#define CONTENT_RENDERER_MEDIA_AUDIO_MOJO_AUDIO_OUTPUT_IPC_H_
+#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_MEDIA_AUDIO_MOJO_AUDIO_OUTPUT_IPC_H_
+#define THIRD_PARTY_BLINK_RENDERER_MODULES_MEDIA_AUDIO_MOJO_AUDIO_OUTPUT_IPC_H_
 
 #include <string>
 
@@ -13,27 +13,27 @@
 #include "base/optional.h"
 #include "base/single_thread_task_runner.h"
 #include "base/time/time.h"
-#include "content/common/content_export.h"
 #include "media/audio/audio_output_ipc.h"
-#include "media/mojo/mojom/audio_data_pipe.mojom.h"
-#include "media/mojo/mojom/audio_output_stream.mojom.h"
+#include "media/mojo/mojom/audio_data_pipe.mojom-blink.h"
+#include "media/mojo/mojom/audio_output_stream.mojom-blink.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "mojo/public/cpp/bindings/receiver.h"
 #include "mojo/public/cpp/bindings/remote.h"
-#include "third_party/blink/public/mojom/media/renderer_audio_output_stream_factory.mojom.h"
+#include "third_party/blink/public/mojom/media/renderer_audio_output_stream_factory.mojom-blink.h"
+#include "third_party/blink/renderer/modules/modules_export.h"
 
-namespace content {
+namespace blink {
 
 // MojoAudioOutputIPC is a renderer-side class for handling creation,
 // initialization and control of an output stream. May only be used on a single
 // thread.
-class CONTENT_EXPORT MojoAudioOutputIPC
+class MODULES_EXPORT MojoAudioOutputIPC
     : public media::AudioOutputIPC,
-      public media::mojom::AudioOutputStreamProviderClient {
+      public media::mojom::blink::AudioOutputStreamProviderClient {
  public:
   using FactoryAccessorCB = base::RepeatingCallback<
-      blink::mojom::RendererAudioOutputStreamFactory*()>;
+      blink::mojom::blink::RendererAudioOutputStreamFactory*()>;
 
   // |factory_accessor| is required to provide a
   // RendererAudioOutputStreamFactory* if IPC is possible.
@@ -58,14 +58,15 @@
   void SetVolume(double volume) override;
 
   // media::mojom::AudioOutputStreamProviderClient implementation.
-  void Created(mojo::PendingRemote<media::mojom::AudioOutputStream> stream,
-               media::mojom::ReadWriteAudioDataPipePtr data_pipe) override;
+  void Created(
+      mojo::PendingRemote<media::mojom::blink::AudioOutputStream> stream,
+      media::mojom::blink::ReadWriteAudioDataPipePtr data_pipe) override;
 
  private:
   static constexpr double kDefaultVolume = 1.0;
 
-  using AuthorizationCB = blink::mojom::RendererAudioOutputStreamFactory::
-      RequestDeviceAuthorizationCallback;
+  using AuthorizationCB = blink::mojom::blink::
+      RendererAudioOutputStreamFactory::RequestDeviceAuthorizationCallback;
 
   bool AuthorizationRequested() const;
   bool StreamCreationRequested() const;
@@ -73,7 +74,7 @@
   void ProviderClientBindingDisconnected(uint32_t disconnect_reason,
                                          const std::string& description);
 
-  mojo::PendingReceiver<media::mojom::AudioOutputStreamProvider>
+  mojo::PendingReceiver<media::mojom::blink::AudioOutputStreamProvider>
   MakeProviderReceiver();
 
   // Tries to acquire a RendererAudioOutputStreamFactory and requests device
@@ -83,10 +84,11 @@
                                     const std::string& device_id,
                                     AuthorizationCB callback);
 
-  void ReceivedDeviceAuthorization(base::TimeTicks auth_start_time,
-                                   media::OutputDeviceStatus status,
-                                   const media::AudioParameters& params,
-                                   const std::string& device_id) const;
+  void ReceivedDeviceAuthorization(
+      base::TimeTicks auth_start_time,
+      media::mojom::blink::OutputDeviceStatus status,
+      const media::AudioParameters& params,
+      const String& device_id) const;
 
   const FactoryAccessorCB factory_accessor_;
 
@@ -95,9 +97,10 @@
   enum { kPaused, kPlaying } expected_state_ = kPaused;
   base::Optional<double> volume_;
 
-  mojo::Receiver<media::mojom::AudioOutputStreamProviderClient> receiver_{this};
-  mojo::Remote<media::mojom::AudioOutputStreamProvider> stream_provider_;
-  mojo::Remote<media::mojom::AudioOutputStream> stream_;
+  mojo::Receiver<media::mojom::blink::AudioOutputStreamProviderClient>
+      receiver_{this};
+  mojo::Remote<media::mojom::blink::AudioOutputStreamProvider> stream_provider_;
+  mojo::Remote<media::mojom::blink::AudioOutputStream> stream_;
   media::AudioOutputIPCDelegate* delegate_ = nullptr;
   scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_;
 
@@ -108,6 +111,6 @@
   DISALLOW_COPY_AND_ASSIGN(MojoAudioOutputIPC);
 };
 
-}  // namespace content
+}  // namespace blink
 
-#endif  // CONTENT_RENDERER_MEDIA_AUDIO_MOJO_AUDIO_OUTPUT_IPC_H_
+#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_MEDIA_AUDIO_MOJO_AUDIO_OUTPUT_IPC_H_
diff --git a/content/renderer/media/audio/mojo_audio_output_ipc_unittest.cc b/third_party/blink/renderer/modules/media/audio/mojo_audio_output_ipc_test.cc
similarity index 85%
rename from content/renderer/media/audio/mojo_audio_output_ipc_unittest.cc
rename to third_party/blink/renderer/modules/media/audio/mojo_audio_output_ipc_test.cc
index 2aee416..26d1810 100644
--- a/content/renderer/media/audio/mojo_audio_output_ipc_unittest.cc
+++ b/third_party/blink/renderer/modules/media/audio/mojo_audio_output_ipc_test.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 "content/renderer/media/audio/mojo_audio_output_ipc.h"
+#include "third_party/blink/renderer/modules/media/audio/mojo_audio_output_ipc.h"
 
 #include <algorithm>
 #include <memory>
@@ -13,7 +13,6 @@
 #include "base/optional.h"
 #include "base/run_loop.h"
 #include "base/test/gtest_util.h"
-#include "base/test/task_environment.h"
 #include "media/audio/audio_device_description.h"
 #include "media/base/audio_parameters.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
@@ -24,6 +23,7 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/platform/scheduler/test/renderer_scheduler_test_support.h"
+#include "third_party/blink/renderer/platform/testing/io_task_runner_testing_platform_support.h"
 
 using testing::_;
 using testing::AtLeast;
@@ -31,7 +31,7 @@
 using testing::Mock;
 using testing::StrictMock;
 
-namespace content {
+namespace blink {
 
 namespace {
 
@@ -46,14 +46,16 @@
 
 MojoAudioOutputIPC::FactoryAccessorCB NullAccessor() {
   return base::BindRepeating(
-      []() -> blink::mojom::RendererAudioOutputStreamFactory* {
+      []() -> blink::mojom::blink::RendererAudioOutputStreamFactory* {
         return nullptr;
       });
 }
 
-class TestStreamProvider : public media::mojom::AudioOutputStreamProvider {
+// TODO(https://crbug.com/787252): Convert the test away from using std::string.
+class TestStreamProvider
+    : public media::mojom::blink::AudioOutputStreamProvider {
  public:
-  explicit TestStreamProvider(media::mojom::AudioOutputStream* stream)
+  explicit TestStreamProvider(media::mojom::blink::AudioOutputStream* stream)
       : stream_(stream) {}
 
   ~TestStreamProvider() override {
@@ -64,13 +66,14 @@
 
   void Acquire(
       const media::AudioParameters& params,
-      mojo::PendingRemote<media::mojom::AudioOutputStreamProviderClient>
+      mojo::PendingRemote<media::mojom::blink::AudioOutputStreamProviderClient>
           pending_provider_client) override {
     EXPECT_EQ(receiver_, base::nullopt);
     EXPECT_NE(stream_, nullptr);
     provider_client_.reset();
     provider_client_.Bind(std::move(pending_provider_client));
-    mojo::PendingRemote<media::mojom::AudioOutputStream> stream_pending_remote;
+    mojo::PendingRemote<media::mojom::blink::AudioOutputStream>
+        stream_pending_remote;
     receiver_.emplace(stream_,
                       stream_pending_remote.InitWithNewPipeAndPassReceiver());
     base::CancelableSyncSocket foreign_socket;
@@ -84,20 +87,22 @@
 
   void SignalErrorToProviderClient() {
     provider_client_.ResetWithReason(
-        static_cast<uint32_t>(media::mojom::AudioOutputStreamObserver::
+        static_cast<uint32_t>(media::mojom::blink::AudioOutputStreamObserver::
                                   DisconnectReason::kPlatformError),
         std::string());
   }
 
  private:
-  media::mojom::AudioOutputStream* stream_;
-  mojo::Remote<media::mojom::AudioOutputStreamProviderClient> provider_client_;
-  base::Optional<mojo::Receiver<media::mojom::AudioOutputStream>> receiver_;
+  media::mojom::blink::AudioOutputStream* stream_;
+  mojo::Remote<media::mojom::blink::AudioOutputStreamProviderClient>
+      provider_client_;
+  base::Optional<mojo::Receiver<media::mojom::blink::AudioOutputStream>>
+      receiver_;
   base::CancelableSyncSocket socket_;
 };
 
 class TestRemoteFactory
-    : public blink::mojom::RendererAudioOutputStreamFactory {
+    : public blink::mojom::blink::RendererAudioOutputStreamFactory {
  public:
   TestRemoteFactory()
       : expect_request_(false),
@@ -106,24 +111,27 @@
   ~TestRemoteFactory() override {}
 
   void RequestDeviceAuthorization(
-      mojo::PendingReceiver<media::mojom::AudioOutputStreamProvider>
+      mojo::PendingReceiver<media::mojom::blink::AudioOutputStreamProvider>
           stream_provider_receiver,
       const base::Optional<base::UnguessableToken>& session_id,
-      const std::string& device_id,
+      const String& device_id,
       RequestDeviceAuthorizationCallback callback) override {
     EXPECT_EQ(session_id, expected_session_id_);
-    EXPECT_EQ(device_id, expected_device_id_);
+    EXPECT_EQ(device_id.Utf8(), expected_device_id_);
     EXPECT_TRUE(expect_request_);
     if (provider_) {
       std::move(callback).Run(
-          media::OutputDeviceStatus::OUTPUT_DEVICE_STATUS_OK, Params(),
-          std::string(kReturnedDeviceId));
+          static_cast<media::mojom::blink::OutputDeviceStatus>(
+              media::OutputDeviceStatus::OUTPUT_DEVICE_STATUS_OK),
+          Params(), String(kReturnedDeviceId));
       provider_receiver_.emplace(provider_.get(),
                                  std::move(stream_provider_receiver));
     } else {
       std::move(callback).Run(
-          media::OutputDeviceStatus::OUTPUT_DEVICE_STATUS_ERROR_NOT_AUTHORIZED,
-          Params(), std::string(""));
+          static_cast<media::mojom::blink::OutputDeviceStatus>(
+              media::OutputDeviceStatus::
+                  OUTPUT_DEVICE_STATUS_ERROR_NOT_AUTHORIZED),
+          Params(), String(""));
     }
     expect_request_ = false;
   }
@@ -168,7 +176,7 @@
   }
 
  private:
-  blink::mojom::RendererAudioOutputStreamFactory* get() {
+  blink::mojom::blink::RendererAudioOutputStreamFactory* get() {
     return this_remote_.get();
   }
 
@@ -176,15 +184,16 @@
   base::Optional<base::UnguessableToken> expected_session_id_;
   std::string expected_device_id_;
 
-  mojo::Remote<blink::mojom::RendererAudioOutputStreamFactory> this_remote_;
-  mojo::Receiver<blink::mojom::RendererAudioOutputStreamFactory> receiver_{
-      this};
+  mojo::Remote<blink::mojom::blink::RendererAudioOutputStreamFactory>
+      this_remote_;
+  mojo::Receiver<blink::mojom::blink::RendererAudioOutputStreamFactory>
+      receiver_{this};
   std::unique_ptr<TestStreamProvider> provider_;
-  base::Optional<mojo::Receiver<media::mojom::AudioOutputStreamProvider>>
+  base::Optional<mojo::Receiver<media::mojom::blink::AudioOutputStreamProvider>>
       provider_receiver_;
 };
 
-class MockStream : public media::mojom::AudioOutputStream {
+class MockStream : public media::mojom::blink::AudioOutputStream {
  public:
   MOCK_METHOD0(Play, void());
   MOCK_METHOD0(Pause, void());
@@ -215,8 +224,8 @@
 }  // namespace
 
 TEST(MojoAudioOutputIPC, AuthorizeWithoutFactory_CallsAuthorizedWithError) {
-  base::test::SingleThreadTaskEnvironment task_environment(
-      base::test::SingleThreadTaskEnvironment::MainThreadType::IO);
+  ScopedTestingPlatformSupport<IOTaskRunnerTestingPlatformSupport> platform;
+
   const base::UnguessableToken session_id = base::UnguessableToken::Create();
   StrictMock<MockDelegate> delegate;
 
@@ -238,8 +247,8 @@
 
 TEST(MojoAudioOutputIPC,
      CreateWithoutAuthorizationWithoutFactory_CallsAuthorizedWithError) {
-  base::test::SingleThreadTaskEnvironment task_environment(
-      base::test::SingleThreadTaskEnvironment::MainThreadType::IO);
+  ScopedTestingPlatformSupport<IOTaskRunnerTestingPlatformSupport> platform;
+
   StrictMock<MockDelegate> delegate;
 
   std::unique_ptr<media::AudioOutputIPC> ipc =
@@ -256,8 +265,8 @@
 }
 
 TEST(MojoAudioOutputIPC, DeviceAuthorized_Propagates) {
-  base::test::SingleThreadTaskEnvironment task_environment(
-      base::test::SingleThreadTaskEnvironment::MainThreadType::IO);
+  ScopedTestingPlatformSupport<IOTaskRunnerTestingPlatformSupport> platform;
+
   const base::UnguessableToken session_id = base::UnguessableToken::Create();
   TestRemoteFactory stream_factory;
   StrictMock<MockDelegate> delegate;
@@ -281,8 +290,8 @@
 }
 
 TEST(MojoAudioOutputIPC, OnDeviceCreated_Propagates) {
-  base::test::SingleThreadTaskEnvironment task_environment(
-      base::test::SingleThreadTaskEnvironment::MainThreadType::IO);
+  ScopedTestingPlatformSupport<IOTaskRunnerTestingPlatformSupport> platform;
+
   const base::UnguessableToken session_id = base::UnguessableToken::Create();
   TestRemoteFactory stream_factory;
   StrictMock<MockStream> stream;
@@ -310,8 +319,8 @@
 
 TEST(MojoAudioOutputIPC,
      CreateWithoutAuthorization_RequestsAuthorizationFirst) {
-  base::test::SingleThreadTaskEnvironment task_environment(
-      base::test::SingleThreadTaskEnvironment::MainThreadType::IO);
+  ScopedTestingPlatformSupport<IOTaskRunnerTestingPlatformSupport> platform;
+
   TestRemoteFactory stream_factory;
   StrictMock<MockStream> stream;
   StrictMock<MockDelegate> delegate;
@@ -339,8 +348,8 @@
 }
 
 TEST(MojoAudioOutputIPC, IsReusable) {
-  base::test::SingleThreadTaskEnvironment task_environment(
-      base::test::SingleThreadTaskEnvironment::MainThreadType::IO);
+  ScopedTestingPlatformSupport<IOTaskRunnerTestingPlatformSupport> platform;
+
   const base::UnguessableToken session_id = base::UnguessableToken::Create();
   TestRemoteFactory stream_factory;
   StrictMock<MockStream> stream;
@@ -372,8 +381,8 @@
 }
 
 TEST(MojoAudioOutputIPC, IsReusableAfterError) {
-  base::test::SingleThreadTaskEnvironment task_environment(
-      base::test::SingleThreadTaskEnvironment::MainThreadType::IO);
+  ScopedTestingPlatformSupport<IOTaskRunnerTestingPlatformSupport> platform;
+
   const base::UnguessableToken session_id = base::UnguessableToken::Create();
   TestRemoteFactory stream_factory;
   StrictMock<MockStream> stream;
@@ -427,8 +436,8 @@
 }
 
 TEST(MojoAudioOutputIPC, DeviceNotAuthorized_Propagates) {
-  base::test::SingleThreadTaskEnvironment task_environment(
-      base::test::SingleThreadTaskEnvironment::MainThreadType::IO);
+  ScopedTestingPlatformSupport<IOTaskRunnerTestingPlatformSupport> platform;
+
   const base::UnguessableToken session_id = base::UnguessableToken::Create();
   TestRemoteFactory stream_factory;
   StrictMock<MockDelegate> delegate;
@@ -460,8 +469,8 @@
   // The authorization IPC message might be aborted by the remote end
   // disconnecting. In this case, the MojoAudioOutputIPC object must still
   // send a notification to unblock the AudioOutputIPCDelegate.
-  base::test::SingleThreadTaskEnvironment task_environment(
-      base::test::SingleThreadTaskEnvironment::MainThreadType::IO);
+  ScopedTestingPlatformSupport<IOTaskRunnerTestingPlatformSupport> platform;
+
   const base::UnguessableToken session_id = base::UnguessableToken::Create();
   TestRemoteFactory stream_factory;
   StrictMock<MockDelegate> delegate;
@@ -492,8 +501,8 @@
   // This test makes sure that the MojoAudioOutputIPC doesn't callback for
   // authorization when the factory disconnects if it already got a callback
   // for authorization.
-  base::test::SingleThreadTaskEnvironment task_environment(
-      base::test::SingleThreadTaskEnvironment::MainThreadType::IO);
+  ScopedTestingPlatformSupport<IOTaskRunnerTestingPlatformSupport> platform;
+
   const base::UnguessableToken session_id = base::UnguessableToken::Create();
   TestRemoteFactory stream_factory;
   stream_factory.PrepareProviderForAuthorization(
@@ -520,8 +529,8 @@
 }
 
 TEST(MojoAudioOutputIPC, AuthorizeNoClose_DCHECKs) {
-  base::test::SingleThreadTaskEnvironment task_environment(
-      base::test::SingleThreadTaskEnvironment::MainThreadType::IO);
+  ScopedTestingPlatformSupport<IOTaskRunnerTestingPlatformSupport> platform;
+
   TestRemoteFactory stream_factory;
   const base::UnguessableToken session_id = base::UnguessableToken::Create();
   StrictMock<MockDelegate> delegate;
@@ -542,8 +551,8 @@
 }
 
 TEST(MojoAudioOutputIPC, CreateNoClose_DCHECKs) {
-  base::test::SingleThreadTaskEnvironment task_environment(
-      base::test::SingleThreadTaskEnvironment::MainThreadType::IO);
+  ScopedTestingPlatformSupport<IOTaskRunnerTestingPlatformSupport> platform;
+
   TestRemoteFactory stream_factory;
   StrictMock<MockDelegate> delegate;
   StrictMock<MockStream> stream;
@@ -566,8 +575,8 @@
 }
 
 TEST(MojoAudioOutputIPC, Play_Plays) {
-  base::test::SingleThreadTaskEnvironment task_environment(
-      base::test::SingleThreadTaskEnvironment::MainThreadType::IO);
+  ScopedTestingPlatformSupport<IOTaskRunnerTestingPlatformSupport> platform;
+
   TestRemoteFactory stream_factory;
   const base::UnguessableToken session_id = base::UnguessableToken::Create();
   StrictMock<MockStream> stream;
@@ -596,8 +605,8 @@
 }
 
 TEST(MojoAudioOutputIPC, Pause_Pauses) {
-  base::test::SingleThreadTaskEnvironment task_environment(
-      base::test::SingleThreadTaskEnvironment::MainThreadType::IO);
+  ScopedTestingPlatformSupport<IOTaskRunnerTestingPlatformSupport> platform;
+
   TestRemoteFactory stream_factory;
   const base::UnguessableToken session_id = base::UnguessableToken::Create();
   StrictMock<MockStream> stream;
@@ -626,8 +635,8 @@
 }
 
 TEST(MojoAudioOutputIPC, SetVolume_SetsVolume) {
-  base::test::SingleThreadTaskEnvironment task_environment(
-      base::test::SingleThreadTaskEnvironment::MainThreadType::IO);
+  ScopedTestingPlatformSupport<IOTaskRunnerTestingPlatformSupport> platform;
+
   TestRemoteFactory stream_factory;
   const base::UnguessableToken session_id = base::UnguessableToken::Create();
   StrictMock<MockStream> stream;
@@ -655,4 +664,4 @@
   base::RunLoop().RunUntilIdle();
 }
 
-}  // namespace content
+}  // namespace blink
diff --git a/third_party/blink/renderer/modules/media/audio/web_audio_output_ipc_factory.cc b/third_party/blink/renderer/modules/media/audio/web_audio_output_ipc_factory.cc
new file mode 100644
index 0000000..49836e94
--- /dev/null
+++ b/third_party/blink/renderer/modules/media/audio/web_audio_output_ipc_factory.cc
@@ -0,0 +1,146 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/public/web/modules/media/audio/web_audio_output_ipc_factory.h"
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/check_op.h"
+#include "base/containers/flat_map.h"
+#include "base/single_thread_task_runner.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
+#include "mojo/public/cpp/bindings/remote.h"
+#include "third_party/blink/public/common/browser_interface_broker_proxy.h"
+#include "third_party/blink/public/mojom/media/renderer_audio_output_stream_factory.mojom-blink.h"
+#include "third_party/blink/renderer/modules/media/audio/mojo_audio_output_ipc.h"
+
+namespace blink {
+
+WebAudioOutputIPCFactory* WebAudioOutputIPCFactory::instance_ = nullptr;
+
+class WebAudioOutputIPCFactory::Impl {
+ public:
+  using StreamFactoryMap = base::flat_map<
+      base::UnguessableToken,
+      mojo::Remote<mojom::blink::RendererAudioOutputStreamFactory>>;
+
+  explicit Impl(scoped_refptr<base::SingleThreadTaskRunner> io_task_runner)
+      : io_task_runner_(std::move(io_task_runner)) {}
+  ~Impl() { DCHECK(factory_remotes_.empty()); }
+
+  mojom::blink::RendererAudioOutputStreamFactory* GetRemoteFactory(
+      const base::UnguessableToken& frame_token) const;
+
+  void RegisterRemoteFactoryOnIOThread(
+      const base::UnguessableToken& frame_token,
+      mojo::PendingRemote<mojom::blink::RendererAudioOutputStreamFactory>
+          factory_pending_remote);
+
+  void MaybeDeregisterRemoteFactoryOnIOThread(
+      const base::UnguessableToken& frame_token);
+
+  // Maps frame id to the corresponding factory.
+  StreamFactoryMap factory_remotes_;
+  const scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(Impl);
+};
+
+WebAudioOutputIPCFactory::WebAudioOutputIPCFactory(
+    scoped_refptr<base::SingleThreadTaskRunner> io_task_runner)
+    : impl_(std::make_unique<Impl>(std::move(io_task_runner))) {
+  DCHECK(!instance_);
+  instance_ = this;
+}
+
+WebAudioOutputIPCFactory::~WebAudioOutputIPCFactory() {
+  // Allow destruction in tests.
+  DCHECK_EQ(instance_, this);
+  instance_ = nullptr;
+}
+
+std::unique_ptr<media::AudioOutputIPC>
+WebAudioOutputIPCFactory::CreateAudioOutputIPC(
+    const base::UnguessableToken& frame_token) const {
+  // Unretained is safe due to the contract at the top of the header file.
+  return std::make_unique<MojoAudioOutputIPC>(
+      base::BindRepeating(&WebAudioOutputIPCFactory::Impl::GetRemoteFactory,
+                          base::Unretained(impl_.get()), frame_token),
+      io_task_runner());
+}
+
+void WebAudioOutputIPCFactory::RegisterRemoteFactory(
+    const base::UnguessableToken& frame_token,
+    blink::BrowserInterfaceBrokerProxy* interface_broker) {
+  mojo::PendingRemote<mojom::blink::RendererAudioOutputStreamFactory>
+      factory_remote;
+  interface_broker->GetInterface(
+      factory_remote.InitWithNewPipeAndPassReceiver());
+  // Unretained is safe due to the contract at the top of the header file.
+  // It's safe to pass the |factory_remote| PendingRemote between threads.
+  io_task_runner()->PostTask(
+      FROM_HERE,
+      base::BindOnce(
+          &WebAudioOutputIPCFactory::Impl::RegisterRemoteFactoryOnIOThread,
+          base::Unretained(impl_.get()), frame_token,
+          std::move(factory_remote)));
+}
+
+void WebAudioOutputIPCFactory::MaybeDeregisterRemoteFactory(
+    const base::UnguessableToken& frame_token) {
+  io_task_runner()->PostTask(
+      FROM_HERE, base::BindOnce(&WebAudioOutputIPCFactory::Impl::
+                                    MaybeDeregisterRemoteFactoryOnIOThread,
+                                base::Unretained(impl_.get()), frame_token));
+}
+
+const scoped_refptr<base::SingleThreadTaskRunner>&
+WebAudioOutputIPCFactory::io_task_runner() const {
+  return impl_->io_task_runner_;
+}
+
+mojom::blink::RendererAudioOutputStreamFactory*
+WebAudioOutputIPCFactory::Impl::GetRemoteFactory(
+    const base::UnguessableToken& frame_token) const {
+  DCHECK(io_task_runner_->BelongsToCurrentThread());
+  auto it = factory_remotes_.find(frame_token);
+  return it == factory_remotes_.end() ? nullptr : it->second.get();
+}
+
+void WebAudioOutputIPCFactory::Impl::RegisterRemoteFactoryOnIOThread(
+    const base::UnguessableToken& frame_token,
+    mojo::PendingRemote<mojom::blink::RendererAudioOutputStreamFactory>
+        factory_pending_remote) {
+  DCHECK(io_task_runner_->BelongsToCurrentThread());
+  std::pair<StreamFactoryMap::iterator, bool> emplace_result =
+      factory_remotes_.emplace(frame_token, std::move(factory_pending_remote));
+
+  DCHECK(emplace_result.second) << "Attempt to register a factory for a "
+                                   "frame which already has a factory "
+                                   "registered.";
+
+  auto& emplaced_factory = emplace_result.first->second;
+  DCHECK(emplaced_factory.is_bound())
+      << "Factory is not bound to a remote implementation.";
+
+  // Unretained is safe because |this| owns the remote, so a connection error
+  // cannot trigger after destruction.
+  emplaced_factory.set_disconnect_handler(base::BindOnce(
+      &WebAudioOutputIPCFactory::Impl::MaybeDeregisterRemoteFactoryOnIOThread,
+      base::Unretained(this), frame_token));
+}
+
+void WebAudioOutputIPCFactory::Impl::MaybeDeregisterRemoteFactoryOnIOThread(
+    const base::UnguessableToken& frame_token) {
+  DCHECK(io_task_runner_->BelongsToCurrentThread());
+  // This function can be called both by the frame and the connection error
+  // handler of the factory remote. Calling erase multiple times even though
+  // there is nothing to erase is safe, so we don't have to handle this in any
+  // particular way.
+  factory_remotes_.erase(frame_token);
+}
+
+}  // namespace blink
diff --git a/content/renderer/media/audio/audio_output_ipc_factory_unittest.cc b/third_party/blink/renderer/modules/media/audio/web_audio_output_ipc_factory_test.cc
similarity index 73%
rename from content/renderer/media/audio/audio_output_ipc_factory_unittest.cc
rename to third_party/blink/renderer/modules/media/audio/web_audio_output_ipc_factory_test.cc
index 24514c3c..5a791edd 100644
--- a/content/renderer/media/audio/audio_output_ipc_factory_unittest.cc
+++ b/third_party/blink/renderer/modules/media/audio/web_audio_output_ipc_factory_test.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 "content/renderer/media/audio/audio_output_ipc_factory.h"
+#include "third_party/blink/public/web/modules/media/audio/web_audio_output_ipc_factory.h"
 
 #include <string>
 #include <utility>
@@ -12,7 +12,6 @@
 #include "base/message_loop/message_pump_type.h"
 #include "base/run_loop.h"
 #include "base/test/bind_test_util.h"
-#include "base/test/task_environment.h"
 #include "base/threading/thread.h"
 #include "media/audio/audio_output_ipc.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
@@ -21,10 +20,12 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/common/browser_interface_broker_proxy.h"
+#include "third_party/blink/public/mojom/media/renderer_audio_output_stream_factory.mojom-blink.h"
+#include "third_party/blink/renderer/platform/testing/io_task_runner_testing_platform_support.h"
 
 using ::testing::_;
 
-namespace content {
+namespace blink {
 
 namespace {
 
@@ -45,20 +46,22 @@
 }
 
 class FakeRemoteFactory
-    : public blink::mojom::RendererAudioOutputStreamFactory {
+    : public mojom::blink::RendererAudioOutputStreamFactory {
  public:
   FakeRemoteFactory() = default;
   ~FakeRemoteFactory() override {}
 
   void RequestDeviceAuthorization(
-      mojo::PendingReceiver<media::mojom::AudioOutputStreamProvider>
+      mojo::PendingReceiver<media::mojom::blink::AudioOutputStreamProvider>
           stream_provider,
       const base::Optional<base::UnguessableToken>& session_id,
-      const std::string& device_id,
+      const String& device_id,
       RequestDeviceAuthorizationCallback callback) override {
     std::move(callback).Run(
-        media::OutputDeviceStatus::OUTPUT_DEVICE_STATUS_ERROR_NOT_AUTHORIZED,
-        media::AudioParameters::UnavailableDeviceParams(), std::string());
+        static_cast<media::mojom::blink::OutputDeviceStatus>(
+            media::OutputDeviceStatus::
+                OUTPUT_DEVICE_STATUS_ERROR_NOT_AUTHORIZED),
+        media::AudioParameters::UnavailableDeviceParams(), WTF::g_empty_string);
     EXPECT_FALSE(on_called_.is_null());
     std::move(on_called_).Run();
   }
@@ -70,12 +73,12 @@
   void Bind(mojo::ScopedMessagePipeHandle handle) {
     EXPECT_FALSE(receiver_.is_bound());
     receiver_.Bind(
-        mojo::PendingReceiver<blink::mojom::RendererAudioOutputStreamFactory>(
+        mojo::PendingReceiver<mojom::blink::RendererAudioOutputStreamFactory>(
             std::move(handle)));
   }
 
  private:
-  mojo::Receiver<blink::mojom::RendererAudioOutputStreamFactory> receiver_{
+  mojo::Receiver<mojom::blink::RendererAudioOutputStreamFactory> receiver_{
       this};
   base::OnceClosure on_called_;
 };
@@ -93,10 +96,10 @@
 
 }  // namespace
 
-class AudioOutputIPCFactoryTest : public testing::Test {
+class WebAudioOutputIPCFactoryTest : public testing::Test {
  public:
-  AudioOutputIPCFactoryTest() {}
-  ~AudioOutputIPCFactoryTest() override {}
+  WebAudioOutputIPCFactoryTest() = default;
+  ~WebAudioOutputIPCFactoryTest() override = default;
 
   void RequestAuthorizationOnIOThread(
       std::unique_ptr<media::AudioOutputIPC> output_ipc) {
@@ -110,10 +113,10 @@
   FakeAudioOutputIPCDelegate fake_delegate;
 };
 
-TEST_F(AudioOutputIPCFactoryTest, CallFactoryFromIOThread) {
-  // This test makes sure that AudioOutputIPCFactory correctly binds the
+TEST_F(WebAudioOutputIPCFactoryTest, CallFactoryFromIOThread) {
+  // This test makes sure that WebAudioOutputIPCFactory correctly binds the
   // RendererAudioOutputStreamFactory to the IO thread.
-  base::test::SingleThreadTaskEnvironment task_environment;
+  ScopedTestingPlatformSupport<IOTaskRunnerTestingPlatformSupport> platform;
   base::RunLoop run_loop;
   auto io_thread = MakeIOThread();
 
@@ -122,11 +125,11 @@
 
   auto& interface_broker = blink::GetEmptyBrowserInterfaceBroker();
   interface_broker.SetBinderForTesting(
-      blink::mojom::RendererAudioOutputStreamFactory::Name_,
+      mojom::blink::RendererAudioOutputStreamFactory::Name_,
       base::BindRepeating(&FakeRemoteFactory::Bind,
                           base::Unretained(&remote_factory)));
 
-  AudioOutputIPCFactory ipc_factory(io_thread->task_runner());
+  WebAudioOutputIPCFactory ipc_factory(io_thread->task_runner());
 
   ipc_factory.RegisterRemoteFactory(TokenFromInt(kRenderFrameId),
                                     &interface_broker);
@@ -138,7 +141,7 @@
   io_thread->task_runner()->PostTask(
       FROM_HERE,
       base::BindOnce(
-          &AudioOutputIPCFactoryTest::RequestAuthorizationOnIOThread,
+          &WebAudioOutputIPCFactoryTest::RequestAuthorizationOnIOThread,
           base::Unretained(this),
           ipc_factory.CreateAudioOutputIPC(TokenFromInt(kRenderFrameId))));
 
@@ -148,15 +151,15 @@
   ipc_factory.MaybeDeregisterRemoteFactory(TokenFromInt(0));
 
   interface_broker.SetBinderForTesting(
-      blink::mojom::RendererAudioOutputStreamFactory::Name_, {});
+      mojom::blink::RendererAudioOutputStreamFactory::Name_, {});
 
   io_thread.reset();
   base::RunLoop().RunUntilIdle();
 }
 
-TEST_F(AudioOutputIPCFactoryTest, SeveralFactories) {
+TEST_F(WebAudioOutputIPCFactoryTest, SeveralFactories) {
   // This test simulates having several frames being created and destructed.
-  base::test::SingleThreadTaskEnvironment task_environment;
+  ScopedTestingPlatformSupport<IOTaskRunnerTestingPlatformSupport> platform;
   auto io_thread = MakeIOThread();
   const int n_factories = 5;
 
@@ -165,7 +168,7 @@
   auto& interface_broker = blink::GetEmptyBrowserInterfaceBroker();
 
   interface_broker.SetBinderForTesting(
-      blink::mojom::RendererAudioOutputStreamFactory::Name_,
+      mojom::blink::RendererAudioOutputStreamFactory::Name_,
       base::BindLambdaForTesting([&](mojo::ScopedMessagePipeHandle handle) {
         static int factory_index = 0;
         DCHECK_LT(factory_index, n_factories);
@@ -174,7 +177,7 @@
 
   base::RunLoop().RunUntilIdle();
 
-  AudioOutputIPCFactory ipc_factory(io_thread->task_runner());
+  WebAudioOutputIPCFactory ipc_factory(io_thread->task_runner());
 
   for (size_t i = 0; i < n_factories; i++) {
     ipc_factory.RegisterRemoteFactory(TokenFromInt(kRenderFrameId + i),
@@ -186,7 +189,7 @@
   io_thread->task_runner()->PostTask(
       FROM_HERE,
       base::BindOnce(
-          &AudioOutputIPCFactoryTest::RequestAuthorizationOnIOThread,
+          &WebAudioOutputIPCFactoryTest::RequestAuthorizationOnIOThread,
           base::Unretained(this),
           ipc_factory.CreateAudioOutputIPC(TokenFromInt(kRenderFrameId))));
   run_loop.Run();
@@ -199,7 +202,7 @@
   io_thread->task_runner()->PostTask(
       FROM_HERE,
       base::BindOnce(
-          &AudioOutputIPCFactoryTest::RequestAuthorizationOnIOThread,
+          &WebAudioOutputIPCFactoryTest::RequestAuthorizationOnIOThread,
           base::Unretained(this),
           ipc_factory.CreateAudioOutputIPC(TokenFromInt(kRenderFrameId + 2))));
   run_loop2.Run();
@@ -211,40 +214,41 @@
   }
 
   interface_broker.SetBinderForTesting(
-      blink::mojom::RendererAudioOutputStreamFactory::Name_, {});
+      mojom::blink::RendererAudioOutputStreamFactory::Name_, {});
 
   io_thread.reset();
   base::RunLoop().RunUntilIdle();
 }
 
-TEST_F(AudioOutputIPCFactoryTest, RegisterDeregisterBackToBack_Deregisters) {
+TEST_F(WebAudioOutputIPCFactoryTest, RegisterDeregisterBackToBack_Deregisters) {
   // This test makes sure that calling Register... followed by Deregister...
   // correctly sequences the registration before the deregistration.
-  base::test::SingleThreadTaskEnvironment task_environment;
+  ScopedTestingPlatformSupport<IOTaskRunnerTestingPlatformSupport> platform;
+
   auto io_thread = MakeIOThread();
 
   FakeRemoteFactory remote_factory;
 
   auto& interface_broker = blink::GetEmptyBrowserInterfaceBroker();
   interface_broker.SetBinderForTesting(
-      blink::mojom::RendererAudioOutputStreamFactory::Name_,
+      mojom::blink::RendererAudioOutputStreamFactory::Name_,
       base::BindRepeating(&FakeRemoteFactory::Bind,
                           base::Unretained(&remote_factory)));
 
-  AudioOutputIPCFactory ipc_factory(io_thread->task_runner());
+  WebAudioOutputIPCFactory ipc_factory(io_thread->task_runner());
 
   ipc_factory.RegisterRemoteFactory(TokenFromInt(kRenderFrameId),
                                     &interface_broker);
   ipc_factory.MaybeDeregisterRemoteFactory(TokenFromInt(kRenderFrameId));
   // That there is no factory remaining at destruction is DCHECKed in the
-  // AudioOutputIPCFactory destructor.
+  // WebAudioOutputIPCFactory destructor.
 
   base::RunLoop().RunUntilIdle();
 
   interface_broker.SetBinderForTesting(
-      blink::mojom::RendererAudioOutputStreamFactory::Name_, {});
+      mojom::blink::RendererAudioOutputStreamFactory::Name_, {});
   io_thread.reset();
   base::RunLoop().RunUntilIdle();
 }
 
-}  // namespace content
+}  // namespace blink
diff --git a/third_party/blink/renderer/modules/webcodecs/BUILD.gn b/third_party/blink/renderer/modules/webcodecs/BUILD.gn
index 497aa35..2b48c582 100644
--- a/third_party/blink/renderer/modules/webcodecs/BUILD.gn
+++ b/third_party/blink/renderer/modules/webcodecs/BUILD.gn
@@ -15,6 +15,8 @@
     "audio_frame.cc",
     "audio_frame.h",
     "codec_config_eval.h",
+    "codec_state_helper.cc",
+    "codec_state_helper.h",
     "decoder_selector.cc",
     "decoder_selector.h",
     "decoder_template.cc",
diff --git a/third_party/blink/renderer/modules/webcodecs/audio_decoder.idl b/third_party/blink/renderer/modules/webcodecs/audio_decoder.idl
index 93c3efd..f985f84 100644
--- a/third_party/blink/renderer/modules/webcodecs/audio_decoder.idl
+++ b/third_party/blink/renderer/modules/webcodecs/audio_decoder.idl
@@ -57,4 +57,7 @@
   //
   // Not recoverable: make a new AudioDecoder if needed.
   [RaisesException] void close();
+
+  // Which state the decoder is in, indicating which methods can be called.
+  readonly attribute CodecState state;
 };
diff --git a/third_party/blink/renderer/modules/webcodecs/codec_state.idl b/third_party/blink/renderer/modules/webcodecs/codec_state.idl
new file mode 100644
index 0000000..3b503ca54
--- /dev/null
+++ b/third_party/blink/renderer/modules/webcodecs/codec_state.idl
@@ -0,0 +1,11 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// https://github.com/WICG/web-codecs
+
+enum CodecState {
+  "unconfigured",
+  "configured",
+  "closed"
+};
\ No newline at end of file
diff --git a/third_party/blink/renderer/modules/webcodecs/codec_state_helper.cc b/third_party/blink/renderer/modules/webcodecs/codec_state_helper.cc
new file mode 100644
index 0000000..4aadc93
--- /dev/null
+++ b/third_party/blink/renderer/modules/webcodecs/codec_state_helper.cc
@@ -0,0 +1,35 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/modules/webcodecs/codec_state_helper.h"
+
+namespace blink {
+
+// static
+bool ThrowIfCodecStateClosed(V8CodecState state,
+                             String operation,
+                             ExceptionState& exception_state) {
+  if (state.AsEnum() != V8CodecState::Enum::kClosed)
+    return false;
+
+  exception_state.ThrowDOMException(
+      DOMExceptionCode::kInvalidStateError,
+      "Cannot call '" + operation + "' on a closed codec.");
+  return true;
+}
+
+// static
+bool ThrowIfCodecStateUnconfigured(V8CodecState state,
+                                   String operation,
+                                   ExceptionState& exception_state) {
+  if (state.AsEnum() != V8CodecState::Enum::kUnconfigured)
+    return false;
+
+  exception_state.ThrowDOMException(
+      DOMExceptionCode::kInvalidStateError,
+      "Cannot call '" + operation + "' on an unconfigured codec.");
+  return true;
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/modules/webcodecs/codec_state_helper.h b/third_party/blink/renderer/modules/webcodecs/codec_state_helper.h
new file mode 100644
index 0000000..f49cc3a
--- /dev/null
+++ b/third_party/blink/renderer/modules/webcodecs/codec_state_helper.h
@@ -0,0 +1,26 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_WEBCODECS_CODEC_STATE_HELPER_H_
+#define THIRD_PARTY_BLINK_RENDERER_MODULES_WEBCODECS_CODEC_STATE_HELPER_H_
+
+#include "third_party/blink/renderer/bindings/modules/v8/v8_codec_state.h"
+#include "third_party/blink/renderer/platform/bindings/exception_state.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+// Returns true and sets the exception state if the passed CodecState is
+// kClosed. The exception message is built from the |operation| name.
+bool ThrowIfCodecStateClosed(V8CodecState, String operation, ExceptionState&);
+
+// Returns true and sets the exception state if the passed CodecState is
+// kUnconfigured. The exception message is built from the |operation| name.
+bool ThrowIfCodecStateUnconfigured(V8CodecState,
+                                   String operation,
+                                   ExceptionState&);
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_WEBCODECS_CODEC_STATE_HELPER_H_
diff --git a/third_party/blink/renderer/modules/webcodecs/decoder_template.cc b/third_party/blink/renderer/modules/webcodecs/decoder_template.cc
index c9b5d679..9762fa5 100644
--- a/third_party/blink/renderer/modules/webcodecs/decoder_template.cc
+++ b/third_party/blink/renderer/modules/webcodecs/decoder_template.cc
@@ -26,6 +26,7 @@
 #include "third_party/blink/renderer/modules/webcodecs/audio_decoder.h"
 #include "third_party/blink/renderer/modules/webcodecs/audio_frame.h"
 #include "third_party/blink/renderer/modules/webcodecs/codec_config_eval.h"
+#include "third_party/blink/renderer/modules/webcodecs/codec_state_helper.h"
 #include "third_party/blink/renderer/modules/webcodecs/video_decoder.h"
 #include "third_party/blink/renderer/modules/webcodecs/video_frame.h"
 #include "third_party/blink/renderer/platform/bindings/exception_code.h"
@@ -42,7 +43,7 @@
 DecoderTemplate<Traits>::DecoderTemplate(ScriptState* script_state,
                                          const InitType* init,
                                          ExceptionState& exception_state)
-    : script_state_(script_state) {
+    : script_state_(script_state), state_(V8CodecState::Enum::kUnconfigured) {
   DVLOG(1) << __func__;
   DCHECK(init->hasOutput());
   DCHECK(init->hasError());
@@ -61,14 +62,16 @@
 }
 
 template <typename Traits>
+bool DecoderTemplate<Traits>::IsClosed() {
+  return state_ == V8CodecState::Enum::kClosed;
+}
+
+template <typename Traits>
 void DecoderTemplate<Traits>::configure(const ConfigType* config,
                                         ExceptionState& exception_state) {
   DVLOG(1) << __func__;
-  if (is_closed_) {
-    exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
-                                      "Cannot configure a closed codec.");
+  if (ThrowIfCodecStateClosed(state_, "decode", exception_state))
     return;
-  }
 
   auto media_config = std::make_unique<MediaConfigType>();
   String console_message;
@@ -88,6 +91,8 @@
       break;
   }
 
+  state_ = V8CodecState(V8CodecState::Enum::kConfigured);
+
   Request* request = MakeGarbageCollected<Request>();
   request->type = Request::Type::kConfigure;
   request->media_config = std::move(media_config);
@@ -99,11 +104,11 @@
 void DecoderTemplate<Traits>::decode(const InputType* chunk,
                                      ExceptionState& exception_state) {
   DVLOG(3) << __func__;
-  if (is_closed_) {
-    exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
-                                      "Cannot decode with a closed codec.");
+  if (ThrowIfCodecStateClosed(state_, "decode", exception_state))
     return;
-  }
+
+  if (ThrowIfCodecStateUnconfigured(state_, "decode", exception_state))
+    return;
 
   Request* request = MakeGarbageCollected<Request>();
   request->type = Request::Type::kDecode;
@@ -116,11 +121,11 @@
 template <typename Traits>
 ScriptPromise DecoderTemplate<Traits>::flush(ExceptionState& exception_state) {
   DVLOG(3) << __func__;
-  if (is_closed_) {
-    exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
-                                      "Cannot flush a closed codec.");
+  if (ThrowIfCodecStateClosed(state_, "flush", exception_state))
     return ScriptPromise();
-  }
+
+  if (ThrowIfCodecStateUnconfigured(state_, "flush", exception_state))
+    return ScriptPromise();
 
   Request* request = MakeGarbageCollected<Request>();
   request->type = Request::Type::kFlush;
@@ -135,11 +140,13 @@
 template <typename Traits>
 void DecoderTemplate<Traits>::reset(ExceptionState& exception_state) {
   DVLOG(3) << __func__;
-  if (is_closed_) {
-    exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
-                                      "Cannot reset a closed codec.");
+  if (ThrowIfCodecStateClosed(state_, "reset", exception_state))
     return;
-  }
+
+  if (state_ == V8CodecState::Enum::kUnconfigured)
+    return;
+
+  state_ = V8CodecState(V8CodecState::Enum::kUnconfigured);
 
   Request* request = MakeGarbageCollected<Request>();
   request->type = Request::Type::kReset;
@@ -151,18 +158,16 @@
 template <typename Traits>
 void DecoderTemplate<Traits>::close(ExceptionState& exception_state) {
   DVLOG(3) << __func__;
-  if (is_closed_) {
-    exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
-                                      "Codec is already closed.");
+  if (ThrowIfCodecStateClosed(state_, "close", exception_state))
     return;
-  }
+
   Shutdown(false);
 }
 
 template <typename Traits>
 void DecoderTemplate<Traits>::ProcessRequests() {
   DVLOG(3) << __func__;
-  DCHECK(!is_closed_);
+  DCHECK(!IsClosed());
   while (!pending_request_ && !requests_.IsEmpty()) {
     Request* request = requests_.front();
     switch (request->type) {
@@ -190,7 +195,7 @@
 template <typename Traits>
 bool DecoderTemplate<Traits>::ProcessConfigureRequest(Request* request) {
   DVLOG(3) << __func__;
-  DCHECK(!is_closed_);
+  DCHECK(!IsClosed());
   DCHECK(!pending_request_);
   DCHECK_EQ(request->type, Request::Type::kConfigure);
   DCHECK(request->media_config);
@@ -242,7 +247,7 @@
 template <typename Traits>
 bool DecoderTemplate<Traits>::ProcessDecodeRequest(Request* request) {
   DVLOG(3) << __func__;
-  DCHECK(!is_closed_);
+  DCHECK_EQ(state_, V8CodecState::Enum::kConfigured);
   DCHECK(!pending_request_);
   DCHECK_EQ(request->type, Request::Type::kDecode);
   DCHECK_GT(requested_decodes_, 0);
@@ -285,7 +290,7 @@
 template <typename Traits>
 bool DecoderTemplate<Traits>::ProcessFlushRequest(Request* request) {
   DVLOG(3) << __func__;
-  DCHECK(!is_closed_);
+  DCHECK(!IsClosed());
   DCHECK(!pending_request_);
   DCHECK_EQ(request->type, Request::Type::kFlush);
 
@@ -315,7 +320,7 @@
 template <typename Traits>
 bool DecoderTemplate<Traits>::ProcessResetRequest(Request* request) {
   DVLOG(3) << __func__;
-  DCHECK(!is_closed_);
+  DCHECK(!IsClosed());
   DCHECK(!pending_request_);
   DCHECK_EQ(request->type, Request::Type::kReset);
   DCHECK_GT(requested_resets_, 0);
@@ -331,7 +336,7 @@
 template <typename Traits>
 void DecoderTemplate<Traits>::HandleError() {
   DVLOG(1) << __func__;
-  if (is_closed_)
+  if (IsClosed())
     return;
 
   Shutdown(true);
@@ -340,14 +345,14 @@
 template <typename Traits>
 void DecoderTemplate<Traits>::Shutdown(bool is_error) {
   DVLOG(3) << __func__;
-  DCHECK(!is_closed_);
+  DCHECK(!IsClosed());
 
   // Store the error callback so that we can use it after clearing state.
   V8WebCodecsErrorCallback* error_cb = error_cb_.Get();
 
   // Prevent any new public API calls during teardown.
   // This should make it safe to call into JS synchronously.
-  is_closed_ = true;
+  state_ = V8CodecState(V8CodecState::Enum::kClosed);
 
   // Prevent any late callbacks running.
   output_cb_.Release();
@@ -379,7 +384,7 @@
 template <typename Traits>
 void DecoderTemplate<Traits>::OnConfigureFlushDone(media::DecodeStatus status) {
   DVLOG(3) << __func__;
-  if (is_closed_)
+  if (IsClosed())
     return;
 
   DCHECK(pending_request_);
@@ -400,7 +405,7 @@
 template <typename Traits>
 void DecoderTemplate<Traits>::OnInitializeDone(media::Status status) {
   DVLOG(3) << __func__;
-  if (is_closed_)
+  if (IsClosed())
     return;
 
   DCHECK(pending_request_);
@@ -423,7 +428,7 @@
 void DecoderTemplate<Traits>::OnDecodeDone(uint32_t id,
                                            media::DecodeStatus status) {
   DVLOG(3) << __func__;
-  if (is_closed_)
+  if (IsClosed())
     return;
 
   if (status != media::DecodeStatus::OK &&
@@ -441,7 +446,7 @@
 template <typename Traits>
 void DecoderTemplate<Traits>::OnFlushDone(media::DecodeStatus status) {
   DVLOG(3) << __func__;
-  if (is_closed_)
+  if (IsClosed())
     return;
 
   DCHECK(pending_request_);
@@ -459,7 +464,7 @@
 template <typename Traits>
 void DecoderTemplate<Traits>::OnResetDone() {
   DVLOG(3) << __func__;
-  if (is_closed_)
+  if (IsClosed())
     return;
 
   DCHECK(pending_request_);
@@ -472,8 +477,9 @@
 template <typename Traits>
 void DecoderTemplate<Traits>::OnOutput(scoped_refptr<MediaOutputType> output) {
   DVLOG(3) << __func__;
-  if (is_closed_)
+  if (state_.AsEnum() != V8CodecState::Enum::kConfigured)
     return;
+
   output_cb_->InvokeAndReportException(
       nullptr, MakeGarbageCollected<OutputType>(output));
 }
diff --git a/third_party/blink/renderer/modules/webcodecs/decoder_template.h b/third_party/blink/renderer/modules/webcodecs/decoder_template.h
index 43732fc..6369944e 100644
--- a/third_party/blink/renderer/modules/webcodecs/decoder_template.h
+++ b/third_party/blink/renderer/modules/webcodecs/decoder_template.h
@@ -13,6 +13,7 @@
 #include "media/base/status.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_promise.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
+#include "third_party/blink/renderer/bindings/modules/v8/v8_codec_state.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_web_codecs_error_callback.h"
 #include "third_party/blink/renderer/modules/modules_export.h"
 #include "third_party/blink/renderer/modules/webcodecs/codec_config_eval.h"
@@ -45,6 +46,7 @@
   ScriptPromise flush(ExceptionState&);
   void reset(ExceptionState&);
   void close(ExceptionState&);
+  String state() const { return state_; }
 
   // GarbageCollected override.
   void Trace(Visitor*) const override;
@@ -103,6 +105,9 @@
   void OnResetDone();
   void OnOutput(scoped_refptr<MediaOutputType>);
 
+  // Helper function making it easier to check |state_|.
+  bool IsClosed();
+
   Member<ScriptState> script_state_;
   Member<OutputCallbackType> output_cb_;
   Member<V8WebCodecsErrorCallback> error_cb_;
@@ -111,6 +116,9 @@
   int32_t requested_decodes_ = 0;
   int32_t requested_resets_ = 0;
 
+  // Which state the codec is in, determining which calls we can receive.
+  V8CodecState state_;
+
   // An in-flight, mutually-exclusive request.
   // Could be a configure, flush, or reset. Decodes go in |pending_decodes_|.
   Member<Request> pending_request_;
@@ -121,7 +129,6 @@
   // duplicates can be elided.
   std::unique_ptr<MediaDecoderType> decoder_;
   bool initializing_sync_ = false;
-  bool is_closed_ = false;
 
   // TODO(sandersd): Can this just be a HashSet by ptr comparison?
   uint32_t pending_decode_id_ = 0;
diff --git a/third_party/blink/renderer/modules/webcodecs/idls.gni b/third_party/blink/renderer/modules/webcodecs/idls.gni
index 4045608..b97d8fa 100644
--- a/third_party/blink/renderer/modules/webcodecs/idls.gni
+++ b/third_party/blink/renderer/modules/webcodecs/idls.gni
@@ -41,6 +41,8 @@
   "video_track_writer_parameters.idl",
 ]
 
+modules_typedefs_enums_only_idl_files = [ "codec_state.idl" ]
+
 # IDL files that either define partial interfaces or target (right side of)
 # `includes`.
 modules_dependency_idl_files = []
diff --git a/third_party/blink/renderer/modules/webcodecs/video_decoder.idl b/third_party/blink/renderer/modules/webcodecs/video_decoder.idl
index c396dbc3..181e46cd 100644
--- a/third_party/blink/renderer/modules/webcodecs/video_decoder.idl
+++ b/third_party/blink/renderer/modules/webcodecs/video_decoder.idl
@@ -18,7 +18,7 @@
   // |init| includes an |output| callback for emitting VideoFrames and an
   // |error| callback for emitting decode errors.
   //
-  // When in an error state, methods other than reset() will fail.
+  // When in an closed state, all methods will fail.
   //
   // TODO(sandersd): Consider adding a state or last error attribute.
   [CallWith=ScriptState, RaisesException] constructor(VideoDecoderInit init);
@@ -73,4 +73,7 @@
   //
   // Not recoverable: make a new VideoDecoder if needed.
   [RaisesException] void close();
+
+  // Which state the decoder is in, indicating which methods can be called.
+  readonly attribute CodecState state;
 };
diff --git a/third_party/blink/renderer/modules/webcodecs/video_encoder.cc b/third_party/blink/renderer/modules/webcodecs/video_encoder.cc
index da98d4d..6eb8b9b1 100644
--- a/third_party/blink/renderer/modules/webcodecs/video_encoder.cc
+++ b/third_party/blink/renderer/modules/webcodecs/video_encoder.cc
@@ -31,6 +31,7 @@
 #include "third_party/blink/renderer/core/dom/dom_exception.h"
 #include "third_party/blink/renderer/core/streams/readable_stream.h"
 #include "third_party/blink/renderer/core/streams/writable_stream.h"
+#include "third_party/blink/renderer/modules/webcodecs/codec_state_helper.h"
 #include "third_party/blink/renderer/modules/webcodecs/encoded_video_chunk.h"
 #include "third_party/blink/renderer/modules/webcodecs/encoded_video_metadata.h"
 #include "third_party/blink/renderer/platform/bindings/enumeration_base.h"
@@ -106,7 +107,7 @@
 VideoEncoder::VideoEncoder(ScriptState* script_state,
                            const VideoEncoderInit* init,
                            ExceptionState& exception_state)
-    : script_state_(script_state) {
+    : state_(V8CodecState::Enum::kUnconfigured), script_state_(script_state) {
   output_callback_ = init->output();
   if (init->hasError())
     error_callback_ = init->error();
@@ -229,6 +230,9 @@
                              ExceptionState& exception_state) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
+  if (ThrowIfCodecStateClosed(state_, "configure", exception_state))
+    return;
+
   auto parsed_config = ParseConfig(config, exception_state);
 
   if (!parsed_config) {
@@ -244,6 +248,8 @@
   // TODO(https://crbug.com/1119892): flush |media_encoder_| if it already
   // exists, otherwise might could lose frames in flight.
 
+  state_ = V8CodecState(V8CodecState::Enum::kConfigured);
+
   Request* request = MakeGarbageCollected<Request>();
   request->type = Request::Type::kConfigure;
   request->config = std::move(parsed_config);
@@ -254,11 +260,12 @@
                           const VideoEncoderEncodeOptions* opts,
                           ExceptionState& exception_state) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  if (!media_encoder_) {
-    exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
-                                      "Encoder is not configured yet.");
+
+  if (ThrowIfCodecStateClosed(state_, "encode", exception_state))
     return;
-  }
+
+  if (ThrowIfCodecStateUnconfigured(state_, "encode", exception_state))
+    return;
 
   // This will fail if |frame| is already destroyed.
   auto* internal_frame = frame->clone(exception_state);
@@ -296,22 +303,25 @@
 
 void VideoEncoder::close(ExceptionState& exception_state) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  if (!media_encoder_)
+
+  if (ThrowIfCodecStateClosed(state_, "close", exception_state))
     return;
 
-  reset(exception_state);
+  state_ = V8CodecState(V8CodecState::Enum::kClosed);
+
+  ClearRequests();
   media_encoder_.reset();
   output_callback_.Clear();
   error_callback_.Clear();
 }
 
-ScriptPromise VideoEncoder::flush(ExceptionState&) {
+ScriptPromise VideoEncoder::flush(ExceptionState& exception_state) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  if (!media_encoder_) {
-    auto* ex = MakeGarbageCollected<DOMException>(
-        DOMExceptionCode::kInvalidStateError, "Encoder is not configured yet.");
-    return ScriptPromise::RejectWithDOMException(script_state_, ex);
-  }
+  if (ThrowIfCodecStateClosed(state_, "flush", exception_state))
+    return ScriptPromise();
+
+  if (ThrowIfCodecStateUnconfigured(state_, "flush", exception_state))
+    return ScriptPromise();
 
   Request* request = MakeGarbageCollected<Request>();
   request->resolver =
@@ -321,10 +331,19 @@
   return request->resolver->Promise();
 }
 
-void VideoEncoder::reset(ExceptionState&) {
+void VideoEncoder::reset(ExceptionState& exception_state) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   // TODO: Not fully implemented yet
+  if (ThrowIfCodecStateClosed(state_, "reset", exception_state))
+    return;
 
+  ClearRequests();
+
+  state_ = V8CodecState(V8CodecState::Enum::kUnconfigured);
+}
+
+void VideoEncoder::ClearRequests() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   while (!requests_.empty()) {
     Request* pending_req = requests_.TakeFirst();
     DCHECK(pending_req);
@@ -337,7 +356,8 @@
 }
 
 void VideoEncoder::CallOutputCallback(EncodedVideoChunk* chunk) {
-  if (!script_state_->ContextIsValid() || !output_callback_)
+  if (!script_state_->ContextIsValid() || !output_callback_ ||
+      state_.AsEnum() != V8CodecState::Enum::kConfigured)
     return;
   ScriptState::Scope scope(script_state_);
   output_callback_->InvokeAndReportException(nullptr, chunk);
@@ -347,6 +367,10 @@
   // Save a temp before we clear the callback.
   V8WebCodecsErrorCallback* error_callback = error_callback_.Get();
 
+  state_ = V8CodecState(V8CodecState::Enum::kClosed);
+
+  ClearRequests();
+
   // Errors are permanent. Shut everything down.
   error_callback_.Clear();
   media_encoder_.reset();
@@ -391,6 +415,8 @@
 
 void VideoEncoder::ProcessEncode(Request* request) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK_EQ(state_, V8CodecState::Enum::kConfigured);
+  DCHECK(media_encoder_);
   DCHECK_EQ(request->type, Request::Type::kEncode);
   DCHECK_GT(requested_encodes_, 0);
 
@@ -406,11 +432,6 @@
     self->ProcessRequests();
   };
 
-  if (!media_encoder_) {
-    HandleError(DOMExceptionCode::kOperationError, "Encoder is not configured");
-    return;
-  }
-
   scoped_refptr<media::VideoFrame> frame = request->frame->frame();
   if (frame->storage_type() == media::VideoFrame::STORAGE_GPU_MEMORY_BUFFER) {
     frame = ConvertToI420Frame(frame);
@@ -432,6 +453,7 @@
 }
 
 void VideoEncoder::ProcessConfigure(Request* request) {
+  DCHECK_NE(state_.AsEnum(), V8CodecState::Enum::kClosed);
   DCHECK(request->config);
   DCHECK_EQ(request->type, Request::Type::kConfigure);
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
@@ -481,6 +503,8 @@
 
 void VideoEncoder::ProcessFlush(Request* request) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK_EQ(state_, V8CodecState::Enum::kConfigured);
+  DCHECK(media_encoder_);
   DCHECK_EQ(request->type, Request::Type::kFlush);
 
   auto done_callback = [](VideoEncoder* self, Request* req,
@@ -502,14 +526,6 @@
     self->ProcessRequests();
   };
 
-  if (!media_encoder_) {
-    auto* ex = MakeGarbageCollected<DOMException>(
-        DOMExceptionCode::kOperationError, "Encoder is not configured");
-    HandleError(ex);
-    request->resolver.Release()->Reject(ex);
-    return;
-  }
-
   stall_request_processing_ = true;
   media_encoder_->Flush(WTF::Bind(done_callback, WrapWeakPersistent(this),
                                   WrapPersistentIfNeeded(request)));
diff --git a/third_party/blink/renderer/modules/webcodecs/video_encoder.h b/third_party/blink/renderer/modules/webcodecs/video_encoder.h
index e338e6f..5d5b863 100644
--- a/third_party/blink/renderer/modules/webcodecs/video_encoder.h
+++ b/third_party/blink/renderer/modules/webcodecs/video_encoder.h
@@ -13,6 +13,7 @@
 #include "media/base/video_color_space.h"
 #include "media/base/video_encoder.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
+#include "third_party/blink/renderer/bindings/modules/v8/v8_codec_state.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_video_encoder_output_callback.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_web_codecs_error_callback.h"
 #include "third_party/blink/renderer/modules/modules_export.h"
@@ -58,6 +59,8 @@
 
   void close(ExceptionState&);
 
+  String state() { return state_; }
+
   // GarbageCollected override.
   void Trace(Visitor*) const override;
 
@@ -103,6 +106,8 @@
   void ProcessConfigure(Request* request);
   void ProcessFlush(Request* request);
 
+  void ClearRequests();
+
   void MediaEncoderOutputCallback(media::VideoEncoderOutput output);
 
   std::unique_ptr<ParsedConfig> ParseConfig(const VideoEncoderConfig*,
@@ -112,6 +117,8 @@
   gfx::Size frame_size_;
   std::unique_ptr<media::VideoEncoder> media_encoder_;
 
+  V8CodecState state_;
+
   Member<ScriptState> script_state_;
   Member<V8VideoEncoderOutputCallback> output_callback_;
   Member<V8WebCodecsErrorCallback> error_callback_;
diff --git a/third_party/blink/renderer/modules/webcodecs/video_encoder.idl b/third_party/blink/renderer/modules/webcodecs/video_encoder.idl
index 3ce6a0c1..41860e1 100644
--- a/third_party/blink/renderer/modules/webcodecs/video_encoder.idl
+++ b/third_party/blink/renderer/modules/webcodecs/video_encoder.idl
@@ -54,4 +54,7 @@
     // rejected.
     [RaisesException]
     void close();
+
+    // Which state the decoder is in, indicating which methods can be called.
+    readonly attribute CodecState state;
 };
diff --git a/third_party/blink/renderer/platform/bindings/wrapper_type_info.h b/third_party/blink/renderer/platform/bindings/wrapper_type_info.h
index 96ee0295..3ad83fdb 100644
--- a/third_party/blink/renderer/platform/bindings/wrapper_type_info.h
+++ b/third_party/blink/renderer/platform/bindings/wrapper_type_info.h
@@ -187,6 +187,13 @@
       wrapper->GetAlignedPointerFromInternalField(offset));
 }
 
+template <typename T, int offset>
+inline T* GetInternalField(v8::Object* wrapper) {
+  DCHECK_LT(offset, wrapper->InternalFieldCount());
+  return reinterpret_cast<T*>(
+      wrapper->GetAlignedPointerFromInternalField(offset));
+}
+
 // The return value can be null if |wrapper| is a global proxy, which points to
 // nothing while a navigation.
 inline ScriptWrappable* ToScriptWrappable(
@@ -203,6 +210,10 @@
   return GetInternalField<ScriptWrappable, kV8DOMWrapperObjectIndex>(wrapper);
 }
 
+inline ScriptWrappable* ToScriptWrappable(v8::Object* wrapper) {
+  return GetInternalField<ScriptWrappable, kV8DOMWrapperObjectIndex>(wrapper);
+}
+
 inline CustomWrappable* ToCustomWrappable(
     const v8::PersistentBase<v8::Object>& wrapper) {
   return GetInternalField<CustomWrappable, kV8DOMWrapperObjectIndex>(wrapper);
diff --git a/third_party/blink/renderer/platform/exported/web_runtime_features.cc b/third_party/blink/renderer/platform/exported/web_runtime_features.cc
index ac930690..efdea67 100644
--- a/third_party/blink/renderer/platform/exported/web_runtime_features.cc
+++ b/third_party/blink/renderer/platform/exported/web_runtime_features.cc
@@ -717,6 +717,10 @@
   RuntimeEnabledFeatures::SetRestrictGamepadAccessEnabled(enable);
 }
 
+void WebRuntimeFeatures::EnableCompositeSVG(bool enable) {
+  RuntimeEnabledFeatures::SetCompositeSVGEnabled(enable);
+}
+
 void WebRuntimeFeatures::EnableCompositingOptimizations(bool enable) {
   RuntimeEnabledFeatures::SetCompositingOptimizationsEnabled(enable);
 }
diff --git a/third_party/blink/renderer/platform/text/character.cc b/third_party/blink/renderer/platform/text/character.cc
index 3832e4ca..20e18546 100644
--- a/third_party/blink/renderer/platform/text/character.cc
+++ b/third_party/blink/renderer/platform/text/character.cc
@@ -268,4 +268,28 @@
          hint_char_script != USCRIPT_COMMON;
 }
 
+// https://mathml-refresh.github.io/mathml-core/#stretchy-operator-axis
+static const UChar stretchy_operator_with_inline_axis[]{
+    0x003D, 0x005E, 0x005F, 0x007E, 0x00AF, 0x02C6, 0x02C7, 0x02C9, 0x02CD,
+    0x02DC, 0x02F7, 0x0302, 0x0332, 0x203E, 0x20D0, 0x20D1, 0x20D6, 0x20D7,
+    0x20E1, 0x2190, 0x2192, 0x2194, 0x2198, 0x2199, 0x219C, 0x219D, 0x219E,
+    0x21A0, 0x21A2, 0x21A3, 0x21A4, 0x21A6, 0x21A9, 0x21AA, 0x21AB, 0x21AC,
+    0x21AD, 0x21B4, 0x21B9, 0x21BC, 0x21BD, 0x21C0, 0x21C1, 0x21C4, 0x21C6,
+    0x21C7, 0x21C9, 0x21CB, 0x21CC, 0x21D0, 0x21D2, 0x21D4, 0x21DA, 0x21DB,
+    0x21DC, 0x21DD, 0x21E0, 0x21E2, 0x21E4, 0x21E5, 0x21E6, 0x21E8, 0x21F0,
+    0x21F6, 0x21FD, 0x21FE, 0x21FF, 0x23B4, 0x23B5, 0x23DC, 0x23DD, 0x23DE,
+    0x23DF, 0x23E0, 0x23E1, 0x2500, 0x27F5, 0x27F6, 0x27F7, 0x27F8, 0x27F9,
+    0x27FA, 0x27FB, 0x27FC, 0x27FD, 0x27FE, 0x27FF, 0x290C, 0x290D, 0x290E,
+    0x290F, 0x2910, 0x294E, 0x2950, 0x2952, 0x2953, 0x2956, 0x2957, 0x295A,
+    0x295B, 0x295E, 0x295F, 0x2B45, 0x2B46, 0xFE35, 0xFE36, 0xFE37, 0xFE38};
+
+bool Character::IsVerticalMathCharacter(UChar32 text_content) {
+  return text_content != kArabicMathematicalOperatorMeemWithHahWithTatweel &&
+         text_content != kArabicMathematicalOperatorHahWithDal &&
+         !std::binary_search(stretchy_operator_with_inline_axis,
+                             stretchy_operator_with_inline_axis +
+                                 base::size(stretchy_operator_with_inline_axis),
+                             text_content);
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/text/character.h b/third_party/blink/renderer/platform/text/character.h
index 9604a26..3a177dbc 100644
--- a/third_party/blink/renderer/platform/text/character.h
+++ b/third_party/blink/renderer/platform/text/character.h
@@ -202,6 +202,8 @@
     return (c - (0x1C90 - 0x10D0));
   }
 
+  static bool IsVerticalMathCharacter(UChar32);
+
  private:
   static bool IsCJKIdeographOrSymbolSlow(UChar32);
   static bool IsHangulSlow(UChar32);
diff --git a/third_party/blink/renderer/platform/text/character_test.cc b/third_party/blink/renderer/platform/text/character_test.cc
index 1870ae9..acf6e2fb 100644
--- a/third_party/blink/renderer/platform/text/character_test.cc
+++ b/third_party/blink/renderer/platform/text/character_test.cc
@@ -411,4 +411,36 @@
   }
 }
 
+TEST(CharacterTest, IsVerticalMathCharacter) {
+  // https://mathml-refresh.github.io/mathml-core/#stretchy-operator-axis
+  const UChar stretchy_operator_with_inline_axis[]{
+      0x003D, 0x005E, 0x005F, 0x007E, 0x00AF, 0x02C6, 0x02C7, 0x02C9, 0x02CD,
+      0x02DC, 0x02F7, 0x0302, 0x0332, 0x203E, 0x20D0, 0x20D1, 0x20D6, 0x20D7,
+      0x20E1, 0x2190, 0x2192, 0x2194, 0x2198, 0x2199, 0x219C, 0x219D, 0x219E,
+      0x21A0, 0x21A2, 0x21A3, 0x21A4, 0x21A6, 0x21A9, 0x21AA, 0x21AB, 0x21AC,
+      0x21AD, 0x21B4, 0x21B9, 0x21BC, 0x21BD, 0x21C0, 0x21C1, 0x21C4, 0x21C6,
+      0x21C7, 0x21C9, 0x21CB, 0x21CC, 0x21D0, 0x21D2, 0x21D4, 0x21DA, 0x21DB,
+      0x21DC, 0x21DD, 0x21E0, 0x21E2, 0x21E4, 0x21E5, 0x21E6, 0x21E8, 0x21F0,
+      0x21F6, 0x21FD, 0x21FE, 0x21FF, 0x23B4, 0x23B5, 0x23DC, 0x23DD, 0x23DE,
+      0x23DF, 0x23E0, 0x23E1, 0x2500, 0x27F5, 0x27F6, 0x27F7, 0x27F8, 0x27F9,
+      0x27FA, 0x27FB, 0x27FC, 0x27FD, 0x27FE, 0x27FF, 0x290C, 0x290D, 0x290E,
+      0x290F, 0x2910, 0x294E, 0x2950, 0x2952, 0x2953, 0x2956, 0x2957, 0x295A,
+      0x295B, 0x295E, 0x295F, 0x2B45, 0x2B46, 0xFE35, 0xFE36, 0xFE37, 0xFE38};
+
+  for (UChar32 test_char = 0; test_char < kMaxCodepoint; test_char++) {
+    if (test_char == kArabicMathematicalOperatorMeemWithHahWithTatweel) {
+      EXPECT_FALSE(Character::IsVerticalMathCharacter(test_char));
+    } else if (test_char == kArabicMathematicalOperatorHahWithDal) {
+      EXPECT_FALSE(Character::IsVerticalMathCharacter(test_char));
+    } else {
+      bool in_vertical = !std::binary_search(
+          stretchy_operator_with_inline_axis,
+          stretchy_operator_with_inline_axis +
+              base::size(stretchy_operator_with_inline_axis),
+          test_char);
+      EXPECT_TRUE(Character::IsVerticalMathCharacter(test_char) == in_vertical);
+    }
+  }
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/wtf/text/character_names.h b/third_party/blink/renderer/platform/wtf/text/character_names.h
index d1a7a2f7..19b0b44 100644
--- a/third_party/blink/renderer/platform/wtf/text/character_names.h
+++ b/third_party/blink/renderer/platform/wtf/text/character_names.h
@@ -41,6 +41,8 @@
 const UChar32 kAegeanWordSeparatorLineCharacter = 0x10100;
 const UChar32 kAegeanWordSeparatorDotCharacter = 0x10101;
 const UChar kArabicLetterMarkCharacter = 0x061C;
+const UChar32 kArabicMathematicalOperatorMeemWithHahWithTatweel = 0x1EEF0;
+const UChar32 kArabicMathematicalOperatorHahWithDal = 0x1EEF1;
 const UChar kBlackCircleCharacter = 0x25CF;
 const UChar kBlackSquareCharacter = 0x25A0;
 const UChar kBlackUpPointingTriangleCharacter = 0x25B2;
@@ -184,6 +186,8 @@
 using WTF::unicode::kAegeanWordSeparatorDotCharacter;
 using WTF::unicode::kAegeanWordSeparatorLineCharacter;
 using WTF::unicode::kArabicLetterMarkCharacter;
+using WTF::unicode::kArabicMathematicalOperatorHahWithDal;
+using WTF::unicode::kArabicMathematicalOperatorMeemWithHahWithTatweel;
 using WTF::unicode::kBlackCircleCharacter;
 using WTF::unicode::kBlackSquareCharacter;
 using WTF::unicode::kBlackUpPointingTriangleCharacter;
diff --git a/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py b/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py
index ce4de99..ae15d72 100755
--- a/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py
+++ b/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py
@@ -831,6 +831,20 @@
     },
     {
         'paths': [
+            'third_party/blink/renderer/modules/media/audio/',
+        ],
+        'allowed': [
+            # TODO(https://crbug.com/787252): Remove most of the entries below,
+            # once the directory is fully Onion soup'ed.
+            'base::flat_map',
+            'base::Bind.*',
+            'base::Unretained',
+            'mojo::WrapCallbackWithDefaultInvokeIfNotRun',
+            'base::ScopedPlatformFile',
+        ]
+    },
+    {
+        'paths': [
             'third_party/blink/renderer/modules/imagecapture/',
         ],
         'allowed': [
diff --git a/third_party/blink/web_tests/FlagExpectations/disable-layout-ng b/third_party/blink/web_tests/FlagExpectations/disable-layout-ng
index e9837e3..f6a1415b 100644
--- a/third_party/blink/web_tests/FlagExpectations/disable-layout-ng
+++ b/third_party/blink/web_tests/FlagExpectations/disable-layout-ng
@@ -122,10 +122,6 @@
 crbug.com/1045668 external/wpt/css/css-sizing/aspect-ratio/block-aspect-ratio-026.tentative.html [ Failure ]
 crbug.com/1045668 external/wpt/css/css-sizing/aspect-ratio/block-aspect-ratio-027.tentative.html [ Failure ]
 crbug.com/1045668 external/wpt/css/css-sizing/aspect-ratio/block-aspect-ratio-028.tentative.html [ Failure ]
-crbug.com/1045668 external/wpt/css/css-sizing/aspect-ratio/flex-aspect-ratio-001.tentative.html [ Failure ]
-crbug.com/1045668 external/wpt/css/css-sizing/aspect-ratio/flex-aspect-ratio-002.tentative.html [ Failure ]
-crbug.com/1045668 external/wpt/css/css-sizing/aspect-ratio/flex-aspect-ratio-004.tentative.html [ Failure ]
-crbug.com/1045668 external/wpt/css/css-sizing/aspect-ratio/flex-aspect-ratio-005.tentative.html [ Failure ]
 crbug.com/1045668 external/wpt/css/css-sizing/aspect-ratio/flex-aspect-ratio-008.tentative.html [ Failure ]
 crbug.com/1045668 external/wpt/css/css-sizing/aspect-ratio/flex-aspect-ratio-009.tentative.html [ Failure ]
 crbug.com/1045668 external/wpt/css/css-sizing/aspect-ratio/intrinsic-size-001.tentative.html [ Failure ]
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index fac130f8c..797bd3fa 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -915,8 +915,6 @@
 crbug.com/591099 fast/selectors/shadow-host-div-with-text.html [ Failure ]
 crbug.com/591099 virtual/text-antialias/selection/inline-block-in-selection-root.html [ Failure ]
 crbug.com/591099 paint/invalidation/media-audio-no-spurious-repaints.html [ Failure Timeout ]
-crbug.com/591099 virtual/layout_ng_block_frag/external/wpt/css/css-multicol/multicol-span-all-list-item-002.html [ Failure ]
-crbug.com/591099 virtual/layout_ng_block_frag/fast/multicol/border-radius-clipped-layer.html [ Failure ]
 crbug.com/591099 editing/selection/paint-hyphen.html [ Failure Pass ]
 crbug.com/591099 external/wpt/dom/ranges/Range-compareBoundaryPoints.html [ Timeout Failure Pass ]
 crbug.com/591099 external/wpt/dom/ranges/Range-set.html [ Failure Timeout ]
@@ -965,12 +963,6 @@
 # If they are not also fixed, reinstate them when removing these lines.
 crbug.com/982211 fast/events/before-unload-return-value-from-listener.html [ Pass Crash Timeout ]
 
-# Crashes/asserts due to inline item reuse.
-crbug.com/636993 virtual/layout_ng_block_frag/fast/multicol/dynamic/remove-inline-and-spanner-after-spanner-foreignObject.html [ Pass Crash Timeout ]
-
-crbug.com/591099 virtual/layout_ng_block_frag/fast/multicol/flowthread-with-floats-destroyed-crash.html [ Crash Pass ]
-
-
 ### With LayoutNGFragmentItem enabled
 crbug.com/982194 [ Win ] external/wpt/css/CSS2/linebox/empty-inline-002.xht [ Failure ]
 crbug.com/982194 [ Win ] external/wpt/css/css-text/white-space/line-edge-white-space-collapse-001.html [ Failure ]
@@ -992,36 +984,59 @@
 crbug.com/982194 [ Win10 ] paint/invalidation/outline/focus-ring-on-continuation-move.html [ Failure ]
 crbug.com/982194 [ Win10 ] paint/invalidation/outline/focus-ring-on-inline-continuation-move.html [ Failure ]
 
+### Tests passing with LayoutNGBlockFragmentation enabled:
+virtual/layout_ng_block_frag/external/wpt/css/css-break/avoid-border-break.html [ Pass ]
+virtual/layout_ng_block_frag/external/wpt/css/css-break/borders-000.html [ Pass ]
+virtual/layout_ng_block_frag/external/wpt/css/css-break/borders-001.html [ Pass ]
+virtual/layout_ng_block_frag/external/wpt/css/css-break/borders-002.html [ Pass ]
+virtual/layout_ng_block_frag/external/wpt/css/css-break/borders-003.html [ Pass ]
+virtual/layout_ng_block_frag/external/wpt/css/css-break/borders-004.html [ Pass ]
+virtual/layout_ng_block_frag/external/wpt/css/css-break/borders-005.html [ Pass ]
+virtual/layout_ng_block_frag/external/wpt/css/css-break/break-at-end-container-edge-000.html [ Pass ]
+virtual/layout_ng_block_frag/external/wpt/css/css-break/break-at-end-container-edge-001.html [ Pass ]
+virtual/layout_ng_block_frag/external/wpt/css/css-break/break-at-end-container-edge-002.html [ Pass ]
+virtual/layout_ng_block_frag/external/wpt/css/css-break/break-at-end-container-edge-004.html [ Pass ]
+virtual/layout_ng_block_frag/external/wpt/css/css-break/break-between-avoid-000.html [ Pass ]
+virtual/layout_ng_block_frag/external/wpt/css/css-break/break-between-avoid-001.html [ Pass ]
+virtual/layout_ng_block_frag/external/wpt/css/css-break/break-between-avoid-003.html [ Pass ]
+virtual/layout_ng_block_frag/external/wpt/css/css-break/break-between-avoid-004.html [ Pass ]
+virtual/layout_ng_block_frag/external/wpt/css/css-break/forced-break-at-fragmentainer-start-000.html [ Pass ]
+virtual/layout_ng_block_frag/external/wpt/css/css-break/overflowed-block-with-no-room-after-000.html [ Pass ]
+virtual/layout_ng_block_frag/external/wpt/css/css-break/overflowed-block-with-no-room-after-001.html [ Pass ]
+virtual/layout_ng_block_frag/external/wpt/css/css-break/tall-line-in-short-fragmentainer-000.html [ Pass ]
+virtual/layout_ng_block_frag/external/wpt/css/css-break/tall-line-in-short-fragmentainer-001.html [ Pass ]
+virtual/layout_ng_block_frag/external/wpt/css/css-break/tall-line-in-short-fragmentainer-002.html [ Pass ]
+virtual/layout_ng_block_frag/external/wpt/css/css-break/trailing-child-margin-000.html [ Pass ]
+virtual/layout_ng_block_frag/external/wpt/css/css-break/trailing-child-margin-002.html [ Pass ]
+virtual/layout_ng_block_frag/external/wpt/css/css-multicol/multicol-span-all-006.html [ Pass ]
+virtual/layout_ng_block_frag/external/wpt/css/css-multicol/multicol-span-all-007.html [ Pass ]
+virtual/layout_ng_block_frag/external/wpt/css/css-multicol/multicol-span-all-008.html [ Pass ]
+virtual/layout_ng_block_frag/external/wpt/css/css-multicol/multicol-span-all-fieldset-001.html [ Pass ]
+virtual/layout_ng_block_frag/external/wpt/css/css-multicol/multicol-span-all-margin-bottom-001.xht [ Pass ]
+virtual/layout_ng_block_frag/external/wpt/css/css-multicol/multicol-span-float-001.xht [ Pass ]
+virtual/layout_ng_block_frag/external/wpt/css/css-multicol/nested-with-too-tall-line.html [ Pass ]
+virtual/layout_ng_block_frag/external/wpt/css/css-multicol/non-adjacent-spanners-000.html [ Pass ]
+virtual/layout_ng_block_frag/external/wpt/css/css-multicol/spanner-fragmentation-001.html [ Pass ]
+virtual/layout_ng_block_frag/fast/multicol/newmulticol/hide-box-vertical-lr.html [ Pass ]
+virtual/layout_ng_block_frag/fast/multicol/span/vertical-lr.html [ Pass ]
+virtual/layout_ng_block_frag/fast/multicol/vertical-lr/abspos-auto-position-on-line.html [ Pass ]
+virtual/layout_ng_block_frag/fast/multicol/vertical-lr/column-break-with-balancing.html [ Pass ]
+virtual/layout_ng_block_frag/fast/multicol/vertical-lr/column-count-with-rules.html [ Pass ]
+virtual/layout_ng_block_frag/fast/multicol/vertical-lr/column-rules.html [ Pass ]
+virtual/layout_ng_block_frag/fast/multicol/vertical-lr/float-big-line.html [ Pass ]
+virtual/layout_ng_block_frag/fast/multicol/vertical-lr/float-break.html [ Pass ]
+virtual/layout_ng_block_frag/fast/multicol/vertical-lr/float-content-break.html [ Pass ]
+virtual/layout_ng_block_frag/fast/multicol/vertical-lr/float-edge.html [ Pass ]
+virtual/layout_ng_block_frag/fast/multicol/vertical-lr/float-paginate.html [ Pass ]
+virtual/layout_ng_block_frag/fast/multicol/vertical-lr/unsplittable-inline-block.html [ Pass ]
 
-### With LayoutNGBlockFragmentation enabled:
-
+### Tests failing with LayoutNGBlockFragmentation enabled:
 crbug.com/1079031 virtual/layout_ng_block_frag/external/wpt/css/css-break/block-end-aligned-abspos-nested.html [ Failure ]
 crbug.com/829028 virtual/layout_ng_block_frag/external/wpt/css/css-break/block-end-aligned-abspos-with-overflow.html [ Failure ]
-crbug.com/829028 virtual/layout_ng_block_frag/external/wpt/css/css-break/borders-000.html [ Pass ]
-crbug.com/829028 virtual/layout_ng_block_frag/external/wpt/css/css-break/borders-001.html [ Pass ]
-crbug.com/829028 virtual/layout_ng_block_frag/external/wpt/css/css-break/borders-002.html [ Pass ]
-crbug.com/829028 virtual/layout_ng_block_frag/external/wpt/css/css-break/borders-003.html [ Pass ]
-crbug.com/829028 virtual/layout_ng_block_frag/external/wpt/css/css-break/borders-004.html [ Pass ]
-crbug.com/829028 virtual/layout_ng_block_frag/external/wpt/css/css-break/borders-005.html [ Pass ]
-crbug.com/829028 virtual/layout_ng_block_frag/external/wpt/css/css-break/break-at-end-container-edge-000.html [ Pass ]
-crbug.com/829028 virtual/layout_ng_block_frag/external/wpt/css/css-break/break-at-end-container-edge-001.html [ Pass ]
-crbug.com/829028 virtual/layout_ng_block_frag/external/wpt/css/css-break/break-at-end-container-edge-002.html [ Pass ]
-crbug.com/829028 virtual/layout_ng_block_frag/external/wpt/css/css-break/break-at-end-container-edge-004.html [ Pass ]
-crbug.com/829028 virtual/layout_ng_block_frag/external/wpt/css/css-break/break-between-avoid-000.html [ Pass ]
-crbug.com/829028 virtual/layout_ng_block_frag/external/wpt/css/css-break/break-between-avoid-001.html [ Pass ]
-crbug.com/829028 virtual/layout_ng_block_frag/external/wpt/css/css-break/break-between-avoid-003.html [ Pass ]
-crbug.com/829028 virtual/layout_ng_block_frag/external/wpt/css/css-break/break-between-avoid-004.html [ Pass ]
 crbug.com/1028595 virtual/layout_ng_block_frag/external/wpt/css/css-break/fieldset-001.html [ Failure ]
-crbug.com/829028 virtual/layout_ng_block_frag/external/wpt/css/css-break/forced-break-at-fragmentainer-start-000.html [ Pass ]
-crbug.com/829028 virtual/layout_ng_block_frag/external/wpt/css/css-break/overflowed-block-with-no-room-after-001.html [ Pass ]
-crbug.com/829028 virtual/layout_ng_block_frag/external/wpt/css/css-break/overflowed-block-with-no-room-after-000.html [ Pass ]
-crbug.com/829028 virtual/layout_ng_block_frag/external/wpt/css/css-break/tall-line-in-short-fragmentainer-000.html [ Pass ]
-crbug.com/829028 virtual/layout_ng_block_frag/external/wpt/css/css-break/tall-line-in-short-fragmentainer-001.html [ Pass ]
-crbug.com/829028 virtual/layout_ng_block_frag/external/wpt/css/css-break/tall-line-in-short-fragmentainer-002.html [ Pass ]
-crbug.com/829028 virtual/layout_ng_block_frag/external/wpt/css/css-break/trailing-child-margin-000.html [ Pass ]
-crbug.com/829028 virtual/layout_ng_block_frag/external/wpt/css/css-break/trailing-child-margin-002.html [ Pass ]
 crbug.com/1066380 virtual/layout_ng_block_frag/external/wpt/css/css-break/widows-orphans-002.html [ Failure ]
 crbug.com/1066380 virtual/layout_ng_block_frag/external/wpt/css/css-break/widows-orphans-004.html [ Failure ]
+crbug.com/829028 virtual/layout_ng_block_frag/external/wpt/css/css-break/widows-orphans-005.html [ Failure ]
 crbug.com/875235 virtual/layout_ng_block_frag/external/wpt/css/css-contain/contain-size-breaks-001.html [ Crash Failure ]
 crbug.com/875235 virtual/layout_ng_block_frag/external/wpt/css/css-contain/contain-size-monolithic-001.html [ Crash Failure ]
 crbug.com/875235 virtual/layout_ng_block_frag/external/wpt/css/css-contain/contain-size-multicol-001.html [ Failure ]
@@ -1070,14 +1085,13 @@
 crbug.com/874051 virtual/layout_ng_block_frag/external/wpt/css/css-multicol/multicol-span-all-fieldset-002.html [ Failure Crash ]
 crbug.com/829028 virtual/layout_ng_block_frag/external/wpt/css/css-multicol/multicol-span-all-fieldset-003.html [ Crash Failure ]
 crbug.com/829028 virtual/layout_ng_block_frag/external/wpt/css/css-multicol/multicol-span-all-list-item-001.html [ Failure ]
+crbug.com/591099 virtual/layout_ng_block_frag/external/wpt/css/css-multicol/multicol-span-all-list-item-002.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_block_frag/external/wpt/css/css-multicol/multicol-width-001.xht [ Failure ]
 crbug.com/591099 virtual/layout_ng_block_frag/external/wpt/css/css-multicol/multicol-width-002.xht [ Failure ]
 crbug.com/591099 virtual/layout_ng_block_frag/external/wpt/css/css-multicol/multicol-width-003.xht [ Crash Failure Timeout ]
 crbug.com/829028 virtual/layout_ng_block_frag/external/wpt/css/css-multicol/multicol-width-004.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_block_frag/external/wpt/css/css-multicol/multicol-width-ch-001.xht [ Failure ]
 crbug.com/591099 virtual/layout_ng_block_frag/external/wpt/css/css-multicol/multicol-width-small-001.xht [ Failure ]
-crbug.com/829028 virtual/layout_ng_block_frag/external/wpt/css/css-multicol/non-adjacent-spanners-000.html [ Pass ]
-crbug.com/829028 virtual/layout_ng_block_frag/external/wpt/css/css-multicol/spanner-fragmentation-001.html [ Pass ]
 crbug.com/875235 virtual/layout_ng_block_frag/external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/contain/contain-size-multicol-002.html [ Failure ]
 crbug.com/875235 virtual/layout_ng_block_frag/external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/contain/contain-size-multicol-003.html [ Failure ]
 crbug.com/875235 virtual/layout_ng_block_frag/external/wpt/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-multicol.html [ Failure ]
@@ -1088,6 +1102,8 @@
 crbug.com/591099 virtual/layout_ng_block_frag/fast/multicol/balance-line-overflow.html [ Failure ]
 crbug.com/994172 virtual/layout_ng_block_frag/fast/multicol/balance-line-underflow-1.html [ Pass Crash ]
 crbug.com/994172 virtual/layout_ng_block_frag/fast/multicol/balance-line-underflow-2.html [ Pass Crash ]
+crbug.com/591099 virtual/layout_ng_block_frag/fast/multicol/border-radius-clipped-layer.html [ Failure ]
+crbug.com/829028 virtual/layout_ng_block_frag/fast/multicol/border-radius-clipped-layer-second-column.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_block_frag/fast/multicol/break-before-first-line-in-first-child.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_block_frag/fast/multicol/caret-range-anonymous-block.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_block_frag/fast/multicol/caret-range-anonymous-block-rtl.html [ Failure ]
@@ -1110,20 +1126,24 @@
 crbug.com/591099 virtual/layout_ng_block_frag/fast/multicol/dynamic/insert-spanner-into-stf-unconstrained-width.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_block_frag/fast/multicol/dynamic/relpos-becomes-static-has-abspos.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_block_frag/fast/multicol/dynamic/remove-column-content-next-to-abspos-between-spanners.html [ Failure ]
+crbug.com/636993 virtual/layout_ng_block_frag/fast/multicol/dynamic/remove-inline-and-spanner-after-spanner-foreignObject.html [ Pass Crash Timeout ]
 crbug.com/591099 virtual/layout_ng_block_frag/fast/multicol/dynamic/static-becomes-relpos-has-abspos.html [ Crash Failure ]
 crbug.com/591099 virtual/layout_ng_block_frag/fast/multicol/event-offset-in-nested.html [ Failure ]
 crbug.com/875235 virtual/layout_ng_block_frag/fast/multicol/fieldset-as-multicol.html [ Failure ]
 crbug.com/829028 virtual/layout_ng_block_frag/fast/multicol/file-upload-as-multicol.html [ Failure ]
+crbug.com/1061423 virtual/layout_ng_block_frag/fast/multicol/flipped-blocks-hit-test.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_block_frag/fast/multicol/float-after-break-after.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_block_frag/fast/multicol/float-avoidance.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_block_frag/fast/multicol/float-margin-at-row-boundary.html [ Failure ]
 crbug.com/829028 virtual/layout_ng_block_frag/fast/multicol/float-margin-at-row-boundary-fixed-multicol-height.html [ Failure ]
 crbug.com/829028 virtual/layout_ng_block_frag/fast/multicol/float-moved-by-child-line-and-unbreakable.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_block_frag/fast/multicol/float-paginate-empty-lines.html [ Failure ]
+crbug.com/1061423 virtual/layout_ng_block_frag/fast/multicol/float-truncation.html [ Failure ]
 crbug.com/829028 virtual/layout_ng_block_frag/fast/multicol/float-with-margin-moved-by-child-block-and-unbreakable.html [ Failure ]
 crbug.com/829028 virtual/layout_ng_block_frag/fast/multicol/float-with-margin-moved-by-child-block.html [ Failure Timeout ]
 crbug.com/829028 virtual/layout_ng_block_frag/fast/multicol/float-with-margin-moved-by-child-line-and-unbreakable.html [ Failure ]
 crbug.com/829028 virtual/layout_ng_block_frag/fast/multicol/float-with-margin-moved-by-child-line.html [ Failure ]
+crbug.com/591099 virtual/layout_ng_block_frag/fast/multicol/flowthread-with-floats-destroyed-crash.html [ Crash Pass ]
 crbug.com/591099 virtual/layout_ng_block_frag/fast/multicol/forced-break-in-nested-columns.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_block_frag/fast/multicol/forced-break-too-short-column.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_block_frag/fast/multicol/hit-test-above-or-below.html [ Failure ]
@@ -1199,10 +1219,8 @@
 crbug.com/591099 virtual/layout_ng_block_frag/fast/multicol/vertical-rl/client-rects-crossing-boundaries-nested.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_block_frag/fast/multicol/vertical-rl/composited-relpos-overlapping-will-change.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_block_frag/fast/multicol/vertical-rl/float-avoidance.html [ Crash Failure ]
-crbug.com/829028 virtual/layout_ng_block_frag/fast/multicol/vertical-lr/float-big-line.html [ Pass ]
-crbug.com/829028 virtual/layout_ng_block_frag/fast/multicol/vertical-lr/float-break.html [ Pass ]
-crbug.com/829028 virtual/layout_ng_block_frag/fast/multicol/vertical-lr/float-content-break.html [ Pass ]
-crbug.com/829028 virtual/layout_ng_block_frag/fast/multicol/vertical-lr/float-paginate.html [ Pass ]
+crbug.com/1061423 virtual/layout_ng_block_frag/fast/multicol/vertical-rl/float-truncation.html [ Failure ]
+crbug.com/1061423 virtual/layout_ng_block_frag/fast/multicol/vertical-lr/float-truncation.html [ Failure ]
 crbug.com/467477 virtual/layout_ng_block_frag/fast/multicol/vertical-rl/nested-columns.html [ Crash Failure ]
 crbug.com/591099 virtual/layout_ng_block_frag/fast/multicol/vertical-rl/offset-top-and-left-at-boundaries-nested.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_block_frag/fast/multicol/vertical-rl/offset-top-and-left-nested.html [ Failure ]
@@ -1224,42 +1242,8 @@
 crbug.com/829028 virtual/layout_ng_block_frag/printing/frameset.html [ Failure ]
 crbug.com/829804 virtual/layout_ng_block_frag/printing/webgl-oversized-printing.html [ Skip ]
 
-### With LayoutNGBlockFragmentation and LayoutNGFragmentItem enabled:
-crbug.com/829028 virtual/layout_ng_block_frag/external/wpt/css/css-break/widows-orphans-005.html [ Failure ]
-crbug.com/829028 virtual/layout_ng_block_frag/fast/multicol/border-radius-clipped-layer-second-column.html [ Failure ]
-crbug.com/1061423 virtual/layout_ng_block_frag/fast/multicol/flipped-blocks-hit-test.html [ Failure ]
-crbug.com/1061423 virtual/layout_ng_block_frag/fast/multicol/float-truncation.html [ Failure ]
-crbug.com/1061423 virtual/layout_ng_block_frag/fast/multicol/vertical-lr/float-truncation.html [ Failure ]
-crbug.com/1061423 virtual/layout_ng_block_frag/fast/multicol/vertical-rl/float-truncation.html [ Failure ]
-
-### Tests passing with LayoutNGBlockFragmentation
-virtual/layout_ng_block_frag/external/wpt/css/css-break/avoid-border-break.html [ Pass ]
-virtual/layout_ng_block_frag/external/wpt/css/css-multicol/multicol-span-all-006.html [ Pass ]
-virtual/layout_ng_block_frag/external/wpt/css/css-multicol/multicol-span-all-007.html [ Pass ]
-virtual/layout_ng_block_frag/external/wpt/css/css-multicol/multicol-span-all-008.html [ Pass ]
-virtual/layout_ng_block_frag/external/wpt/css/css-multicol/multicol-span-all-fieldset-001.html [ Pass ]
-virtual/layout_ng_block_frag/external/wpt/css/css-multicol/multicol-span-all-margin-bottom-001.xht [ Pass ]
-virtual/layout_ng_block_frag/external/wpt/css/css-multicol/multicol-span-float-001.xht [ Pass ]
-virtual/layout_ng_block_frag/external/wpt/css/css-multicol/nested-with-too-tall-line.html [ Pass ]
-virtual/layout_ng_block_frag/fast/multicol/newmulticol/hide-box-vertical-lr.html [ Pass ]
-virtual/layout_ng_block_frag/fast/multicol/span/vertical-lr.html [ Pass ]
-virtual/layout_ng_block_frag/fast/multicol/vertical-lr/abspos-auto-position-on-line.html [ Pass ]
-virtual/layout_ng_block_frag/fast/multicol/vertical-lr/column-break-with-balancing.html [ Pass ]
-virtual/layout_ng_block_frag/fast/multicol/vertical-lr/column-count-with-rules.html [ Pass ]
-virtual/layout_ng_block_frag/fast/multicol/vertical-lr/column-rules.html [ Pass ]
-virtual/layout_ng_block_frag/fast/multicol/vertical-lr/float-edge.html [ Pass ]
-virtual/layout_ng_block_frag/fast/multicol/vertical-lr/unsplittable-inline-block.html [ Pass ]
-
 ### With LayoutNGFragmentTraversal (and LayoutNGFragmentItem) enabled:
 
-virtual/layout_ng_fragment_traversal/external/wpt/css/CSS2/linebox/inline-formatting-context-023.xht [ Pass ]
-virtual/layout_ng_fragment_traversal/external/wpt/css/CSS2/linebox/line-height-126.xht [ Pass ]
-virtual/layout_ng_fragment_traversal/external/wpt/css/CSS2/normal-flow/max-height-applies-to-012.xht [ Pass ]
-virtual/layout_ng_fragment_traversal/external/wpt/css/CSS2/normal-flow/max-width-applies-to-012.xht [ Pass ]
-virtual/layout_ng_fragment_traversal/external/wpt/css/CSS2/normal-flow/min-height-applies-to-012.xht [ Pass ]
-virtual/layout_ng_fragment_traversal/external/wpt/css/CSS2/normal-flow/min-width-applies-to-012.xht [ Pass ]
-virtual/layout_ng_fragment_traversal/external/wpt/css/CSS2/normal-flow/width-applies-to-012.xht [ Pass ]
-
 crbug.com/982194 [ Mac ] virtual/layout_ng_fragment_traversal/fast/table/027-vertical.html [ Failure ]
 crbug.com/982194 [ Mac ] virtual/layout_ng_fragment_traversal/fast/table/border-collapsing/003-vertical.html [ Failure ]
 
diff --git a/third_party/blink/web_tests/WPTOverrideExpectations b/third_party/blink/web_tests/WPTOverrideExpectations
index 4d74e90..11f9a2c 100644
--- a/third_party/blink/web_tests/WPTOverrideExpectations
+++ b/third_party/blink/web_tests/WPTOverrideExpectations
@@ -946,11 +946,6 @@
 external/wpt/orientation-sensor/RelativeOrientationSensor-iframe-access.https.html [ Pass ] # wpt_subtest_failure
 external/wpt/orientation-sensor/RelativeOrientationSensor.https.html [ Pass ] # wpt_subtest_failure
 external/wpt/paint-timing/sibling-painting-first-image.html [ Failure Pass ] # wpt_subtest_failure
-external/wpt/payment-request/payment-is-showing.https.html [ Failure ] # wpt_subtest_failure
-external/wpt/payment-request/payment-request-canmakepayment-method-protection.https.html [ Pass ] # wpt_subtest_failure
-external/wpt/payment-request/payment-request-canmakepayment-method.https.html [ Failure ]
-external/wpt/payment-request/payment-request-hasenrolledinstrument-method.tentative.https.html [ Failure ] # wpt_subtest_failure
-external/wpt/payment-request/payment-request-show-method.https.html [ Failure ]
 external/wpt/picture-in-picture/css-selector.html [ Pass ] # wpt_subtest_failure
 external/wpt/picture-in-picture/disable-picture-in-picture.html [ Pass ] # wpt_subtest_failure
 external/wpt/picture-in-picture/enter-picture-in-picture.html [ Pass ] # wpt_subtest_failure
diff --git a/third_party/blink/web_tests/android/ChromiumWPTExpectations b/third_party/blink/web_tests/android/ChromiumWPTExpectations
index efed7a7..d1556508 100644
--- a/third_party/blink/web_tests/android/ChromiumWPTExpectations
+++ b/third_party/blink/web_tests/android/ChromiumWPTExpectations
@@ -1877,7 +1877,7 @@
 crbug.com/1050754 external/wpt/eventsource/shared-worker/eventsource-onopen.htm [ Failure ]
 crbug.com/1050754 external/wpt/eventsource/shared-worker/eventsource-prototype.htm [ Failure ]
 crbug.com/1050754 external/wpt/eventsource/shared-worker/eventsource-url.htm [ Failure ]
-crbug.com/1050754 external/wpt/feature-policy/experimental-features/document-write.tentative.html [ Failure ]
+crbug.com/1050754 external/wpt/document-policy/experimental-features/document-write.tentative.html [ Failure ]
 crbug.com/1050754 external/wpt/feature-policy/experimental-features/focus-without-user-activation-disabled-tentative.html [ Failure ]
 crbug.com/1050754 external/wpt/feature-policy/experimental-features/focus-without-user-activation-enabled-tentative.sub.html [ Timeout ]
 crbug.com/1050754 external/wpt/feature-policy/experimental-features/sync-script.tentative.https.sub.html [ Failure ]
@@ -1902,8 +1902,8 @@
 crbug.com/1050754 external/wpt/feature-policy/picture-in-picture-supported-by-feature-policy.html [ Failure ]
 crbug.com/1050754 external/wpt/feature-policy/reporting/camera-report-only.https.html [ Timeout ]
 crbug.com/1050754 external/wpt/feature-policy/reporting/camera-reporting.https.html [ Timeout ]
-crbug.com/1050754 external/wpt/feature-policy/reporting/document-write-report-only.html [ Timeout ]
-crbug.com/1050754 external/wpt/feature-policy/reporting/document-write-reporting.html [ Failure ]
+crbug.com/1050754 external/wpt/document-policy/reporting/document-write-report-only-tentative.html [ Timeout ]
+crbug.com/1050754 external/wpt/document-policy/reporting/document-write-reporting-tentative.html [ Failure ]
 crbug.com/1050754 external/wpt/feature-policy/reporting/encrypted-media-report-only.https.html [ Timeout ]
 crbug.com/1050754 external/wpt/feature-policy/reporting/encrypted-media-reporting.https.html [ Timeout ]
 crbug.com/1050754 external/wpt/feature-policy/reporting/fullscreen-report-only.html [ Timeout ]
diff --git a/third_party/blink/web_tests/android/WeblayerWPTExpectations b/third_party/blink/web_tests/android/WeblayerWPTExpectations
index 3ac1604..88f2020 100644
--- a/third_party/blink/web_tests/android/WeblayerWPTExpectations
+++ b/third_party/blink/web_tests/android/WeblayerWPTExpectations
@@ -1777,7 +1777,7 @@
 crbug.com/1050754 external/wpt/eventsource/shared-worker/eventsource-onopen.htm [ Failure ]
 crbug.com/1050754 external/wpt/eventsource/shared-worker/eventsource-prototype.htm [ Failure ]
 crbug.com/1050754 external/wpt/eventsource/shared-worker/eventsource-url.htm [ Failure ]
-crbug.com/1050754 external/wpt/feature-policy/experimental-features/document-write.tentative.html [ Failure ]
+crbug.com/1050754 external/wpt/document-policy/experimental-features/document-write.tentative.html [ Failure ]
 crbug.com/1050754 external/wpt/feature-policy/experimental-features/focus-without-user-activation-disabled-tentative.html [ Failure ]
 crbug.com/1050754 external/wpt/feature-policy/experimental-features/focus-without-user-activation-enabled-tentative.sub.html [ Timeout ]
 crbug.com/1050754 external/wpt/feature-policy/experimental-features/sync-script.tentative.https.sub.html [ Failure ]
@@ -1803,8 +1803,8 @@
 crbug.com/1050754 external/wpt/feature-policy/picture-in-picture-supported-by-feature-policy.html [ Failure ]
 crbug.com/1050754 external/wpt/feature-policy/reporting/camera-report-only.https.html [ Timeout ]
 crbug.com/1050754 external/wpt/feature-policy/reporting/camera-reporting.https.html [ Timeout ]
-crbug.com/1050754 external/wpt/feature-policy/reporting/document-write-report-only.html [ Timeout ]
-crbug.com/1050754 external/wpt/feature-policy/reporting/document-write-reporting.html [ Failure ]
+crbug.com/1050754 external/wpt/document-policy/reporting/document-write-report-only-tentative.html [ Timeout ]
+crbug.com/1050754 external/wpt/document-policy/reporting/document-write-reporting-tentative.html [ Failure ]
 crbug.com/1050754 external/wpt/feature-policy/reporting/encrypted-media-report-only.https.html [ Timeout ]
 crbug.com/1050754 external/wpt/feature-policy/reporting/encrypted-media-reporting.https.html [ Timeout ]
 crbug.com/1050754 external/wpt/feature-policy/reporting/fullscreen-report-only.html [ Failure Timeout ]
diff --git a/third_party/blink/web_tests/android/WebviewWPTExpectations b/third_party/blink/web_tests/android/WebviewWPTExpectations
index a990eb5b..f70fea0 100644
--- a/third_party/blink/web_tests/android/WebviewWPTExpectations
+++ b/third_party/blink/web_tests/android/WebviewWPTExpectations
@@ -1924,7 +1924,7 @@
 crbug.com/1050754 external/wpt/eventsource/shared-worker/eventsource-onopen.htm [ Failure ]
 crbug.com/1050754 external/wpt/eventsource/shared-worker/eventsource-prototype.htm [ Failure ]
 crbug.com/1050754 external/wpt/eventsource/shared-worker/eventsource-url.htm [ Failure ]
-crbug.com/1050754 external/wpt/feature-policy/experimental-features/document-write.tentative.html [ Failure ]
+crbug.com/1050754 external/wpt/document-policy/experimental-features/document-write.tentative.html [ Failure ]
 crbug.com/1050754 external/wpt/feature-policy/experimental-features/focus-without-user-activation-disabled-tentative.html [ Failure ]
 crbug.com/1050754 external/wpt/feature-policy/experimental-features/focus-without-user-activation-enabled-tentative.sub.html [ Timeout ]
 crbug.com/1050754 external/wpt/feature-policy/experimental-features/sync-script.tentative.https.sub.html [ Failure ]
@@ -1951,8 +1951,8 @@
 crbug.com/1050754 external/wpt/feature-policy/picture-in-picture-supported-by-feature-policy.html [ Failure ]
 crbug.com/1050754 external/wpt/feature-policy/reporting/camera-report-only.https.html [ Timeout ]
 crbug.com/1050754 external/wpt/feature-policy/reporting/camera-reporting.https.html [ Timeout ]
-crbug.com/1050754 external/wpt/feature-policy/reporting/document-write-report-only.html [ Timeout ]
-crbug.com/1050754 external/wpt/feature-policy/reporting/document-write-reporting.html [ Failure ]
+crbug.com/1050754 external/wpt/document-policy/reporting/document-write-report-only-tentative.html [ Timeout ]
+crbug.com/1050754 external/wpt/document-policy/reporting/document-write-reporting-tentative.html [ Failure ]
 crbug.com/1050754 external/wpt/feature-policy/reporting/encrypted-media-report-only.https.html [ Timeout ]
 crbug.com/1050754 external/wpt/feature-policy/reporting/encrypted-media-reporting.https.html [ Timeout ]
 crbug.com/1050754 external/wpt/feature-policy/reporting/fullscreen-report-only.html [ Failure ]
diff --git a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
index 0bd04ae3..93813a0d 100644
--- a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
+++ b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
@@ -207272,6 +207272,10 @@
         ]
        },
        "the-offscreen-canvas": {
+        "offscreencanvas.commit-expected.txt": [
+         "15d774a7fea1b183bf8e87740b8ba8cee46071c0",
+         []
+        ],
         "offscreencanvas.transferrable-expected.txt": [
          "c03435e3c6ab6f3c0fa274b6d7063a1848a2a302",
          []
@@ -208143,11 +208147,15 @@
        []
       ],
       "common.js": [
-       "8a3cd133734dc38d18c190c77c8ebc947c260030",
+       "5a1744cd9ceaa438bdc2e50dea3f66acb8657eb3",
        []
       ],
       "coop-coep.py": [
-       "dd232e601bf7182bd2ac07e15791973d60f587e8",
+       "285b961e9969175161ffe51791c20afec5c4fdbb",
+       []
+      ],
+      "coop-same-origin-repeated.asis": [
+       "082478e1599eb8974319a31334e41f46ab0fb91d",
        []
       ],
       "iframe-popup.sub.html": [
@@ -218869,7 +218877,7 @@
      []
     ],
     "console.idl": [
-     "3cd3c06b54b022c61857a6a29e8e97bf4fa9b47b",
+     "01e155e443d8d5a313e6e5048a4a4a510d6dbbb2",
      []
     ],
     "construct-stylesheets.idl": [
@@ -219429,7 +219437,7 @@
      []
     ],
     "webauthn.idl": [
-     "607c4765485041e5a8e5fe65571469bedf1d6fd0",
+     "ed6437751e17814dc0cec756ea136d9ec7eee857",
      []
     ],
     "webdriver.idl": [
@@ -225828,7 +225836,11 @@
       []
      ],
      "animation-timeline-in-keyframe-expected.txt": [
-      "0af056bbffc521c1ad6615ff3beed9d0a9a3bc0f",
+      "e714ac78cda2f6e8d04efcb295911dd97ebc7a36",
+      []
+     ],
+     "animation-timeline-none-expected.txt": [
+      "3b56d986e1b02045d758b25bb301a129702fafcf",
       []
      ],
      "animation-timeline-parsing-expected.txt": [
@@ -235721,7 +235733,7 @@
        []
       ],
       "README.md": [
-       "277cbe550c065782b0e315de04651c9e068cb3ad",
+       "8684f2cc7ee36960ef3ba9a7dafeef8db591a6f7",
        []
       ],
       "example": {
@@ -235766,7 +235778,7 @@
         []
        ],
        "echo_client.py": [
-        "e4459a19bc68c7879a174f572122382914e6f7dd",
+        "dafda059b1139935a571b811daa5a10d4158c61b",
         []
        ],
        "echo_noext_wsh.py": [
@@ -235836,7 +235848,7 @@
         []
        ],
        "extensions.py": [
-        "344539f6fce14bee65d7fa7a583acbb65f023aff",
+        "314a949d4550ab6ff5500f5254387d2b35cae535",
         []
        ],
        "fast_masking.i": [
@@ -235858,7 +235870,7 @@
         ]
        },
        "http_header_util.py": [
-        "ded90b247b8ded9f254daba70909cbc8ff3c82d6",
+        "21fde59af10c3b6e373fc2d64f19401c6ed7dc38",
         []
        ],
        "memorizingfile.py": [
@@ -235870,7 +235882,7 @@
         []
        ],
        "request_handler.py": [
-        "e038d9f43acd37bf5a1b10a0ce5585bd51ed602d",
+        "5e9c875dc7daff9929a47d448afcb5aa5bad1b2c",
         []
        ],
        "server_util.py": [
@@ -235878,7 +235890,7 @@
         []
        ],
        "standalone.py": [
-        "1042c496c58a23b6ce4c23eee0ba462095452671",
+        "b075d989f052bb08d15a5a83b6222457da94821c",
         []
        ],
        "stream.py": [
@@ -235922,7 +235934,7 @@
         ]
        },
        "client_for_testing.py": [
-        "450a6acd5376223c603abe264b967796832fb573",
+        "f9aa8119e342a25ccf39a37db64921c67e8e0545",
         []
        ],
        "mock.py": [
@@ -235930,19 +235942,19 @@
         []
        ],
        "run_all.py": [
-        "f622015539e47fdf518875158f6bd6f36994faba",
+        "ea52223cea6d1458adc39dcd2f8b956c885fb419",
         []
        ],
        "set_sys_path.py": [
-        "9fce7b34b6751d635dcda6e0d4a5711951a0a33b",
+        "48d0e116a5843c1adfedb8cc4c5918c5c2003277",
         []
        ],
        "test_dispatch.py": [
-        "ae63980f2581f9f6136e85dc16bb64ccbe373dc9",
+        "132dd92d76cef648e2516702e3530e918fb6f4b9",
         []
        ],
        "test_endtoend.py": [
-        "445d29c67ddf07b3f123245977f95cfe0148c83a",
+        "8d457ff68595feb59a70d0c3e0145d567202444d",
         []
        ],
        "test_extensions.py": [
@@ -235970,7 +235982,7 @@
         []
        ],
        "test_msgutil.py": [
-        "943f21b8d297668770cb91c501c2b2fc8258b9eb",
+        "1122c281b7874bfb3dc1a93f55c4ed3062107825",
         []
        ],
        "test_stream.py": [
@@ -235978,7 +235990,7 @@
         []
        ],
        "test_util.py": [
-        "20347941051455ebee58823b4b0be93762912b0a",
+        "bf4bd32bbac30c52d99997fecc7bbb83cf7516ad",
         []
        ],
        "testdata": {
@@ -238912,6 +238924,10 @@
        "cb762eff806849df46dc758ef7b98b63f27f54c9",
        []
       ],
+      "classic_script.js": [
+       "db69b96188d7ca20b8bb12b429aead8fcfdc7b64",
+       []
+      ],
       "resource1.js": [
        "7bd437364a24b6e94493c2acca939991b15cd41c",
        []
@@ -238930,6 +238946,10 @@
       ]
      },
      "dynamic1": {
+      "classic_script.js": [
+       "5fcf045906c1ab151512a3e425b7c5c6f79cee59",
+       []
+      ],
       "resource1.js": [
        "6fd1de600e5599bfe4740b06da6d089e65838f55",
        []
@@ -238948,6 +238968,10 @@
       ]
      },
      "dynamic2": {
+      "classic_script.js": [
+       "546d8a503d90f4de7ca4f45098c8753160c0b169",
+       []
+      ],
       "resource1.js": [
        "c6f751cec49ebe67e3a9a827ef3764ab730a6f17",
        []
@@ -238995,15 +239019,15 @@
        []
       ],
       "dynamic1-crossorigin.wbn": [
-       "a35e12aa0a2cb5b6757a9efabb1f3a657a456835",
+       "5ebb86178e9bf5cccfa09878210c7866f9d80478",
        []
       ],
       "dynamic1.wbn": [
-       "22db19e7ce14d40cc4ba87d120486c24da1a4070",
+       "9e69e1fd89834bd2d238859413fc8e09d8b6b181",
        []
       ],
       "dynamic2.wbn": [
-       "44c4b1ee86662af2b474447335267cf7b274e62b",
+       "e4d04d60c2aa9050c6454ad9f88af407a844669d",
        []
       ],
       "location.wbn": [
@@ -240241,7 +240265,7 @@
      []
     ],
     "RTCRtpTransceiver.https-expected.txt": [
-     "b0e1b21f7e884b1be93b6dd0d5b40290d5e30861",
+     "71f96185ebfbc893db80491752604d518432372d",
      []
     ],
     "RTCStats-helper.js": [
@@ -315384,7 +315408,7 @@
        ]
       ],
       "request-structure.html": [
-       "806606cf627c115aad4c3fd89d4cc2377a3c8a61",
+       "e137a7ea5120ff24de310020fe330a6725cdb9d7",
        [
         null,
         {}
@@ -331943,7 +331967,7 @@
        },
        "the-offscreen-canvas": {
         "offscreencanvas.commit.html": [
-         "35cba368116db58cbcb3cdcb9e244a18dbda8650",
+         "fa0e2ac5fc8b73d316599d8147c039751fd1da3d",
          [
           null,
           {}
@@ -337652,6 +337676,13 @@
        {}
       ]
      ],
+     "header-parsing.https.html": [
+      "2839c381b807efa2ab934b2ff66c932d5f179e9f",
+      [
+       null,
+       {}
+      ]
+     ],
      "historical": {
       "coep-navigate-popup-unsafe-inherit.https.html": [
        "21feef73ddf2a9cdfffc58ef2d17d45773fb2b7d",
@@ -337981,6 +338012,15 @@
      ],
      "reporting": {
       "access-reporting": {
+       "access-to-coop-page-from-other_coop-ro.https.html": [
+        "c349b9a708e7cb579db921cc1d574869dc0b081c",
+        [
+         null,
+         {
+          "timeout": "long"
+         }
+        ]
+       ],
        "openee-accessed_openee-coop-ro.https.html": [
         "ef969738c33f8cacc8a269f5159e60900f98a7c7",
         [
@@ -380826,7 +380866,14 @@
       ]
      ],
      "animation-timeline-in-keyframe.html": [
-      "bdf40e0747ff96d58cabdfc820373434cd5e2fd8",
+      "75483331390d9fb6571307c3158cc37adcd893ee",
+      [
+       null,
+       {}
+      ]
+     ],
+     "animation-timeline-none.html": [
+      "d5d585366c9e3c451f9d19f0c573798d1413644e",
       [
        null,
        {}
@@ -382897,7 +382944,7 @@
       ]
      ],
      "fetch-request-resources.https.html": [
-      "fe227d9ce2f4943ec8a2aa936882f7b46daca28f",
+      "50864ca78955ea2d52b8e6f71abe2dd6dfef431a",
       [
        null,
        {}
@@ -400775,7 +400822,7 @@
       ]
      ],
      "subresource-loading-from-web-bundle.tentative.html": [
-      "3ab6d7a23e57201eb67833c87283819103193665",
+      "1e0e78a320403e67d447a6df99ef819bd85c0106",
       [
        null,
        {}
@@ -405627,7 +405674,7 @@
      ]
     ],
     "RTCRtpTransceiver.https.html": [
-     "487774982298376b7ce27a2cbfaea7ad3c716e28",
+     "45c8664429fc44cb0caf67145641c4cd61052bb7",
      [
       null,
       {
@@ -410297,7 +410344,7 @@
      ]
     ],
     "set.window.js": [
-     "228ce60296697a21520b8c635bd352ed1fbefb2c",
+     "1c20907939ad0b7b42987f9b2c573e4a17c10707",
      [
       "webstorage/set.window.html",
       {}
diff --git a/third_party/blink/web_tests/external/wpt/feature-policy/experimental-features/document-write.tentative.html b/third_party/blink/web_tests/external/wpt/document-policy/experimental-features/document-write.tentative.html
similarity index 88%
rename from third_party/blink/web_tests/external/wpt/feature-policy/experimental-features/document-write.tentative.html
rename to third_party/blink/web_tests/external/wpt/document-policy/experimental-features/document-write.tentative.html
index 7e09ef7e7..551703c6 100644
--- a/third_party/blink/web_tests/external/wpt/feature-policy/experimental-features/document-write.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/document-policy/experimental-features/document-write.tentative.html
@@ -2,7 +2,7 @@
 <title>'document-write' tests</title>
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
-<script src="/feature-policy/experimental-features/resources/common.js"></script>
+<script src="/document-policy/experimental-features/resources/common.js"></script>
 <style>
 html, body {
   height: 100%;
@@ -20,7 +20,8 @@
   }
 
   let iframeElement = document.querySelector("iframe");
-  let url = url_base + "document-write.html";
+  const allowed_url = url_base + "document-write-allowed.html";
+  const disallowed_url = url_base + "document-write-disallowed.html";
 
   let text_to_write = "<div>FOO<\/div>";
   let test_cases = [{
@@ -49,7 +50,7 @@
   test_cases.forEach((tc) => {
     promise_test(async() => {
       let iframeElement = newIframe();
-      await loadUrlInIframe(iframeElement, url);
+      await loadUrlInIframe(iframeElement, allowed_url);
       await sendMessageAndGetResponse(iframeElement.contentWindow, tc).then((response) => {
         assert_false(
           response.did_throw_exception,
@@ -70,8 +71,7 @@
   test_cases.forEach((tc) => {
     promise_test(async() => {
       let iframeElement = newIframe();
-      setFeatureState(iframeElement, "document-write", "'none'");
-      await loadUrlInIframe(iframeElement, url);
+      await loadUrlInIframe(iframeElement, disallowed_url);
       await sendMessageAndGetResponse(iframeElement.contentWindow, tc).then((response) => {
         assert_true(
           response.did_throw_exception,
diff --git a/third_party/blink/web_tests/external/wpt/feature-policy/experimental-features/resources/document-write.html b/third_party/blink/web_tests/external/wpt/document-policy/experimental-features/resources/document-write-allowed.html
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/feature-policy/experimental-features/resources/document-write.html
rename to third_party/blink/web_tests/external/wpt/document-policy/experimental-features/resources/document-write-allowed.html
diff --git a/third_party/blink/web_tests/external/wpt/feature-policy/experimental-features/resources/document-write.html b/third_party/blink/web_tests/external/wpt/document-policy/experimental-features/resources/document-write-disallowed.html
similarity index 100%
copy from third_party/blink/web_tests/external/wpt/feature-policy/experimental-features/resources/document-write.html
copy to third_party/blink/web_tests/external/wpt/document-policy/experimental-features/resources/document-write-disallowed.html
diff --git a/third_party/blink/web_tests/external/wpt/document-policy/experimental-features/resources/document-write-disallowed.html.headers b/third_party/blink/web_tests/external/wpt/document-policy/experimental-features/resources/document-write-disallowed.html.headers
new file mode 100644
index 0000000..32846f7
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/document-policy/experimental-features/resources/document-write-disallowed.html.headers
@@ -0,0 +1 @@
+Document-Policy: document-write=?0
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/feature-policy/reporting/document-write-report-only.html b/third_party/blink/web_tests/external/wpt/document-policy/reporting/document-write-report-only-tentative.html
similarity index 83%
rename from third_party/blink/web_tests/external/wpt/feature-policy/reporting/document-write-report-only.html
rename to third_party/blink/web_tests/external/wpt/document-policy/reporting/document-write-report-only-tentative.html
index 69ce9f8..bf17807 100644
--- a/third_party/blink/web_tests/external/wpt/feature-policy/reporting/document-write-report-only.html
+++ b/third_party/blink/web_tests/external/wpt/document-policy/reporting/document-write-report-only-tentative.html
@@ -8,7 +8,7 @@
     <script>
 var check_report_format = ([reports, observer]) => {
   let report = reports[0];
-  assert_equals(report.type, "feature-policy-violation");
+  assert_equals(report.type, "document-policy-violation");
   assert_equals(report.body.featureId, "document-write");
   assert_equals(report.body.disposition, "report");
 };
@@ -16,7 +16,7 @@
 promise_test(async t => {
   const report = new Promise(resolve => {
     new ReportingObserver((reports, observer) => resolve([reports, observer]),
-                          {types: ['feature-policy-violation']}).observe();
+                          {types: ['document-policy-violation']}).observe();
   });
   document.write("This should be written into the document");
   check_report_format(await report);
diff --git a/third_party/blink/web_tests/external/wpt/document-policy/reporting/document-write-report-only-tentative.html.headers b/third_party/blink/web_tests/external/wpt/document-policy/reporting/document-write-report-only-tentative.html.headers
new file mode 100644
index 0000000..924dac8
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/document-policy/reporting/document-write-report-only-tentative.html.headers
@@ -0,0 +1 @@
+Document-Policy-Report-Only: document-write=?0
diff --git a/third_party/blink/web_tests/external/wpt/feature-policy/reporting/document-write-reporting.html b/third_party/blink/web_tests/external/wpt/document-policy/reporting/document-write-reporting-tentative.html
similarity index 86%
rename from third_party/blink/web_tests/external/wpt/feature-policy/reporting/document-write-reporting.html
rename to third_party/blink/web_tests/external/wpt/document-policy/reporting/document-write-reporting-tentative.html
index 50af640..65a584c 100644
--- a/third_party/blink/web_tests/external/wpt/feature-policy/reporting/document-write-reporting.html
+++ b/third_party/blink/web_tests/external/wpt/document-policy/reporting/document-write-reporting-tentative.html
@@ -10,7 +10,7 @@
 
 var check_report_format = (reports, observer) => {
   let report = reports[0];
-  assert_equals(report.type, "feature-policy-violation");
+  assert_equals(report.type, "document-policy-violation");
   assert_equals(report.url, document.location.href);
   assert_equals(report.body.featureId, "document-write");
   assert_equals(report.body.sourceFile, document.location.href);
@@ -20,7 +20,7 @@
 };
 
 new ReportingObserver(t.step_func_done(check_report_format),
-                      {types: ['feature-policy-violation']}).observe();
+                      {types: ['document-policy-violation']}).observe();
 
 t.step_func(() => {
   assert_throws_dom('NotAllowedError',
diff --git a/third_party/blink/web_tests/external/wpt/document-policy/reporting/document-write-reporting-tentative.html.headers b/third_party/blink/web_tests/external/wpt/document-policy/reporting/document-write-reporting-tentative.html.headers
new file mode 100644
index 0000000..6d05e96
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/document-policy/reporting/document-write-reporting-tentative.html.headers
@@ -0,0 +1 @@
+Document-Policy: document-write=?0
diff --git a/third_party/blink/web_tests/external/wpt/feature-policy/reporting/document-write-report-only.html.headers b/third_party/blink/web_tests/external/wpt/feature-policy/reporting/document-write-report-only.html.headers
deleted file mode 100644
index bd14187..0000000
--- a/third_party/blink/web_tests/external/wpt/feature-policy/reporting/document-write-report-only.html.headers
+++ /dev/null
@@ -1 +0,0 @@
-Feature-Policy-Report-Only: document-write 'none'
diff --git a/third_party/blink/web_tests/external/wpt/feature-policy/reporting/document-write-reporting.html.headers b/third_party/blink/web_tests/external/wpt/feature-policy/reporting/document-write-reporting.html.headers
deleted file mode 100644
index 57102d5..0000000
--- a/third_party/blink/web_tests/external/wpt/feature-policy/reporting/document-write-reporting.html.headers
+++ /dev/null
@@ -1 +0,0 @@
-Feature-Policy: document-write 'none'
diff --git a/third_party/blink/web_tests/external/wpt/fetch/api/request/request-structure.html b/third_party/blink/web_tests/external/wpt/fetch/api/request/request-structure.html
index 806606c..e137a7e 100644
--- a/third_party/blink/web_tests/external/wpt/fetch/api/request/request-structure.html
+++ b/third_party/blink/web_tests/external/wpt/fetch/api/request/request-structure.html
@@ -58,7 +58,7 @@
             break;
 
           case "destination":
-            defaultValue = "empty";
+            defaultValue = "";
             newValue = "worker";
             break;
 
diff --git a/third_party/blink/web_tests/external/wpt/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.commit-expected.txt b/third_party/blink/web_tests/external/wpt/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.commit-expected.txt
new file mode 100644
index 0000000..15d774a
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.commit-expected.txt
@@ -0,0 +1,5 @@
+This is a testharness.js-based test.
+FAIL Test that calling OffscreenCanvas's commit pushes its contents to its placeholder. assert_equals: Green channel of the pixel at (5, 5) expected 255 but got 0
+PASS Test that calling commit on an OffscreenCanvas that is not transferred from a HTMLCanvasElement is a noop.
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.commit.html b/third_party/blink/web_tests/external/wpt/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.commit.html
index 35cba36..fa0e2ac 100644
--- a/third_party/blink/web_tests/external/wpt/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.commit.html
+++ b/third_party/blink/web_tests/external/wpt/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.commit.html
@@ -14,7 +14,7 @@
     _assertPixel(canvas, 5,5, expectedR, expectedG, expectedB, expectedA, "5,5", expectedClrStr);
 }
 
-test(function() {
+async_test(function(t) {
     var placeholder = document.createElement('canvas');
     placeholder.width = placeholder.height = 10;
     var offscreenCanvas = placeholder.transferControlToOffscreen();
@@ -25,8 +25,9 @@
     // place holder contents should still be transparent black at this moment.
     verifyPlaceholder(placeholder, 0,0,0,0, "0,0,0,0");
     // Set timeout acts as a sync barrier to allow commit to propagate
-    setTimeout(function() {
+    t.step_timeout(function() {
         verifyPlaceholder(placeholder, 0,255,0,255, "0,255,0,255");
+        t.done();
     }, 0);
 }, "Test that calling OffscreenCanvas's commit pushes its contents to its placeholder.");
 
diff --git a/third_party/blink/web_tests/external/wpt/html/cross-origin-opener-policy/header-parsing.https.html b/third_party/blink/web_tests/external/wpt/html/cross-origin-opener-policy/header-parsing.https.html
new file mode 100644
index 0000000..2839c381
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/cross-origin-opener-policy/header-parsing.https.html
@@ -0,0 +1,52 @@
+<!doctype html>
+<meta charset=utf-8>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="resources/common.js"></script>
+
+<div id=log></div>
+<script>
+
+let tests = [
+  // popup Origin, popup COOP, expect opener
+
+  // None of the following should be recognized as "same-origin" (hence the
+  // "expected opener" value of `true`).
+  [SAME_ORIGIN, { percentEncoded: "same%FForigin" }, true], // non-ASCII byte
+  [SAME_ORIGIN, "same-origin;", true],
+  [SAME_ORIGIN, "\u000bsame-origin\u000b", true], // vertical tab
+  [SAME_ORIGIN, "\u000csame-origin\u000c", true], // form feed
+  [SAME_ORIGIN, "\u000dsame-origin\u000d", true], // carriage return
+  [SAME_ORIGIN, "Same-origin", true],
+  [SAME_ORIGIN, "same-origin;\tfoo=bar", true],
+  [SAME_ORIGIN, "same-origin ;foo=bar", true],
+  [SAME_ORIGIN, "same-origin; foo=bar;", true],
+  [SAME_ORIGIN, "\"same-origin\"", true], // HTTP structured fields "string" item
+  [SAME_ORIGIN, ":c2FtZS1vcmlnaW4=:", true], // HTTP structured fields "byte sequence" item
+  [SAME_ORIGIN, "?1", true], // HTTP structured fields "boolean" item
+  [SAME_ORIGIN, "1", true], // HTTP structured fields "integer or decimal" item
+  [SAME_ORIGIN, "$same-origin", true], // the item type is unrecognized
+  [SAME_ORIGIN, "same-origin same-origin", true],
+  [SAME_ORIGIN, "same-origin,same-origin", true],
+  [SAME_ORIGIN, "*same-origin ", true],
+
+  // All of the following should be recognized as "same-origin" (hence the
+  // "expected opener" value of `false`).
+  [SAME_ORIGIN, " same-origin", false],
+  [SAME_ORIGIN, "same-origin ", false],
+  [SAME_ORIGIN, "\tsame-origin", false],
+  [SAME_ORIGIN, "same-origin\t", false],
+  [SAME_ORIGIN, "same-origin;same-origin", false],
+  [SAME_ORIGIN, "same-origin; foo=bar", false],
+];
+
+run_coop_tests("unspecified", tests);
+
+async_test((t) => {
+  const channelName = `none_to_${SAME_ORIGIN.name}_duplicated-header`;
+  const url = `${SAME_ORIGIN.origin}/html/cross-origin-opener-policy/resources/coop-same-origin-repeated.asis?channel=${channelName}`;
+
+  url_test(t, url, channelName, true);
+}, `unspecified document opening popup to ${SAME_ORIGIN.origin} with repeated COOP header`);
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/html/cross-origin-opener-policy/resources/common.js b/third_party/blink/web_tests/external/wpt/html/cross-origin-opener-policy/resources/common.js
index 8a3cd133..5a1744c 100644
--- a/third_party/blink/web_tests/external/wpt/html/cross-origin-opener-policy/resources/common.js
+++ b/third_party/blink/web_tests/external/wpt/html/cross-origin-opener-policy/resources/common.js
@@ -49,8 +49,16 @@
   });
 }
 
+function percent_encode(objectOrString) {
+  if (typeof objectOrString === "object") {
+    return objectOrString.percentEncoded;
+  }
+  return encodeURIComponent(objectOrString);
+}
+
 function coop_coep_test(t, host, coop, coep, channelName, hasOpener, openerDOMAccess, callback) {
-  url_test(t, `${host.origin}/html/cross-origin-opener-policy/resources/coop-coep.py?coop=${encodeURIComponent(coop)}&coep=${coep}&channel=${channelName}`, channelName, hasOpener, openerDOMAccess, callback);
+  const coopPercentEncoded = percent_encode(coop);
+  url_test(t, `${host.origin}/html/cross-origin-opener-policy/resources/coop-coep.py?coop=${coopPercentEncoded}&coep=${coep}&channel=${encodeURIComponent(channelName)}`, channelName, hasOpener, openerDOMAccess, callback);
 }
 
 function coop_test(t, host, coop, channelName, hasOpener, callback) {
@@ -59,11 +67,12 @@
 
 function run_coop_tests(documentCOOPValueTitle, testArray) {
   for (const test of testArray) {
+    let coopName = typeof test[1] === "object" ? test[1].percentEncoded : test[1];
     async_test(t => {
       coop_test(t, test[0], test[1],
-                `${documentCOOPValueTitle}_to_${test[0].name}_${test[1].replace(/ /g,"-")}`,
+                `${documentCOOPValueTitle}_to_${test[0].name}_${coopName.replace(/ /g,"-")}`,
                 test[2], () => { t.done(); });
-    }, `${documentCOOPValueTitle} document opening popup to ${test[0].origin} with COOP: "${test[1]}"`);
+    }, `${documentCOOPValueTitle} document opening popup to ${test[0].origin} with COOP: ${format_value(coopName)}`);
   }
 }
 
@@ -91,5 +100,5 @@
               assert_equals(payload.name, expects_name? name:"", 'name');
       });
       document.body.append(frame);
-  }, `${documentTitle} with ${iframe_origin.name} iframe opening popup a ${popup_origin.name} with COOP: ${popup_coop}`);
-}
\ No newline at end of file
+  }, `${documentTitle} with ${iframe_origin.name} iframe opening popup a ${popup_origin.name} with COOP: ${format_value(popup_coop)}`);
+}
diff --git a/third_party/blink/web_tests/external/wpt/html/cross-origin-opener-policy/resources/coop-coep.py b/third_party/blink/web_tests/external/wpt/html/cross-origin-opener-policy/resources/coop-coep.py
index dd232e6..285b961 100644
--- a/third_party/blink/web_tests/external/wpt/html/cross-origin-opener-policy/resources/coop-coep.py
+++ b/third_party/blink/web_tests/external/wpt/html/cross-origin-opener-policy/resources/coop-coep.py
@@ -70,7 +70,7 @@
       iframe.contentWindow.postMessage(payload, "*");
     };
     const channelName = new URL(location).searchParams.get("channel");
-    iframe.src = `${get_host_info().HTTPS_ORIGIN}/html/cross-origin-opener-policy/resources/postback.html?channel=${channelName}`;
+    iframe.src = `${get_host_info().HTTPS_ORIGIN}/html/cross-origin-opener-policy/resources/postback.html?channel=${encodeURIComponent(channelName)}`;
     document.body.appendChild(iframe);
   }
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/html/cross-origin-opener-policy/resources/coop-same-origin-repeated.asis b/third_party/blink/web_tests/external/wpt/html/cross-origin-opener-policy/resources/coop-same-origin-repeated.asis
new file mode 100644
index 0000000..082478e1
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/cross-origin-opener-policy/resources/coop-same-origin-repeated.asis
@@ -0,0 +1,24 @@
+HTTP/1.1 200 OK
+Cross-Origin-Opener-Policy: same-origin
+Cross-Origin-Opener-Policy: same-origin
+Server: BaseHTTP/0.3 Python/2.7.15+
+Date: Wed, 18 Dec 2019 00:47:08 GMT
+
+<!doctype html>
+<meta charset=utf-8>
+<script src="/common/get-host-info.sub.js"></script>
+<iframe></iframe>
+<script>
+  const navigate = new URL(location).searchParams.get("navigate");
+  if (navigate !== null) {
+    self.location = navigate;
+  } else {
+    const iframe = document.querySelector("iframe");
+    iframe.onload = () => {
+      const payload = { name: self.name, opener: !!self.opener };
+      iframe.contentWindow.postMessage(payload, "*");
+    };
+    const channelName = new URL(location).searchParams.get("channel");
+    iframe.src = `${get_host_info().HTTPS_ORIGIN}/html/cross-origin-opener-policy/resources/postback.html?channel=${channelName}`;
+  }
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/interfaces/console.idl b/third_party/blink/web_tests/external/wpt/interfaces/console.idl
index 3cd3c06..01e155e 100644
--- a/third_party/blink/web_tests/external/wpt/interfaces/console.idl
+++ b/third_party/blink/web_tests/external/wpt/interfaces/console.idl
@@ -6,29 +6,29 @@
 [Exposed=(Window,Worker,Worklet)]
 namespace console { // but see namespace object requirements below
   // Logging
-  void assert(optional boolean condition = false, any... data);
-  void clear();
-  void debug(any... data);
-  void error(any... data);
-  void info(any... data);
-  void log(any... data);
-  void table(optional any tabularData, optional sequence<DOMString> properties);
-  void trace(any... data);
-  void warn(any... data);
-  void dir(optional any item, optional object? options);
-  void dirxml(any... data);
+  undefined assert(optional boolean condition = false, any... data);
+  undefined clear();
+  undefined debug(any... data);
+  undefined error(any... data);
+  undefined info(any... data);
+  undefined log(any... data);
+  undefined table(optional any tabularData, optional sequence<DOMString> properties);
+  undefined trace(any... data);
+  undefined warn(any... data);
+  undefined dir(optional any item, optional object? options);
+  undefined dirxml(any... data);
 
   // Counting
-  void count(optional DOMString label = "default");
-  void countReset(optional DOMString label = "default");
+  undefined count(optional DOMString label = "default");
+  undefined countReset(optional DOMString label = "default");
 
   // Grouping
-  void group(any... data);
-  void groupCollapsed(any... data);
-  void groupEnd();
+  undefined group(any... data);
+  undefined groupCollapsed(any... data);
+  undefined groupEnd();
 
   // Timing
-  void time(optional DOMString label = "default");
-  void timeLog(optional DOMString label = "default", any... data);
-  void timeEnd(optional DOMString label = "default");
+  undefined time(optional DOMString label = "default");
+  undefined timeLog(optional DOMString label = "default", any... data);
+  undefined timeEnd(optional DOMString label = "default");
 };
diff --git a/third_party/blink/web_tests/external/wpt/interfaces/webauthn.idl b/third_party/blink/web_tests/external/wpt/interfaces/webauthn.idl
index 607c476..ed64377 100644
--- a/third_party/blink/web_tests/external/wpt/interfaces/webauthn.idl
+++ b/third_party/blink/web_tests/external/wpt/interfaces/webauthn.idl
@@ -77,8 +77,8 @@
 
 dictionary AuthenticatorSelectionCriteria {
     DOMString                    authenticatorAttachment;
-    boolean                      requireResidentKey = false;
     DOMString                    residentKey;
+    boolean                      requireResidentKey = false;
     DOMString                    userVerification = "preferred";
 };
 
diff --git a/third_party/blink/web_tests/external/wpt/scroll-animations/css/animation-timeline-in-keyframe-expected.txt b/third_party/blink/web_tests/external/wpt/scroll-animations/css/animation-timeline-in-keyframe-expected.txt
index 0af056b..e714ac78 100644
--- a/third_party/blink/web_tests/external/wpt/scroll-animations/css/animation-timeline-in-keyframe-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/scroll-animations/css/animation-timeline-in-keyframe-expected.txt
@@ -1,4 +1,4 @@
 This is a testharness.js-based test.
-FAIL The animation-timeline property may not be used in keyframes assert_equals: expected (string) "bar" but got (undefined) undefined
+FAIL The animation-timeline property may not be used in keyframes assert_equals: expected (string) "auto" but got (undefined) undefined
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/external/wpt/scroll-animations/css/animation-timeline-in-keyframe.html b/third_party/blink/web_tests/external/wpt/scroll-animations/css/animation-timeline-in-keyframe.html
index bdf40e0..7548333 100644
--- a/third_party/blink/web_tests/external/wpt/scroll-animations/css/animation-timeline-in-keyframe.html
+++ b/third_party/blink/web_tests/external/wpt/scroll-animations/css/animation-timeline-in-keyframe.html
@@ -7,11 +7,10 @@
 <style>
   @keyframes test {
     from { width: 100px; animation-timeline: foo; }
-    to { width: 100px: animation-timeline: foo; }
+    to { width: 100px; animation-timeline: foo; }
   }
   #target {
     width: 50px;
-    animation-timeline: bar;
     animation-name: test;
     animation-duration: 1s;
     animation-play-state: paused;
@@ -23,6 +22,6 @@
   let style = getComputedStyle(document.getElementById('target'));
   // Checking 'width' verifies that the animation is applied at all.
   assert_equals(style.width, '100px');
-  assert_equals(style.animationTimeline, 'bar');
+  assert_equals(style.animationTimeline, 'auto');
 }, 'The animation-timeline property may not be used in keyframes');
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/scroll-animations/css/animation-timeline-none-expected.txt b/third_party/blink/web_tests/external/wpt/scroll-animations/css/animation-timeline-none-expected.txt
new file mode 100644
index 0000000..3b56d98
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/scroll-animations/css/animation-timeline-none-expected.txt
@@ -0,0 +1,5 @@
+This is a testharness.js-based test.
+FAIL Animation with animation-timeline:none has no effect value assert_equals: expected "0px" but got "100px"
+FAIL Animation with unknown timeline name has no effect value assert_equals: expected "0px" but got "100px"
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/scroll-animations/css/animation-timeline-none.html b/third_party/blink/web_tests/external/wpt/scroll-animations/css/animation-timeline-none.html
new file mode 100644
index 0000000..d5d5853
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/scroll-animations/css/animation-timeline-none.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<link rel="help" src="https://drafts.csswg.org/css-animations-2/#typedef-timeline-name">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/web-animations/testcommon.js"></script>
+<style>
+  @keyframes expand {
+    from { width: 100px; }
+    to { width: 200px; }
+  }
+
+  .test, #ref {
+    width: 0px;
+    animation-name: expand;
+    animation-duration: 10s;
+    animation-play-state: paused;
+  }
+
+  #element_timeline_none {
+    animation-timeline: none;
+  }
+  #element_unknown_timeline {
+    animation-timeline: unknown_timeline;
+  }
+
+</style>
+<div class=test id=element_timeline_none></div>
+<div class=test id=element_unknown_timeline></div>
+<div id=ref></div>
+<script>
+  promise_test(async (t) => {
+    assert_equals(getComputedStyle(element_timeline_none).width, '0px');
+    assert_equals(getComputedStyle(ref).width, '100px');
+    await waitForNextFrame();
+    assert_equals(getComputedStyle(element_timeline_none).width, '0px');
+    assert_equals(getComputedStyle(ref).width, '100px');
+  }, 'Animation with animation-timeline:none has no effect value');
+
+  promise_test(async (t) => {
+    assert_equals(getComputedStyle(element_unknown_timeline).width, '0px');
+    assert_equals(getComputedStyle(ref).width, '100px');
+    await waitForNextFrame();
+    assert_equals(getComputedStyle(element_unknown_timeline).width, '0px');
+    assert_equals(getComputedStyle(ref).width, '100px');
+  }, 'Animation with unknown timeline name has no effect value');
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/service-workers/service-worker/fetch-request-resources.https.html b/third_party/blink/web_tests/external/wpt/service-workers/service-worker/fetch-request-resources.https.html
index fe227d9..50864ca 100644
--- a/third_party/blink/web_tests/external/wpt/service-workers/service-worker/fetch-request-resources.https.html
+++ b/third_party/blink/web_tests/external/wpt/service-workers/service-worker/fetch-request-resources.https.html
@@ -117,7 +117,7 @@
       credentials: expected_credentials,
       redirect: 'follow',
       integrity: '',
-      destination: 'empty',
+      destination: '',
       message: `fetch (url:${actual_url} mode:${mode} ` +
                `credentials:${credentials})`
     };
diff --git a/third_party/blink/web_tests/external/wpt/tools/third_party/pywebsocket3/README.md b/third_party/blink/web_tests/external/wpt/tools/third_party/pywebsocket3/README.md
index 277cbe5..8684f2cc 100644
--- a/third_party/blink/web_tests/external/wpt/tools/third_party/pywebsocket3/README.md
+++ b/third_party/blink/web_tests/external/wpt/tools/third_party/pywebsocket3/README.md
@@ -10,7 +10,7 @@
 $ pydoc mod_pywebsocket
 ```
 
-Please see [Wiki](../../wiki) for more details.
+Please see [Wiki](https://github.com/GoogleChromeLabs/pywebsocket3/wiki) for more details.
 
 # INSTALL #
 
diff --git a/third_party/blink/web_tests/external/wpt/tools/third_party/pywebsocket3/example/echo_client.py b/third_party/blink/web_tests/external/wpt/tools/third_party/pywebsocket3/example/echo_client.py
index e4459a19..dafda059b 100755
--- a/third_party/blink/web_tests/external/wpt/tools/third_party/pywebsocket3/example/echo_client.py
+++ b/third_party/blink/web_tests/external/wpt/tools/third_party/pywebsocket3/example/echo_client.py
@@ -51,7 +51,7 @@
 import codecs
 from hashlib import sha1
 import logging
-from optparse import OptionParser
+import argparse
 import os
 import random
 import re
@@ -602,76 +602,78 @@
     if six.PY2:
         sys.stdout = codecs.getwriter('utf-8')(sys.stdout)
 
-    parser = OptionParser()
+    parser = argparse.ArgumentParser()
     # We accept --command_line_flag style flags which is the same as Google
     # gflags in addition to common --command-line-flag style flags.
-    parser.add_option('-s',
-                      '--server-host',
-                      '--server_host',
-                      dest='server_host',
-                      type='string',
-                      default='localhost',
-                      help='server host')
-    parser.add_option('-p',
-                      '--server-port',
-                      '--server_port',
-                      dest='server_port',
-                      type='int',
-                      default=_UNDEFINED_PORT,
-                      help='server port')
-    parser.add_option('-o',
-                      '--origin',
-                      dest='origin',
-                      type='string',
-                      default=None,
-                      help='origin')
-    parser.add_option('-r',
-                      '--resource',
-                      dest='resource',
-                      type='string',
-                      default='/echo',
-                      help='resource path')
-    parser.add_option('-m',
-                      '--message',
-                      dest='message',
-                      type='string',
-                      help=('comma-separated messages to send. '
-                            '%s will force close the connection from server.' %
-                            _GOODBYE_MESSAGE))
-    parser.add_option('-q',
-                      '--quiet',
-                      dest='verbose',
-                      action='store_false',
-                      default=True,
-                      help='suppress messages')
-    parser.add_option('-t',
-                      '--tls',
-                      dest='use_tls',
-                      action='store_true',
-                      default=False,
-                      help='use TLS (wss://).')
-    parser.add_option('-k',
-                      '--socket-timeout',
-                      '--socket_timeout',
-                      dest='socket_timeout',
-                      type='int',
-                      default=_TIMEOUT_SEC,
-                      help='Timeout(sec) for sockets')
-    parser.add_option('--use-permessage-deflate',
-                      '--use_permessage_deflate',
-                      dest='use_permessage_deflate',
-                      action='store_true',
-                      default=False,
-                      help='Use the permessage-deflate extension.')
-    parser.add_option('--log-level',
-                      '--log_level',
-                      type='choice',
-                      dest='log_level',
-                      default='warn',
-                      choices=['debug', 'info', 'warn', 'error', 'critical'],
-                      help='Log level.')
+    parser.add_argument('-s',
+                        '--server-host',
+                        '--server_host',
+                        dest='server_host',
+                        type=six.text_type,
+                        default='localhost',
+                        help='server host')
+    parser.add_argument('-p',
+                        '--server-port',
+                        '--server_port',
+                        dest='server_port',
+                        type=int,
+                        default=_UNDEFINED_PORT,
+                        help='server port')
+    parser.add_argument('-o',
+                        '--origin',
+                        dest='origin',
+                        type=six.text_type,
+                        default=None,
+                        help='origin')
+    parser.add_argument('-r',
+                        '--resource',
+                        dest='resource',
+                        type=six.text_type,
+                        default='/echo',
+                        help='resource path')
+    parser.add_argument(
+        '-m',
+        '--message',
+        dest='message',
+        type=six.text_type,
+        default=u'Hello,\u65e5\u672c',
+        help=('comma-separated messages to send. '
+              '%s will force close the connection from server.' %
+              _GOODBYE_MESSAGE))
+    parser.add_argument('-q',
+                        '--quiet',
+                        dest='verbose',
+                        action='store_false',
+                        default=True,
+                        help='suppress messages')
+    parser.add_argument('-t',
+                        '--tls',
+                        dest='use_tls',
+                        action='store_true',
+                        default=False,
+                        help='use TLS (wss://).')
+    parser.add_argument('-k',
+                        '--socket-timeout',
+                        '--socket_timeout',
+                        dest='socket_timeout',
+                        type=int,
+                        default=_TIMEOUT_SEC,
+                        help='Timeout(sec) for sockets')
+    parser.add_argument('--use-permessage-deflate',
+                        '--use_permessage_deflate',
+                        dest='use_permessage_deflate',
+                        action='store_true',
+                        default=False,
+                        help='Use the permessage-deflate extension.')
+    parser.add_argument('--log-level',
+                        '--log_level',
+                        type=six.text_type,
+                        dest='log_level',
+                        default='warn',
+                        choices=['debug', 'info', 'warn', 'error', 'critical'],
+                        help='Log level.')
 
-    (options, unused_args) = parser.parse_args()
+    options = parser.parse_args()
 
     logging.basicConfig(level=logging.getLevelName(options.log_level.upper()))
 
@@ -682,11 +684,6 @@
         else:
             options.server_port = common.DEFAULT_WEB_SOCKET_PORT
 
-    # optparse doesn't seem to handle non-ascii default values.
-    # Set default message here.
-    if not options.message:
-        options.message = u'Hello,\u65e5\u672c'  # "Japan" in Japanese
-
     EchoClient(options).run()
 
 
diff --git a/third_party/blink/web_tests/external/wpt/tools/third_party/pywebsocket3/mod_pywebsocket/extensions.py b/third_party/blink/web_tests/external/wpt/tools/third_party/pywebsocket3/mod_pywebsocket/extensions.py
index 344539f6..314a949 100644
--- a/third_party/blink/web_tests/external/wpt/tools/third_party/pywebsocket3/mod_pywebsocket/extensions.py
+++ b/third_party/blink/web_tests/external/wpt/tools/third_party/pywebsocket3/mod_pywebsocket/extensions.py
@@ -273,6 +273,7 @@
 
         If this method has been called with True and an offer without the
         client_max_window_bits extension parameter is received,
+
         - (When processing the permessage-deflate extension) this processor
           declines the request.
         - (When processing the permessage-compress extension) this processor
diff --git a/third_party/blink/web_tests/external/wpt/tools/third_party/pywebsocket3/mod_pywebsocket/http_header_util.py b/third_party/blink/web_tests/external/wpt/tools/third_party/pywebsocket3/mod_pywebsocket/http_header_util.py
index ded90b2..21fde59 100644
--- a/third_party/blink/web_tests/external/wpt/tools/third_party/pywebsocket3/mod_pywebsocket/http_header_util.py
+++ b/third_party/blink/web_tests/external/wpt/tools/third_party/pywebsocket3/mod_pywebsocket/http_header_util.py
@@ -120,7 +120,7 @@
 
 
 def consume_lwses(state):
-    """Consumes *LWS from the head."""
+    r"""Consumes \*LWS from the head."""
 
     while consume_lws(state):
         pass
diff --git a/third_party/blink/web_tests/external/wpt/tools/third_party/pywebsocket3/mod_pywebsocket/request_handler.py b/third_party/blink/web_tests/external/wpt/tools/third_party/pywebsocket3/mod_pywebsocket/request_handler.py
index e038d9f..5e9c875 100644
--- a/third_party/blink/web_tests/external/wpt/tools/third_party/pywebsocket3/mod_pywebsocket/request_handler.py
+++ b/third_party/blink/web_tests/external/wpt/tools/third_party/pywebsocket3/mod_pywebsocket/request_handler.py
@@ -208,7 +208,7 @@
             return False
 
         if self._options.use_basic_auth:
-            auth = self.headers.getheader('Authorization')
+            auth = self.headers.get('Authorization')
             if auth != self._options.basic_auth_credential:
                 self.send_response(401)
                 self.send_header('WWW-Authenticate',
diff --git a/third_party/blink/web_tests/external/wpt/tools/third_party/pywebsocket3/mod_pywebsocket/standalone.py b/third_party/blink/web_tests/external/wpt/tools/third_party/pywebsocket3/mod_pywebsocket/standalone.py
index 1042c496..b075d98 100755
--- a/third_party/blink/web_tests/external/wpt/tools/third_party/pywebsocket3/mod_pywebsocket/standalone.py
+++ b/third_party/blink/web_tests/external/wpt/tools/third_party/pywebsocket3/mod_pywebsocket/standalone.py
@@ -157,8 +157,9 @@
 from six.moves import configparser
 import base64
 import logging
-import optparse
+import argparse
 import os
+import six
 import sys
 import traceback
 
@@ -174,188 +175,192 @@
 
 
 def _build_option_parser():
-    parser = optparse.OptionParser()
+    parser = argparse.ArgumentParser()
 
-    parser.add_option('--config',
-                      dest='config_file',
-                      type='string',
-                      default=None,
-                      help=('Path to configuration file. See the file comment '
-                            'at the top of this file for the configuration '
-                            'file format'))
-    parser.add_option('-H',
-                      '--server-host',
-                      '--server_host',
-                      dest='server_host',
-                      default='',
-                      help='server hostname to listen to')
-    parser.add_option('-V',
-                      '--validation-host',
-                      '--validation_host',
-                      dest='validation_host',
-                      default=None,
-                      help='server hostname to validate in absolute path.')
-    parser.add_option('-p',
-                      '--port',
-                      dest='port',
-                      type='int',
-                      default=common.DEFAULT_WEB_SOCKET_PORT,
-                      help='port to listen to')
-    parser.add_option('-P',
-                      '--validation-port',
-                      '--validation_port',
-                      dest='validation_port',
-                      type='int',
-                      default=None,
-                      help='server port to validate in absolute path.')
-    parser.add_option('-w',
-                      '--websock-handlers',
-                      '--websock_handlers',
-                      dest='websock_handlers',
-                      default='.',
-                      help=('The root directory of WebSocket handler files. '
-                            'If the path is relative, --document-root is used '
-                            'as the base.'))
-    parser.add_option('-m',
-                      '--websock-handlers-map-file',
-                      '--websock_handlers_map_file',
-                      dest='websock_handlers_map_file',
-                      default=None,
-                      help=('WebSocket handlers map file. '
-                            'Each line consists of alias_resource_path and '
-                            'existing_resource_path, separated by spaces.'))
-    parser.add_option('-s',
-                      '--scan-dir',
-                      '--scan_dir',
-                      dest='scan_dir',
-                      default=None,
-                      help=('Must be a directory under --websock-handlers. '
-                            'Only handlers under this directory are scanned '
-                            'and registered to the server. '
-                            'Useful for saving scan time when the handler '
-                            'root directory contains lots of files that are '
-                            'not handler file or are handler files but you '
-                            'don\'t want them to be registered. '))
-    parser.add_option('--allow-handlers-outside-root-dir',
-                      '--allow_handlers_outside_root_dir',
-                      dest='allow_handlers_outside_root_dir',
-                      action='store_true',
-                      default=False,
-                      help=('Scans WebSocket handlers even if their canonical '
-                            'path is not under --websock-handlers.'))
-    parser.add_option('-d',
-                      '--document-root',
-                      '--document_root',
-                      dest='document_root',
-                      default='.',
-                      help='Document root directory.')
-    parser.add_option('-x',
-                      '--cgi-paths',
-                      '--cgi_paths',
-                      dest='cgi_paths',
-                      default=None,
-                      help=('CGI paths relative to document_root.'
-                            'Comma-separated. (e.g -x /cgi,/htbin) '
-                            'Files under document_root/cgi_path are handled '
-                            'as CGI programs. Must be executable.'))
-    parser.add_option('-t',
-                      '--tls',
-                      dest='use_tls',
-                      action='store_true',
-                      default=False,
-                      help='use TLS (wss://)')
-    parser.add_option('-k',
-                      '--private-key',
-                      '--private_key',
-                      dest='private_key',
-                      default='',
-                      help='TLS private key file.')
-    parser.add_option('-c',
-                      '--certificate',
-                      dest='certificate',
-                      default='',
-                      help='TLS certificate file.')
-    parser.add_option('--tls-client-auth',
-                      dest='tls_client_auth',
-                      action='store_true',
-                      default=False,
-                      help='Requests TLS client auth on every connection.')
-    parser.add_option('--tls-client-cert-optional',
-                      dest='tls_client_cert_optional',
-                      action='store_true',
-                      default=False,
-                      help=('Makes client certificate optional even though '
-                            'TLS client auth is enabled.'))
-    parser.add_option('--tls-client-ca',
-                      dest='tls_client_ca',
-                      default='',
-                      help=('Specifies a pem file which contains a set of '
-                            'concatenated CA certificates which are used to '
-                            'validate certificates passed from clients'))
-    parser.add_option('--basic-auth',
-                      dest='use_basic_auth',
-                      action='store_true',
-                      default=False,
-                      help='Requires Basic authentication.')
-    parser.add_option('--basic-auth-credential',
-                      dest='basic_auth_credential',
-                      default='test:test',
-                      help='Specifies the credential of basic authentication '
-                      'by username:password pair (e.g. test:test).')
-    parser.add_option('-l',
-                      '--log-file',
-                      '--log_file',
-                      dest='log_file',
-                      default='',
-                      help='Log file.')
+    parser.add_argument(
+        '--config',
+        dest='config_file',
+        type=six.text_type,
+        default=None,
+        help=('Path to configuration file. See the file comment '
+              'at the top of this file for the configuration '
+              'file format'))
+    parser.add_argument('-H',
+                        '--server-host',
+                        '--server_host',
+                        dest='server_host',
+                        default='',
+                        help='server hostname to listen to')
+    parser.add_argument('-V',
+                        '--validation-host',
+                        '--validation_host',
+                        dest='validation_host',
+                        default=None,
+                        help='server hostname to validate in absolute path.')
+    parser.add_argument('-p',
+                        '--port',
+                        dest='port',
+                        type=int,
+                        default=common.DEFAULT_WEB_SOCKET_PORT,
+                        help='port to listen to')
+    parser.add_argument('-P',
+                        '--validation-port',
+                        '--validation_port',
+                        dest='validation_port',
+                        type=int,
+                        default=None,
+                        help='server port to validate in absolute path.')
+    parser.add_argument(
+        '-w',
+        '--websock-handlers',
+        '--websock_handlers',
+        dest='websock_handlers',
+        default='.',
+        help=('The root directory of WebSocket handler files. '
+              'If the path is relative, --document-root is used '
+              'as the base.'))
+    parser.add_argument('-m',
+                        '--websock-handlers-map-file',
+                        '--websock_handlers_map_file',
+                        dest='websock_handlers_map_file',
+                        default=None,
+                        help=('WebSocket handlers map file. '
+                              'Each line consists of alias_resource_path and '
+                              'existing_resource_path, separated by spaces.'))
+    parser.add_argument('-s',
+                        '--scan-dir',
+                        '--scan_dir',
+                        dest='scan_dir',
+                        default=None,
+                        help=('Must be a directory under --websock-handlers. '
+                              'Only handlers under this directory are scanned '
+                              'and registered to the server. '
+                              'Useful for saving scan time when the handler '
+                              'root directory contains lots of files that are '
+                              'not handler file or are handler files but you '
+                              'don\'t want them to be registered. '))
+    parser.add_argument(
+        '--allow-handlers-outside-root-dir',
+        '--allow_handlers_outside_root_dir',
+        dest='allow_handlers_outside_root_dir',
+        action='store_true',
+        default=False,
+        help=('Scans WebSocket handlers even if their canonical '
+              'path is not under --websock-handlers.'))
+    parser.add_argument('-d',
+                        '--document-root',
+                        '--document_root',
+                        dest='document_root',
+                        default='.',
+                        help='Document root directory.')
+    parser.add_argument('-x',
+                        '--cgi-paths',
+                        '--cgi_paths',
+                        dest='cgi_paths',
+                        default=None,
+                        help=('CGI paths relative to document_root.'
+                              'Comma-separated. (e.g -x /cgi,/htbin) '
+                              'Files under document_root/cgi_path are handled '
+                              'as CGI programs. Must be executable.'))
+    parser.add_argument('-t',
+                        '--tls',
+                        dest='use_tls',
+                        action='store_true',
+                        default=False,
+                        help='use TLS (wss://)')
+    parser.add_argument('-k',
+                        '--private-key',
+                        '--private_key',
+                        dest='private_key',
+                        default='',
+                        help='TLS private key file.')
+    parser.add_argument('-c',
+                        '--certificate',
+                        dest='certificate',
+                        default='',
+                        help='TLS certificate file.')
+    parser.add_argument('--tls-client-auth',
+                        dest='tls_client_auth',
+                        action='store_true',
+                        default=False,
+                        help='Requests TLS client auth on every connection.')
+    parser.add_argument('--tls-client-cert-optional',
+                        dest='tls_client_cert_optional',
+                        action='store_true',
+                        default=False,
+                        help=('Makes client certificate optional even though '
+                              'TLS client auth is enabled.'))
+    parser.add_argument('--tls-client-ca',
+                        dest='tls_client_ca',
+                        default='',
+                        help=('Specifies a pem file which contains a set of '
+                              'concatenated CA certificates which are used to '
+                              'validate certificates passed from clients'))
+    parser.add_argument('--basic-auth',
+                        dest='use_basic_auth',
+                        action='store_true',
+                        default=False,
+                        help='Requires Basic authentication.')
+    parser.add_argument(
+        '--basic-auth-credential',
+        dest='basic_auth_credential',
+        default='test:test',
+        help='Specifies the credential of basic authentication '
+        'by username:password pair (e.g. test:test).')
+    parser.add_argument('-l',
+                        '--log-file',
+                        '--log_file',
+                        dest='log_file',
+                        default='',
+                        help='Log file.')
     # Custom log level:
     # - FINE: Prints status of each frame processing step
-    parser.add_option('--log-level',
-                      '--log_level',
-                      type='choice',
-                      dest='log_level',
-                      default='warn',
-                      choices=[
-                          'fine', 'debug', 'info', 'warning', 'warn', 'error',
-                          'critical'
-                      ],
-                      help='Log level.')
-    parser.add_option(
+    parser.add_argument('--log-level',
+                        '--log_level',
+                        type=six.text_type,
+                        dest='log_level',
+                        default='warn',
+                        choices=[
+                            'fine', 'debug', 'info', 'warning', 'warn',
+                            'error', 'critical'
+                        ],
+                        help='Log level.')
+    parser.add_argument(
         '--deflate-log-level',
         '--deflate_log_level',
-        type='choice',
+        type=six.text_type,
         dest='deflate_log_level',
         default='warn',
         choices=['debug', 'info', 'warning', 'warn', 'error', 'critical'],
         help='Log level for _Deflater and _Inflater.')
-    parser.add_option('--thread-monitor-interval-in-sec',
-                      '--thread_monitor_interval_in_sec',
-                      dest='thread_monitor_interval_in_sec',
-                      type='int',
-                      default=-1,
-                      help=('If positive integer is specified, run a thread '
-                            'monitor to show the status of server threads '
-                            'periodically in the specified inteval in '
-                            'second. If non-positive integer is specified, '
-                            'disable the thread monitor.'))
-    parser.add_option('--log-max',
-                      '--log_max',
-                      dest='log_max',
-                      type='int',
-                      default=_DEFAULT_LOG_MAX_BYTES,
-                      help='Log maximum bytes')
-    parser.add_option('--log-count',
-                      '--log_count',
-                      dest='log_count',
-                      type='int',
-                      default=_DEFAULT_LOG_BACKUP_COUNT,
-                      help='Log backup count')
-    parser.add_option('-q',
-                      '--queue',
-                      dest='request_queue_size',
-                      type='int',
-                      default=_DEFAULT_REQUEST_QUEUE_SIZE,
-                      help='request queue size')
+    parser.add_argument('--thread-monitor-interval-in-sec',
+                        '--thread_monitor_interval_in_sec',
+                        dest='thread_monitor_interval_in_sec',
+                        type=int,
+                        default=-1,
+                        help=('If positive integer is specified, run a thread '
+                              'monitor to show the status of server threads '
+                              'periodically in the specified inteval in '
+                              'second. If non-positive integer is specified, '
+                              'disable the thread monitor.'))
+    parser.add_argument('--log-max',
+                        '--log_max',
+                        dest='log_max',
+                        type=int,
+                        default=_DEFAULT_LOG_MAX_BYTES,
+                        help='Log maximum bytes')
+    parser.add_argument('--log-count',
+                        '--log_count',
+                        dest='log_count',
+                        type=int,
+                        default=_DEFAULT_LOG_BACKUP_COUNT,
+                        help='Log backup count')
+    parser.add_argument('-q',
+                        '--queue',
+                        dest='request_queue_size',
+                        type=int,
+                        default=_DEFAULT_REQUEST_QUEUE_SIZE,
+                        help='request queue size')
 
     return parser
 
@@ -364,7 +369,7 @@
     parser = _build_option_parser()
 
     # First, parse options without configuration file.
-    temporary_options, temporary_args = parser.parse_args(args=args)
+    temporary_options, temporary_args = parser.parse_known_args(args=args)
     if temporary_args:
         logging.critical('Unrecognized positional arguments: %r',
                          temporary_args)
@@ -390,7 +395,7 @@
             args = args_from_config
         else:
             args = args_from_config + args
-        return parser.parse_args(args=args)
+        return parser.parse_known_args(args=args)
     else:
         return temporary_options, temporary_args
 
diff --git a/third_party/blink/web_tests/external/wpt/tools/third_party/pywebsocket3/test/client_for_testing.py b/third_party/blink/web_tests/external/wpt/tools/third_party/pywebsocket3/test/client_for_testing.py
index 450a6ac..f9aa811 100755
--- a/third_party/blink/web_tests/external/wpt/tools/third_party/pywebsocket3/test/client_for_testing.py
+++ b/third_party/blink/web_tests/external/wpt/tools/third_party/pywebsocket3/test/client_for_testing.py
@@ -316,6 +316,11 @@
 
         fields.append(u'Sec-WebSocket-Version: %d\r\n' % self._options.version)
 
+        if self._options.use_basic_auth:
+            credential = 'Basic ' + base64.b64encode(
+                self._options.basic_auth_credential.encode('UTF-8')).decode()
+            fields.append(u'Authorization: %s\r\n' % credential)
+
         # Setting up extensions.
         if len(self._options.extensions) > 0:
             fields.append(u'Sec-WebSocket-Extensions: %s\r\n' %
@@ -609,6 +614,8 @@
         self.server_port = -1
         self.socket_timeout = 1000
         self.use_tls = False
+        self.use_basic_auth = False
+        self.basic_auth_credential = 'test:test'
         self.extensions = []
 
 
diff --git a/third_party/blink/web_tests/external/wpt/tools/third_party/pywebsocket3/test/run_all.py b/third_party/blink/web_tests/external/wpt/tools/third_party/pywebsocket3/test/run_all.py
index f6220155..ea52223c 100755
--- a/third_party/blink/web_tests/external/wpt/tools/third_party/pywebsocket3/test/run_all.py
+++ b/third_party/blink/web_tests/external/wpt/tools/third_party/pywebsocket3/test/run_all.py
@@ -43,9 +43,10 @@
 
 from __future__ import absolute_import
 import logging
-import optparse
+import argparse
 import os
 import re
+import six
 import sys
 import unittest
 
@@ -64,19 +65,19 @@
 def _suite():
     loader = unittest.TestLoader()
     return loader.loadTestsFromNames(
-        _list_test_modules(os.path.join(os.path.split(__file__)[0], '.')))
+        _list_test_modules(os.path.dirname(__file__)))
 
 
 if __name__ == '__main__':
-    parser = optparse.OptionParser()
-    parser.add_option(
+    parser = argparse.ArgumentParser()
+    parser.add_argument(
         '--log-level',
         '--log_level',
-        type='choice',
+        type=six.text_type,
         dest='log_level',
         default='warning',
         choices=['debug', 'info', 'warning', 'warn', 'error', 'critical'])
-    options, args = parser.parse_args()
+    options, args = parser.parse_known_args()
     logging.basicConfig(level=logging.getLevelName(options.log_level.upper()),
                         format='%(levelname)s %(asctime)s '
                         '%(filename)s:%(lineno)d] '
diff --git a/third_party/blink/web_tests/external/wpt/tools/third_party/pywebsocket3/test/set_sys_path.py b/third_party/blink/web_tests/external/wpt/tools/third_party/pywebsocket3/test/set_sys_path.py
index 9fce7b3..48d0e116 100644
--- a/third_party/blink/web_tests/external/wpt/tools/third_party/pywebsocket3/test/set_sys_path.py
+++ b/third_party/blink/web_tests/external/wpt/tools/third_party/pywebsocket3/test/set_sys_path.py
@@ -36,6 +36,6 @@
 import sys
 
 # Add the parent directory to sys.path to enable importing mod_pywebsocket.
-sys.path.insert(0, os.path.join(os.path.split(__file__)[0], '..'))
+sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
 
 # vi:sts=4 sw=4 et
diff --git a/third_party/blink/web_tests/external/wpt/tools/third_party/pywebsocket3/test/test_dispatch.py b/third_party/blink/web_tests/external/wpt/tools/third_party/pywebsocket3/test/test_dispatch.py
index ae63980..132dd92 100755
--- a/third_party/blink/web_tests/external/wpt/tools/third_party/pywebsocket3/test/test_dispatch.py
+++ b/third_party/blink/web_tests/external/wpt/tools/third_party/pywebsocket3/test/test_dispatch.py
@@ -41,8 +41,8 @@
 from test import mock
 from six.moves import zip
 
-_TEST_HANDLERS_DIR = os.path.join(
-    os.path.split(__file__)[0], 'testdata', 'handlers')
+_TEST_HANDLERS_DIR = os.path.join(os.path.dirname(__file__), 'testdata',
+                                  'handlers')
 
 _TEST_HANDLERS_SUB_DIR = os.path.join(_TEST_HANDLERS_DIR, 'sub')
 
diff --git a/third_party/blink/web_tests/external/wpt/tools/third_party/pywebsocket3/test/test_endtoend.py b/third_party/blink/web_tests/external/wpt/tools/third_party/pywebsocket3/test/test_endtoend.py
index 445d29c..8d457ff6 100755
--- a/third_party/blink/web_tests/external/wpt/tools/third_party/pywebsocket3/test/test_endtoend.py
+++ b/third_party/blink/web_tests/external/wpt/tools/third_party/pywebsocket3/test/test_endtoend.py
@@ -113,6 +113,18 @@
     client.assert_connection_closed()
 
 
+def _check_handshake_with_basic_auth(client):
+    client.connect()
+
+    client.send_message(_GOODBYE_MESSAGE)
+    client.assert_receive(_GOODBYE_MESSAGE)
+
+    client.assert_receive_close()
+    client.send_close()
+
+    client.assert_connection_closed()
+
+
 class EndToEndTestBase(unittest.TestCase):
     """Base class for end-to-end tests that launch pywebsocket standalone
     server as a separate process, connect to it using the client_for_testing
@@ -121,7 +133,7 @@
     """
     def setUp(self):
         self.server_stderr = None
-        self.top_dir = os.path.join(os.path.split(__file__)[0], '..')
+        self.top_dir = os.path.join(os.path.dirname(__file__), '..')
         os.putenv('PYTHONPATH', os.path.pathsep.join(sys.path))
         self.standalone_command = os.path.join(self.top_dir, 'mod_pywebsocket',
                                                'standalone.py')
@@ -146,7 +158,7 @@
                                 stdout=stdout,
                                 stderr=stderr)
 
-    def _run_server(self):
+    def _run_server(self, extra_args=[]):
         args = [
             self.standalone_command, '-H', 'localhost', '-V', 'localhost',
             '-p',
@@ -161,6 +173,8 @@
             args.append('--log-level')
             args.append(logging.getLevelName(log_level).lower())
 
+        args += extra_args
+
         return self._run_python_command(args, stderr=self.server_stderr)
 
     def _close_server(self, server):
@@ -187,8 +201,11 @@
     def setUp(self):
         EndToEndTestBase.setUp(self)
 
-    def _run_test_with_client_options(self, test_function, options):
-        server = self._run_server()
+    def _run_test_with_options(self,
+                               test_function,
+                               options,
+                               server_options=[]):
+        server = self._run_server(server_options)
         try:
             # TODO(tyoshino): add some logic to poll the server until it
             # becomes ready
@@ -203,7 +220,7 @@
             self._close_server(server)
 
     def _run_test(self, test_function):
-        self._run_test_with_client_options(test_function, self._options)
+        self._run_test_with_options(test_function, self._options)
 
     def _run_permessage_deflate_test(self, offer, response_checker,
                                      test_function):
@@ -227,8 +244,11 @@
         finally:
             self._close_server(server)
 
-    def _run_close_with_code_and_reason_test(self, test_function, code,
-                                             reason):
+    def _run_close_with_code_and_reason_test(self,
+                                             test_function,
+                                             code,
+                                             reason,
+                                             server_options=[]):
         server = self._run_server()
         try:
             time.sleep(_SERVER_WARMUP_IN_SEC)
@@ -528,16 +548,21 @@
 
         self._run_test(test_function)
 
-    # TODO(toyoshim): Add tests to verify invalid absolute uri handling like
-    # host unmatch, port unmatch and invalid port description (':' without port
-    # number).
-
     def test_absolute_uri(self):
         """Tests absolute uri request."""
 
         options = self._options
         options.resource = 'ws://localhost:%d/echo' % options.server_port
-        self._run_test_with_client_options(_echo_check_procedure, options)
+        self._run_test_with_options(_echo_check_procedure, options)
+
+    def test_invalid_absolute_uri(self):
+        """Tests invalid absolute uri request."""
+
+        options = self._options
+        options.resource = 'ws://invalidlocalhost:%d/echo' % options.server_port
+        options.server_stderr = subprocess.PIPE
+
+        self._run_http_fallback_test(options, 404)
 
     def test_origin_check(self):
         """Tests http fallback on origin check fail."""
@@ -549,6 +574,24 @@
         self.server_stderr = subprocess.PIPE
         self._run_http_fallback_test(options, 403)
 
+    def test_invalid_resource(self):
+        """Tests invalid resource path."""
+
+        options = self._options
+        options.resource = '/no_resource'
+
+        self.server_stderr = subprocess.PIPE
+        self._run_http_fallback_test(options, 404)
+
+    def test_fragmentized_resource(self):
+        """Tests resource name with fragment"""
+
+        options = self._options
+        options.resource = '/echo#fragment'
+
+        self.server_stderr = subprocess.PIPE
+        self._run_http_fallback_test(options, 400)
+
     def test_version_check(self):
         """Tests http fallback on version check fail."""
 
@@ -556,6 +599,32 @@
         options.version = 99
         self._run_http_fallback_test(options, 400)
 
+    def test_basic_auth_connection(self):
+        """Test successful basic auth"""
+
+        options = self._options
+        options.use_basic_auth = True
+
+        self.server_stderr = subprocess.PIPE
+        self._run_test_with_options(_check_handshake_with_basic_auth,
+                                    options,
+                                    server_options=['--basic-auth'])
+
+    def test_invalid_basic_auth_connection(self):
+        """Tests basic auth with invalid credentials"""
+
+        options = self._options
+        options.use_basic_auth = True
+        options.basic_auth_credential = 'invalid:test'
+
+        self.server_stderr = subprocess.PIPE
+
+        with self.assertRaises(client_for_testing.HttpStatusException) as e:
+            self._run_test_with_options(_check_handshake_with_basic_auth,
+                                        options,
+                                        server_options=['--basic-auth'])
+            self.assertEqual(101, e.exception.status)
+
 
 class EndToEndTestWithEchoClient(EndToEndTestBase):
     def setUp(self):
diff --git a/third_party/blink/web_tests/external/wpt/tools/third_party/pywebsocket3/test/test_msgutil.py b/third_party/blink/web_tests/external/wpt/tools/third_party/pywebsocket3/test/test_msgutil.py
index 943f21b..1122c28 100755
--- a/third_party/blink/web_tests/external/wpt/tools/third_party/pywebsocket3/test/test_msgutil.py
+++ b/third_party/blink/web_tests/external/wpt/tools/third_party/pywebsocket3/test/test_msgutil.py
@@ -552,7 +552,6 @@
         compressed_empty = compressed_empty[:-4]
         expected += b'\x80%c' % len(compressed_empty)
         expected += compressed_empty
-        print('%r' % expected)
         self.assertEqual(expected, request.connection.written_data())
 
     def test_send_message_fragmented_empty_last_frame(self):
@@ -732,7 +731,6 @@
 
             frame_count += 1
 
-        print("Chunk sizes: %r" % chunk_sizes)
         self.assertTrue(len(chunk_sizes) > 10)
 
         # Close frame
@@ -834,9 +832,7 @@
                     compress = None
                     finish_used = True
 
-        print("Chunk sizes: %r" % chunk_sizes)
         self.assertTrue(len(chunk_sizes) > 10)
-        print("Methods: %r" % methods)
         self.assertTrue(sync_used)
         self.assertTrue(finish_used)
 
diff --git a/third_party/blink/web_tests/external/wpt/tools/third_party/pywebsocket3/test/test_util.py b/third_party/blink/web_tests/external/wpt/tools/third_party/pywebsocket3/test/test_util.py
index 20347941..bf4bd32 100755
--- a/third_party/blink/web_tests/external/wpt/tools/third_party/pywebsocket3/test/test_util.py
+++ b/third_party/blink/web_tests/external/wpt/tools/third_party/pywebsocket3/test/test_util.py
@@ -45,7 +45,7 @@
 from six import PY3
 from six import int2byte
 
-_TEST_DATA_DIR = os.path.join(os.path.split(__file__)[0], 'testdata')
+_TEST_DATA_DIR = os.path.join(os.path.dirname(__file__), 'testdata')
 
 
 class UtilTest(unittest.TestCase):
@@ -167,7 +167,6 @@
             [int2byte(random.randint(0, 255)) for i in range(100 * 1024)])
 
         chunked_input = get_random_section(source, 10)
-        print("Input chunk sizes: %r" % [len(c) for c in chunked_input])
 
         deflater = util._Deflater(15)
         compressed = []
@@ -176,8 +175,6 @@
         compressed.append(deflater.compress_and_finish(b''))
 
         chunked_expectation = get_random_section(source, 10)
-        print("Expectation chunk sizes: %r" %
-              [len(c) for c in chunked_expectation])
 
         inflater = util._Inflater(15)
         inflater.append(b''.join(compressed))
diff --git a/third_party/blink/web_tests/external/wpt/webcodecs/audio-decoder.html b/third_party/blink/web_tests/external/wpt/webcodecs/audio-decoder.html
index 78450c3..af82b2cd 100644
--- a/third_party/blink/web_tests/external/wpt/webcodecs/audio-decoder.html
+++ b/third_party/blink/web_tests/external/wpt/webcodecs/audio-decoder.html
@@ -34,6 +34,8 @@
     numberOfChannels: 2
   }
 
+  assert_equals(decoder.state, "unconfigured");
+
   // Empty codec rejected.
   config.codec = '';
   assert_throws_js(TypeError, () => { decoder.configure(config); });
@@ -54,7 +56,101 @@
   config.codec = 'opus';
   decoder.configure(config);
 
+  assert_equals(decoder.state, "configured");
+
+  // Test we can configure after a reset.
+  decoder.reset()
+  assert_equals(decoder.state, "unconfigured");
+
+  decoder.configure(config);
+  assert_equals(decoder.state, "configured");
+
   asyncDone(t);
 }, 'Test AudioDecoder.configure() codec validity');
+
+function getFakeChunk() {
+  return new EncodedAudioChunk({
+    type:'key',
+    timestamp:0,
+    data:Uint8Array.of(0)
+  });
+}
+
+promise_test(t => {
+  let decoder = new AudioDecoder({
+    output(frame) {
+      t.step(() => {
+        throw "unexpected output";
+      });
+    },
+    error(e) {
+      t.step(() => {
+        throw "unexpected error";
+      });
+    }
+  });
+
+  decoder.close();
+
+  assert_equals(decoder.state, "closed");
+
+  let config = {
+    sampleRate: 48000,
+    numberOfChannels: 2,
+    codec: 'opus'
+  }
+
+  let fakeChunk = getFakeChunk();
+
+  assert_throws_dom("InvalidStateError",
+                    () => decoder.configure(config),
+                    "configure");
+  assert_throws_dom("InvalidStateError",
+                    () => decoder.decode(fakeChunk),
+                    "decode");
+  assert_throws_dom("InvalidStateError",
+                    () => decoder.reset(),
+                    "reset");
+  assert_throws_dom("InvalidStateError",
+                    () => decoder.close(),
+                    "close");
+  return promise_rejects_dom(t, 'InvalidStateError', decoder.flush(), 'flush');
+}, 'Closed decoder');
+
+promise_test(t => {
+  let decoder = new AudioDecoder({
+    output(frame) {
+      t.step(() => {
+        throw "unexpected output";
+      });
+    },
+    error(e) {
+      t.step(() => {
+        throw "unexpected error";
+      });
+    }
+  });
+
+  assert_equals(decoder.state, "unconfigured");
+
+  // Reseting an unconfigured encoder is a no-op.
+  decoder.reset();
+  assert_equals(decoder.state, "unconfigured");
+
+  let config = {
+    sampleRate: 48000,
+    numberOfChannels: 2,
+    codec: 'opus'
+  }
+
+  let fakeChunk = getFakeChunk();
+
+  assert_throws_dom("InvalidStateError",
+                    () => decoder.decode(fakeChunk),
+                    "decode");
+  return promise_rejects_dom(t, 'InvalidStateError', decoder.flush(), 'flush');
+}, 'Unconfigured decoder');
+
+
 </script>
 </html>
diff --git a/third_party/blink/web_tests/external/wpt/webcodecs/video-decoder.html b/third_party/blink/web_tests/external/wpt/webcodecs/video-decoder.html
index 79f6256..7a37fed 100644
--- a/third_party/blink/web_tests/external/wpt/webcodecs/video-decoder.html
+++ b/third_party/blink/web_tests/external/wpt/webcodecs/video-decoder.html
@@ -68,6 +68,8 @@
     error(error) { t.unreached_func("Unexpected error:" + error).call(); },
   });
 
+  assert_equals(decoder.state, "unconfigured");
+
   asyncDone(t);
 }, 'Test VideoDecoder construction');
 
@@ -77,7 +79,6 @@
     error(error) { t.unreached_func("Unexpected error:" + error).call(); },
   });
 
-
   let config = {};
 
   // Bogus codec rejected.
@@ -100,6 +101,27 @@
   config.codec = 'vp09.00.10.08';
   decoder.configure(config);
 
+  assert_equals(decoder.state, "configured");
+
+  // Verify a failed reconfigure does not affect the current configuration.
+  config.codec = 'bogus';
+  assert_throws_js(TypeError, () => { decoder.configure(config); });
+
+  assert_equals(decoder.state, "configured");
+
+  // Verify we can reconfigure.
+  config.codec = 'vp09.00.10.08';
+  decoder.configure(config);
+
+  assert_equals(decoder.state, "configured");
+
+  // Test we can configure after a reset.
+  decoder.reset()
+  assert_equals(decoder.state, "unconfigured");
+
+  decoder.configure(config);
+  assert_equals(decoder.state, "configured");
+
   asyncDone(t);
 }, 'Test VideoDecoder.configure() codec validity');
 
@@ -152,13 +174,15 @@
 
   decoder.close();
 
+  assert_equals(decoder.state, "closed");
+
   let fakeChunk = getFakeChunk();
   assert_throws_dom("InvalidStateError",
                     () => decoder.configure({codec: vp9.codec}),
                     "configure");
   assert_throws_dom("InvalidStateError",
                     () => decoder.decode(fakeChunk),
-                    "reset");
+                    "decode");
   assert_throws_dom("InvalidStateError",
                     () => decoder.reset(),
                     "reset");
@@ -169,7 +193,6 @@
 }, 'Closed decoder');
 
 promise_test(t => {
-  let numErrors = 0;
   let decoder = new VideoDecoder({
     output(frame) {
       t.step(() => {
@@ -177,17 +200,24 @@
       });
     },
     error(e) {
-      numErrors++;
+      t.step(() => {
+        throw "unexpected error";
+      });
     }
   });
 
-  let fakeChunk = getFakeChunk();
-  decoder.decode(fakeChunk);
+  assert_equals(decoder.state, "unconfigured");
 
-  return decoder.flush().then(
-      () => { throw "flush succeeded unexpectedly"; },
-      () => { assert_equals(numErrors, 1, "errors"); });
-}, 'Decode without configure');
+  // Reseting an unconfigured encoder is a no-op.
+  decoder.reset();
+  assert_equals(decoder.state, "unconfigured");
+
+  let fakeChunk = getFakeChunk();
+  assert_throws_dom("InvalidStateError",
+                    () => decoder.decode(fakeChunk),
+                    "decode");
+  return promise_rejects_dom(t, 'InvalidStateError', decoder.flush(), 'flush');
+}, 'Unconfigured decoder');
 
 promise_test(t => {
   let numErrors = 0;
@@ -209,7 +239,10 @@
 
   return decoder.flush().then(
       () => { throw "flush succeeded unexpectedly"; },
-      () => { assert_equals(numErrors, 1, "errors"); });
+      () => {
+        assert_equals(numErrors, 1, "errors");
+        assert_equals(decoder.state, "closed");
+      });
 }, 'Decode corrupt VP9 frame');
 
 promise_test(t => {
diff --git a/third_party/blink/web_tests/external/wpt/webcodecs/video-encoder.html b/third_party/blink/web_tests/external/wpt/webcodecs/video-encoder.html
index b56e7d1..368789b 100644
--- a/third_party/blink/web_tests/external/wpt/webcodecs/video-encoder.html
+++ b/third_party/blink/web_tests/external/wpt/webcodecs/video-encoder.html
@@ -9,6 +9,13 @@
 <script src="/common/media.js"></script>
 <script>
 
+const defaultConfig = {
+  codec: 'vp8',
+  framerate: 25,
+  width: 640,
+  height: 480
+};
+
 async function generateBitmap(width, height) {
   return createImageBitmap(document.getElementById('frame_image'),
                                   { resizeWidth: width,
@@ -34,6 +41,9 @@
     output(chunk) { t.unreached_func("Unexpected output").call(); },
     error(error) { t.unreached_func("Unexpected error:" + error).call(); },
   });
+
+  assert_equals(encoder.state, "unconfigured");
+
   encoder.close();
 
   asyncDone(t);
@@ -45,25 +55,23 @@
     error(error) { t.unreached_func("Unexpected error:" + error).call(); },
   });
 
-  const requiredConfigPairs = {
-    codec: 'vp8',
-    framerate: 25,
-    width: 640,
-    height: 480
-  };
+  const requiredConfigPairs = defaultConfig;
   let incrementalConfig = {};
 
   for (let key in requiredConfigPairs) {
     // Configure should fail while required keys are missing.
     assert_throws_js(TypeError, () => { encoder.configure(incrementalConfig); });
     incrementalConfig[key] = requiredConfigPairs[key];
+    assert_equals(encoder.state, "unconfigured");
   }
 
   // Configure should pass once incrementalConfig meets all requirements.
   encoder.configure(incrementalConfig);
+  assert_equals(encoder.state, "configured");
 
   // We should be able to reconfigure the encoder.
   encoder.configure(incrementalConfig);
+  assert_equals(encoder.state, "configured");
 
   let config = incrementalConfig;
 
@@ -83,6 +91,16 @@
   config.codec = 'video/webm; codecs="vp9"';
   assert_throws_js(TypeError, () => { encoder.configure(config); });
 
+  // The failed configures should not affect the current config.
+  assert_equals(encoder.state, "configured");
+
+  // Test we can configure after a reset.
+  encoder.reset()
+  assert_equals(encoder.state, "unconfigured");
+
+  encoder.configure(defaultConfig);
+  assert_equals(encoder.state, "configured");
+
   encoder.close();
 
   asyncDone(t);
@@ -120,13 +138,7 @@
   // No encodes yet.
   assert_equals(encoder.encodeQueueSize, 0);
 
-  const config = {
-    codec: 'vp8',
-    framerate: 25,
-    width: 640,
-    height: 480
-  };
-  encoder.configure(config);
+  encoder.configure(defaultConfig);
 
   // Still no encodes.
   assert_equals(encoder.encodeQueueSize, 0);
@@ -165,12 +177,8 @@
   // No encodes yet.
   assert_equals(encoder.encodeQueueSize, 0);
 
-  const config = {
-    codec: 'vp8',
-    framerate: 25,
-    width: 640,
-    height: 480
-  };
+  let config = defaultConfig;
+
   encoder.configure(config);
 
   let frame1 = await createVideoFrame(640, 480, 0);
@@ -238,13 +246,7 @@
     // No encodes yet.
     assert_equals(encoder.encodeQueueSize, 0);
 
-    const config = {
-      codec: 'vp8',
-      framerate: 25,
-      width: 640,
-      height: 480
-    };
-    encoder.configure(config);
+    encoder.configure(defaultConfig);
 
     encoder.encode(frame);
 
@@ -257,5 +259,68 @@
   asyncDone(t);
 }, 'Test encoder consumes (destroys) frames.');
 
+promise_test(async t => {
+  let encoder = new VideoEncoder({
+    output(frame) {
+      t.step(() => {
+        throw "unexpected output";
+      });
+    },
+    error(e) {
+      t.step(() => {
+        throw "unexpected error";
+      });
+    }
+  });
+
+  encoder.close();
+
+  assert_equals(encoder.state, "closed")
+
+  let frame = await createVideoFrame(640, 480, 0);
+
+  assert_throws_dom("InvalidStateError",
+                    () => encoder.configure(defaultConfig),
+                    "configure");
+  assert_throws_dom("InvalidStateError",
+                    () => encoder.encode(frame),
+                    "encode");
+  assert_throws_dom("InvalidStateError",
+                    () => encoder.reset(),
+                    "reset");
+  assert_throws_dom("InvalidStateError",
+                    () => encoder.close(),
+                    "close");
+  return promise_rejects_dom(t, 'InvalidStateError', encoder.flush(), 'flush');
+}, 'Closed encoder');
+
+promise_test(async t => {
+  let encoder = new VideoEncoder({
+    output(frame) {
+      t.step(() => {
+        throw "unexpected output";
+      });
+    },
+    error(e) {
+      t.step(() => {
+        throw "unexpected error";
+      });
+    }
+  });
+
+  assert_equals(encoder.state, "unconfigured");
+
+  let frame = await createVideoFrame(640, 480, 0);
+
+  // Resetting an unconfigured encoder is a no-op.
+  encoder.reset();
+  assert_equals(encoder.state, "unconfigured");
+
+  assert_throws_dom("InvalidStateError",
+                    () => encoder.encode(frame),
+                    "encode");
+  return promise_rejects_dom(t, 'InvalidStateError', encoder.flush(), 'flush');
+}, 'Unconfigured encoder');
+
 </script>
 </html>
diff --git a/third_party/blink/web_tests/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt b/third_party/blink/web_tests/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt
index b6406a0..41643a4 100644
--- a/third_party/blink/web_tests/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt
+++ b/third_party/blink/web_tests/http/tests/serviceworker/webexposed/global-interface-listing-service-worker-expected.txt
@@ -17,6 +17,7 @@
 interface AudioDecoder
     attribute @@toStringTag
     getter decodeQueueSize
+    getter state
     method close
     method configure
     method constructor
diff --git a/third_party/blink/web_tests/virtual/css-scroll-timeline/external/wpt/scroll-animations/css/animation-timeline-none-expected.txt b/third_party/blink/web_tests/virtual/css-scroll-timeline/external/wpt/scroll-animations/css/animation-timeline-none-expected.txt
new file mode 100644
index 0000000..8f1d1d7
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/css-scroll-timeline/external/wpt/scroll-animations/css/animation-timeline-none-expected.txt
@@ -0,0 +1,5 @@
+This is a testharness.js-based test.
+PASS Animation with animation-timeline:none has no effect value
+PASS Animation with unknown timeline name has no effect value
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/webexposed/feature-policy-features-expected.txt b/third_party/blink/web_tests/webexposed/feature-policy-features-expected.txt
index 49d979ba..d00c758 100644
--- a/third_party/blink/web_tests/webexposed/feature-policy-features-expected.txt
+++ b/third_party/blink/web_tests/webexposed/feature-policy-features-expected.txt
@@ -24,7 +24,6 @@
 clipboard-write
 cross-origin-isolated
 document-domain
-document-write
 downloads
 encrypted-media
 execution-while-not-rendered
diff --git a/third_party/blink/web_tests/webexposed/global-interface-listing-dedicated-worker-expected.txt b/third_party/blink/web_tests/webexposed/global-interface-listing-dedicated-worker-expected.txt
index 1f7b39c2..1543525 100644
--- a/third_party/blink/web_tests/webexposed/global-interface-listing-dedicated-worker-expected.txt
+++ b/third_party/blink/web_tests/webexposed/global-interface-listing-dedicated-worker-expected.txt
@@ -18,6 +18,7 @@
 [Worker] interface AudioDecoder
 [Worker]     attribute @@toStringTag
 [Worker]     getter decodeQueueSize
+[Worker]     getter state
 [Worker]     method close
 [Worker]     method configure
 [Worker]     method constructor
diff --git a/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt b/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
index c535addb..01f9c19c8 100644
--- a/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
+++ b/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
@@ -271,6 +271,7 @@
 interface AudioDecoder
     attribute @@toStringTag
     getter decodeQueueSize
+    getter state
     method close
     method configure
     method constructor
@@ -8802,6 +8803,7 @@
 interface VideoDecoder
     attribute @@toStringTag
     getter decodeQueueSize
+    getter state
     method close
     method configure
     method constructor
@@ -8811,6 +8813,7 @@
 interface VideoEncoder
     attribute @@toStringTag
     getter encodeQueueSize
+    getter state
     method close
     method configure
     method constructor
diff --git a/third_party/blink/web_tests/webexposed/global-interface-listing-shared-worker-expected.txt b/third_party/blink/web_tests/webexposed/global-interface-listing-shared-worker-expected.txt
index 32346cd..d145bab2 100644
--- a/third_party/blink/web_tests/webexposed/global-interface-listing-shared-worker-expected.txt
+++ b/third_party/blink/web_tests/webexposed/global-interface-listing-shared-worker-expected.txt
@@ -18,6 +18,7 @@
 [Worker] interface AudioDecoder
 [Worker]     attribute @@toStringTag
 [Worker]     getter decodeQueueSize
+[Worker]     getter state
 [Worker]     method close
 [Worker]     method configure
 [Worker]     method constructor
diff --git a/third_party/closure_compiler/externs/automation.js b/third_party/closure_compiler/externs/automation.js
index 239347b..866f773 100644
--- a/third_party/closure_compiler/externs/automation.js
+++ b/third_party/closure_compiler/externs/automation.js
@@ -210,7 +210,6 @@
   MATH: 'math',
   MENU: 'menu',
   MENU_BAR: 'menuBar',
-  MENU_BUTTON: 'menuButton',
   MENU_ITEM: 'menuItem',
   MENU_ITEM_CHECK_BOX: 'menuItemCheckBox',
   MENU_ITEM_RADIO: 'menuItemRadio',
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 2cda02d..d93130e 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -3340,6 +3340,8 @@
   <int value="4" label="M_TO_P"/>
   <int value="5" label="N_TO_P"/>
   <int value="6" label="P_TO_Q"/>
+  <int value="7" label="N_TO_R"/>
+  <int value="8" label="P_TO_R"/>
 </enum>
 
 <enum name="ArcShareFilesOnExit">
@@ -16996,6 +16998,7 @@
   <int value="6" label="OversizedImages"/>
   <int value="7" label="UnsizedMedia"/>
   <int value="8" label="LayoutAnimations"/>
+  <int value="9" label="DocumentWrite"/>
 </enum>
 
 <enum name="DocumentScanSaneBackend">
@@ -64991,6 +64994,20 @@
   <int value="10" label="Undo">The sync was aborted with an undo button.</int>
 </enum>
 
+<enum name="SigninInterceptHeuristicOutcome">
+  <int value="0" label="Intercept: profile switch"/>
+  <int value="1" label="Intercept: multi-user"/>
+  <int value="2" label="Intercept: enterprise"/>
+  <int value="3" label="Abort: Sync signin"/>
+  <int value="4" label="Abort: intercept in progress"/>
+  <int value="5" label="Abort: account not new"/>
+  <int value="6" label="Abort: single account"/>
+  <int value="7" label="Abort: account info fetch timeout"/>
+  <int value="8" label="Abort: account info not compatible"/>
+  <int value="9" label="Abort: profile creation disallowed"/>
+  <int value="10" label="Abort: shut down"/>
+</enum>
+
 <enum name="SigninInterceptResult">
   <int value="0" label="Accepted"/>
   <int value="1" label="Declined"/>
@@ -68795,9 +68812,11 @@
 <enum name="SyncErrorInfobarTypes">
   <summary>Possible errors that can trigger a sync error infobar.</summary>
   <int value="1" label="Sign in needs update"/>
-  <int value="2" label="Service unavailable"/>
+  <int value="2" label="Service unavailable (deprecated)"/>
   <int value="3" label="Needs passphrase"/>
   <int value="4" label="Unrecoverable error"/>
+  <int value="5" label="Sync settings not confirmed"/>
+  <int value="6" label="Sync needs trusted vault key"/>
 </enum>
 
 <!-- This must be kept current with
@@ -69289,6 +69308,17 @@
   <int value="2" label="Not loaded on creation"/>
 </enum>
 
+<enum name="TabbedPaintPreviewCompositorFailureReason">
+  <int value="0" label="OK"/>
+  <int value="1" label="URL Mismatch"/>
+  <int value="2" label="Compositor Service Disconnected"/>
+  <int value="3" label="Compositor Client Disconnected"/>
+  <int value="4" label="Protobuf Deserialization Error"/>
+  <int value="5" label="Compositor Deserialization Error"/>
+  <int value="6" label="Invalid Root Frame SKP"/>
+  <int value="7" label="Invalid Request"/>
+</enum>
+
 <enum name="TabbedPaintPreviewExitCause">
   <int value="0" label="Pull to Refresh"/>
   <int value="1" label="Actionbar Action"/>
@@ -75038,6 +75068,20 @@
   <int value="13" label="android_asset or android_res"/>
 </enum>
 
+<enum name="WebViewUserAgentType">
+  <int value="0" label="Valid"/>
+  <int value="1" label="Invalid - contains one or more nulls"/>
+  <int value="2"
+      label="Invalid - but could be extra headers (with correct line endings)"/>
+  <int value="3"
+      label="Invalid - but could be extra headers (but with wrong line
+             endings)"/>
+  <int value="4"
+      label="Invalid - but could be attempting to terminate headers"/>
+  <int value="5"
+      label="Invalid - none of the above; random garbage or an unknown case"/>
+</enum>
+
 <enum name="WebViewVisibility">
   <int value="0" label="Visible"/>
   <int value="1" label="NotVisible"/>
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index f7c64bb..e1c874b 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -5317,6 +5317,17 @@
   </summary>
 </histogram>
 
+<histogram name="Android.WebView.ExtraHeaders.Valid" enum="BooleanValid"
+    expires_after="2021-01-14">
+  <owner>torne@chromium.org</owner>
+  <owner>src/android_webview/OWNERS</owner>
+  <summary>
+    Recorded when an app passes extra headers to
+    WebView.loadUrl(url,extra_headers). We check if the header names and values
+    are valid.
+  </summary>
+</histogram>
+
 <histogram name="Android.WebView.ExtraHeadersRedirect"
     enum="WebViewExtraHeadersRedirect" expires_after="2020-12-01">
   <owner>torne@chromium.org</owner>
@@ -5716,6 +5727,17 @@
   </summary>
 </histogram>
 
+<histogram name="Android.WebView.UserAgent.Valid" enum="WebViewUserAgentType"
+    expires_after="2021-01-14">
+  <owner>torne@chromium.org</owner>
+  <owner>src/android_webview/OWNERS</owner>
+  <summary>
+    Recorded when an app passes a custom user agent to
+    WebSettings.setUserAgentString. We check if the UA is valid, and if not we
+    try to interpret it in several ways and record which (if any) made sense.
+  </summary>
+</histogram>
+
 <histogram name="Android.WebView.VariationsEnableState"
     enum="AndroidWebViewVariationsEnableState" expires_after="M69">
   <obsolete>
@@ -7476,7 +7498,7 @@
 </histogram>
 
 <histogram name="Apps.AppListPlayStoreQueryState"
-    enum="AppListPlayStoreQueryState" expires_after="2020-10-25">
+    enum="AppListPlayStoreQueryState" expires_after="2021-02-21">
   <owner>hejq@chromium.org</owner>
   <summary>The state of a Play Store app search request.</summary>
 </histogram>
@@ -20955,7 +20977,7 @@
 </histogram>
 
 <histogram name="Blink.PrePaint.UpdateTime" units="microseconds"
-    expires_after="2020-12-20">
+    expires_after="2021-02-21">
 <!-- Name completed by histogram_suffixes name="BlinkUpdateTimePreFCPSuffixes" -->
 
 <!-- Name completed by histogram_suffixes name="BlinkUpdateTimePostFCPSuffixes" -->
@@ -24039,6 +24061,18 @@
   </summary>
 </histogram>
 
+<histogram name="Browser.PaintPreview.TabbedPlayer.CompositorFailureReason"
+    enum="TabbedPaintPreviewCompositorFailureReason" expires_after="2021-02-21">
+  <owner>ckitagawa@chromium.org</owner>
+  <owner>mahmoudi@chromium.org</owner>
+  <owner>fredmello@chromium.org</owner>
+  <summary>
+    Records the the reason for exiting the compositor process for the
+    TabbedPaintPreviewPlayer. Recorded when the compositor returns an error or
+    disconnects.
+  </summary>
+</histogram>
+
 <histogram name="Browser.PaintPreview.TabbedPlayer.ExitCause"
     enum="TabbedPaintPreviewExitCause" expires_after="2021-02-14">
   <owner>ckitagawa@chromium.org</owner>
@@ -28504,7 +28538,7 @@
 </histogram>
 
 <histogram name="Compositing.Browser.LayersUpdateTime" units="microseconds"
-    expires_after="2020-12-20">
+    expires_after="2021-02-21">
   <owner>schenney@chromium.org</owner>
   <owner>animations-dev@chromium.org</owner>
   <summary>
@@ -28520,7 +28554,7 @@
 </histogram>
 
 <histogram name="Compositing.Browser.LayerTreeImpl.CalculateDrawPropertiesUs"
-    units="microseconds" expires_after="2020-12-20">
+    units="microseconds" expires_after="2021-02-21">
   <owner>schenney@chromium.org</owner>
   <owner>paint-dev@chromium.org</owner>
   <summary>
@@ -36134,7 +36168,7 @@
 </histogram>
 
 <histogram name="Cryptohome.AsyncDBusRequest" units="ms"
-    expires_after="2020-12-20">
+    expires_after="2021-02-21">
   <owner>zuan@chromium.org</owner>
   <owner>cros-hwsec+uma@chromium.org</owner>
   <summary>
@@ -53205,7 +53239,7 @@
 </histogram>
 
 <histogram name="Event.Latency.ScrollBegin.Wheel.TimeToScrollUpdateSwapBegin4"
-    units="microseconds" expires_after="2020-12-20">
+    units="microseconds" expires_after="2021-02-21">
   <owner>tdresser@chromium.org</owner>
   <summary>
     Time between initial creation of a wheel event and the start of the frame
@@ -54055,7 +54089,7 @@
 </histogram>
 
 <histogram name="Event.Latency.ScrollUpdate.Wheel.TimeToScrollUpdateSwapBegin4"
-    units="microseconds" expires_after="2020-12-20">
+    units="microseconds" expires_after="2021-02-21">
   <owner>tdresser@chromium.org</owner>
   <summary>
     Time between initial creation of a wheel event and start of the frame swap
@@ -165354,6 +165388,35 @@
   </summary>
 </histogram>
 
+<histogram name="Signin.Intercept.AccountInfoFetchDuration" units="ms"
+    expires_after="2021-08-12">
+  <owner>alexilin@chromium.org</owner>
+  <owner>droger@chromium.org</owner>
+  <summary>
+    Records the duration of the account info fetch after signin interception.
+  </summary>
+</histogram>
+
+<histogram name="Signin.Intercept.HeuristicOutcome"
+    enum="SigninInterceptHeuristicOutcome" expires_after="2021-08-12">
+  <owner>droger@chromium.org</owner>
+  <owner>alexilin@chromium.org</owner>
+  <summary>
+    Records the outcome of the signin interception heuristic, which runs for
+    each signin interception.
+  </summary>
+</histogram>
+
+<histogram name="Signin.Intercept.ProfileCreationDuration" units="ms"
+    expires_after="2021-08-12">
+  <owner>alexilin@chromium.org</owner>
+  <owner>droger@chromium.org</owner>
+  <summary>
+    Records the duration of the signed-in profile creation after signin
+    interception.
+  </summary>
+</histogram>
+
 <histogram base="true" name="Signin.InterceptResult"
     enum="SigninInterceptResult" expires_after="2021-08-12">
 <!-- Name completed by histogram_suffixes name="SigninInterceptType" -->
@@ -176759,7 +176822,7 @@
 </histogram>
 
 <histogram base="true" name="Sync.ModelTypeConfigurationTime.Ephemeral"
-    units="ms" expires_after="2020-12-20">
+    units="ms" expires_after="2021-02-21">
   <owner>jkrcal@chromium.org</owner>
   <owner>mastiz@chromium.org</owner>
   <summary>
@@ -178111,10 +178174,9 @@
 </histogram>
 
 <histogram name="Sync.SyncErrorInfobarDisplayed" enum="SyncErrorInfobarTypes"
-    expires_after="2020-09-27">
-  <owner>droger@chromium.org</owner>
-  <owner>mastiz@chromium.org</owner>
-  <owner>treib@chromium.org</owner>
+    expires_after="2021-08-24">
+  <owner>fernandex@chromium.org</owner>
+  <owner>chrome-signin-team@google.com</owner>
   <summary>
     Enumeration of error conditions that displays an infobar to the user. iOS
     only.
@@ -184789,7 +184851,7 @@
 </histogram>
 
 <histogram name="UKM.UnsentLogs.DroppedSize" units="bytes"
-    expires_after="2020-12-20">
+    expires_after="2021-02-21">
   <owner>rkaplow@chromium.org</owner>
   <owner>ukm-team@google.com</owner>
   <summary>
@@ -193288,7 +193350,7 @@
 </histogram>
 
 <histogram name="WebFont.LocalFontUsed" enum="BooleanUsage"
-    expires_after="2020-12-20">
+    expires_after="2021-02-21">
   <owner>hajimehoshi@chromium.org</owner>
   <owner>kenjibaheux@chromium.org</owner>
   <owner>kouhei@chromium.org</owner>
@@ -194262,7 +194324,7 @@
 </histogram>
 
 <histogram name="WebRTC.Audio.ReceiverDeviceDelayMs" units="ms"
-    expires_after="2020-12-20">
+    expires_after="2021-02-21">
   <owner>hlundin@chromium.org</owner>
   <summary>
     The sound card's buffering delay for the receiving side. Sampled once every
@@ -194271,7 +194333,7 @@
 </histogram>
 
 <histogram name="WebRTC.Audio.ReceiverJitterBufferDelayMs" units="ms"
-    expires_after="2020-12-20">
+    expires_after="2021-02-21">
   <owner>hlundin@chromium.org</owner>
   <summary>
     The jitter buffer delay for the receiving side. Sampled once every 10 ms
@@ -194584,7 +194646,7 @@
 </histogram>
 
 <histogram name="WebRTC.Call.AudioBitrateReceivedInKbps" units="kbps"
-    expires_after="2020-12-20">
+    expires_after="2021-02-21">
   <owner>holmer@chromium.org</owner>
   <summary>
     Average audio bitrate received during a call, counted from first packet
@@ -194594,7 +194656,7 @@
 </histogram>
 
 <histogram name="WebRTC.Call.BitrateReceivedInKbps" units="kbps"
-    expires_after="2020-12-20">
+    expires_after="2021-02-21">
   <owner>holmer@chromium.org</owner>
   <summary>
     Average total bitrate received during a call (audio + video + RTCP), counted
@@ -194622,7 +194684,7 @@
 </histogram>
 
 <histogram name="WebRTC.Call.PacerBitrateInKbps" units="kbps"
-    expires_after="2020-12-20">
+    expires_after="2021-02-21">
   <owner>holmer@chromium.org</owner>
   <summary>
     Average pacer bitrate during a call, counted from first packet sent until
@@ -194632,7 +194694,7 @@
 </histogram>
 
 <histogram name="WebRTC.Call.RtcpBitrateReceivedInBps" units="bits/s"
-    expires_after="2020-12-20">
+    expires_after="2021-02-21">
   <owner>holmer@chromium.org</owner>
   <summary>
     Average RTCP bitrate received during a call, counted from first packet
@@ -194681,7 +194743,7 @@
 </histogram>
 
 <histogram name="WebRTC.Call.VideoBitrateReceivedInKbps" units="kbps"
-    expires_after="2020-12-20">
+    expires_after="2021-02-21">
   <owner>holmer@chromium.org</owner>
   <summary>
     Average video bitrate received during a call, counted from first packet
@@ -195452,7 +195514,7 @@
 </histogram>
 
 <histogram name="WebRTC.Video.AverageRoundTripTimeInMilliseconds" units="ms"
-    expires_after="2020-12-20">
+    expires_after="2021-02-21">
   <owner>holmer@chromium.org</owner>
   <summary>
     The average round-trip time of a WebRTC call in milliseconds. Recorded when
@@ -195461,7 +195523,7 @@
 </histogram>
 
 <histogram name="WebRTC.Video.AVSyncOffsetInMs" units="ms"
-    expires_after="2020-12-20">
+    expires_after="2021-02-21">
   <owner>asapersson@chromium.org</owner>
   <summary>
     The absolute value of the sync offset between a rendered video frame and the
@@ -195564,7 +195626,7 @@
 </histogram>
 
 <histogram name="WebRTC.Video.CurrentDelayInMs" units="ms"
-    expires_after="2020-12-20">
+    expires_after="2021-02-21">
   <owner>asapersson@chromium.org</owner>
   <summary>
     Average current delay for a received video stream. This is the actual delay
@@ -195838,7 +195900,7 @@
 </histogram>
 
 <histogram name="WebRTC.Video.InputHeightInPixels" units="pixels"
-    expires_after="2020-12-20">
+    expires_after="2021-02-21">
   <owner>asapersson@chromium.org</owner>
   <summary>
     The average input height per frame (for incoming frames to video engine) for
@@ -195847,7 +195909,7 @@
 </histogram>
 
 <histogram name="WebRTC.Video.InputWidthInPixels" units="pixels"
-    expires_after="2020-12-20">
+    expires_after="2021-02-21">
   <owner>asapersson@chromium.org</owner>
   <summary>
     The average input width per frame (for incoming frames to video engine) for
@@ -195886,7 +195948,7 @@
 </histogram>
 
 <histogram name="WebRTC.Video.JitterBufferDelayInMs" units="ms"
-    expires_after="2020-12-20">
+    expires_after="2021-02-21">
   <owner>asapersson@chromium.org</owner>
   <summary>
     Average jitter buffer delay for a received video stream. Recorded when a
@@ -195895,7 +195957,7 @@
 </histogram>
 
 <histogram name="WebRTC.Video.KeyFramesReceivedInPermille" units="permille"
-    expires_after="2020-12-20">
+    expires_after="2021-02-21">
   <owner>asapersson@chromium.org</owner>
   <summary>
     Permille of frames that are key frames for a received video stream. Recorded
@@ -196020,7 +196082,7 @@
 </histogram>
 
 <histogram name="WebRTC.Video.PaddingBitrateSentInKbps" units="kbps"
-    expires_after="2020-12-20">
+    expires_after="2021-02-21">
   <owner>asapersson@chromium.org</owner>
   <summary>
     The number of sent padding bits per second for a sent video stream. Recorded
@@ -196039,7 +196101,7 @@
 </histogram>
 
 <histogram name="WebRTC.Video.PliPacketsReceivedPerMinute"
-    units="packets/minute" expires_after="2020-12-20">
+    units="packets/minute" expires_after="2021-02-21">
   <owner>asapersson@chromium.org</owner>
   <summary>
     The number of received RTCP PLI packets per minute for a sent video stream.
@@ -196765,7 +196827,7 @@
 </histogram>
 
 <histogram name="WebRTC.Video.SentHeightInPixels" units="pixels"
-    expires_after="2020-12-20">
+    expires_after="2021-02-21">
   <owner>asapersson@chromium.org</owner>
   <summary>
     The average sent height per frame for a sent video stream. Recorded when a
@@ -196794,7 +196856,7 @@
 </histogram>
 
 <histogram name="WebRTC.Video.SentWidthInPixels" units="pixels"
-    expires_after="2020-12-20">
+    expires_after="2021-02-21">
   <owner>asapersson@chromium.org</owner>
   <summary>
     The average sent width per frame for a sent video stream. Recorded when a
@@ -196803,7 +196865,7 @@
 </histogram>
 
 <histogram name="WebRTC.Video.TargetDelayInMs" units="ms"
-    expires_after="2020-12-20">
+    expires_after="2021-02-21">
   <owner>asapersson@chromium.org</owner>
   <summary>
     Average target delay (jitter delay + decode time + render delay) for a
@@ -196832,7 +196894,7 @@
 </histogram>
 
 <histogram name="WebRTC.Video.UniqueNackRequestsReceivedInPercent" units="%"
-    expires_after="2020-12-20">
+    expires_after="2021-02-21">
   <owner>asapersson@chromium.org</owner>
   <summary>
     Percentage of unique RTCP NACK requests that are received in response to a
@@ -196841,7 +196903,7 @@
 </histogram>
 
 <histogram name="WebRTC.Video.UniqueNackRequestsSentInPercent" units="%"
-    expires_after="2020-12-20">
+    expires_after="2021-02-21">
   <owner>asapersson@chromium.org</owner>
   <summary>
     Percentage of unique RTCP NACK requests that are sent in response to a
diff --git a/tools/metrics/histograms/histograms_xml/UMA/histograms.xml b/tools/metrics/histograms/histograms_xml/UMA/histograms.xml
index 04dec668..ee2cf57 100644
--- a/tools/metrics/histograms/histograms_xml/UMA/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/UMA/histograms.xml
@@ -385,7 +385,7 @@
 </histogram>
 
 <histogram name="UMA.PersistentHistograms.InitResult"
-    enum="PersistentHistogramsInitResult" expires_after="2020-12-20">
+    enum="PersistentHistogramsInitResult" expires_after="2021-02-21">
   <owner>bcwhite@chromium.org</owner>
   <owner>src/base/metrics/OWNERS</owner>
   <summary>
diff --git a/tools/perf/core/perfetto_binary_roller/binary_deps.json b/tools/perf/core/perfetto_binary_roller/binary_deps.json
index 5999033e..8eed525 100644
--- a/tools/perf/core/perfetto_binary_roller/binary_deps.json
+++ b/tools/perf/core/perfetto_binary_roller/binary_deps.json
@@ -6,11 +6,11 @@
         },
         "mac": {
             "hash": "856218c5e01964b2e9d6b1643de0909de34f5e01",
-            "remote_path": "perfetto_binaries/trace_processor_shell/mac/cd8de1d295b4bedf5b0b00a6655575881df8696a/trace_processor_shell"
+            "remote_path": "perfetto_binaries/trace_processor_shell/mac/41db2fb64914bb8c4cd2261f8ecedf1ef3b28942/trace_processor_shell"
         },
         "linux": {
             "hash": "9898e7dd58b976adcc52b6b910d711f8ab130b3f",
-            "remote_path": "perfetto_binaries/trace_processor_shell/linux/cd8de1d295b4bedf5b0b00a6655575881df8696a/trace_processor_shell"
+            "remote_path": "perfetto_binaries/trace_processor_shell/linux/41db2fb64914bb8c4cd2261f8ecedf1ef3b28942/trace_processor_shell"
         }
     },
     "power_profile.sql": {
diff --git a/tools/win/IdleWakeups/README.md b/tools/win/IdleWakeups/README.md
index 841df0a..0bf16e1 100644
--- a/tools/win/IdleWakeups/README.md
+++ b/tools/win/IdleWakeups/README.md
@@ -10,12 +10,14 @@
 IdleWakeups.exe can then be found in src/tools/win/IdleWakeups/x64/Debug/.
 
 # Usage
-"IdleWakeups.exe" to match all Chrome processes.
-"IdleWakeups.exe Firefox" to match all Firefox processes.
-"IdleWakeups.exe msedge" to match all Edge processes.
+`IdleWakeups.exe` to match all Chrome processes.
+
+`IdleWakeups.exe Firefox` to match all Firefox processes.
+
+`IdleWakeups.exe msedge` to match all Edge processes.
 
 The process matching the provided parameter is identified by case-sensitive
-string prefix, e.g., "some_process" and "some_process.exe" would both work.
+string prefix, e.g., `some_process` and `some_process.exe` would both work.
 
 When the tool starts it begins gathering and aggregating CPU usage, private
 working set size, number of context switches / sec, and power usage for all
@@ -25,5 +27,5 @@
 CPU usage is normalized to one CPU core, with 100% meaning one CPU core is
 fully utilized.
 
-Intel Power Gadget is required to allow IdleWakeups tool to query power usage.
-https://software.intel.com/en-us/articles/intel-power-gadget-20
+[Intel Power Gadget](https://software.intel.com/en-us/articles/intel-power-gadget-20)
+is required to allow IdleWakeups tool to query power usage.
diff --git a/tools/xdisplaycheck/BUILD.gn b/tools/xdisplaycheck/BUILD.gn
deleted file mode 100644
index 1dc067c..0000000
--- a/tools/xdisplaycheck/BUILD.gn
+++ /dev/null
@@ -1,9 +0,0 @@
-# Copyright 2014 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-executable("xdisplaycheck") {
-  sources = [ "xdisplaycheck.cc" ]
-
-  configs += [ "//build/config/linux:x11" ]
-}
diff --git a/tools/xdisplaycheck/xdisplaycheck.cc b/tools/xdisplaycheck/xdisplaycheck.cc
deleted file mode 100644
index 2e901ee..0000000
--- a/tools/xdisplaycheck/xdisplaycheck.cc
+++ /dev/null
@@ -1,119 +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.
-//
-// This is a small program that tries to connect to the X server.  It
-// continually retries until it connects or 30 seconds pass.  If it fails
-// to connect to the X server or fails to find needed functiona, it returns
-// an error code of -1.
-//
-// This is to help verify that a useful X server is available before we start
-// start running tests on the build bots.
-
-#include <errno.h>
-#include <stdio.h>
-#include <string.h>
-#include <time.h>
-#include <X11/Xlib.h>
-
-#if defined(USE_AURA)
-#include <X11/extensions/XInput2.h>
-#endif
-
-void Sleep(int duration_ms) {
-  struct timespec sleep_time, remaining;
-
-  // Contains the portion of duration_ms >= 1 sec.
-  sleep_time.tv_sec = duration_ms / 1000;
-  duration_ms -= sleep_time.tv_sec * 1000;
-
-  // Contains the portion of duration_ms < 1 sec.
-  sleep_time.tv_nsec = duration_ms * 1000 * 1000;  // nanoseconds.
-
-  while (nanosleep(&sleep_time, &remaining) == -1 && errno == EINTR)
-    sleep_time = remaining;
-}
-
-class XScopedDisplay {
- public:
-  XScopedDisplay() : display_(nullptr) {}
-  ~XScopedDisplay() {
-    if (display_) XCloseDisplay(display_);
-  }
-
-  void set(Display* display) { display_ = display; }
-  Display* display() { return display_; }
-
- private:
-  Display* display_;
-};
-
-int main(int argc, char* argv[]) {
-  XScopedDisplay scoped_display;
-  if (argv[1] && strcmp(argv[1], "--noserver") == 0) {
-    scoped_display.set(XOpenDisplay(NULL));
-    if (scoped_display.display()) {
-      fprintf(stderr, "Found unexpected connectable display %s\n",
-              XDisplayName(NULL));
-    }
-    // Return success when we got an unexpected display so that the code
-    // without the --noserver is the same, but slow, rather than inverted.
-    return !scoped_display.display();
-  }
-
-  int kNumTries = 78;  // 78*77/2 * 10 = 30s of waiting
-  int tries;
-  for (tries = 0; tries < kNumTries; ++tries) {
-    scoped_display.set(XOpenDisplay(NULL));
-    if (scoped_display.display())
-      break;
-    Sleep(10 * tries);
-  }
-
-  if (!scoped_display.display()) {
-    fprintf(stderr, "Failed to connect to %s\n", XDisplayName(NULL));
-    return -1;
-  }
-
-  fprintf(stderr, "Connected after %d retries\n", tries);
-
-#if defined(USE_AURA)
-  // Check for XInput2
-  int opcode, event, err;
-  if (!XQueryExtension(scoped_display.display(), "XInputExtension", &opcode,
-                       &event, &err)) {
-    fprintf(stderr,
-        "Failed to get XInputExtension on %s.\n", XDisplayName(NULL));
-    return -2;
-  }
-
-  int major = 2, minor = 0;
-  if (XIQueryVersion(scoped_display.display(), &major, &minor) == BadRequest) {
-    fprintf(stderr,
-        "Server does not have XInput2 on %s.\n", XDisplayName(NULL));
-    return -3;
-  }
-
-  // Ask for the list of devices. This can cause some Xvfb to crash.
-  int count = 0;
-  XIDeviceInfo* devices =
-      XIQueryDevice(scoped_display.display(), XIAllDevices, &count);
-  if (devices)
-    XIFreeDeviceInfo(devices);
-
-  fprintf(stderr,
-      "XInput2 verified initially sane on %s.\n", XDisplayName(NULL));
-#endif
-  return 0;
-}
-
-#if defined(LEAK_SANITIZER)
-// XOpenDisplay leaks memory if it takes more than one try to connect. This
-// causes LSan bots to fail. We don't care about memory leaks in xdisplaycheck
-// anyway, so just disable LSan completely.
-// This function isn't referenced from the executable itself. Make sure it isn't
-// stripped by the linker.
-__attribute__((used))
-__attribute__((visibility("default")))
-extern "C" int __lsan_is_turned_off() { return 1; }
-#endif
diff --git a/ui/accessibility/ax_assistant_structure.cc b/ui/accessibility/ax_assistant_structure.cc
index a1b09d27..1d34198 100644
--- a/ui/accessibility/ax_assistant_structure.cc
+++ b/ui/accessibility/ax_assistant_structure.cc
@@ -459,7 +459,6 @@
       return kAXDialogClassname;
     case ax::mojom::Role::kRootWebArea:
       return has_parent ? kAXViewClassname : kAXWebViewClassname;
-    case ax::mojom::Role::kMenuButton:
     case ax::mojom::Role::kMenuItem:
     case ax::mojom::Role::kMenuItemCheckBox:
     case ax::mojom::Role::kMenuItemRadio:
diff --git a/ui/accessibility/ax_enum_util.cc b/ui/accessibility/ax_enum_util.cc
index f0f11cb..f17e077 100644
--- a/ui/accessibility/ax_enum_util.cc
+++ b/ui/accessibility/ax_enum_util.cc
@@ -518,8 +518,6 @@
       return "menu";
     case ax::mojom::Role::kMenuBar:
       return "menuBar";
-    case ax::mojom::Role::kMenuButton:
-      return "menuButton";
     case ax::mojom::Role::kMenuItem:
       return "menuItem";
     case ax::mojom::Role::kMenuItemCheckBox:
@@ -904,8 +902,6 @@
     return ax::mojom::Role::kMenu;
   if (0 == strcmp(role, "menuBar"))
     return ax::mojom::Role::kMenuBar;
-  if (0 == strcmp(role, "menuButton"))
-    return ax::mojom::Role::kMenuButton;
   if (0 == strcmp(role, "menuItem"))
     return ax::mojom::Role::kMenuItem;
   if (0 == strcmp(role, "menuItemCheckBox"))
diff --git a/ui/accessibility/ax_enums.mojom b/ui/accessibility/ax_enums.mojom
index 53b114e2..ff5def8f 100644
--- a/ui/accessibility/ax_enums.mojom
+++ b/ui/accessibility/ax_enums.mojom
@@ -242,7 +242,6 @@
   kMath,
   kMenu,
   kMenuBar,
-  kMenuButton, // TODO: kMenuButton is not used anywhere, consider removal.
   kMenuItem,
   kMenuItemCheckBox,
   kMenuItemRadio,
diff --git a/ui/accessibility/ax_node_data_unittest.cc b/ui/accessibility/ax_node_data_unittest.cc
index 492bf6a..90a96af 100644
--- a/ui/accessibility/ax_node_data_unittest.cc
+++ b/ui/accessibility/ax_node_data_unittest.cc
@@ -174,7 +174,6 @@
       ax::mojom::Role::kLink,
       ax::mojom::Role::kListBox,
       ax::mojom::Role::kListBoxOption,
-      ax::mojom::Role::kMenuButton,
       ax::mojom::Role::kMenuItem,
       ax::mojom::Role::kMenuItemCheckBox,
       ax::mojom::Role::kMenuItemRadio,
diff --git a/ui/accessibility/ax_role_properties.cc b/ui/accessibility/ax_role_properties.cc
index b9756d9..f776bb6 100644
--- a/ui/accessibility/ax_role_properties.cc
+++ b/ui/accessibility/ax_role_properties.cc
@@ -83,7 +83,6 @@
     case ax::mojom::Role::kLink:
     case ax::mojom::Role::kListBox:
     case ax::mojom::Role::kListBoxOption:
-    case ax::mojom::Role::kMenuButton:
     case ax::mojom::Role::kMenuItem:
     case ax::mojom::Role::kMenuItemCheckBox:
     case ax::mojom::Role::kMenuItemRadio:
@@ -152,7 +151,6 @@
     case ax::mojom::Role::kListGrid:
     case ax::mojom::Role::kMenu:
     case ax::mojom::Role::kMenuBar:
-    case ax::mojom::Role::kMenuButton:
     case ax::mojom::Role::kMenuItem:
     case ax::mojom::Role::kMenuItemCheckBox:
     case ax::mojom::Role::kMenuItemRadio:
@@ -381,7 +379,6 @@
   switch (role) {
     case ax::mojom::Role::kMenu:
     case ax::mojom::Role::kMenuBar:
-    case ax::mojom::Role::kMenuButton:
     case ax::mojom::Role::kMenuItem:
     case ax::mojom::Role::kMenuItemCheckBox:
     case ax::mojom::Role::kMenuItemRadio:
diff --git a/ui/accessibility/extensions/chromevoxclassic/cvox2/background/automation_predicate.js b/ui/accessibility/extensions/chromevoxclassic/cvox2/background/automation_predicate.js
index 312d926c..1ed1ca2 100644
--- a/ui/accessibility/extensions/chromevoxclassic/cvox2/background/automation_predicate.js
+++ b/ui/accessibility/extensions/chromevoxclassic/cvox2/background/automation_predicate.js
@@ -86,7 +86,6 @@
     case 'details':
     case 'disclosureTriangle':
     case 'form':
-    case 'menuButton':
     case 'menuListPopup':
     case 'popUpButton':
     case 'radioButton':
diff --git a/ui/accessibility/platform/atk_util_auralinux.cc b/ui/accessibility/platform/atk_util_auralinux.cc
index 3fd67575..9cfbdf8 100644
--- a/ui/accessibility/platform/atk_util_auralinux.cc
+++ b/ui/accessibility/platform/atk_util_auralinux.cc
@@ -5,6 +5,7 @@
 #include <atk/atk.h>
 #include <map>
 #include <memory>
+#include <set>
 #include <string>
 #include <utility>
 
@@ -53,7 +54,7 @@
   return *active_key_snoop_functions;
 }
 
-using AXPlatformNodeSet = std::unordered_set<ui::AXPlatformNodeAuraLinux*>;
+using AXPlatformNodeSet = std::set<ui::AXPlatformNodeAuraLinux*>;
 static AXPlatformNodeSet& GetNodesWithPostponedEvents() {
   static base::NoDestructor<AXPlatformNodeSet> nodes_with_postponed_events_list;
   return *nodes_with_postponed_events_list;
diff --git a/ui/accessibility/platform/ax_platform_node_auralinux.cc b/ui/accessibility/platform/ax_platform_node_auralinux.cc
index b1293b4c..fd8dfd2 100644
--- a/ui/accessibility/platform/ax_platform_node_auralinux.cc
+++ b/ui/accessibility/platform/ax_platform_node_auralinux.cc
@@ -2854,8 +2854,6 @@
       return ATK_ROLE_MARQUEE;
     case ax::mojom::Role::kMenu:
       return ATK_ROLE_MENU;
-    case ax::mojom::Role::kMenuButton:
-      return ATK_ROLE_MENU_ITEM;
     case ax::mojom::Role::kMenuBar:
       return ATK_ROLE_MENU_BAR;
     case ax::mojom::Role::kMenuItem:
diff --git a/ui/accessibility/platform/ax_platform_node_mac.mm b/ui/accessibility/platform/ax_platform_node_mac.mm
index 5bc70451..96d46d4 100644
--- a/ui/accessibility/platform/ax_platform_node_mac.mm
+++ b/ui/accessibility/platform/ax_platform_node_mac.mm
@@ -159,7 +159,6 @@
       {ax::mojom::Role::kMath, NSAccessibilityGroupRole},
       {ax::mojom::Role::kMenu, NSAccessibilityMenuRole},
       {ax::mojom::Role::kMenuBar, NSAccessibilityMenuBarRole},
-      {ax::mojom::Role::kMenuButton, NSAccessibilityMenuButtonRole},
       {ax::mojom::Role::kMenuItem, NSAccessibilityMenuItemRole},
       {ax::mojom::Role::kMenuItemCheckBox, NSAccessibilityMenuItemRole},
       {ax::mojom::Role::kMenuItemRadio, NSAccessibilityMenuItemRole},
diff --git a/ui/accessibility/platform/ax_platform_node_win.cc b/ui/accessibility/platform/ax_platform_node_win.cc
index 64e997a..5aec80f 100644
--- a/ui/accessibility/platform/ax_platform_node_win.cc
+++ b/ui/accessibility/platform/ax_platform_node_win.cc
@@ -5315,21 +5315,16 @@
     case ax::mojom::Role::kMenuBar:
       return ROLE_SYSTEM_MENUBAR;
 
-    case ax::mojom::Role::kMenuButton:
     case ax::mojom::Role::kMenuItem:
     case ax::mojom::Role::kMenuItemCheckBox:
     case ax::mojom::Role::kMenuItemRadio:
       return ROLE_SYSTEM_MENUITEM;
 
     case ax::mojom::Role::kMenuListPopup:
-      if (IsAncestorComboBox())
-        return ROLE_SYSTEM_LIST;
-      return ROLE_SYSTEM_MENUPOPUP;
+      return ROLE_SYSTEM_LIST;
 
     case ax::mojom::Role::kMenuListOption:
-      if (IsAncestorComboBox())
-        return ROLE_SYSTEM_LISTITEM;
-      return ROLE_SYSTEM_MENUITEM;
+      return ROLE_SYSTEM_LISTITEM;
 
     case ax::mojom::Role::kMeter:
       return ROLE_SYSTEM_PROGRESSBAR;
@@ -6143,7 +6138,6 @@
     case ax::mojom::Role::kMenuBar:
       return L"menubar";
 
-    case ax::mojom::Role::kMenuButton:
     case ax::mojom::Role::kMenuItem:
       return L"menuitem";
 
@@ -6154,14 +6148,10 @@
       return L"menuitemradio";
 
     case ax::mojom::Role::kMenuListPopup:
-      if (IsAncestorComboBox())
-        return L"list";
-      return L"menu";
+      return L"list";
 
     case ax::mojom::Role::kMenuListOption:
-      if (IsAncestorComboBox())
-        return L"listitem";
-      return L"menuitem";
+      return L"listitem";
 
     case ax::mojom::Role::kMeter:
       return L"progressbar";
@@ -6810,7 +6800,6 @@
     case ax::mojom::Role::kMenuBar:
       return UIA_MenuBarControlTypeId;
 
-    case ax::mojom::Role::kMenuButton:
     case ax::mojom::Role::kMenuItem:
       return UIA_MenuItemControlTypeId;
 
@@ -6821,14 +6810,10 @@
       return UIA_RadioButtonControlTypeId;
 
     case ax::mojom::Role::kMenuListPopup:
-      if (IsAncestorComboBox())
-        return UIA_ListControlTypeId;
-      return UIA_MenuControlTypeId;
+      return UIA_ListControlTypeId;
 
     case ax::mojom::Role::kMenuListOption:
-      if (IsAncestorComboBox())
-        return UIA_ListItemControlTypeId;
-      return UIA_MenuItemControlTypeId;
+      return UIA_ListItemControlTypeId;
 
     case ax::mojom::Role::kMeter:
       return UIA_ProgressBarControlTypeId;
@@ -7811,18 +7796,6 @@
   return S_OK;
 }
 
-// TODO(dmazzoni): Remove this function once combo box refactoring is
-// complete.
-bool AXPlatformNodeWin::IsAncestorComboBox() {
-  auto* parent =
-      static_cast<AXPlatformNodeWin*>(FromNativeViewAccessible(GetParent()));
-  if (!parent)
-    return false;
-  if (parent->MSAARole() == ROLE_SYSTEM_COMBOBOX)
-    return true;
-  return parent->IsAncestorComboBox();
-}
-
 bool AXPlatformNodeWin::IsPlaceholderText() const {
   if (GetData().role != ax::mojom::Role::kStaticText)
     return false;
diff --git a/ui/android/BUILD.gn b/ui/android/BUILD.gn
index ec14b8b8..696b1ad5 100644
--- a/ui/android/BUILD.gn
+++ b/ui/android/BUILD.gn
@@ -220,7 +220,7 @@
   ]
 }
 
-android_library("ui_full_java") {
+android_library("ui_no_recycler_view_java") {
   sources = [
     "java/src/org/chromium/ui/AsyncViewProvider.java",
     "java/src/org/chromium/ui/AsyncViewStub.java",
@@ -298,11 +298,7 @@
     "java/src/org/chromium/ui/modelutil/PropertyModel.java",
     "java/src/org/chromium/ui/modelutil/PropertyModelChangeProcessor.java",
     "java/src/org/chromium/ui/modelutil/PropertyObservable.java",
-    "java/src/org/chromium/ui/modelutil/RecyclerViewAdapter.java",
     "java/src/org/chromium/ui/modelutil/SimpleList.java",
-    "java/src/org/chromium/ui/modelutil/SimpleRecyclerViewAdapter.java",
-    "java/src/org/chromium/ui/modelutil/SimpleRecyclerViewMcp.java",
-    "java/src/org/chromium/ui/modelutil/SimpleRecyclerViewMcpBase.java",
     "java/src/org/chromium/ui/resources/HandleViewResources.java",
     "java/src/org/chromium/ui/resources/LayoutResource.java",
     "java/src/org/chromium/ui/resources/Resource.java",
@@ -349,11 +345,12 @@
     ":ui_utils_java",
     "//base:base_java",
     "//base:jni_java",
-    "//third_party/android_deps:android_support_v7_appcompat_java",
     "//third_party/android_deps:androidx_annotation_annotation_java",
+    "//third_party/android_deps:androidx_appcompat_appcompat_java",
     "//third_party/android_deps:androidx_appcompat_appcompat_resources_java",
     "//third_party/android_deps:androidx_asynclayoutinflater_asynclayoutinflater_java",
-    "//third_party/android_deps:androidx_recyclerview_recyclerview_java",
+    "//third_party/android_deps:androidx_core_core_java",
+    "//third_party/android_deps:androidx_vectordrawable_vectordrawable_animated_java",
     "//ui/base/cursor/mojom:cursor_type_java",
   ]
   annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ]
@@ -361,6 +358,28 @@
   resources_package = "org.chromium.ui"
 }
 
+android_library("ui_recycler_view_java") {
+  sources = [
+    "java/src/org/chromium/ui/modelutil/RecyclerViewAdapter.java",
+    "java/src/org/chromium/ui/modelutil/SimpleRecyclerViewAdapter.java",
+    "java/src/org/chromium/ui/modelutil/SimpleRecyclerViewMcp.java",
+    "java/src/org/chromium/ui/modelutil/SimpleRecyclerViewMcpBase.java",
+  ]
+  annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ]
+  deps = [
+    ":ui_no_recycler_view_java",
+    "//third_party/android_deps:androidx_annotation_annotation_java",
+    "//third_party/android_deps:androidx_recyclerview_recyclerview_java",
+  ]
+}
+
+java_group("ui_full_java") {
+  deps = [
+    ":ui_no_recycler_view_java",
+    ":ui_recycler_view_java",
+  ]
+}
+
 android_library("ui_java_test_support") {
   testonly = true
   sources = [
diff --git a/ui/android/java/src/org/chromium/ui/modelutil/ListModelChangeProcessor.java b/ui/android/java/src/org/chromium/ui/modelutil/ListModelChangeProcessor.java
index d0c5f126..1affc49 100644
--- a/ui/android/java/src/org/chromium/ui/modelutil/ListModelChangeProcessor.java
+++ b/ui/android/java/src/org/chromium/ui/modelutil/ListModelChangeProcessor.java
@@ -5,15 +5,14 @@
 package org.chromium.ui.modelutil;
 
 import androidx.annotation.Nullable;
-import androidx.recyclerview.widget.RecyclerView;
 
 /**
  * A model change processor for use with a {@link ListObservable} model. The
  * {@link ListModelChangeProcessor} should be registered as a list observer of the model.
  * Internally uses a view binder to bind model properties to a view like a TabLayout.
  *
- * Do not use this class to fill {@link RecyclerView}s - consider using the
- * {@link SimpleRecyclerViewMcp} which was specifically designed for that use case!
+ * Do not use this class to fill {@link androidx.recyclerview.widget.RecyclerView}s - consider using
+ * the {@link SimpleRecyclerViewMcp} which was specifically designed for that use case!
  *
  * @param <M> The {@link ListObservable} model.
  * @param <V> The view object that is changing.
@@ -65,4 +64,4 @@
         assert source == mModel;
         mViewBinder.onItemsChanged(mModel, mView, index, count);
     }
-}
\ No newline at end of file
+}
diff --git a/ui/base/cocoa/command_dispatcher.h b/ui/base/cocoa/command_dispatcher.h
index 1203c0a..c2e437dc 100644
--- a/ui/base/cocoa/command_dispatcher.h
+++ b/ui/base/cocoa/command_dispatcher.h
@@ -86,6 +86,11 @@
   // the event to be passed to the MainMenu, which will handle the key
   // equivalent.
   kPassToMainMenu,
+
+  // The CommandDispatcherDelegate determined the event should not be handled.
+  // This can occur when an event has been sent via key repeat that we've
+  // determined should not be triggered via repeat.
+  kDrop,
 };
 
 }  // namespace ui
diff --git a/ui/base/cocoa/command_dispatcher.mm b/ui/base/cocoa/command_dispatcher.mm
index 1ae7b17..c905b6be 100644
--- a/ui/base/cocoa/command_dispatcher.mm
+++ b/ui/base/cocoa/command_dispatcher.mm
@@ -130,7 +130,8 @@
   // First, give the delegate an opportunity to consume this event.
   ui::PerformKeyEquivalentResult result =
       [_delegate prePerformKeyEquivalent:event window:_owner];
-  if (result == ui::PerformKeyEquivalentResult::kHandled)
+  if (result == ui::PerformKeyEquivalentResult::kHandled ||
+      result == ui::PerformKeyEquivalentResult::kDrop)
     return YES;
   if (result == ui::PerformKeyEquivalentResult::kPassToMainMenu)
     return NO;
diff --git a/ui/base/models/simple_menu_model.h b/ui/base/models/simple_menu_model.h
index 5f1116b..3166989 100644
--- a/ui/base/models/simple_menu_model.h
+++ b/ui/base/models/simple_menu_model.h
@@ -27,6 +27,9 @@
 // The breadth of MenuModel is not exposed through this API.
 class COMPONENT_EXPORT(UI_BASE) SimpleMenuModel : public MenuModel {
  public:
+  // Default icon size to be used for context menus.
+  static constexpr int kDefaultIconSize = 16;
+
   class COMPONENT_EXPORT(UI_BASE) Delegate : public AcceleratorProvider {
    public:
     ~Delegate() override {}
diff --git a/ui/base/x/xwmstartupcheck/BUILD.gn b/ui/base/x/xwmstartupcheck/BUILD.gn
index debd8fa..50f34ca6 100644
--- a/ui/base/x/xwmstartupcheck/BUILD.gn
+++ b/ui/base/x/xwmstartupcheck/BUILD.gn
@@ -7,7 +7,10 @@
 
   sources = [ "xwmstartupcheck.cc" ]
 
-  deps = [ "//base" ]
+  deps = [
+    "//base",
+    "//ui/gfx/x",
+  ]
 
   configs += [ "//build/config/linux:x11" ]
 }
diff --git a/ui/base/x/xwmstartupcheck/xwmstartupcheck.cc b/ui/base/x/xwmstartupcheck/xwmstartupcheck.cc
index 09ef42f..3a18a8e 100644
--- a/ui/base/x/xwmstartupcheck/xwmstartupcheck.cc
+++ b/ui/base/x/xwmstartupcheck/xwmstartupcheck.cc
@@ -7,12 +7,15 @@
 // BEFORE the Wm starts.
 //
 
+#include <time.h>
+
 #include <cerrno>
 #include <cstdio>
 
-#include <time.h>
-
-#include <X11/Xlib.h>
+#include "base/command_line.h"
+#include "ui/gfx/x/connection.h"
+#include "ui/gfx/x/x11.h"
+#include "ui/gfx/x/xproto.h"
 
 void CalculateTimeout(const timespec& now,
                       const timespec& deadline,
@@ -24,53 +27,37 @@
   timeout->tv_sec = 0;
 }
 
-class XScopedDisplay {
- public:
-  explicit XScopedDisplay(Display* display) : display_(display) {}
-  ~XScopedDisplay() {
-    if (display_)
-      XCloseDisplay(display_);
-  }
-
-  Display* display() const { return display_; }
-
- private:
-  Display* const display_;
-};
-
 int main(int argc, char* argv[]) {
-  // Connects to a display specified in the current process' env value DISPLAY.
-  XScopedDisplay scoped_display(XOpenDisplay(nullptr));
+  base::CommandLine::Init(argc, argv);
+
+  // Connects to a X server specified in the current process' env value DISPLAY.
+  x11::Connection connection;
 
   // No display found - fail early.
-  if (!scoped_display.display()) {
-    fprintf(stderr, "Couldn't connect to a display.\n");
+  if (!connection.Ready()) {
+    fprintf(stderr, "Couldn't connect to the X11 server.\n");
     return 1;
   }
 
-  auto* xdisplay = scoped_display.display();
-
-  auto root_window = DefaultRootWindow(xdisplay);
-  if (!root_window) {
-    fprintf(stderr, "Couldn't find root window.\n");
-    return 1;
-  }
-
-  auto dummmy_window = XCreateSimpleWindow(
-      xdisplay, root_window, 0 /*x*/, 0 /*y*/, 1 /*width*/, 1 /*height*/,
-      0 /*border width*/, 0 /*border*/, 0 /*background*/);
-  if (!dummmy_window) {
+  auto dummy_window = connection.GenerateId<x11::Window>();
+  auto req = connection.CreateWindow({
+      .wid = dummy_window,
+      .parent = connection.default_root(),
+      .width = 1,
+      .height = 1,
+      // We are only interested in the ReparentNotify events that are sent
+      // whenever our dummy window is reparented because of a wm start.
+      .event_mask = x11::EventMask::StructureNotify,
+  });
+  if (req.Sync().error) {
     fprintf(stderr, "Couldn't create a dummy window.");
     return 1;
   }
 
-  XMapWindow(xdisplay, dummmy_window);
-  // We are only interested in the ReparentNotify events that are sent whenever
-  // our dummy window is reparented because of a wm start.
-  XSelectInput(xdisplay, dummmy_window, StructureNotifyMask);
-  XFlush(xdisplay);
+  connection.MapWindow({dummy_window});
+  connection.Flush();
 
-  int display_fd = ConnectionNumber(xdisplay);
+  int display_fd = ConnectionNumber(connection.display());
 
   // Set deadline as 30s.
   struct timespec now, deadline;
@@ -82,7 +69,6 @@
   struct timeval tv;
   CalculateTimeout(now, deadline, &tv);
 
-  XEvent ev;
   do {
     fd_set in_fds;
     FD_ZERO(&in_fds);
@@ -95,14 +81,14 @@
         break;
       }
     } else if (ret > 0) {
-      while (XPending(xdisplay)) {
-        XNextEvent(xdisplay, &ev);
+      connection.ReadResponses();
+      for (const auto& event : connection.events()) {
         // If we got ReparentNotify, a wm has started up and we can stop
         // execution.
-        if (ev.type == ReparentNotify) {
+        if (event.As<x11::ReparentNotifyEvent>())
           return 0;
-        }
       }
+      connection.events().clear();
     }
     // Calculate next timeout. If it's less or equal to 0, give up.
     clock_gettime(CLOCK_REALTIME, &now);
@@ -114,7 +100,7 @@
 
 #if defined(LEAK_SANITIZER)
 // XOpenDisplay leaks memory if it takes more than one try to connect. This
-// causes LSan bots to fail. We don't care about memory leaks in xdisplaycheck
+// causes LSan bots to fail. We don't care about memory leaks in xwmstartupcheck
 // anyway, so just disable LSan completely.
 // This function isn't referenced from the executable itself. Make sure it isn't
 // stripped by the linker.
diff --git a/ui/compositor/layer.cc b/ui/compositor/layer.cc
index 12f75f8..9434c6e7 100644
--- a/ui/compositor/layer.cc
+++ b/ui/compositor/layer.cc
@@ -273,6 +273,7 @@
   clone->SetMasksToBounds(GetMasksToBounds());
   clone->SetOpacity(GetTargetOpacity());
   clone->SetVisible(GetTargetVisibility());
+  clone->SetClipRect(GetTargetClipRect());
   clone->SetAcceptEvents(accept_events());
   clone->SetFillsBoundsOpaquely(fills_bounds_opaquely_);
   clone->SetFillsBoundsCompletely(fills_bounds_completely_);
@@ -497,6 +498,14 @@
   return cc_layer_->masks_to_bounds();
 }
 
+gfx::Rect Layer::GetTargetClipRect() const {
+  if (animator_ &&
+      animator_->IsAnimatingProperty(LayerAnimationElement::CLIP)) {
+    return animator_->GetTargetClipRect();
+  }
+  return clip_rect();
+}
+
 void Layer::SetClipRect(const gfx::Rect& clip_rect) {
   GetAnimator()->SetClipRect(clip_rect);
 }
diff --git a/ui/compositor/layer.h b/ui/compositor/layer.h
index b3f6b8bc..8290f3f 100644
--- a/ui/compositor/layer.h
+++ b/ui/compositor/layer.h
@@ -203,6 +203,7 @@
   // Sets/gets the clip rect for the layer. |clip_rect| is in layer space and
   // relative to |this| layer. Prefer SetMasksToBounds() to set the clip to the
   // bounds of |this| layer. This clips the subtree rooted at |this| layer.
+  gfx::Rect GetTargetClipRect() const;
   void SetClipRect(const gfx::Rect& clip_rect);
   gfx::Rect clip_rect() const { return cc_layer_->clip_rect(); }
 
diff --git a/ui/compositor/layer_unittest.cc b/ui/compositor/layer_unittest.cc
index 57c280b7..5e477daf 100644
--- a/ui/compositor/layer_unittest.cc
+++ b/ui/compositor/layer_unittest.cc
@@ -746,11 +746,14 @@
   transform.Scale(2, 1);
   transform.Translate(10, 5);
 
+  gfx::Rect clip_rect(1, 1, 2, 2);
+
   layer->SetTransform(transform);
   layer->SetColor(SK_ColorRED);
   layer->SetLayerInverted(true);
   layer->AddCacheRenderSurfaceRequest();
   layer->AddTrilinearFilteringRequest();
+  layer->SetClipRect(clip_rect);
   layer->SetRoundedCornerRadius({1, 2, 4, 5});
   layer->SetIsFastRoundedCorner(true);
 
@@ -767,12 +770,14 @@
   // Cloning should not preserve trilinear_filtering flag.
   EXPECT_NE(layer->cc_layer_for_testing()->trilinear_filtering(),
             clone->cc_layer_for_testing()->trilinear_filtering());
+  EXPECT_EQ(clip_rect, clone->clip_rect());
   EXPECT_EQ(layer->rounded_corner_radii(), clone->rounded_corner_radii());
   EXPECT_EQ(layer->is_fast_rounded_corner(), clone->is_fast_rounded_corner());
 
   layer->SetTransform(gfx::Transform());
   layer->SetColor(SK_ColorGREEN);
   layer->SetLayerInverted(false);
+  layer->SetClipRect(gfx::Rect(10, 10, 10, 10));
   layer->SetIsFastRoundedCorner(false);
   layer->SetRoundedCornerRadius({3, 6, 9, 12});
 
@@ -781,6 +786,7 @@
   EXPECT_EQ(SK_ColorRED, clone->background_color());
   EXPECT_EQ(SK_ColorRED, clone->GetTargetColor());
   EXPECT_TRUE(clone->layer_inverted());
+  EXPECT_EQ(clip_rect, clone->clip_rect());
   EXPECT_FALSE(layer->is_fast_rounded_corner());
   EXPECT_TRUE(clone->is_fast_rounded_corner());
   EXPECT_NE(layer->rounded_corner_radii(), clone->rounded_corner_radii());
diff --git a/ui/gl/gl_image_shared_memory_unittest.cc b/ui/gl/gl_image_shared_memory_unittest.cc
index 1208405..7df0ea3 100644
--- a/ui/gl/gl_image_shared_memory_unittest.cc
+++ b/ui/gl/gl_image_shared_memory_unittest.cc
@@ -51,7 +51,7 @@
     GLImageSharedMemoryTestDelegate<gfx::BufferFormat::RGBX_8888>,
     GLImageSharedMemoryTestDelegate<gfx::BufferFormat::RGBA_8888>,
     GLImageSharedMemoryTestDelegate<gfx::BufferFormat::BGRX_8888>,
-#if defined(OS_LINUX)
+#if defined(OS_LINUX) || defined(OS_CHROMEOS)
     // Fails on Win nVidia and linux android: the test writes nothing (we read
     // back the color used to clear the buffer).
     // TODO(mcasas): enable those paltforms https://crbug.com/803451.
diff --git a/ui/ozone/platform/scenic/ozone_platform_scenic.cc b/ui/ozone/platform/scenic/ozone_platform_scenic.cc
index ec639a6..b7c7dae 100644
--- a/ui/ozone/platform/scenic/ozone_platform_scenic.cc
+++ b/ui/ozone/platform/scenic/ozone_platform_scenic.cc
@@ -12,6 +12,7 @@
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
 #include "base/message_loop/message_pump_type.h"
+#include "base/no_destructor.h"
 #include "base/notreached.h"
 #include "base/task/current_thread.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
@@ -43,14 +44,6 @@
 
 namespace {
 
-constexpr OzonePlatform::PlatformProperties kScenicPlatformProperties{
-    .needs_view_token = true,
-    .custom_frame_pref_default = false,
-    .use_system_title_bar = false,
-    .message_pump_type_for_gpu = base::MessagePumpType::IO,
-    .supports_vulkan_swap_chain = true,
-};
-
 class ScenicPlatformEventSource : public ui::PlatformEventSource {
  public:
   ScenicPlatformEventSource() = default;
@@ -105,7 +98,17 @@
   }
 
   const PlatformProperties& GetPlatformProperties() override {
-    return kScenicPlatformProperties;
+    static base::NoDestructor<OzonePlatform::PlatformProperties> properties;
+    static bool initialised = false;
+    if (!initialised) {
+      properties->needs_view_token = true;
+      properties->message_pump_type_for_gpu = base::MessagePumpType::IO;
+      properties->supports_vulkan_swap_chain = true;
+
+      initialised = true;
+    }
+
+    return *properties;
   }
 
   std::unique_ptr<display::NativeDisplayDelegate> CreateNativeDisplayDelegate()
diff --git a/ui/ozone/platform/wayland/ozone_platform_wayland.cc b/ui/ozone/platform/wayland/ozone_platform_wayland.cc
index 8de40eb6..6d19339d 100644
--- a/ui/ozone/platform/wayland/ozone_platform_wayland.cc
+++ b/ui/ozone/platform/wayland/ozone_platform_wayland.cc
@@ -12,6 +12,7 @@
 #include "base/bind.h"
 #include "base/memory/ptr_util.h"
 #include "base/message_loop/message_pump_type.h"
+#include "base/no_destructor.h"
 #include "base/threading/sequenced_task_runner_handle.h"
 #include "ui/base/buildflags.h"
 #include "ui/base/cursor/cursor_factory.h"
@@ -63,23 +64,6 @@
 
 namespace {
 
-constexpr OzonePlatform::PlatformProperties kWaylandPlatformProperties = {
-    // Supporting server-side decorations requires a support of
-    // xdg-decorations. But this protocol has been accepted into the upstream
-    // recently, and it will take time before it is taken by compositors. For
-    // now, always use custom frames and disallow switching to server-side
-    // frames.
-    // https://github.com/wayland-project/wayland-protocols/commit/76d1ae8c65739eff3434ef219c58a913ad34e988
-    .custom_frame_pref_default = true,
-
-    // Wayland doesn't provide clients with global screen coordinates. Instead,
-    // it forces clients to position windows relative to their top level windows
-    // if the have child-parent relationship. In case of toplevel windows,
-    // clients simply don't know their position on screens and always assume
-    // they are located at some arbitrary position.
-    .ignore_screen_bounds_for_menus = true,
-};
-
 constexpr OzonePlatform::InitializedHostProperties
     kWaylandInitializedHostProperties = {
         /*supports_overlays=*/false,
@@ -232,7 +216,29 @@
   }
 
   const PlatformProperties& GetPlatformProperties() override {
-    return kWaylandPlatformProperties;
+    static base::NoDestructor<OzonePlatform::PlatformProperties> properties;
+    static bool initialised = false;
+    if (!initialised) {
+      // Supporting server-side decorations requires a support of
+      // xdg-decorations. But this protocol has been accepted into the upstream
+      // recently, and it will take time before it is taken by compositors. For
+      // now, always use custom frames and disallow switching to server-side
+      // frames.
+      // https://github.com/wayland-project/wayland-protocols/commit/76d1ae8c65739eff3434ef219c58a913ad34e988
+      properties->custom_frame_pref_default = true;
+
+      // Wayland doesn't provide clients with global screen coordinates.
+      // Instead, it forces clients to position windows relative to their top
+      // level windows if the have child-parent relationship. In case of
+      // toplevel windows, clients simply don't know their position on screens
+      // and always assume they are located at some arbitrary position.
+      properties->ignore_screen_bounds_for_menus = true;
+      properties->app_modal_dialogs_use_event_blocker = true;
+
+      initialised = true;
+    }
+
+    return *properties;
   }
 
   const InitializedHostProperties& GetInitializedHostProperties() override {
diff --git a/ui/ozone/platform/x11/ozone_platform_x11.cc b/ui/ozone/platform/x11/ozone_platform_x11.cc
index 182c556d..322a7cb 100644
--- a/ui/ozone/platform/x11/ozone_platform_x11.cc
+++ b/ui/ozone/platform/x11/ozone_platform_x11.cc
@@ -9,6 +9,7 @@
 
 #include "base/message_loop/message_pump_type.h"
 #include "base/metrics/histogram_functions.h"
+#include "base/no_destructor.h"
 #include "base/strings/utf_string_conversions.h"
 #include "build/build_config.h"
 #include "ui/base/buildflags.h"
@@ -64,30 +65,12 @@
 
 namespace {
 
-static OzonePlatform::PlatformProperties kX11PlatformProperties{
-    .needs_view_token = false,
-    .custom_frame_pref_default = false,
-    .use_system_title_bar = true,
-
-    // When the Ozone X11 backend is running, use a UI loop to grab Expose
-    // events. See GLSurfaceGLX and https://crbug.com/326995.
-    .message_pump_type_for_gpu = base::MessagePumpType::UI,
-    // When the Ozone X11 backend is running, use a UI loop to dispatch
-    // SHM completion events.
-    .message_pump_type_for_viz_compositor = base::MessagePumpType::UI,
-    .supports_vulkan_swap_chain = true,
-    .platform_shows_drag_image = false,
-    .supports_global_application_menus = true};
-
 // Singleton OzonePlatform implementation for X11 platform.
 class OzonePlatformX11 : public OzonePlatform,
                          public ui::OSExchangeDataProviderFactoryOzone {
  public:
   OzonePlatformX11() {
     SetInstance(this);
-
-    kX11PlatformProperties.custom_frame_pref_default =
-        ui::GetCustomFramePrefDefault();
   }
 
   ~OzonePlatformX11() override {}
@@ -170,7 +153,28 @@
   }
 
   const PlatformProperties& GetPlatformProperties() override {
-    return kX11PlatformProperties;
+    static base::NoDestructor<OzonePlatform::PlatformProperties> properties;
+    static bool initialised = false;
+    if (!initialised) {
+      properties->custom_frame_pref_default = ui::GetCustomFramePrefDefault();
+      properties->use_system_title_bar = true;
+
+      // When the Ozone X11 backend is running, use a UI loop to grab Expose
+      // events. See GLSurfaceGLX and https://crbug.com/326995.
+      properties->message_pump_type_for_gpu = base::MessagePumpType::UI;
+      // When the Ozone X11 backend is running, use a UI loop to dispatch
+      // SHM completion events.
+      properties->message_pump_type_for_viz_compositor =
+          base::MessagePumpType::UI;
+      properties->supports_vulkan_swap_chain = true;
+      properties->platform_shows_drag_image = false;
+      properties->supports_global_application_menus = true;
+      properties->app_modal_dialogs_use_event_blocker = true;
+
+      initialised = true;
+    }
+
+    return *properties;
   }
 
   void InitializeUI(const InitParams& params) override {
diff --git a/ui/ozone/public/ozone_platform.cc b/ui/ozone/public/ozone_platform.cc
index c7027b2..bcc577c5 100644
--- a/ui/ozone/public/ozone_platform.cc
+++ b/ui/ozone/public/ozone_platform.cc
@@ -36,6 +36,9 @@
 
 }  // namespace
 
+OzonePlatform::PlatformProperties::PlatformProperties() = default;
+OzonePlatform::PlatformProperties::~PlatformProperties() = default;
+
 OzonePlatform::OzonePlatform() {
   DCHECK(!g_instance) << "There should only be a single OzonePlatform.";
   g_instance = this;
diff --git a/ui/ozone/public/ozone_platform.h b/ui/ozone/public/ozone_platform.h
index 797c79d0..764945e 100644
--- a/ui/ozone/public/ozone_platform.h
+++ b/ui/ozone/public/ozone_platform.h
@@ -70,6 +70,11 @@
 
   // Struct used to indicate platform properties.
   struct PlatformProperties {
+    PlatformProperties();
+    PlatformProperties(const PlatformProperties& other) = delete;
+    PlatformProperties& operator=(const PlatformProperties& other) = delete;
+    ~PlatformProperties();
+
     // Fuchsia only: set to true when the platforms requires |view_token| field
     // in PlatformWindowInitProperties when creating a window.
     bool needs_view_token = false;
@@ -105,6 +110,10 @@
     // Linux only, but see a TODO in BrowserDesktopWindowTreeHostLinux.
     // Determines whether the platform supports the global application menu.
     bool supports_global_application_menus = false;
+
+    // Determines if the application modal dialogs should use the event blocker
+    // to allow the only browser window receiving UI events.
+    bool app_modal_dialogs_use_event_blocker = false;
   };
 
   // Properties available in the host process after initialization.
diff --git a/ui/views/BUILD.gn b/ui/views/BUILD.gn
index 97205b1..cf2df50 100644
--- a/ui/views/BUILD.gn
+++ b/ui/views/BUILD.gn
@@ -905,8 +905,6 @@
     "test/scoped_views_test_helper.h",
     "test/slider_test_api.cc",
     "test/slider_test_api.h",
-    "test/test_ax_event_observer.cc",
-    "test/test_ax_event_observer.h",
     "test/test_layout_manager.cc",
     "test/test_layout_manager.h",
     "test/test_layout_provider.cc",
diff --git a/ui/views/bubble/bubble_dialog_delegate_view_unittest.cc b/ui/views/bubble/bubble_dialog_delegate_view_unittest.cc
index 1a742163..8806f523 100644
--- a/ui/views/bubble/bubble_dialog_delegate_view_unittest.cc
+++ b/ui/views/bubble/bubble_dialog_delegate_view_unittest.cc
@@ -75,7 +75,12 @@
     return should_show_close_button_;
   }
 
-  void set_title_view(View* title_view) { title_view_.reset(title_view); }
+  template <typename T>
+  T* set_title_view(std::unique_ptr<T> title_view) {
+    T* const ret = title_view.get();
+    title_view_ = std::move(title_view);
+    return ret;
+  }
   void show_close_button() { should_show_close_button_ = true; }
   void hide_buttons() {
     should_show_close_button_ = false;
@@ -404,8 +409,8 @@
   TestBubbleDialogDelegateView* bubble_delegate =
       new TestBubbleDialogDelegateView(anchor_widget->GetContentsView());
   constexpr int kTitleHeight = 20;
-  View* title_view = new StaticSizedView(gfx::Size(10, kTitleHeight));
-  bubble_delegate->set_title_view(title_view);
+  View* title_view = bubble_delegate->set_title_view(
+      std::make_unique<StaticSizedView>(gfx::Size(10, kTitleHeight)));
   Widget* bubble_widget =
       BubbleDialogDelegateView::CreateBubble(bubble_delegate);
   bubble_widget->Show();
@@ -480,8 +485,9 @@
       CreateTestWidget(Widget::InitParams::TYPE_WINDOW);
   TestBubbleDialogDelegateView* bubble_delegate =
       new TestBubbleDialogDelegateView(anchor_widget->GetContentsView());
-  StyledLabel* title_view = new StyledLabel(base::ASCIIToUTF16("123"), nullptr);
-  bubble_delegate->set_title_view(title_view);
+  StyledLabel* title_view =
+      bubble_delegate->set_title_view(std::make_unique<StyledLabel>());
+  title_view->SetText(base::ASCIIToUTF16("123"));
 
   Widget* bubble_widget =
       BubbleDialogDelegateView::CreateBubble(bubble_delegate);
diff --git a/ui/views/controls/button/button_unittest.cc b/ui/views/controls/button/button_unittest.cc
index d57aa9a..b2e72b78 100644
--- a/ui/views/controls/button/button_unittest.cc
+++ b/ui/views/controls/button/button_unittest.cc
@@ -37,7 +37,7 @@
 #include "ui/views/controls/link.h"
 #include "ui/views/controls/textfield/textfield.h"
 #include "ui/views/style/platform_style.h"
-#include "ui/views/test/test_ax_event_observer.h"
+#include "ui/views/test/ax_event_counter.h"
 #include "ui/views/test/view_metadata_test_utils.h"
 #include "ui/views/test/views_test_base.h"
 #include "ui/views/widget/widget_utils.h"
@@ -962,10 +962,10 @@
 // Verifies setting the tooltip text will call NotifyAccessibilityEvent.
 TEST_F(ButtonTest, SetTooltipTextNotifiesAccessibilityEvent) {
   base::string16 test_tooltip_text = base::ASCIIToUTF16("Test Tooltip Text");
-  test::TestAXEventObserver observer;
-  EXPECT_EQ(0, observer.text_changed_event_count());
+  test::AXEventCounter counter(views::AXEventManager::Get());
+  EXPECT_EQ(0, counter.GetCount(ax::mojom::Event::kTextChanged));
   button()->SetTooltipText(test_tooltip_text);
-  EXPECT_EQ(1, observer.text_changed_event_count());
+  EXPECT_EQ(1, counter.GetCount(ax::mojom::Event::kTextChanged));
   EXPECT_EQ(test_tooltip_text, button()->GetTooltipText(gfx::Point()));
   ui::AXNodeData data;
   button()->GetAccessibleNodeData(&data);
diff --git a/ui/views/controls/button/checkbox_unittest.cc b/ui/views/controls/button/checkbox_unittest.cc
index bb554d2..2f9983a 100644
--- a/ui/views/controls/button/checkbox_unittest.cc
+++ b/ui/views/controls/button/checkbox_unittest.cc
@@ -52,7 +52,8 @@
 
 TEST_F(CheckboxTest, AccessibilityTest) {
   const base::string16 label_text = base::ASCIIToUTF16("Some label");
-  StyledLabel label(label_text, nullptr);
+  StyledLabel label;
+  label.SetText(label_text);
   checkbox()->SetAssociatedLabel(&label);
 
   ui::AXNodeData ax_data;
diff --git a/ui/views/controls/combobox/combobox_unittest.cc b/ui/views/controls/combobox/combobox_unittest.cc
index 1581b5f6..3737d9b 100644
--- a/ui/views/controls/combobox/combobox_unittest.cc
+++ b/ui/views/controls/combobox/combobox_unittest.cc
@@ -31,8 +31,8 @@
 #include "ui/events/types/event_type.h"
 #include "ui/views/controls/combobox/combobox_listener.h"
 #include "ui/views/style/platform_style.h"
+#include "ui/views/test/ax_event_counter.h"
 #include "ui/views/test/combobox_test_api.h"
-#include "ui/views/test/test_ax_event_observer.h"
 #include "ui/views/test/view_metadata_test_utils.h"
 #include "ui/views/test/views_test_base.h"
 #include "ui/views/widget/unique_widget_ptr.h"
@@ -830,10 +830,10 @@
 TEST_F(ComboboxTest, SetTooltipTextNotifiesAccessibilityEvent) {
   InitCombobox(nullptr);
   base::string16 test_tooltip_text = ASCIIToUTF16("Test Tooltip Text");
-  test::TestAXEventObserver observer;
-  EXPECT_EQ(0, observer.text_changed_event_count());
+  test::AXEventCounter counter(views::AXEventManager::Get());
+  EXPECT_EQ(0, counter.GetCount(ax::mojom::Event::kTextChanged));
   combobox_->SetTooltipText(test_tooltip_text);
-  EXPECT_EQ(1, observer.text_changed_event_count());
+  EXPECT_EQ(1, counter.GetCount(ax::mojom::Event::kTextChanged));
   EXPECT_EQ(test_tooltip_text, combobox_->GetAccessibleName());
   ui::AXNodeData data;
   combobox_->GetAccessibleNodeData(&data);
diff --git a/ui/views/controls/image_view_unittest.cc b/ui/views/controls/image_view_unittest.cc
index e5d9479..f7dfecc 100644
--- a/ui/views/controls/image_view_unittest.cc
+++ b/ui/views/controls/image_view_unittest.cc
@@ -20,7 +20,7 @@
 #include "ui/gfx/image/image_skia.h"
 #include "ui/views/border.h"
 #include "ui/views/layout/box_layout.h"
-#include "ui/views/test/test_ax_event_observer.h"
+#include "ui/views/test/ax_event_counter.h"
 #include "ui/views/test/views_test_base.h"
 #include "ui/views/widget/widget.h"
 
@@ -151,10 +151,10 @@
 // Verifies setting the accessible name will be call NotifyAccessibilityEvent.
 TEST_P(ImageViewTest, SetAccessibleNameNotifiesAccessibilityEvent) {
   base::string16 test_tooltip_text = base::ASCIIToUTF16("Test Tooltip Text");
-  test::TestAXEventObserver observer;
-  EXPECT_EQ(0, observer.text_changed_event_count());
+  test::AXEventCounter counter(views::AXEventManager::Get());
+  EXPECT_EQ(0, counter.GetCount(ax::mojom::Event::kTextChanged));
   image_view()->SetAccessibleName(test_tooltip_text);
-  EXPECT_EQ(1, observer.text_changed_event_count());
+  EXPECT_EQ(1, counter.GetCount(ax::mojom::Event::kTextChanged));
   EXPECT_EQ(test_tooltip_text, image_view()->GetAccessibleName());
   ui::AXNodeData data;
   image_view()->GetAccessibleNodeData(&data);
diff --git a/ui/views/controls/styled_label.cc b/ui/views/controls/styled_label.cc
index a494af8..96dfd7d 100644
--- a/ui/views/controls/styled_label.cc
+++ b/ui/views/controls/styled_label.cc
@@ -78,11 +78,7 @@
   std::vector<std::unique_ptr<View>> owned_views;
 };
 
-StyledLabel::StyledLabel(const base::string16& text,
-                         StyledLabelListener* listener)
-    : listener_(listener) {
-  base::TrimWhitespace(text, base::TRIM_TRAILING, &text_);
-}
+StyledLabel::StyledLabel(StyledLabelListener* listener) : listener_(listener) {}
 
 StyledLabel::~StyledLabel() = default;
 
@@ -90,7 +86,11 @@
   return text_;
 }
 
-void StyledLabel::SetText(const base::string16& text) {
+void StyledLabel::SetText(base::string16 text) {
+  // Failing to trim trailing whitespace will cause later confusion when the
+  // text elider tries to do so internally. There's no obvious reason to
+  // preserve trailing whitespace anyway.
+  base::TrimWhitespace(std::move(text), base::TRIM_TRAILING, &text);
   if (text_ == text)
     return;
 
diff --git a/ui/views/controls/styled_label.h b/ui/views/controls/styled_label.h
index b12461b..b87ca92 100644
--- a/ui/views/controls/styled_label.h
+++ b/ui/views/controls/styled_label.h
@@ -105,18 +105,18 @@
     // smaller width than this will force a recomputation.
     gfx::Size total_size;
 
-    // The sizes of each line of child views.  |size| can be computed directly
-    // from these values and is kept separately just for convenience.
+    // The sizes of each line of child views.  |total_size| can be computed
+    // directly from these values and is kept separately just for convenience.
     std::vector<gfx::Size> line_sizes;
   };
 
-  // Note that any trailing whitespace in |text| will be trimmed.
-  StyledLabel(const base::string16& text, StyledLabelListener* listener);
+  explicit StyledLabel(StyledLabelListener* listener = nullptr);
   ~StyledLabel() override;
 
-  // Sets the text to be displayed, and clears any previous styling.
+  // Sets the text to be displayed, and clears any previous styling.  Trailing
+  // whitespace is trimmed from the text.
   const base::string16& GetText() const;
-  void SetText(const base::string16& text);
+  void SetText(base::string16 text);
 
   // Returns the FontList that should be used. |style_info| is an optional
   // argument that takes precedence over the default values.
diff --git a/ui/views/controls/styled_label_unittest.cc b/ui/views/controls/styled_label_unittest.cc
index c42c1452..5f805656 100644
--- a/ui/views/controls/styled_label_unittest.cc
+++ b/ui/views/controls/styled_label_unittest.cc
@@ -54,7 +54,8 @@
   }
 
   void InitStyledLabel(const std::string& ascii_text) {
-    styled_ = std::make_unique<StyledLabel>(ASCIIToUTF16(ascii_text), this);
+    styled_ = std::make_unique<StyledLabel>(this);
+    styled_->SetText(ASCIIToUTF16(ascii_text));
     styled_->set_owned_by_client();
   }
 
@@ -330,7 +331,8 @@
 
   // Sanity check that |bold_text| with normal font style would fit on a single
   // line in a styled label with width |styled_width|.
-  StyledLabel unstyled(ASCIIToUTF16(bold_text), this);
+  StyledLabel unstyled(this);
+  unstyled.SetText(ASCIIToUTF16(bold_text));
   unstyled.SetBounds(0, 0, styled_width, pref_height);
   unstyled.Layout();
   EXPECT_EQ(1u, unstyled.children().size());
diff --git a/ui/views/controls/textfield/textfield_unittest.cc b/ui/views/controls/textfield/textfield_unittest.cc
index e331133..c3c9b52 100644
--- a/ui/views/controls/textfield/textfield_unittest.cc
+++ b/ui/views/controls/textfield/textfield_unittest.cc
@@ -51,7 +51,7 @@
 #include "ui/views/controls/textfield/textfield_test_api.h"
 #include "ui/views/focus/focus_manager.h"
 #include "ui/views/style/platform_style.h"
-#include "ui/views/test/test_ax_event_observer.h"
+#include "ui/views/test/ax_event_counter.h"
 #include "ui/views/test/test_views_delegate.h"
 #include "ui/views/test/views_test_base.h"
 #include "ui/views/test/widget_test.h"
@@ -3474,10 +3474,10 @@
 TEST_F(TextfieldTest, SetAccessibleNameNotifiesAccessibilityEvent) {
   InitTextfield();
   base::string16 test_tooltip_text = ASCIIToUTF16("Test Accessible Name");
-  test::TestAXEventObserver observer;
-  EXPECT_EQ(0, observer.text_changed_event_count());
+  test::AXEventCounter counter(views::AXEventManager::Get());
+  EXPECT_EQ(0, counter.GetCount(ax::mojom::Event::kTextChanged));
   textfield_->SetAccessibleName(test_tooltip_text);
-  EXPECT_EQ(1, observer.text_changed_event_count());
+  EXPECT_EQ(1, counter.GetCount(ax::mojom::Event::kTextChanged));
   EXPECT_EQ(test_tooltip_text, textfield_->GetAccessibleName());
   ui::AXNodeData data;
   textfield_->GetAccessibleNodeData(&data);
diff --git a/ui/views/metadata/metadata_impl_macros.h b/ui/views/metadata/metadata_impl_macros.h
index 1cf40b5..6a54f1ef 100644
--- a/ui/views/metadata/metadata_impl_macros.h
+++ b/ui/views/metadata/metadata_impl_macros.h
@@ -27,25 +27,25 @@
 // This will fail to compile if the property accessors aren't in the form of
 // SetXXXX and GetXXXX.
 #define ADD_PROPERTY_METADATA(class_name, property_type, property_name)        \
-  std::unique_ptr<METADATA_PROPERTY_TYPE_INTERNAL(class_name, property_type,   \
+  std::unique_ptr<METADATA_PROPERTY_TYPE_INTERNAL(property_type,               \
                                                   property_name)>              \
       property_name##_prop = std::make_unique<METADATA_PROPERTY_TYPE_INTERNAL( \
-          class_name, property_type, property_name)>();                        \
+          property_type, property_name)>();                                    \
   property_name##_prop->SetMemberName(#property_name);                         \
   property_name##_prop->SetMemberType(#property_type);                         \
   AddMemberData(std::move(property_name##_prop));
 
 // This will fail to compile if the property accessor isn't in the form of
 // GetXXXX.
-#define ADD_READONLY_PROPERTY_METADATA(class_name, property_type,    \
-                                       property_name)                \
-  std::unique_ptr<METADATA_READONLY_PROPERTY_TYPE_INTERNAL(          \
-      class_name, property_type, property_name)>                     \
-      property_name##_prop =                                         \
-          std::make_unique<METADATA_READONLY_PROPERTY_TYPE_INTERNAL( \
-              class_name, property_type, property_name)>();          \
-  property_name##_prop->SetMemberName(#property_name);               \
-  property_name##_prop->SetMemberType(#property_type);               \
+#define ADD_READONLY_PROPERTY_METADATA(class_name, property_type,          \
+                                       property_name)                      \
+  std::unique_ptr<METADATA_READONLY_PROPERTY_TYPE_INTERNAL(property_type,  \
+                                                           property_name)> \
+      property_name##_prop =                                               \
+          std::make_unique<METADATA_READONLY_PROPERTY_TYPE_INTERNAL(       \
+              property_type, property_name)>();                            \
+  property_name##_prop->SetMemberName(#property_name);                     \
+  property_name##_prop->SetMemberType(#property_type);                     \
   AddMemberData(std::move(property_name##_prop));
 
 #endif  // UI_VIEWS_METADATA_METADATA_IMPL_MACROS_H_
diff --git a/ui/views/metadata/metadata_macros_internal.h b/ui/views/metadata/metadata_macros_internal.h
index 59da36d..eb2b753f 100644
--- a/ui/views/metadata/metadata_macros_internal.h
+++ b/ui/views/metadata/metadata_macros_internal.h
@@ -37,32 +37,34 @@
   class METADATA_CLASS_NAME_INTERNAL(class_name)                         \
       : public views::metadata::ClassMetaData {                          \
    public:                                                               \
+    using ViewClass = class_name;                                        \
     explicit METADATA_CLASS_NAME_INTERNAL(class_name)()                  \
         : ClassMetaData(file, line) {                                    \
       BuildMetaData();                                                   \
     }                                                                    \
+    METADATA_CLASS_NAME_INTERNAL(class_name)                             \
+    (const METADATA_CLASS_NAME_INTERNAL(class_name) &) = delete;         \
+    METADATA_CLASS_NAME_INTERNAL(class_name) & operator=(                \
+        const METADATA_CLASS_NAME_INTERNAL(class_name) &) = delete;      \
                                                                          \
    private:                                                              \
     friend class class_name;                                             \
     virtual void BuildMetaData();                                        \
     static views::metadata::ClassMetaData* meta_data_ ALLOW_UNUSED_TYPE; \
-    DISALLOW_COPY_AND_ASSIGN(METADATA_CLASS_NAME_INTERNAL(class_name));  \
   }
 
-#define METADATA_PROPERTY_TYPE_INTERNAL(class_name, property_type,          \
-                                        property_name)                      \
-  views::metadata::ClassPropertyMetaData<                                   \
-      class_name, property_type, decltype(&class_name::Set##property_name), \
-      &class_name::Set##property_name,                                      \
-      decltype(std::declval<class_name>().Get##property_name()),            \
-      &class_name::Get##property_name>
+#define METADATA_PROPERTY_TYPE_INTERNAL(property_type, property_name)     \
+  views::metadata::ClassPropertyMetaData<                                 \
+      ViewClass, property_type, decltype(&ViewClass::Set##property_name), \
+      &ViewClass::Set##property_name,                                     \
+      decltype(std::declval<ViewClass>().Get##property_name()),           \
+      &ViewClass::Get##property_name>
 
-#define METADATA_READONLY_PROPERTY_TYPE_INTERNAL(class_name, property_type, \
-                                                 property_name)             \
-  views::metadata::ClassPropertyReadOnlyMetaData<                           \
-      class_name, property_type,                                            \
-      decltype(std::declval<class_name>().Get##property_name()),            \
-      &class_name::Get##property_name>
+#define METADATA_READONLY_PROPERTY_TYPE_INTERNAL(property_type, property_name) \
+  views::metadata::ClassPropertyReadOnlyMetaData<                              \
+      ViewClass, property_type,                                                \
+      decltype(std::declval<ViewClass>().Get##property_name()),                \
+      &ViewClass::Get##property_name>
 
 #define BEGIN_METADATA_INTERNAL(class_name)                                 \
   views::metadata::ClassMetaData* class_name::METADATA_CLASS_NAME_INTERNAL( \
diff --git a/ui/views/test/test_ax_event_observer.cc b/ui/views/test/test_ax_event_observer.cc
deleted file mode 100644
index 1fbaec0..0000000
--- a/ui/views/test/test_ax_event_observer.cc
+++ /dev/null
@@ -1,28 +0,0 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ui/views/test/test_ax_event_observer.h"
-
-#include "ui/accessibility/ax_enums.mojom.h"
-#include "ui/views/accessibility/ax_event_manager.h"
-#include "ui/views/accessibility/ax_event_observer.h"
-
-namespace views {
-namespace test {
-
-TestAXEventObserver::TestAXEventObserver() {
-  AXEventManager::Get()->AddObserver(this);
-}
-
-TestAXEventObserver::~TestAXEventObserver() {
-  AXEventManager::Get()->RemoveObserver(this);
-}
-
-void TestAXEventObserver::OnViewEvent(View* view, ax::mojom::Event event_type) {
-  if (event_type == ax::mojom::Event::kTextChanged)
-    ++text_changed_event_count_;
-}
-
-}  // namespace test
-}  // namespace views
diff --git a/ui/views/test/test_ax_event_observer.h b/ui/views/test/test_ax_event_observer.h
deleted file mode 100644
index 718974c..0000000
--- a/ui/views/test/test_ax_event_observer.h
+++ /dev/null
@@ -1,33 +0,0 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef UI_VIEWS_TEST_TEST_AX_EVENT_OBSERVER_H_
-#define UI_VIEWS_TEST_TEST_AX_EVENT_OBSERVER_H_
-
-#include "ui/views/accessibility/ax_event_observer.h"
-
-namespace views {
-namespace test {
-
-// Observes all Views accessibility events for tests.
-class TestAXEventObserver : public AXEventObserver {
- public:
-  TestAXEventObserver();
-  TestAXEventObserver(const TestAXEventObserver&) = delete;
-  TestAXEventObserver& operator=(const TestAXEventObserver&) = delete;
-  ~TestAXEventObserver() override;
-
-  // AXEventObserver:
-  void OnViewEvent(View* view, ax::mojom::Event event_type) override;
-
-  int text_changed_event_count() const { return text_changed_event_count_; }
-
- private:
-  int text_changed_event_count_ = 0;
-};
-
-}  // namespace test
-}  // namespace views
-
-#endif  // UI_VIEWS_TEST_TEST_AX_EVENT_OBSERVER_H_