diff --git a/DEPS b/DEPS
index 6e71afd0..9fbc698 100644
--- a/DEPS
+++ b/DEPS
@@ -167,11 +167,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': '5899485b9a87e868fe5d9e410956bd29d3158c06',
+  'skia_revision': '8a97436f7afdf92c9f26fbec34e508dcac324863',
   # 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': 'a3b4ceff6b8da6dacfe458af87cc6a8c91de5dde',
+  'v8_revision': '8fcfd612487a3773e1dfdbe0e6598089de096260',
   # 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.
@@ -179,7 +179,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ANGLE
   # and whatever else without interference from each other.
-  'angle_revision': 'e600ac2a03bbd6cd7e0cb8ef0706fd3b7b9a7912',
+  'angle_revision': 'd5735c1814f76d347d1915f7c4d61b03f497afdf',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
@@ -187,7 +187,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
-  'pdfium_revision': 'e5c62b37f645cec436a0b08cad7c9f185f5694f3',
+  'pdfium_revision': '74dbc8af75f9dc2490e498f3de57c84edb2fb4fe',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling BoringSSL
   # and whatever else without interference from each other.
@@ -230,7 +230,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling catapult
   # and whatever else without interference from each other.
-  'catapult_revision': 'f7b20a05decf39ce51b6728d773a023e2bc5759b',
+  'catapult_revision': 'fa588881c5b5986f6bd264277fcedf990f3c4c09',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -887,7 +887,7 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '1458d572f95002172a9335174cd48a3a67bcd079',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'b7a7f1c05edbe213fcd92f08b203c796d46d8912',
 
   'src/third_party/devtools-node-modules':
     Var('chromium_git') + '/external/github.com/ChromeDevTools/devtools-node-modules' + '@' + Var('devtools_node_modules_revision'),
@@ -1280,7 +1280,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' + '26f21f5e0ac0b6021c5abb92321d16d3454996bb',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' + '81425dcacf7414f7e5fdd66d2802256d0c61aedf',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '6f3e5028eb65d0b4c5fdd792106ac4c84eee1eb3',
@@ -1470,7 +1470,7 @@
     Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + '7c4e67ff117d6c640e6dd17989afe2fb7da7eecb',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + 'b4161d3c0d43e06d73c311e33d8fbed216fd233c',
+    Var('webrtc_git') + '/src.git' + '@' + '6e9395c6b7850c4f20edafe50f236e0cc7146345',
 
   'src/third_party/xdg-utils': {
       'url': Var('chromium_git') + '/chromium/deps/xdg-utils.git' + '@' + 'd80274d5869b17b8c9067a1022e4416ee7ed5e0d',
diff --git a/ash/assistant/assistant_controller.cc b/ash/assistant/assistant_controller.cc
index f87ee17..720fe456 100644
--- a/ash/assistant/assistant_controller.cc
+++ b/ash/assistant/assistant_controller.cc
@@ -32,13 +32,6 @@
 
 namespace ash {
 
-namespace {
-
-// Scheme of the Android intent url.
-constexpr char kAndroidIntentScheme[] = "intent";
-
-}  // namespace
-
 AssistantController::AssistantController() {
   assistant_state_controller_.AddObserver(this);
   chromeos::CrasAudioHandler::Get()->AddAudioObserver(this);
@@ -234,7 +227,7 @@
   }
 
   auto* android_helper = AndroidIntentHelper::GetInstance();
-  if (url.SchemeIs(kAndroidIntentScheme) && !android_helper) {
+  if (IsAndroidIntent(url) && !android_helper) {
     NOTREACHED();
     return;
   }
@@ -243,7 +236,7 @@
   // open the specified |url| in a new browser tab.
   NotifyOpeningUrl(url, in_background, from_server);
 
-  if (url.SchemeIs(kAndroidIntentScheme)) {
+  if (IsAndroidIntent(url)) {
     android_helper->LaunchAndroidIntent(url.spec());
   } else {
     // The new tab should be opened with a user activation since the user
diff --git a/ash/public/cpp/BUILD.gn b/ash/public/cpp/BUILD.gn
index 0820f3934..f8439da 100644
--- a/ash/public/cpp/BUILD.gn
+++ b/ash/public/cpp/BUILD.gn
@@ -308,6 +308,7 @@
 source_set("unit_tests") {
   testonly = true
   sources = [
+    "android_intent_helper_unittest.cc",
     "app_list/app_list_config_provider_unittest.cc",
     "default_scale_factor_retriever_unittest.cc",
     "pagination/pagination_model_unittest.cc",
diff --git a/ash/public/cpp/android_intent_helper.cc b/ash/public/cpp/android_intent_helper.cc
index e5dc099..d774526 100644
--- a/ash/public/cpp/android_intent_helper.cc
+++ b/ash/public/cpp/android_intent_helper.cc
@@ -7,8 +7,16 @@
 
 namespace ash {
 namespace {
+
 AndroidIntentHelper* g_android_intent_helper = nullptr;
-}
+
+// Scheme of the Android intent url.
+constexpr char kAndroidIntentScheme[] = "intent";
+
+// Prefix of the Android intent ref fragment.
+constexpr char kAndroidIntentPrefix[] = "Intent;";
+
+}  // namespace
 
 // static
 AndroidIntentHelper* AndroidIntentHelper::GetInstance() {
@@ -25,4 +33,10 @@
   g_android_intent_helper = nullptr;
 }
 
+bool IsAndroidIntent(const GURL& url) {
+  return url.SchemeIs(kAndroidIntentScheme) ||
+         base::StartsWith(url.ref(), kAndroidIntentPrefix,
+                          base::CompareCase::SENSITIVE);
+}
+
 }  // namespace ash
diff --git a/ash/public/cpp/android_intent_helper.h b/ash/public/cpp/android_intent_helper.h
index ee67731..b09b3c8 100644
--- a/ash/public/cpp/android_intent_helper.h
+++ b/ash/public/cpp/android_intent_helper.h
@@ -34,6 +34,8 @@
   DISALLOW_COPY_AND_ASSIGN(AndroidIntentHelper);
 };
 
+ASH_PUBLIC_EXPORT bool IsAndroidIntent(const GURL& url);
+
 }  // namespace ash
 
 #endif  // ASH_PUBLIC_CPP_ANDROID_INTENT_HELPER_H_
diff --git a/ash/public/cpp/android_intent_helper_unittest.cc b/ash/public/cpp/android_intent_helper_unittest.cc
new file mode 100644
index 0000000..6a98ba6
--- /dev/null
+++ b/ash/public/cpp/android_intent_helper_unittest.cc
@@ -0,0 +1,27 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ash/public/cpp/android_intent_helper.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace ash {
+
+using AndroidIntentHelperTest = testing::Test;
+
+TEST_F(AndroidIntentHelperTest, AndroidIntentURL) {
+  const std::string intent_url_type_1 = "intent://abc";
+  EXPECT_TRUE(IsAndroidIntent(GURL(intent_url_type_1)));
+
+  const std::string intent_url_type_2 =
+      "http://www.youtube.com/watch?v=abc;"
+      "#Intent;action=android.intent.action.VIEW;"
+      "package=com.google.android.youtube;end";
+  EXPECT_TRUE(IsAndroidIntent(GURL(intent_url_type_2)));
+
+  const std::string normal_url = "http://www.google.com";
+  EXPECT_FALSE(IsAndroidIntent(GURL(normal_url)));
+}
+
+}  // namespace ash
diff --git a/ash/public/cpp/shelf_config.h b/ash/public/cpp/shelf_config.h
index 696b18f..e6ff43d 100644
--- a/ash/public/cpp/shelf_config.h
+++ b/ash/public/cpp/shelf_config.h
@@ -137,6 +137,7 @@
   float shelf_tooltip_preview_min_ratio() const {
     return shelf_tooltip_preview_min_ratio_;
   }
+  int shelf_blur_radius() const { return shelf_blur_radius_; }
 
   // Gets the current color for the shelf control buttons.
   SkColor GetShelfControlButtonColor() const;
@@ -151,6 +152,9 @@
   // available.
   SkColor GetDefaultShelfColor() const;
 
+  // Returns the current blur radius to use for the control buttons.
+  int GetShelfControlButtonBlurRadius() const;
+
  private:
   friend class ShelfConfigTest;
 
@@ -220,6 +224,9 @@
   const float shelf_tooltip_preview_max_ratio_;
   const float shelf_tooltip_preview_min_ratio_;
 
+  // The blur radius used for the shelf.
+  const int shelf_blur_radius_;
+
   base::ObserverList<Observer> observers_;
 
   DISALLOW_COPY_AND_ASSIGN(ShelfConfig);
diff --git a/ash/shelf/hotseat_widget.cc b/ash/shelf/hotseat_widget.cc
index 86c8c23..493e26d 100644
--- a/ash/shelf/hotseat_widget.cc
+++ b/ash/shelf/hotseat_widget.cc
@@ -117,6 +117,8 @@
       scrollable_shelf_view_->GetHotseatBackgroundBounds();
   if (opaque_background_.bounds() != background_bounds)
     opaque_background_.SetBounds(background_bounds);
+
+  opaque_background_.SetBackgroundBlur(ShelfConfig::Get()->shelf_blur_radius());
 }
 
 void HotseatWidget::DelegateView::OnTabletModeChanged() {
diff --git a/ash/shelf/scrollable_shelf_view.cc b/ash/shelf/scrollable_shelf_view.cc
index a95be308..51964289 100644
--- a/ash/shelf/scrollable_shelf_view.cc
+++ b/ash/shelf/scrollable_shelf_view.cc
@@ -373,6 +373,10 @@
   return shelf_container_view_;
 }
 
+bool ScrollableShelfView::ShouldAdjustForTest() const {
+  return CalculateAdjustedOffset();
+}
+
 int ScrollableShelfView::CalculateScrollUpperBound() const {
   if (layout_strategy_ == kNotShowArrowButtons)
     return 0;
@@ -645,15 +649,16 @@
   shelf_container_view_->TranslateShelfView(total_offset);
 
   UpdateTappableIconIndices();
+
+  // Ensures that the app icons are shown correctly when the scrollable shelf
+  // is idle (not under scrolling or during animation process).
+  if (!during_scrolling_animation_ && scroll_status_ == kNotInScroll)
+    AdjustOffset();
 }
 
 void ScrollableShelfView::ChildPreferredSizeChanged(views::View* child) {
   // Add/remove a shelf icon may change the layout strategy.
   Layout();
-
-  // Ensures that the app icons are shown correctly when the layout strategy
-  // changes.
-  AdjustOffsetAfterScrolling();
 }
 
 void ScrollableShelfView::OnMouseEvent(ui::MouseEvent* event) {
@@ -891,7 +896,7 @@
   }
 
   if (event.type() == ui::ET_GESTURE_END) {
-    AdjustOffsetAfterScrolling();
+    AdjustOffset();
     return true;
   }
 
@@ -1207,22 +1212,13 @@
   return abs(main_axis_offset) > threshold;
 }
 
-void ScrollableShelfView::AdjustOffsetAfterScrolling() {
+void ScrollableShelfView::AdjustOffset() {
   // The type of scrolling offset is float to ensure that ScrollableShelfView
-  // is responsive to slow gesture scrolling. However, when scrolling ends, the
-  // scrolling offset should be floored.
+  // is responsive to slow gesture scrolling. However, after offset adjustment,
+  // the scrolling offset should be floored.
   scroll_offset_ = gfx::ToFlooredVector2d(scroll_offset_);
 
-  // Returns early when it does not need to adjust the shelf view's location.
-  const int scroll_distance = GetShelf()->IsHorizontalAlignment()
-                                  ? scroll_offset_.x()
-                                  : scroll_offset_.y();
-  if (scroll_distance >= CalculateScrollUpperBound())
-    return;
-
-  const int residue = GetActualScrollOffset() % GetUnit();
-  int offset =
-      residue > GetGestureDragThreshold() ? GetUnit() - residue : -residue;
+  const int offset = CalculateAdjustedOffset();
 
   // Returns early when it does not need to adjust the shelf view's location.
   if (!offset)
@@ -1234,4 +1230,19 @@
     ScrollByYOffset(offset, /*animate=*/true);
 }
 
+int ScrollableShelfView::CalculateAdjustedOffset() const {
+  // Returns early when it does not need to adjust the shelf view's location.
+  const int scroll_distance = GetShelf()->IsHorizontalAlignment()
+                                  ? scroll_offset_.x()
+                                  : scroll_offset_.y();
+  if (scroll_distance >= CalculateScrollUpperBound())
+    return 0;
+
+  const int remainder = GetActualScrollOffset() % GetUnit();
+  int offset = remainder > GetGestureDragThreshold() ? GetUnit() - remainder
+                                                     : -remainder;
+
+  return offset;
+}
+
 }  // namespace ash
diff --git a/ash/shelf/scrollable_shelf_view.h b/ash/shelf/scrollable_shelf_view.h
index 5b8922e..40894a3 100644
--- a/ash/shelf/scrollable_shelf_view.h
+++ b/ash/shelf/scrollable_shelf_view.h
@@ -69,6 +69,7 @@
   gfx::Rect GetHotseatBackgroundBounds();
 
   views::View* GetShelfContainerViewForTest();
+  bool ShouldAdjustForTest() const;
 
   ShelfView* shelf_view() { return shelf_view_; }
   ShelfContainerView* shelf_container_view() { return shelf_container_view_; }
@@ -249,8 +250,12 @@
   bool ShouldHandleScroll(const gfx::Vector2dF& offset,
                           bool is_gesture_fling) const;
 
-  // Ensures that the app icons are shown correctly when scrolling ends.
-  void AdjustOffsetAfterScrolling();
+  // Ensures that the app icons are shown correctly.
+  void AdjustOffset();
+
+  // Returns the offset by which the shelf view should be translated to ensure
+  // the correct UI.
+  int CalculateAdjustedOffset() const;
 
   LayoutStrategy layout_strategy_ = kNotShowArrowButtons;
 
diff --git a/ash/shelf/scrollable_shelf_view_unittest.cc b/ash/shelf/scrollable_shelf_view_unittest.cc
index 951ca412..319e2f52 100644
--- a/ash/shelf/scrollable_shelf_view_unittest.cc
+++ b/ash/shelf/scrollable_shelf_view_unittest.cc
@@ -77,9 +77,10 @@
   DISALLOW_COPY_AND_ASSIGN(ScrollableShelfViewTest);
 };
 
-// Verifies that the display rotation should not break the scrollable shelf's
-// UI behavior (https://crbug.com/1000764).
-TEST_F(ScrollableShelfViewTest, CorrectUIAfterDisplayRotation) {
+// Verifies that the display rotation from the short side to the long side
+// should not break the scrollable shelf's UI
+// behavior(https://crbug.com/1000764).
+TEST_F(ScrollableShelfViewTest, CorrectUIAfterDisplayRotationShortToLong) {
   // Changes the display setting in order that the display's height is greater
   // than the width.
   UpdateDisplay("600x800");
@@ -117,6 +118,7 @@
   // After rotation, checks the following things:
   // (1) The scrollable shelf has the correct layout strategy.
   // (2) The last app icon has the correct bounds.
+  // (3) The scrollable shelf does not need further adjustment.
   EXPECT_EQ(ScrollableShelfView::kShowLeftArrowButton,
             scrollable_shelf_view_->layout_strategy_for_test());
   views::ViewModel* view_model = shelf_view_->view_model();
@@ -126,6 +128,42 @@
   const gfx::Rect shelf_container_bounds =
       scrollable_shelf_view_->shelf_container_view()->GetBoundsInScreen();
   EXPECT_EQ(icon_bounds.right(), shelf_container_bounds.right());
+  EXPECT_FALSE(scrollable_shelf_view_->ShouldAdjustForTest());
+}
+
+// Verifies that the display rotation from the long side to the short side
+// should not break the scrollable shelf's UI behavior
+// (https://crbug.com/1000764).
+TEST_F(ScrollableShelfViewTest, CorrectUIAfterDisplayRotationLongToShort) {
+  // Changes the display setting in order that the display's width is greater
+  // than the height.
+  UpdateDisplay("600x300");
+
+  display::Display display = GetPrimaryDisplay();
+  AddAppShortcutUntilOverflow();
+
+  // Presses the right arrow until reaching the last page of shelf icons.
+  const views::View* right_arrow = scrollable_shelf_view_->right_arrow();
+  const gfx::Point center_point =
+      right_arrow->GetBoundsInScreen().CenterPoint();
+  while (right_arrow->GetVisible()) {
+    GetEventGenerator()->MoveMouseTo(center_point);
+    GetEventGenerator()->PressLeftButton();
+    GetEventGenerator()->ReleaseLeftButton();
+  }
+  ASSERT_EQ(ScrollableShelfView::kShowLeftArrowButton,
+            scrollable_shelf_view_->layout_strategy_for_test());
+
+  // Rotates the display by 90 degrees. In order to reproduce the bug,
+  // both arrow buttons should show after rotation.
+  display::DisplayManager* display_manager = Shell::Get()->display_manager();
+  display_manager->SetDisplayRotation(display.id(), display::Display::ROTATE_90,
+                                      display::Display::RotationSource::ACTIVE);
+  ASSERT_EQ(ScrollableShelfView::kShowButtons,
+            scrollable_shelf_view_->layout_strategy_for_test());
+
+  // Verifies that the scrollable shelf does not need further adjustment.
+  EXPECT_FALSE(scrollable_shelf_view_->ShouldAdjustForTest());
 }
 
 // When hovering mouse on a shelf icon, the tooltip only shows for the visible
diff --git a/ash/shelf/shelf_config.cc b/ash/shelf/shelf_config.cc
index 5714a92..b50e95f 100644
--- a/ash/shelf/shelf_config.cc
+++ b/ash/shelf/shelf_config.cc
@@ -55,8 +55,9 @@
       status_indicator_offset_from_shelf_edge_(1),
       shelf_tooltip_preview_height_(128),
       shelf_tooltip_preview_max_width_(192),
-      shelf_tooltip_preview_max_ratio_(1.5),     // = 3/2
-      shelf_tooltip_preview_min_ratio_(0.666) {  // = 2/3
+      shelf_tooltip_preview_max_ratio_(1.5),    // = 3/2
+      shelf_tooltip_preview_min_ratio_(0.666),  // = 2/3
+      shelf_blur_radius_(30) {
   UpdateIsDense();
 }
 
@@ -250,8 +251,7 @@
 }
 
 SkColor ShelfConfig::GetDefaultShelfColor() const {
-  if (!features::IsBackgroundBlurEnabled() ||
-      chromeos::switches::ShouldShowShelfHotseat()) {
+  if (!features::IsBackgroundBlurEnabled()) {
     return AshColorProvider::Get()->GetBaseLayerColor(
         AshColorProvider::BaseLayerType::kTransparentWithoutBlur,
         AshColorProvider::AshColorMode::kDark);
@@ -279,6 +279,15 @@
   return SkColorSetA(final_color, 189);  // 74% opacity
 }
 
+int ShelfConfig::GetShelfControlButtonBlurRadius() const {
+  if (features::IsBackgroundBlurEnabled() &&
+      chromeos::switches::ShouldShowShelfHotseat() && IsTabletMode() &&
+      !is_in_app()) {
+    return shelf_blur_radius_;
+  }
+  return 0;
+}
+
 void ShelfConfig::OnShelfConfigUpdated() {
   for (auto& observer : observers_)
     observer.OnShelfConfigUpdated();
diff --git a/ash/shelf/shelf_navigation_widget.cc b/ash/shelf/shelf_navigation_widget.cc
index 963be58d..04a2626 100644
--- a/ash/shelf/shelf_navigation_widget.cc
+++ b/ash/shelf/shelf_navigation_widget.cc
@@ -14,6 +14,7 @@
 #include "ash/strings/grit/ash_strings.h"
 #include "ash/system/status_area_widget.h"
 #include "ash/wm/tablet_mode/tablet_mode_controller.h"
+#include "chromeos/constants/chromeos_switches.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/compositor/scoped_layer_animation_settings.h"
 #include "ui/views/animation/bounds_animator.h"
@@ -57,9 +58,16 @@
   Delegate(Shelf* shelf, ShelfView* shelf_view);
   ~Delegate() override;
 
+  // Initializes the view.
+  void Init(ui::Layer* parent_layer);
+
+  void UpdateOpaqueBackground();
+
   // views::View:
   FocusTraversable* GetPaneFocusTraversable() override;
   void GetAccessibleNodeData(ui::AXNodeData* node_data) override;
+  void ReorderChildLayers(ui::Layer* parent_layer) override;
+  void OnBoundsChanged(const gfx::Rect& old_bounds) override;
 
   // views::AccessiblePaneView:
   View* GetDefaultFocusableChild() override;
@@ -77,16 +85,22 @@
   }
 
  private:
+  void SetParentLayer(ui::Layer* layer);
+
   BackButton* back_button_ = nullptr;
   HomeButton* home_button_ = nullptr;
   // When true, the default focus of the navigation widget is the last
   // focusable child.
   bool default_last_focusable_child_ = false;
 
+  // A background layer that may be visible depending on shelf state.
+  ui::Layer opaque_background_;
+
   DISALLOW_COPY_AND_ASSIGN(Delegate);
 };
 
-ShelfNavigationWidget::Delegate::Delegate(Shelf* shelf, ShelfView* shelf_view) {
+ShelfNavigationWidget::Delegate::Delegate(Shelf* shelf, ShelfView* shelf_view)
+    : opaque_background_(ui::LAYER_SOLID_COLOR) {
   set_allow_deactivate_on_esc(true);
 
   const int control_size = ShelfConfig::Get()->control_size();
@@ -108,6 +122,31 @@
 
 ShelfNavigationWidget::Delegate::~Delegate() = default;
 
+void ShelfNavigationWidget::Delegate::Init(ui::Layer* parent_layer) {
+  SetParentLayer(parent_layer);
+  UpdateOpaqueBackground();
+}
+
+void ShelfNavigationWidget::Delegate::UpdateOpaqueBackground() {
+  opaque_background_.SetColor(ShelfConfig::Get()->GetShelfControlButtonColor());
+
+  if (chromeos::switches::ShouldShowShelfHotseat() && IsTabletMode() &&
+      ShelfConfig::Get()->is_in_app()) {
+    opaque_background_.SetVisible(false);
+    return;
+  }
+  opaque_background_.SetVisible(true);
+
+  int radius = ShelfConfig::Get()->control_border_radius();
+  gfx::RoundedCornersF rounded_corners = {radius, radius, radius, radius};
+  if (opaque_background_.rounded_corner_radii() != rounded_corners)
+    opaque_background_.SetRoundedCornerRadius(rounded_corners);
+
+  opaque_background_.SetBounds(GetLocalBounds());
+  opaque_background_.SetBackgroundBlur(
+      ShelfConfig::Get()->GetShelfControlButtonBlurRadius());
+}
+
 bool ShelfNavigationWidget::Delegate::CanActivate() const {
   // We don't want mouse clicks to activate us, but we need to allow
   // activation when the user is using the keyboard (FocusCycler).
@@ -125,11 +164,27 @@
   node_data->SetName(l10n_util::GetStringUTF8(IDS_ASH_SHELF_ACCESSIBLE_NAME));
 }
 
+void ShelfNavigationWidget::Delegate::ReorderChildLayers(
+    ui::Layer* parent_layer) {
+  views::View::ReorderChildLayers(parent_layer);
+  parent_layer->StackAtBottom(&opaque_background_);
+}
+
+void ShelfNavigationWidget::Delegate::OnBoundsChanged(
+    const gfx::Rect& old_bounds) {
+  UpdateOpaqueBackground();
+}
+
 views::View* ShelfNavigationWidget::Delegate::GetDefaultFocusableChild() {
   return default_last_focusable_child_ ? GetLastFocusableChild()
                                        : GetFirstFocusableChild();
 }
 
+void ShelfNavigationWidget::Delegate::SetParentLayer(ui::Layer* layer) {
+  layer->Add(&opaque_background_);
+  ReorderLayers();
+}
+
 ShelfNavigationWidget::ShelfNavigationWidget(Shelf* shelf,
                                              ShelfView* shelf_view)
     : shelf_(shelf),
@@ -160,6 +215,7 @@
   params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
   params.parent = container;
   Init(std::move(params));
+  delegate_->Init(GetLayer());
   set_focus_on_creation(false);
   GetFocusManager()->set_arrow_key_traversal_enabled_for_widget(true);
   SetContentsView(delegate_);
@@ -247,9 +303,7 @@
       tablet_mode ? GetSecondButtonBounds() : GetFirstButtonBounds());
   GetBackButton()->SetBoundsRect(GetFirstButtonBounds());
 
-  delegate_->SetBackground(views::CreateRoundedRectBackground(
-      ShelfConfig::Get()->GetShelfControlButtonColor(),
-      ShelfConfig::Get()->control_border_radius()));
+  delegate_->UpdateOpaqueBackground();
 }
 
 }  // namespace ash
diff --git a/ash/system/tray/tray_background_view.cc b/ash/system/tray/tray_background_view.cc
index 733f1b51..b9cb836 100644
--- a/ash/system/tray/tray_background_view.cc
+++ b/ash/system/tray/tray_background_view.cc
@@ -115,34 +115,6 @@
   DISALLOW_COPY_AND_ASSIGN(TrayWidgetObserver);
 };
 
-class TrayBackground : public views::Background {
- public:
-  explicit TrayBackground(TrayBackgroundView* tray_background_view)
-      : tray_background_view_(tray_background_view) {}
-
-  ~TrayBackground() override = default;
-
- private:
-  // Overridden from views::Background.
-  void Paint(gfx::Canvas* canvas, views::View* view) const override {
-    gfx::ScopedCanvas scoped_canvas(canvas);
-    cc::PaintFlags background_flags;
-    background_flags.setAntiAlias(true);
-    background_flags.setColor(ShelfConfig::Get()->GetShelfControlButtonColor());
-
-    gfx::Rect bounds = tray_background_view_->GetBackgroundBounds();
-    const float dsf = canvas->UndoDeviceScaleFactor();
-    canvas->DrawRoundRect(gfx::ScaleToRoundedRect(bounds, dsf),
-                          ShelfConfig::Get()->control_border_radius() * dsf,
-                          background_flags);
-  }
-
-  // Reference to the TrayBackgroundView for which this is a background.
-  TrayBackgroundView* tray_background_view_;
-
-  DISALLOW_COPY_AND_ASSIGN(TrayBackground);
-};
-
 ////////////////////////////////////////////////////////////////////////////////
 // TrayBackgroundView
 
@@ -151,7 +123,6 @@
     : ActionableView(TrayPopupInkDropStyle::FILL_BOUNDS),
       shelf_(shelf),
       tray_container_(new TrayContainer(shelf)),
-      background_(new TrayBackground(this)),
       is_active_(false),
       separator_visible_(true),
       visible_preferred_(false),
@@ -164,7 +135,6 @@
       ShelfConfig::Get()->shelf_ink_drop_visible_opacity());
 
   SetLayoutManager(std::make_unique<views::FillLayout>());
-  SetBackground(std::unique_ptr<views::Background>(background_));
   SetInstallFocusRingOnFocus(true);
   focus_ring()->SetColor(ShelfConfig::Get()->shelf_focus_border_color());
   SetFocusPainter(nullptr);
@@ -173,8 +143,13 @@
 
   tray_event_filter_.reset(new TrayEventFilter);
 
-  SetPaintToLayer();
+  // Use layer color to provide background color. Note that children views
+  // need to have their own layers to be visible.
+  SetPaintToLayer(ui::LAYER_SOLID_COLOR);
   layer()->SetFillsBoundsOpaquely(false);
+
+  UpdateBackground();
+
   // Start the tray items not visible, because visibility changes are animated.
   views::View::SetVisible(false);
 }
@@ -444,6 +419,9 @@
   path->addRoundRect(gfx::RectToSkRect(GetBackgroundBounds()), border_radius,
                      border_radius);
   SetProperty(views::kHighlightPathKey, path.release());
+
+  UpdateBackground();
+
   // Bypass ActionableView::OnBoundsChanged which sets its own highlight path.
   Button::OnBoundsChanged(previous_bounds);
 }
@@ -485,4 +463,14 @@
   return insets;
 }
 
+void TrayBackgroundView::UpdateBackground() {
+  const int radius = ShelfConfig::Get()->control_border_radius();
+  gfx::RoundedCornersF rounded_corners = {radius, radius, radius, radius};
+  layer()->SetRoundedCornerRadius(rounded_corners);
+  layer()->SetBackgroundBlur(
+      ShelfConfig::Get()->GetShelfControlButtonBlurRadius());
+  layer()->SetColor(ShelfConfig::Get()->GetShelfControlButtonColor());
+  layer()->SetClipRect(GetBackgroundBounds());
+}
+
 }  // namespace ash
diff --git a/ash/system/tray/tray_background_view.h b/ash/system/tray/tray_background_view.h
index e020080..48d5ba4 100644
--- a/ash/system/tray/tray_background_view.h
+++ b/ash/system/tray/tray_background_view.h
@@ -18,7 +18,6 @@
 
 namespace ash {
 class Shelf;
-class TrayBackground;
 class TrayContainer;
 class TrayEventFilter;
 
@@ -148,6 +147,8 @@
   // Helper function that calculates background insets relative to local bounds.
   gfx::Insets GetBackgroundInsets() const;
 
+  // Updates the background layer.
+  void UpdateBackground();
 
   // The shelf containing the system tray for this view.
   Shelf* shelf_;
@@ -155,9 +156,6 @@
   // Convenience pointer to the contents view.
   TrayContainer* tray_container_;
 
-  // Owned by the view passed to SetContents().
-  TrayBackground* background_;
-
   // Determines if the view is active. This changes how  the ink drop ripples
   // behave.
   bool is_active_;
diff --git a/ash/system/tray/tray_container.cc b/ash/system/tray/tray_container.cc
index ee69963..de61a44 100644
--- a/ash/system/tray/tray_container.cc
+++ b/ash/system/tray/tray_container.cc
@@ -20,6 +20,8 @@
 
   ShelfConfig::Get()->AddObserver(this);
 
+  SetPaintToLayer();
+  layer()->SetFillsBoundsOpaquely(false);
   UpdateLayout();
 }
 
diff --git a/ash/wm/back_gesture_affordance.cc b/ash/wm/back_gesture_affordance.cc
index 7bfc528..9cd824f 100644
--- a/ash/wm/back_gesture_affordance.cc
+++ b/ash/wm/back_gesture_affordance.cc
@@ -4,7 +4,10 @@
 
 #include "ash/wm/back_gesture_affordance.h"
 
+#include "ash/public/cpp/shell_window_ids.h"
+#include "ash/wm/window_util.h"
 #include "components/vector_icons/vector_icons.h"
+#include "ui/aura/window.h"
 #include "ui/compositor/paint_recorder.h"
 #include "ui/events/event.h"
 #include "ui/gfx/animation/animation_delegate.h"
@@ -12,6 +15,7 @@
 #include "ui/gfx/canvas.h"
 #include "ui/gfx/color_palette.h"
 #include "ui/gfx/paint_vector_icon.h"
+#include "ui/views/view.h"
 
 namespace ash {
 
@@ -46,21 +50,96 @@
 
 constexpr SkColor kRippleColor = SkColorSetA(gfx::kGoogleBlue600, 0x4C);  // 30%
 
-}  // namespace
+class AffordanceView : public views::View {
+ public:
+  AffordanceView() {
+    SetPaintToLayer(ui::LAYER_TEXTURED);
+    layer()->SetFillsBoundsOpaquely(false);
+  }
 
-BackGestureAffordance::BackGestureAffordance(const gfx::Point& location)
-    : painted_layer_(ui::LAYER_TEXTURED) {
-  painted_layer_.SetFillsBoundsOpaquely(false);
-  gfx::Rect painted_layer_bounds(
+  ~AffordanceView() override = default;
+
+  // Schedule painting on given |drag_progress| and |abort_progress|.
+  void Paint(float drag_progress, float abort_progress) {
+    drag_progress_ = drag_progress;
+    abort_progress_ = abort_progress;
+    SchedulePaint();
+  }
+
+ private:
+  // views::View:
+  void OnPaint(gfx::Canvas* canvas) override {
+    views::View::OnPaint(canvas);
+
+    const gfx::PointF center_point(kRippleBurstRadius, kRippleBurstRadius);
+    const float progress = (1 - abort_progress_) * drag_progress_;
+
+    // Draw the ripple.
+    cc::PaintFlags ripple_flags;
+    ripple_flags.setAntiAlias(true);
+    ripple_flags.setStyle(cc::PaintFlags::kFill_Style);
+    ripple_flags.setColor(kRippleColor);
+
+    const bool exceed_full_progress = progress >= 1.f;
+    float ripple_radius = 0.f;
+    if (exceed_full_progress) {
+      const float factor = (kRippleBurstRadius - kRippleMaxRadius) /
+                           (kBurstTransform / kMaxTransform - 1);
+      ripple_radius = (kRippleMaxRadius - factor) + factor * progress;
+    } else {
+      ripple_radius =
+          kBackgroundRadius + progress * (kRippleMaxRadius - kBackgroundRadius);
+    }
+    canvas->DrawCircle(center_point, ripple_radius, ripple_flags);
+
+    // Draw the arrow background circle.
+    cc::PaintFlags bg_flags;
+    bg_flags.setAntiAlias(true);
+    bg_flags.setStyle(cc::PaintFlags::kFill_Style);
+
+    bg_flags.setColor(exceed_full_progress ? gfx::kGoogleBlue600
+                                           : SK_ColorWHITE);
+    canvas->DrawCircle(center_point, kBackgroundRadius, bg_flags);
+
+    // Draw the arrow.
+    const float arrow_x = center_point.x() - kArrowSize / 2.f;
+    const float arrow_y = center_point.y() - kArrowSize / 2.f;
+    if (exceed_full_progress) {
+      canvas->DrawImageInt(gfx::CreateVectorIcon(vector_icons::kBackArrowIcon,
+                                                 kArrowSize, SK_ColorWHITE),
+                           static_cast<int>(arrow_x),
+                           static_cast<int>(arrow_y));
+    } else {
+      canvas->DrawImageInt(
+          gfx::CreateVectorIcon(vector_icons::kBackArrowIcon, kArrowSize,
+                                gfx::kGoogleBlue600),
+          static_cast<int>(arrow_x), static_cast<int>(arrow_y));
+    }
+  }
+
+  float drag_progress_ = 0.f;
+  float abort_progress_ = 0.f;
+
+  DISALLOW_COPY_AND_ASSIGN(AffordanceView);
+};
+
+// Get the bounds of the affordance widget, which is outside of the left edge of
+// the display.
+gfx::Rect GetWidgetBounds(const gfx::Point& location) {
+  gfx::Rect widget_bounds(
       gfx::Rect(2 * kRippleBurstRadius, 2 * kRippleBurstRadius));
   gfx::Point origin;
   // TODO(crbug.com/1002733): Handle the case while origin y is outside display.
   origin.set_y(location.y() - kArrowAboveStartPosition - kRippleBurstRadius);
   origin.set_x(-kRippleBurstRadius - kBackgroundRadius);
-  painted_layer_bounds.set_origin(origin);
-  painted_layer_.SetBounds(painted_layer_bounds);
+  widget_bounds.set_origin(origin);
+  return widget_bounds;
+}
 
-  painted_layer_.set_delegate(this);
+}  // namespace
+
+BackGestureAffordance::BackGestureAffordance(const gfx::Point& location) {
+  CreateAffordanceWidget(location);
 }
 
 BackGestureAffordance::~BackGestureAffordance() {}
@@ -99,15 +178,34 @@
   animation_->Start();
 }
 
+void BackGestureAffordance::CreateAffordanceWidget(const gfx::Point& location) {
+  affordance_widget_ = std::make_unique<views::Widget>();
+  views::Widget::InitParams params(
+      views::Widget::InitParams::TYPE_WINDOW_FRAMELESS);
+  params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW;
+  params.accept_events = true;
+  params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
+  params.name = "BackGestureAffordance";
+  params.activatable = views::Widget::InitParams::ACTIVATABLE_NO;
+  params.parent = window_util::GetRootWindowAt(location)->GetChildById(
+      kShellWindowId_AlwaysOnTopContainer);
+  affordance_widget_->Init(std::move(params));
+  affordance_widget_->SetContentsView(new AffordanceView());
+  affordance_widget_->SetBounds(GetWidgetBounds(location));
+  affordance_widget_->Show();
+  affordance_widget_->SetOpacity(1.f);
+}
+
 void BackGestureAffordance::UpdateTransform() {
   const float offset = (1 - abort_progress_) * drag_progress_ * kMaxTransform;
   gfx::Transform transform;
   transform.Translate(offset, 0);
-  painted_layer_.SetTransform(transform);
+  affordance_widget_->GetContentsView()->SetTransform(transform);
 }
 
 void BackGestureAffordance::SchedulePaint() {
-  painted_layer_.SchedulePaint(gfx::Rect(painted_layer_.size()));
+  static_cast<AffordanceView*>(affordance_widget_->GetContentsView())
+      ->Paint(drag_progress_, abort_progress_);
 }
 
 void BackGestureAffordance::SetAbortProgress(float progress) {
@@ -132,63 +230,13 @@
     return;
   complete_progress_ = progress;
 
-  painted_layer_.SetOpacity(gfx::Tween::CalculateValue(
-      gfx::Tween::FAST_OUT_SLOW_IN, 1 - complete_progress_));
+  affordance_widget_->GetContentsView()->layer()->SetOpacity(
+      gfx::Tween::CalculateValue(gfx::Tween::FAST_OUT_SLOW_IN,
+                                 1 - complete_progress_));
 
   SchedulePaint();
 }
 
-void BackGestureAffordance::OnPaintLayer(const ui::PaintContext& context) {
-  ui::PaintRecorder recorder(context, painted_layer_.size());
-  gfx::Canvas* canvas = recorder.canvas();
-
-  const gfx::PointF center_point(kRippleBurstRadius, kRippleBurstRadius);
-  const float progress = (1 - abort_progress_) * drag_progress_;
-
-  // Draw the ripple.
-  cc::PaintFlags ripple_flags;
-  ripple_flags.setAntiAlias(true);
-  ripple_flags.setStyle(cc::PaintFlags::kFill_Style);
-  ripple_flags.setColor(kRippleColor);
-
-  const bool exceed_full_progress = progress >= 1.f;
-  float ripple_radius = 0.f;
-  if (exceed_full_progress) {
-    const float factor = (kRippleBurstRadius - kRippleMaxRadius) /
-                         (kBurstTransform / kMaxTransform - 1);
-    ripple_radius = (kRippleMaxRadius - factor) + factor * progress;
-  } else {
-    ripple_radius =
-        kBackgroundRadius + progress * (kRippleMaxRadius - kBackgroundRadius);
-  }
-  canvas->DrawCircle(center_point, ripple_radius, ripple_flags);
-
-  // Draw the arrow background circle.
-  cc::PaintFlags bg_flags;
-  bg_flags.setAntiAlias(true);
-  bg_flags.setStyle(cc::PaintFlags::kFill_Style);
-
-  bg_flags.setColor(exceed_full_progress ? gfx::kGoogleBlue600 : SK_ColorWHITE);
-  canvas->DrawCircle(center_point, kBackgroundRadius, bg_flags);
-
-  // Draw the arrow.
-  const float arrow_x = center_point.x() - kArrowSize / 2.f;
-  const float arrow_y = center_point.y() - kArrowSize / 2.f;
-  if (exceed_full_progress) {
-    canvas->DrawImageInt(gfx::CreateVectorIcon(vector_icons::kBackArrowIcon,
-                                               kArrowSize, SK_ColorWHITE),
-                         static_cast<int>(arrow_x), static_cast<int>(arrow_y));
-  } else {
-    canvas->DrawImageInt(gfx::CreateVectorIcon(vector_icons::kBackArrowIcon,
-                                               kArrowSize, gfx::kGoogleBlue600),
-                         static_cast<int>(arrow_x), static_cast<int>(arrow_y));
-  }
-}
-
-void BackGestureAffordance::OnDeviceScaleFactorChanged(
-    float old_device_scale_factor,
-    float new_device_scale_factor) {}
-
 void BackGestureAffordance::AnimationEnded(const gfx::Animation* animation) {}
 
 void BackGestureAffordance::AnimationProgressed(
diff --git a/ash/wm/back_gesture_affordance.h b/ash/wm/back_gesture_affordance.h
index a1b40b3..8ccc59c 100644
--- a/ash/wm/back_gesture_affordance.h
+++ b/ash/wm/back_gesture_affordance.h
@@ -8,16 +8,15 @@
 #include <memory>
 
 #include "base/macros.h"
-#include "ui/compositor/layer.h"
 #include "ui/gfx/animation/animation_delegate.h"
 #include "ui/gfx/animation/linear_animation.h"
+#include "ui/views/widget/widget.h"
 
 namespace ash {
 
-// This class is responsible for creating, painting, and positioning the layer
-// for the back gesture affordance.
-class BackGestureAffordance : public ui::LayerDelegate,
-                              public gfx::AnimationDelegate {
+// This class is responsible for creating, painting, and positioning the back
+// gesture affordance.
+class BackGestureAffordance : public gfx::AnimationDelegate {
  public:
   explicit BackGestureAffordance(const gfx::Point& location);
   ~BackGestureAffordance() override;
@@ -34,28 +33,23 @@
   // Completes the affordance and fading it out.
   void Complete();
 
-  ui::Layer* painted_layer() { return &painted_layer_; }
-
  private:
   enum class State { DRAGGING, ABORTING, COMPLETING };
 
+  void CreateAffordanceWidget(const gfx::Point& location);
+
   void UpdateTransform();
   void SchedulePaint();
   void SetAbortProgress(float progress);
   void SetCompleteProgress(float progress);
 
-  // ui::LayerDelegate:
-  void OnPaintLayer(const ui::PaintContext& context) override;
-  void OnDeviceScaleFactorChanged(float old_device_scale_factor,
-                                  float new_device_scale_factor) override;
-
   // gfx::AnimationDelegate:
   void AnimationEnded(const gfx::Animation* animation) override;
   void AnimationProgressed(const gfx::Animation* animation) override;
   void AnimationCanceled(const gfx::Animation* animation) override;
 
-  // Layer that actually paints the affordance.
-  ui::Layer painted_layer_;
+  // Widget of the affordance with AffordanceView as the content.
+  std::unique_ptr<views::Widget> affordance_widget_;
 
   // Values that determine current state of the affordance.
   State state_ = State::DRAGGING;
diff --git a/ash/wm/toplevel_window_event_handler.cc b/ash/wm/toplevel_window_event_handler.cc
index 90a2977..265457b 100644
--- a/ash/wm/toplevel_window_event_handler.cc
+++ b/ash/wm/toplevel_window_event_handler.cc
@@ -848,12 +848,6 @@
         break;
       back_gesture_affordance_ =
           std::make_unique<BackGestureAffordance>(event->location());
-
-      aura::Window* window = static_cast<aura::Window*>(event->target());
-      DCHECK(window);
-      ui::Layer* parent = window->layer()->parent();
-      parent->Add(back_gesture_affordance_->painted_layer());
-      parent->StackAtTop(back_gesture_affordance_->painted_layer());
       return true;
     }
     case ui::ET_GESTURE_SCROLL_UPDATE:
diff --git a/base/BUILD.gn b/base/BUILD.gn
index da70270..143c4c4 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -2167,6 +2167,11 @@
   ]
 
   if (is_win) {
+    sources += [
+      "win/static_constants.cc",
+      "win/static_constants.h",
+    ]
+
     public_deps = [
       "//base/win:pe_image",
     ]
diff --git a/base/feature_list_unittest.cc b/base/feature_list_unittest.cc
index 813fb252..de9bc5d 100644
--- a/base/feature_list_unittest.cc
+++ b/base/feature_list_unittest.cc
@@ -18,6 +18,7 @@
 #include "base/strings/string_piece.h"
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
+#include "base/test/scoped_feature_list.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace base {
@@ -45,26 +46,14 @@
 
 class FeatureListTest : public testing::Test {
  public:
-  FeatureListTest() : feature_list_(nullptr) {
-    RegisterFeatureListInstance(std::make_unique<FeatureList>());
+  FeatureListTest() {
+    // Provide an empty FeatureList to each test by default.
+    scoped_feature_list_.InitWithFeatureList(std::make_unique<FeatureList>());
   }
-  ~FeatureListTest() override { ClearFeatureListInstance(); }
-
-  void RegisterFeatureListInstance(std::unique_ptr<FeatureList> feature_list) {
-    FeatureList::ClearInstanceForTesting();
-    feature_list_ = feature_list.get();
-    FeatureList::SetInstance(std::move(feature_list));
-  }
-  void ClearFeatureListInstance() {
-    FeatureList::ClearInstanceForTesting();
-    feature_list_ = nullptr;
-  }
-
-  FeatureList* feature_list() { return feature_list_; }
+  ~FeatureListTest() override = default;
 
  private:
-  // Weak. Owned by the FeatureList::SetInstance().
-  FeatureList* feature_list_;
+  test::ScopedFeatureList scoped_feature_list_;
 
   DISALLOW_COPY_AND_ASSIGN(FeatureListTest);
 };
@@ -96,11 +85,11 @@
                                     test_case.enable_features,
                                     test_case.disable_features));
 
-    ClearFeatureListInstance();
-    std::unique_ptr<FeatureList> feature_list(new FeatureList);
+    auto feature_list = std::make_unique<FeatureList>();
     feature_list->InitializeFromCommandLine(test_case.enable_features,
                                             test_case.disable_features);
-    RegisterFeatureListInstance(std::move(feature_list));
+    test::ScopedFeatureList scoped_feature_list;
+    scoped_feature_list.InitWithFeatureList(std::move(feature_list));
 
     EXPECT_EQ(test_case.expected_feature_on_state,
               FeatureList::IsEnabled(kFeatureOnByDefault))
@@ -115,19 +104,23 @@
   // Tests that CheckFeatureIdentity() correctly detects when two different
   // structs with the same feature name are passed to it.
 
+  test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitWithFeatureList(std::make_unique<FeatureList>());
+  FeatureList* feature_list = FeatureList::GetInstance();
+
   // Call it twice for each feature at the top of the file, since the first call
   // makes it remember the entry and the second call will verify it.
-  EXPECT_TRUE(feature_list()->CheckFeatureIdentity(kFeatureOnByDefault));
-  EXPECT_TRUE(feature_list()->CheckFeatureIdentity(kFeatureOnByDefault));
-  EXPECT_TRUE(feature_list()->CheckFeatureIdentity(kFeatureOffByDefault));
-  EXPECT_TRUE(feature_list()->CheckFeatureIdentity(kFeatureOffByDefault));
+  EXPECT_TRUE(feature_list->CheckFeatureIdentity(kFeatureOnByDefault));
+  EXPECT_TRUE(feature_list->CheckFeatureIdentity(kFeatureOnByDefault));
+  EXPECT_TRUE(feature_list->CheckFeatureIdentity(kFeatureOffByDefault));
+  EXPECT_TRUE(feature_list->CheckFeatureIdentity(kFeatureOffByDefault));
 
   // Now, call it with a distinct struct for |kFeatureOnByDefaultName|, which
   // should return false.
   struct Feature kFeatureOnByDefault2 {
     kFeatureOnByDefaultName, FEATURE_ENABLED_BY_DEFAULT
   };
-  EXPECT_FALSE(feature_list()->CheckFeatureIdentity(kFeatureOnByDefault2));
+  EXPECT_FALSE(feature_list->CheckFeatureIdentity(kFeatureOnByDefault2));
 }
 
 TEST_F(FeatureListTest, FieldTrialOverrides) {
@@ -150,10 +143,8 @@
     const auto& test_case = test_cases[i];
     SCOPED_TRACE(base::StringPrintf("Test[%" PRIuS "]", i));
 
-    ClearFeatureListInstance();
-
     FieldTrialList field_trial_list(nullptr);
-    std::unique_ptr<FeatureList> feature_list(new FeatureList);
+    auto feature_list = std::make_unique<FeatureList>();
 
     FieldTrial* trial1 = FieldTrialList::CreateFieldTrial("TrialExample1", "A");
     FieldTrial* trial2 = FieldTrialList::CreateFieldTrial("TrialExample2", "B");
@@ -161,7 +152,8 @@
                                              test_case.trial1_state, trial1);
     feature_list->RegisterFieldTrialOverride(kFeatureOffByDefaultName,
                                              test_case.trial2_state, trial2);
-    RegisterFeatureListInstance(std::move(feature_list));
+    test::ScopedFeatureList scoped_feature_list;
+    scoped_feature_list.InitWithFeatureList(std::move(feature_list));
 
     // Initially, neither trial should be active.
     EXPECT_FALSE(FieldTrialList::IsTrialActive(trial1->trial_name()));
@@ -185,7 +177,7 @@
 
 TEST_F(FeatureListTest, FieldTrialAssociateUseDefault) {
   FieldTrialList field_trial_list(nullptr);
-  std::unique_ptr<FeatureList> feature_list(new FeatureList);
+  auto feature_list = std::make_unique<FeatureList>();
 
   FieldTrial* trial1 = FieldTrialList::CreateFieldTrial("TrialExample1", "A");
   FieldTrial* trial2 = FieldTrialList::CreateFieldTrial("TrialExample2", "B");
@@ -193,7 +185,8 @@
       kFeatureOnByDefaultName, FeatureList::OVERRIDE_USE_DEFAULT, trial1);
   feature_list->RegisterFieldTrialOverride(
       kFeatureOffByDefaultName, FeatureList::OVERRIDE_USE_DEFAULT, trial2);
-  RegisterFeatureListInstance(std::move(feature_list));
+  test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitWithFeatureList(std::move(feature_list));
 
   // Initially, neither trial should be active.
   EXPECT_FALSE(FieldTrialList::IsTrialActive(trial1->trial_name()));
@@ -213,10 +206,8 @@
 }
 
 TEST_F(FeatureListTest, CommandLineEnableTakesPrecedenceOverFieldTrial) {
-  ClearFeatureListInstance();
-
   FieldTrialList field_trial_list(nullptr);
-  std::unique_ptr<FeatureList> feature_list(new FeatureList);
+  auto feature_list = std::make_unique<FeatureList>();
 
   // The feature is explicitly enabled on the command-line.
   feature_list->InitializeFromCommandLine(kFeatureOffByDefaultName, "");
@@ -225,7 +216,8 @@
   FieldTrial* trial = FieldTrialList::CreateFieldTrial("TrialExample2", "A");
   feature_list->RegisterFieldTrialOverride(
       kFeatureOffByDefaultName, FeatureList::OVERRIDE_DISABLE_FEATURE, trial);
-  RegisterFeatureListInstance(std::move(feature_list));
+  test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitWithFeatureList(std::move(feature_list));
 
   EXPECT_FALSE(FieldTrialList::IsTrialActive(trial->trial_name()));
   // Command-line should take precedence.
@@ -237,10 +229,8 @@
 }
 
 TEST_F(FeatureListTest, CommandLineDisableTakesPrecedenceOverFieldTrial) {
-  ClearFeatureListInstance();
-
   FieldTrialList field_trial_list(nullptr);
-  std::unique_ptr<FeatureList> feature_list(new FeatureList);
+  auto feature_list = std::make_unique<FeatureList>();
 
   // The feature is explicitly disabled on the command-line.
   feature_list->InitializeFromCommandLine("", kFeatureOffByDefaultName);
@@ -249,7 +239,8 @@
   FieldTrial* trial = FieldTrialList::CreateFieldTrial("TrialExample2", "A");
   feature_list->RegisterFieldTrialOverride(
       kFeatureOffByDefaultName, FeatureList::OVERRIDE_ENABLE_FEATURE, trial);
-  RegisterFeatureListInstance(std::move(feature_list));
+  test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitWithFeatureList(std::move(feature_list));
 
   EXPECT_FALSE(FieldTrialList::IsTrialActive(trial->trial_name()));
   // Command-line should take precedence.
@@ -261,10 +252,8 @@
 }
 
 TEST_F(FeatureListTest, IsFeatureOverriddenFromCommandLine) {
-  ClearFeatureListInstance();
-
   FieldTrialList field_trial_list(nullptr);
-  std::unique_ptr<FeatureList> feature_list(new FeatureList);
+  auto feature_list = std::make_unique<FeatureList>();
 
   // No features are overridden from the command line yet
   EXPECT_FALSE(feature_list->IsFeatureOverriddenFromCommandLine(
@@ -304,7 +293,8 @@
       kFeatureOnByDefaultName, FeatureList::OVERRIDE_DISABLE_FEATURE));
   EXPECT_FALSE(feature_list->IsFeatureOverriddenFromCommandLine(
       kFeatureOnByDefaultName, FeatureList::OVERRIDE_ENABLE_FEATURE));
-  RegisterFeatureListInstance(std::move(feature_list));
+  test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitWithFeatureList(std::move(feature_list));
 
   // Check the expected feature states for good measure.
   EXPECT_TRUE(FeatureList::IsEnabled(kFeatureOffByDefault));
@@ -336,10 +326,8 @@
                                     test_case.enable_features,
                                     test_case.disable_features));
 
-    ClearFeatureListInstance();
-
     FieldTrialList field_trial_list(nullptr);
-    std::unique_ptr<FeatureList> feature_list(new FeatureList);
+    auto feature_list = std::make_unique<FeatureList>();
     feature_list->InitializeFromCommandLine(test_case.enable_features,
                                             test_case.disable_features);
 
@@ -364,7 +352,8 @@
     EXPECT_EQ(test_case.expected_enable_trial_created, enable_trial != nullptr);
     EXPECT_EQ(test_case.expected_disable_trial_created,
               disable_trial != nullptr);
-    RegisterFeatureListInstance(std::move(feature_list));
+    test::ScopedFeatureList scoped_feature_list;
+    scoped_feature_list.InitWithFeatureList(std::move(feature_list));
 
     EXPECT_FALSE(FieldTrialList::IsTrialActive(kTrialName));
     if (disable_trial) {
@@ -380,8 +369,6 @@
 }
 
 TEST_F(FeatureListTest, RegisterExtraFeatureOverrides) {
-  ClearFeatureListInstance();
-
   auto feature_list = std::make_unique<FeatureList>();
   std::vector<FeatureList::FeatureOverrideInfo> overrides;
   overrides.push_back({std::cref(kFeatureOnByDefault),
@@ -389,15 +376,14 @@
   overrides.push_back({std::cref(kFeatureOffByDefault),
                        FeatureList::OverrideState::OVERRIDE_ENABLE_FEATURE});
   feature_list->RegisterExtraFeatureOverrides(std::move(overrides));
-  RegisterFeatureListInstance(std::move(feature_list));
+  test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitWithFeatureList(std::move(feature_list));
 
   EXPECT_FALSE(FeatureList::IsEnabled(kFeatureOnByDefault));
   EXPECT_TRUE(FeatureList::IsEnabled(kFeatureOffByDefault));
 }
 
 TEST_F(FeatureListTest, InitializeFromCommandLineThenRegisterExtraOverrides) {
-  ClearFeatureListInstance();
-
   auto feature_list = std::make_unique<FeatureList>();
   feature_list->InitializeFromCommandLine(kFeatureOnByDefaultName,
                                           kFeatureOffByDefaultName);
@@ -407,7 +393,8 @@
   overrides.push_back({std::cref(kFeatureOffByDefault),
                        FeatureList::OverrideState::OVERRIDE_ENABLE_FEATURE});
   feature_list->RegisterExtraFeatureOverrides(std::move(overrides));
-  RegisterFeatureListInstance(std::move(feature_list));
+  test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitWithFeatureList(std::move(feature_list));
 
   // The InitializeFromCommandLine supersedes the RegisterExtraFeatureOverrides
   // because it was called first.
@@ -423,9 +410,8 @@
 }
 
 TEST_F(FeatureListTest, GetFeatureOverrides) {
-  ClearFeatureListInstance();
   FieldTrialList field_trial_list(nullptr);
-  std::unique_ptr<FeatureList> feature_list(new FeatureList);
+  auto feature_list = std::make_unique<FeatureList>();
   feature_list->InitializeFromCommandLine("A,X", "D");
 
   Feature feature_b = {"B", FEATURE_ENABLED_BY_DEFAULT};
@@ -442,7 +428,8 @@
                                            FeatureList::OVERRIDE_ENABLE_FEATURE,
                                            trial);
 
-  RegisterFeatureListInstance(std::move(feature_list));
+  test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitWithFeatureList(std::move(feature_list));
 
   std::string enable_features;
   std::string disable_features;
@@ -458,16 +445,16 @@
 }
 
 TEST_F(FeatureListTest, GetFeatureOverrides_UseDefault) {
-  ClearFeatureListInstance();
   FieldTrialList field_trial_list(nullptr);
-  std::unique_ptr<FeatureList> feature_list(new FeatureList);
+  auto feature_list = std::make_unique<FeatureList>();
   feature_list->InitializeFromCommandLine("A,X", "D");
 
   FieldTrial* trial = FieldTrialList::CreateFieldTrial("Trial", "Group");
   feature_list->RegisterFieldTrialOverride(
       kFeatureOffByDefaultName, FeatureList::OVERRIDE_USE_DEFAULT, trial);
 
-  RegisterFeatureListInstance(std::move(feature_list));
+  test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitWithFeatureList(std::move(feature_list));
 
   std::string enable_features;
   std::string disable_features;
@@ -478,25 +465,25 @@
 }
 
 TEST_F(FeatureListTest, GetFieldTrial) {
-  ClearFeatureListInstance();
   FieldTrialList field_trial_list(nullptr);
   FieldTrial* trial = FieldTrialList::CreateFieldTrial("Trial", "Group");
-  std::unique_ptr<FeatureList> feature_list(new FeatureList);
+  auto feature_list = std::make_unique<FeatureList>();
   feature_list->RegisterFieldTrialOverride(
       kFeatureOnByDefaultName, FeatureList::OVERRIDE_USE_DEFAULT, trial);
-  RegisterFeatureListInstance(std::move(feature_list));
+  test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitWithFeatureList(std::move(feature_list));
 
   EXPECT_EQ(trial, FeatureList::GetFieldTrial(kFeatureOnByDefault));
   EXPECT_EQ(nullptr, FeatureList::GetFieldTrial(kFeatureOffByDefault));
 }
 
 TEST_F(FeatureListTest, InitializeFromCommandLine_WithFieldTrials) {
-  ClearFeatureListInstance();
   FieldTrialList field_trial_list(nullptr);
   FieldTrialList::CreateFieldTrial("Trial", "Group");
-  std::unique_ptr<FeatureList> feature_list(new FeatureList);
+  auto feature_list = std::make_unique<FeatureList>();
   feature_list->InitializeFromCommandLine("A,OffByDefault<Trial,X", "D");
-  RegisterFeatureListInstance(std::move(feature_list));
+  test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitWithFeatureList(std::move(feature_list));
 
   EXPECT_FALSE(FieldTrialList::IsTrialActive("Trial"));
   EXPECT_TRUE(FeatureList::IsEnabled(kFeatureOffByDefault));
@@ -504,14 +491,14 @@
 }
 
 TEST_F(FeatureListTest, InitializeFromCommandLine_UseDefault) {
-  ClearFeatureListInstance();
   FieldTrialList field_trial_list(nullptr);
   FieldTrialList::CreateFieldTrial("T1", "Group");
   FieldTrialList::CreateFieldTrial("T2", "Group");
-  std::unique_ptr<FeatureList> feature_list(new FeatureList);
+  auto feature_list = std::make_unique<FeatureList>();
   feature_list->InitializeFromCommandLine(
       "A,*OffByDefault<T1,*OnByDefault<T2,X", "D");
-  RegisterFeatureListInstance(std::move(feature_list));
+  test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitWithFeatureList(std::move(feature_list));
 
   EXPECT_FALSE(FieldTrialList::IsTrialActive("T1"));
   EXPECT_FALSE(FeatureList::IsEnabled(kFeatureOffByDefault));
@@ -523,10 +510,10 @@
 }
 
 TEST_F(FeatureListTest, InitializeInstance) {
-  ClearFeatureListInstance();
-
   std::unique_ptr<base::FeatureList> feature_list(new base::FeatureList);
-  FeatureList::SetInstance(std::move(feature_list));
+  test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitWithFeatureList(std::move(feature_list));
+
   EXPECT_TRUE(FeatureList::IsEnabled(kFeatureOnByDefault));
   EXPECT_FALSE(FeatureList::IsEnabled(kFeatureOffByDefault));
 
@@ -542,7 +529,9 @@
 }
 
 TEST_F(FeatureListTest, UninitializedInstance_IsEnabledReturnsFalse) {
-  ClearFeatureListInstance();
+  std::unique_ptr<FeatureList> original_feature_list =
+      FeatureList::ClearInstanceForTesting();
+
   // This test case simulates the calling pattern found in code which does not
   // explicitly initialize the features list.
   // All IsEnabled() calls should return the default value in this scenario.
@@ -550,6 +539,9 @@
   EXPECT_TRUE(FeatureList::IsEnabled(kFeatureOnByDefault));
   EXPECT_EQ(nullptr, FeatureList::GetInstance());
   EXPECT_FALSE(FeatureList::IsEnabled(kFeatureOffByDefault));
+
+  if (original_feature_list)
+    FeatureList::RestoreInstanceForTesting(std::move(original_feature_list));
 }
 
 TEST_F(FeatureListTest, StoreAndRetrieveFeaturesFromSharedMemory) {
diff --git a/base/file_version_info_win_unittest.cc b/base/file_version_info_win_unittest.cc
index 0bab8568..6c81bb7 100644
--- a/base/file_version_info_win_unittest.cc
+++ b/base/file_version_info_win_unittest.cc
@@ -16,7 +16,6 @@
 #include "base/path_service.h"
 #include "base/scoped_native_library.h"
 #include "base/strings/string_util.h"
-#include "build/build_config.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 using base::FilePath;
@@ -158,9 +157,6 @@
             version_info_win->GetFileVersion());
 }
 
-#if defined(ARCH_CPU_64_BITS)
-// TODO(bug_1011439): Change no_version_info.dll to 32 bit, so this test will
-// work for 32-bit Windows as well.
 TYPED_TEST(FileVersionInfoTest, NoVersionInfo) {
   FilePath dll_path = GetTestDataPath();
   dll_path = dll_path.AppendASCII("no_version_info.dll");
@@ -168,4 +164,3 @@
   TypeParam factory(dll_path);
   ASSERT_FALSE(factory.Create());
 }
-#endif
diff --git a/base/test/data/file_version_info_unittest/no_version_info.dll b/base/test/data/file_version_info_unittest/no_version_info.dll
index 23dae63..2c03d64a 100755
--- a/base/test/data/file_version_info_unittest/no_version_info.dll
+++ b/base/test/data/file_version_info_unittest/no_version_info.dll
Binary files differ
diff --git a/base/test/launcher/unit_test_launcher.cc b/base/test/launcher/unit_test_launcher.cc
index fb32008..039a69c 100644
--- a/base/test/launcher/unit_test_launcher.cc
+++ b/base/test/launcher/unit_test_launcher.cc
@@ -46,11 +46,6 @@
 // This constant controls how many tests are run in a single batch by default.
 const size_t kDefaultTestBatchLimit = 10;
 
-const char kHelpFlag[] = "help";
-
-// Flag to run all tests in a single process.
-const char kSingleProcessTestsFlag[] = "single-process-tests";
-
 void PrintUsage() {
   fprintf(stdout,
           "Runs tests using the gtest framework, each batch of tests being\n"
@@ -155,7 +150,8 @@
 
   if (CommandLine::ForCurrentProcess()->HasSwitch(kGTestHelpFlag) ||
       CommandLine::ForCurrentProcess()->HasSwitch(kGTestListTestsFlag) ||
-      CommandLine::ForCurrentProcess()->HasSwitch(kSingleProcessTestsFlag) ||
+      CommandLine::ForCurrentProcess()->HasSwitch(
+          switches::kSingleProcessTests) ||
       CommandLine::ForCurrentProcess()->HasSwitch(
           switches::kTestChildProcess) ||
       force_single_process) {
@@ -163,7 +159,7 @@
   }
 #endif
 
-  if (CommandLine::ForCurrentProcess()->HasSwitch(kHelpFlag)) {
+  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kHelpFlag)) {
     PrintUsage();
     return 0;
   }
@@ -313,7 +309,7 @@
 
   new_cmd_line.AppendSwitchPath(switches::kTestLauncherOutput, output_file);
   new_cmd_line.AppendSwitchPath(kGTestFlagfileFlag, flag_file);
-  new_cmd_line.AppendSwitch(kSingleProcessTestsFlag);
+  new_cmd_line.AppendSwitch(switches::kSingleProcessTests);
 
   return new_cmd_line;
 }
diff --git a/base/test/test_switches.cc b/base/test/test_switches.cc
index 620eae4..f1d04d5 100644
--- a/base/test/test_switches.cc
+++ b/base/test/test_switches.cc
@@ -4,6 +4,13 @@
 
 #include "base/test/test_switches.h"
 
+// Flag to show the help message.
+const char switches::kHelpFlag[] = "help";
+
+// Flag to run all tests and the launcher in a single process. Useful for
+// debugging a specific test in a debugger.
+const char switches::kSingleProcessTests[] = "single-process-tests";
+
 // Maximum number of tests to run in a single batch.
 const char switches::kTestLauncherBatchLimit[] = "test-launcher-batch-limit";
 
diff --git a/base/test/test_switches.h b/base/test/test_switches.h
index 6743fb8..2d6b5f4 100644
--- a/base/test/test_switches.h
+++ b/base/test/test_switches.h
@@ -11,6 +11,8 @@
 
 // All switches in alphabetical order. The switches should be documented
 // alongside the definition of their values in the .cc file.
+extern const char kHelpFlag[];
+extern const char kSingleProcessTests[];
 extern const char kTestLauncherBatchLimit[];
 extern const char kTestLauncherBotMode[];
 extern const char kTestLauncherDebugLauncher[];
diff --git a/base/values.cc b/base/values.cc
index 89c0728..8421549 100644
--- a/base/values.cc
+++ b/base/values.cc
@@ -147,6 +147,18 @@
   return std::make_unique<Value>(std::move(val));
 }
 
+// static
+const DictionaryValue& Value::AsDictionaryValue(const Value& val) {
+  CHECK(val.is_dict());
+  return static_cast<const DictionaryValue&>(val);
+}
+
+// static
+const ListValue& Value::AsListValue(const Value& val) {
+  CHECK(val.is_list());
+  return static_cast<const ListValue&>(val);
+}
+
 Value::Value(Value&& that) noexcept {
   InternalMoveConstructFrom(std::move(that));
 }
diff --git a/base/values.h b/base/values.h
index 0d1cfc3c..c2b68a7 100644
--- a/base/values.h
+++ b/base/values.h
@@ -111,6 +111,8 @@
   // Adaptors for converting from the old way to the new way and vice versa.
   static Value FromUniquePtrValue(std::unique_ptr<Value> val);
   static std::unique_ptr<Value> ToUniquePtrValue(Value val);
+  static const DictionaryValue& AsDictionaryValue(const Value& val);
+  static const ListValue& AsListValue(const Value& val);
 
   Value(Value&& that) noexcept;
   Value() noexcept {}  // A null value
diff --git a/base/win/static_constants.cc b/base/win/static_constants.cc
new file mode 100644
index 0000000..f9894a2
--- /dev/null
+++ b/base/win/static_constants.cc
@@ -0,0 +1,13 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/win/static_constants.h"
+
+namespace base {
+namespace win {
+
+const char kApplicationVerifierDllName[] = "verifier.dll";
+
+}  // namespace win
+}  // namespace base
diff --git a/base/win/static_constants.h b/base/win/static_constants.h
new file mode 100644
index 0000000..98a631f7
--- /dev/null
+++ b/base/win/static_constants.h
@@ -0,0 +1,21 @@
+// 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.
+
+// Defines constants needed before imports (like base.dll) are fully resolved.
+// For example, constants defined here can be used by interceptions (i.e. hooks)
+// in the sandbox, which run before imports are resolved, and can therefore only
+// reference static variables.
+
+#ifndef BASE_WIN_STATIC_CONSTANTS_H_
+#define BASE_WIN_STATIC_CONSTANTS_H_
+
+namespace base {
+namespace win {
+
+extern const char kApplicationVerifierDllName[];
+
+}  // namespace win
+}  // namespace base
+
+#endif  // BASE_WIN_STATIC_CONSTANTS_H_
diff --git a/build/android/gyp/dex.py b/build/android/gyp/dex.py
index 5182691..0994f48 100755
--- a/build/android/gyp/dex.py
+++ b/build/android/gyp/dex.py
@@ -45,6 +45,10 @@
       '--incremental-dir',
       help='Path of directory to put intermediate dex files.')
   parser.add_argument(
+      '--merge-incrementals',
+      action='store_true',
+      help='Combine all per-class .dex files into a single classes.dex')
+  parser.add_argument(
       '--main-dex-list-path',
       help='File containing a list of the classes to include in the main dex.')
   parser.add_argument(
@@ -269,24 +273,30 @@
 
 
 def _CreateFinalDex(options, d8_inputs, tmp_dir, dex_cmd):
-  if options.multi_dex and options.main_dex_list_path:
-    # Provides a list of classes that should be included in the main dex file.
-    dex_cmd = dex_cmd + ['--main-dex-list', options.main_dex_list_path]
+  tmp_dex_output = os.path.join(tmp_dir, 'tmp_dex_output.zip')
+  if (options.merge_incrementals or options.output.endswith('.dex')
+      or not all(f.endswith('.dex') for f in d8_inputs)):
+    if options.multi_dex and options.main_dex_list_path:
+      # Provides a list of classes that should be included in the main dex file.
+      dex_cmd = dex_cmd + ['--main-dex-list', options.main_dex_list_path]
 
-  tmp_dex_dir = os.path.join(tmp_dir, 'tmp_dex_dir')
-  os.mkdir(tmp_dex_dir)
-  _RunD8(dex_cmd, d8_inputs, tmp_dex_dir)
+    tmp_dex_dir = os.path.join(tmp_dir, 'tmp_dex_dir')
+    os.mkdir(tmp_dex_dir)
+    _RunD8(dex_cmd, d8_inputs, tmp_dex_dir)
+    logging.info('Performed dex merging')
 
-  dex_files = [os.path.join(tmp_dex_dir, f) for f in os.listdir(tmp_dex_dir)]
+    dex_files = [os.path.join(tmp_dex_dir, f) for f in os.listdir(tmp_dex_dir)]
 
-  if not options.output.endswith('.dex'):
-    tmp_dex_output = os.path.join(tmp_dir, 'tmp_dex_output.zip')
-    _ZipAligned(sorted(dex_files), tmp_dex_output)
+    if options.output.endswith('.dex'):
+      if len(dex_files) > 1:
+        raise Exception('%d files created, expected 1' % len(dex_files))
+      tmp_dex_output = dex_files[0]
+    else:
+      _ZipAligned(sorted(dex_files), tmp_dex_output)
   else:
-    # Output to a .dex file.
-    if len(dex_files) > 1:
-      raise Exception('%d files created, expected 1' % len(dex_files))
-    tmp_dex_output = dex_files[0]
+    # Skip dexmerger. Just put all incrementals into the .jar individually.
+    _ZipAligned(sorted(d8_inputs), tmp_dex_output)
+    logging.info('Quick-zipped %d files', len(d8_inputs))
 
   if options.dexlayout_profile:
     tmp_dex_output = _PerformDexlayout(tmp_dir, tmp_dex_output, options)
@@ -344,15 +354,18 @@
     changes = None
   class_files = _ExtractClassFiles(changes, tmp_extract_dir,
                                    options.class_inputs)
+  logging.info('Extracted class files: %d', len(class_files))
 
   # If the only change is deleting a file, class_files will be empty.
   if class_files:
     # Dex necessary classes into intermediate dex files.
     dex_cmd = dex_cmd + ['--intermediate', '--file-per-class']
     _RunD8(dex_cmd, class_files, options.incremental_dir)
+    logging.info('Dexed class files.')
 
 
 def _OnStaleMd5(changes, options, final_dex_inputs, dex_cmd):
+  logging.info('_OnStaleMd5')
   with build_utils.TempDir() as tmp_dir:
     if options.incremental_dir:
       # Create directory for all intermediate dex files.
@@ -360,14 +373,16 @@
         os.makedirs(options.incremental_dir)
 
       _DeleteStaleIncrementalDexFiles(options.incremental_dir, final_dex_inputs)
+      logging.info('Stale files deleted')
       _CreateIntermediateDexFiles(changes, options, tmp_dir, dex_cmd)
 
     _CreateFinalDex(options, final_dex_inputs, tmp_dir, dex_cmd)
+  logging.info('Dex finished for: %s', options.output)
 
 
 def main(args):
   logging.basicConfig(
-      level=logging.INFO,
+      level=logging.INFO if os.environ.get('DEX_DEBUG') else logging.WARNING,
       format='%(levelname).1s %(relativeCreated)6d %(message)s')
   options = _ParseArgs(args)
 
diff --git a/build/android/gyp/javac.py b/build/android/gyp/javac.py
index 6826826a..9745a340 100755
--- a/build/android/gyp/javac.py
+++ b/build/android/gyp/javac.py
@@ -501,7 +501,7 @@
 
 def main(argv):
   logging.basicConfig(
-      level=logging.INFO if os.environ.get('_JAVAC_DEBUG') else logging.WARNING,
+      level=logging.INFO if os.environ.get('JAVAC_DEBUG') else logging.WARNING,
       format='%(levelname).1s %(relativeCreated)6d %(message)s')
   colorama.init()
 
diff --git a/build/config/android/internal_rules.gni b/build/config/android/internal_rules.gni
index 291c66d..dff3439 100644
--- a/build/config/android/internal_rules.gni
+++ b/build/config/android/internal_rules.gni
@@ -1359,6 +1359,11 @@
             "--incremental-dir",
             rebase_path("$target_out_dir/$target_name", root_build_dir),
           ]
+          if (is_java_debug) {
+            # The performance of incremental install is unbearable if each
+            # lib.dex.jar file has multiple classes.dex files in it.
+            args += [ "--merge-incrementals" ]
+          }
         }
 
         if (_enable_multidex) {
diff --git a/build/linux/sysroot_scripts/sysroot-creator-sid.sh b/build/linux/sysroot_scripts/sysroot-creator-sid.sh
index f3bf1de2..d098b75 100755
--- a/build/linux/sysroot_scripts/sysroot-creator-sid.sh
+++ b/build/linux/sysroot_scripts/sysroot-creator-sid.sh
@@ -29,6 +29,7 @@
 HAS_ARCH_I386=1
 HAS_ARCH_ARM=1
 HAS_ARCH_ARM64=1
+HAS_ARCH_ARMEL=1
 HAS_ARCH_MIPS=1
 HAS_ARCH_MIPS64EL=1
 
@@ -395,4 +396,13 @@
   libubsan0
 "
 
+DEBIAN_PACKAGES_ARMEL="
+  libasan3
+  libdrm-exynos1
+  libdrm-freedreno1
+  libdrm-omap1
+  libdrm-tegra0
+  libubsan0
+"
+
 . "${SCRIPT_DIR}/sysroot-creator.sh"
diff --git a/build/linux/sysroot_scripts/sysroot-creator.sh b/build/linux/sysroot_scripts/sysroot-creator.sh
index 88645dad3..48513f2 100644
--- a/build/linux/sysroot_scripts/sysroot-creator.sh
+++ b/build/linux/sysroot_scripts/sysroot-creator.sh
@@ -48,6 +48,7 @@
 readonly HAS_ARCH_I386=${HAS_ARCH_I386:=0}
 readonly HAS_ARCH_ARM=${HAS_ARCH_ARM:=0}
 readonly HAS_ARCH_ARM64=${HAS_ARCH_ARM64:=0}
+readonly HAS_ARCH_ARMEL=${HAS_ARCH_ARMEL:=0}
 readonly HAS_ARCH_MIPS=${HAS_ARCH_MIPS:=0}
 readonly HAS_ARCH_MIPS64EL=${HAS_ARCH_MIPS64EL:=0}
 
@@ -65,6 +66,7 @@
 readonly DEBIAN_DEP_LIST_I386="generated_package_lists/${DIST}.i386"
 readonly DEBIAN_DEP_LIST_ARM="generated_package_lists/${DIST}.arm"
 readonly DEBIAN_DEP_LIST_ARM64="generated_package_lists/${DIST}.arm64"
+readonly DEBIAN_DEP_LIST_ARMEL="generated_package_lists/${DIST}.armel"
 readonly DEBIAN_DEP_LIST_MIPS="generated_package_lists/${DIST}.mipsel"
 readonly DEBIAN_DEP_LIST_MIPS64EL="generated_package_lists/${DIST}.mips64el"
 
@@ -148,6 +150,9 @@
     *ARM64)
       ARCH=ARM64
       ;;
+    *ARMEL)
+      ARCH=ARMEL
+      ;;
     *)
       echo "ERROR: Unable to determine architecture based on: $1"
       exit 1
@@ -267,6 +272,11 @@
     ${DEBIAN_PACKAGES_ARM64:=}"
 }
 
+GeneratePackageListARMEL() {
+  GeneratePackageListCommon "$1" armel "${DEBIAN_PACKAGES}
+    ${DEBIAN_PACKAGES_ARMEL:=}"
+}
+
 GeneratePackageListMips() {
   GeneratePackageListCommon "$1" mipsel "${DEBIAN_PACKAGES}"
 }
@@ -350,13 +360,15 @@
   HacksAndPatchesCommon arm linux-gnueabihf arm-linux-gnueabihf-strip
 }
 
-
 HacksAndPatchesARM64() {
   # Use the unstripped libdbus for arm64 to prevent linker errors.
   # https://bugs.chromium.org/p/webrtc/issues/detail?id=8535
   HacksAndPatchesCommon aarch64 linux-gnu true
 }
 
+HacksAndPatchesARMEL() {
+  HacksAndPatchesCommon arm linux-gnueabi arm-linux-gnueabi-strip
+}
 
 HacksAndPatchesMips() {
   HacksAndPatchesCommon mipsel linux-gnu mipsel-linux-gnu-strip
@@ -423,6 +435,7 @@
   if [ "${ARCH}" != "MIPS" ]; then
     libdirs="${libdirs} lib64"
   fi
+
   find $libdirs -type l -printf '%p %l\n' | while read link target; do
     # skip links with non-absolute paths
     echo "${target}" | grep -qs ^/ || continue
@@ -485,6 +498,9 @@
   VerifyLibraryDepsCommon aarch64 linux-gnu
 }
 
+VerifyLibraryDepsARMEL() {
+  VerifyLibraryDepsCommon arm linux-gnueabi
+}
 
 VerifyLibraryDepsMips() {
   VerifyLibraryDepsCommon mipsel linux-gnu
@@ -577,6 +593,26 @@
 }
 
 #@
+#@ BuildSysrootARMEL
+#@
+#@    Build everything and package it
+BuildSysrootARMEL() {
+  if [ "$HAS_ARCH_ARMEL" = "0" ]; then
+    return
+  fi
+  ClearInstallDir
+  local package_file="${DEBIAN_DEP_LIST_ARMEL}"
+  GeneratePackageListARMEL "$package_file"
+  local files_and_sha256sums="$(cat ${package_file})"
+  StripChecksumsFromPackageList "$package_file"
+  InstallIntoSysroot ${files_and_sha256sums}
+  CleanupJailSymlinks
+  HacksAndPatchesARMEL
+  VerifyLibraryDepsARMEL
+  CreateTarBall
+}
+
+#@
 #@ BuildSysrootMips
 #@
 #@    Build everything and package it
@@ -625,6 +661,7 @@
   RunCommand BuildSysrootI386
   RunCommand BuildSysrootARM
   RunCommand BuildSysrootARM64
+  RunCommand BuildSysrootARMEL
   RunCommand BuildSysrootMips
   RunCommand BuildSysrootMips64el
 }
@@ -678,6 +715,16 @@
 }
 
 #@
+#@ UploadSysrootARMEL
+#@
+UploadSysrootARMEL() {
+  if [ "$HAS_ARCH_ARMEL" = "0" ]; then
+    return
+  fi
+  UploadSysroot "$@"
+}
+
+#@
 #@ UploadSysrootMips
 #@
 UploadSysrootMips() {
@@ -706,6 +753,7 @@
   RunCommand UploadSysrootI386 "$@"
   RunCommand UploadSysrootARM "$@"
   RunCommand UploadSysrootARM64 "$@"
+  RunCommand UploadSysrootARMEL "$@"
   RunCommand UploadSysrootMips "$@"
   RunCommand UploadSysrootMips64el "$@"
 
@@ -812,6 +860,9 @@
   if [ "$HAS_ARCH_ARM64" = "1" ]; then
     echo ARM64
   fi
+  if [ "$HAS_ARCH_ARMEL" = "1" ]; then
+    echo ARMEL
+  fi
   if [ "$HAS_ARCH_MIPS" = "1" ]; then
     echo Mips
   fi
diff --git a/build/linux/sysroot_scripts/sysroots.json b/build/linux/sysroot_scripts/sysroots.json
index d49621ce..188909d 100644
--- a/build/linux/sysroot_scripts/sysroots.json
+++ b/build/linux/sysroot_scripts/sysroots.json
@@ -14,6 +14,11 @@
         "SysrootDir": "debian_sid_arm64-sysroot",
         "Tarball": "debian_sid_arm64_sysroot.tar.xz"
     },
+    "sid_armel": {
+        "Sha1Sum": "07a9da7e88e8b6fa550b053e4338c2caa4db15f0",
+        "SysrootDir": "debian_sid_armel-sysroot",
+        "Tarball": "debian_sid_armel_sysroot.tar.xz"
+    },
     "sid_i386": {
         "Sha1Sum": "9e6279438ece6fb42b5333ca90d5e9d0c188a403",
         "SysrootDir": "debian_sid_i386-sysroot",
diff --git a/build_overrides/angle.gni b/build_overrides/angle.gni
index d92916a..c4bda38 100644
--- a/build_overrides/angle.gni
+++ b/build_overrides/angle.gni
@@ -13,4 +13,5 @@
 angle_googletest_dir = "//third_party/googletest/src"
 angle_jsoncpp_dir = "//third_party/jsoncpp"
 angle_libpng_dir = "//third_party/libpng"
+angle_spirv_cross_dir = "//third_party/spirv-cross/spirv-cross"
 angle_spirv_tools_dir = "//third_party/SPIRV-Tools/src"
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/notifications/NotificationPlatformBridgeTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/notifications/NotificationPlatformBridgeTest.java
index f9fa032..07a930e 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/notifications/NotificationPlatformBridgeTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/notifications/NotificationPlatformBridgeTest.java
@@ -30,6 +30,7 @@
 
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.base.test.util.CommandLineFlags;
+import org.chromium.base.test.util.DisableIf;
 import org.chromium.base.test.util.Feature;
 import org.chromium.base.test.util.MinAndroidSdkLevel;
 import org.chromium.base.test.util.RetryOnFailure;
@@ -609,6 +610,7 @@
     @MediumTest
     @Feature({"Browser", "Notifications"})
     @RetryOnFailure
+    @DisableIf.Build(sdk_is_greater_than = 25, message = "https://crbug.com/999357")
     public void testShowNotificationWithoutIcon() throws Exception {
         mNotificationTestRule.setNotificationContentSettingForOrigin(
                 ContentSettingValues.ALLOW, mPermissionTestRule.getOrigin());
diff --git a/chrome/app/settings_chromium_strings.grdp b/chrome/app/settings_chromium_strings.grdp
index c0761ee..223d43e 100644
--- a/chrome/app/settings_chromium_strings.grdp
+++ b/chrome/app/settings_chromium_strings.grdp
@@ -23,10 +23,6 @@
     </message>
   </if>
   <if expr="chromeos">
-    <message name="IDS_SETTINGS_ABOUT_OS_BANNER" desc="Banner displayed in the browser About page that links to the OS About page.">
-      For Chromium OS details, please see <ph name="LINK_BEGIN">&lt;a href="chrome://os-settings/help"&gt;</ph>
-      About Chromium OS<ph name="LINK_END">&lt;/a&gt;</ph>
-    </message>
     <message name="IDS_SETTINGS_ABOUT_OS" desc="Menu title for the About Chromium OS page.">
       About Chromium OS
     </message>
diff --git a/chrome/app/settings_google_chrome_strings.grdp b/chrome/app/settings_google_chrome_strings.grdp
index dd7861d..2474a21 100644
--- a/chrome/app/settings_google_chrome_strings.grdp
+++ b/chrome/app/settings_google_chrome_strings.grdp
@@ -23,10 +23,6 @@
     </message>
   </if>
   <if expr="chromeos">
-    <message name="IDS_SETTINGS_ABOUT_OS_BANNER" desc="Banner displayed in the browser About page that links to the OS About page.">
-      For Chrome OS details, please see <ph name="LINK_BEGIN">&lt;a href="chrome://os-settings/help"&gt;</ph>
-      About Chrome OS<ph name="LINK_END">&lt;/a&gt;</ph>
-    </message>
     <message name="IDS_SETTINGS_ABOUT_OS" desc="Menu title for the About Chrome OS page.">
       About Chrome OS
     </message>
diff --git a/chrome/app/settings_strings.grdp b/chrome/app/settings_strings.grdp
index ca0d70a..0136e884 100644
--- a/chrome/app/settings_strings.grdp
+++ b/chrome/app/settings_strings.grdp
@@ -192,11 +192,14 @@
   <message name="IDS_SETTINGS_CAPTIONS_BACKGROUND_OPACITY" desc="Name of the caption background opacity preference.">
     Background opacity
   </message>
-  <message name="IDS_SETTINGS_CAPTIONS_OPACITY_MIN" desc="Value of the minimum captions text opacity setting.">
-    0
+  <message name="IDS_SETTINGS_CAPTIONS_OPACITY_OPAQUE" desc="Name of the opaque opacity preference.">
+    Opaque
   </message>
-  <message name="IDS_SETTINGS_CAPTIONS_OPACITY_MAX" desc="Value of the maximum captions text opacity setting.">
-    100
+  <message name="IDS_SETTINGS_CAPTIONS_OPACITY_SEMI_TRANSPARENT" desc="Name of the semi-transparent opacity preference.">
+    Semi-transparent
+  </message>
+  <message name="IDS_SETTINGS_CAPTIONS_OPACITY_TRANSPARENT" desc="Name of the transparent opacity preference.">
+    Transparent
   </message>
   <message name="IDS_SETTINGS_CAPTIONS_TEXT_SHADOW" desc="Name of the caption text shadow preference.">
     Text shadow
@@ -2877,6 +2880,9 @@
     <message name="IDS_SETTINGS_LANGUAGES_ADD_DICTIONARY_WORD_LENGTH_ERROR" desc="Error message displayed to the user when the word is too long in the text input used to add a new word to the custom spell check dictionary.">
       Cannot exceed 99 letters
     </message>
+    <message name="IDS_SETTINGS_LANGUAGES_DELETE_DICTIONARY_WORD_BUTTON" desc="Message shown over the button to delete a custom word.">
+      Delete word
+    </message>
     <message name="IDS_SETTINGS_LANGUAGES_DICTIONARY_WORDS" desc="Header for the list of custom dictionary words used for spell check.">
       Custom words
     </message>
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_CAPTIONS.png.sha1 b/chrome/app/settings_strings_grdp/IDS_SETTINGS_CAPTIONS.png.sha1
index ed36b53..bb673d3 100644
--- a/chrome/app/settings_strings_grdp/IDS_SETTINGS_CAPTIONS.png.sha1
+++ b/chrome/app/settings_strings_grdp/IDS_SETTINGS_CAPTIONS.png.sha1
@@ -1 +1 @@
-39f3d045711d6364907ad1e709fa85cfac133726
\ No newline at end of file
+9e5cc39d8f7e3468604037bee09decfa463359ff
\ No newline at end of file
diff --git a/chrome/app/theme/default_100_percent/common/captions-preview-bg.png b/chrome/app/theme/default_100_percent/common/captions-preview-bg.png
new file mode 100644
index 0000000..3b2fe7b
--- /dev/null
+++ b/chrome/app/theme/default_100_percent/common/captions-preview-bg.png
Binary files differ
diff --git a/chrome/app/theme/theme_resources.grd b/chrome/app/theme/theme_resources.grd
index 86b3b062..3f8e6bf 100644
--- a/chrome/app/theme/theme_resources.grd
+++ b/chrome/app/theme/theme_resources.grd
@@ -25,6 +25,9 @@
       <!-- KEEP THESE IN ALPHABETICAL ORDER!  DO NOT ADD TO RANDOM PLACES JUST
            BECAUSE YOUR RESOURCES ARE FUNCTIONALLY RELATED OR FALL UNDER THE
            SAME CONDITIONALS. -->
+      <if expr="is_linux or is_win or chromeos">
+        <structure type="chrome_scaled_image" name="IDR_ACCESSIBILITY_CAPTIONS_PREVIEW_BACKGROUND" file="common/captions-preview-bg.png" />
+      </if>
       <if expr="toolkit_views and not is_macosx">
         <structure type="chrome_scaled_image" name="IDR_APP_WINDOW_CLOSE" file="common/app_window_close.png" />
         <structure type="chrome_scaled_image" name="IDR_APP_WINDOW_CLOSE_H" file="common/app_window_close_hover.png" />
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 312ed04..15edad8 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -975,6 +975,9 @@
     {security_state::features::kMarkHttpAsFeatureParameterName,
      security_state::features::
          kMarkHttpAsParameterWarningAndDangerousOnFormEdits}};
+const FeatureEntry::FeatureParam kMarkHttpAsDangerWarning[] = {
+    {security_state::features::kMarkHttpAsFeatureParameterName,
+     security_state::features::kMarkHttpAsParameterDangerWarning}};
 
 // The "Enabled" state for this feature is "0" and representing setting A.
 const FeatureEntry::FeatureParam kTabHoverCardsSettingB[] = {
@@ -991,7 +994,9 @@
      base::size(kMarkHttpAsDangerous), nullptr},
     {"(mark with a Not Secure warning and dangerous on form edits)",
      kMarkHttpAsWarningAndDangerousOnFormEdits,
-     base::size(kMarkHttpAsWarningAndDangerousOnFormEdits), nullptr}};
+     base::size(kMarkHttpAsWarningAndDangerousOnFormEdits), nullptr},
+    {"(mark with a grey triangle icon)", kMarkHttpAsDangerWarning,
+     base::size(kMarkHttpAsDangerWarning), nullptr}};
 
 #if defined(OS_ANDROID)
 const FeatureEntry::FeatureParam kTranslateForceTriggerOnEnglishHeuristic[] = {
@@ -3483,6 +3488,11 @@
      kOsDesktop,
      FEATURE_VALUE_TYPE(features::kExperimentalAccessibilityLabels)},
 
+    {"enable-accessibility-expose-aria-annotations",
+     flag_descriptions::kAccessibilityExposeARIAAnnotationsName,
+     flag_descriptions::kAccessibilityExposeARIAAnnotationsDescription, kOsAll,
+     FEATURE_VALUE_TYPE(features::kEnableAccessibilityExposeARIAAnnotations)},
+
     {"enable-accessibility-expose-display-none",
      flag_descriptions::kAccessibilityExposeDisplayNoneName,
      flag_descriptions::kAccessibilityExposeDisplayNoneDescription, kOsAll,
diff --git a/chrome/browser/browser_resources.grd b/chrome/browser/browser_resources.grd
index bcde3bc5..6bad9bc 100644
--- a/chrome/browser/browser_resources.grd
+++ b/chrome/browser/browser_resources.grd
@@ -545,7 +545,7 @@
         <include name="IDR_IME_WINDOW_CLOSE_C" file="resources\input_ime\ime_window_close_click.png" type="BINDATA" />
         <include name="IDR_IME_WINDOW_CLOSE_H" file="resources\input_ime\ime_window_close_hover.png" type="BINDATA" />
       </if>
-      <include name="IDR_SSL_ERROR_ASSISTANT_PB" file="${root_gen_dir}/chrome/browser/resources/ssl/ssl_error_assistant/ssl_error_assistant.pb" use_base_dir="false" type="BINDATA" />
+      <include name="IDR_SSL_ERROR_ASSISTANT_PB" file="${root_gen_dir}/chrome/browser/resources/ssl/ssl_error_assistant/ssl_error_assistant.pb" use_base_dir="false" type="BINDATA" compress="gzip" />
       <if expr="is_android or is_linux">
         <include name="IDR_SANDBOX_INTERNALS_HTML" file="resources\sandbox_internals\sandbox_internals.html" flattenhtml="true" allowexternalscript="true" type="BINDATA" compress="gzip" />
         <include name="IDR_SANDBOX_INTERNALS_JS" file="resources\sandbox_internals\sandbox_internals.js" type="BINDATA" compress="gzip" />
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn
index 7c704a8..0916a67 100644
--- a/chrome/browser/chromeos/BUILD.gn
+++ b/chrome/browser/chromeos/BUILD.gn
@@ -811,6 +811,8 @@
     "crostini/fake_crostini_installer_ui_delegate.h",
     "crostini/throttle/crostini_throttle.cc",
     "crostini/throttle/crostini_throttle.h",
+    "crostini/throttle/crostini_active_window_throttle_observer.cc",
+    "crostini/throttle/crostini_active_window_throttle_observer.h",
     "cryptauth/client_app_metadata_provider_service.cc",
     "cryptauth/client_app_metadata_provider_service.h",
     "cryptauth/client_app_metadata_provider_service_factory.cc",
@@ -1937,6 +1939,8 @@
     "printing/history/print_job_database.h",
     "printing/history/print_job_database_impl.cc",
     "printing/history/print_job_database_impl.h",
+    "printing/history/print_job_history_cleaner.cc",
+    "printing/history/print_job_history_cleaner.h",
     "printing/history/print_job_history_service.cc",
     "printing/history/print_job_history_service.h",
     "printing/history/print_job_history_service_factory.cc",
@@ -2538,6 +2542,7 @@
     "crostini/crosvm_metrics_unittest.cc",
     "crostini/crosvm_process_list_unittest.cc",
     "crostini/throttle/crostini_throttle_unittest.cc",
+    "crostini/throttle/crostini_active_window_throttle_observer_unittest.cc",
     "cryptauth/client_app_metadata_provider_service_unittest.cc",
     "customization/customization_document_unittest.cc",
     "dbus/proxy_resolution_service_provider_unittest.cc",
@@ -2786,6 +2791,7 @@
     "printing/history/mock_print_job_history_service.cc",
     "printing/history/mock_print_job_history_service.h",
     "printing/history/print_job_database_impl_unittest.cc",
+    "printing/history/print_job_history_cleaner_unittest.cc",
     "printing/history/print_job_history_service_impl_unittest.cc",
     "printing/history/print_job_info_proto_conversions_unittest.cc",
     "printing/history/test_print_job_database.cc",
diff --git a/chrome/browser/chromeos/crostini/crostini_manager.cc b/chrome/browser/chromeos/crostini/crostini_manager.cc
index f371d3e..9e5a0059 100644
--- a/chrome/browser/chromeos/crostini/crostini_manager.cc
+++ b/chrome/browser/chromeos/crostini/crostini_manager.cc
@@ -27,6 +27,7 @@
 #include "chrome/browser/chromeos/crostini/crostini_pref_names.h"
 #include "chrome/browser/chromeos/crostini/crostini_remover.h"
 #include "chrome/browser/chromeos/crostini/crostini_reporting_util.h"
+#include "chrome/browser/chromeos/crostini/throttle/crostini_throttle.h"
 #include "chrome/browser/chromeos/file_manager/path_util.h"
 #include "chrome/browser/chromeos/file_manager/volume_manager.h"
 #include "chrome/browser/chromeos/guest_os/guest_os_share_path.h"
@@ -633,6 +634,7 @@
   if (chromeos::PowerManagerClient::Get()) {
     chromeos::PowerManagerClient::Get()->AddObserver(this);
   }
+  CrostiniThrottle::GetForBrowserContext(profile_);
 }
 
 CrostiniManager::~CrostiniManager() {
diff --git a/chrome/browser/chromeos/crostini/throttle/crostini_active_window_throttle_observer.cc b/chrome/browser/chromeos/crostini/throttle/crostini_active_window_throttle_observer.cc
new file mode 100644
index 0000000..3010ae4
--- /dev/null
+++ b/chrome/browser/chromeos/crostini/throttle/crostini_active_window_throttle_observer.cc
@@ -0,0 +1,28 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/chromeos/crostini/throttle/crostini_active_window_throttle_observer.h"
+
+#include "ash/public/cpp/app_types.h"
+#include "ui/aura/client/aura_constants.h"
+#include "ui/aura/window.h"
+
+namespace crostini {
+
+CrostiniActiveWindowThrottleObserver::CrostiniActiveWindowThrottleObserver()
+    : WindowThrottleObserverBase(ThrottleObserver::PriorityLevel::CRITICAL,
+                                 "CrostiniWindowIsActiveWindow") {}
+
+bool CrostiniActiveWindowThrottleObserver::ProcessWindowActivation(
+    ActivationReason reason,
+    aura::Window* gained_active,
+    aura::Window* lost_active) {
+  if (!gained_active)
+    return false;
+  // Return true if the gained_active window is a Crostini app.
+  return gained_active->GetProperty(aura::client::kAppType) ==
+         static_cast<int>(ash::AppType::CROSTINI_APP);
+}
+
+}  // namespace crostini
diff --git a/chrome/browser/chromeos/crostini/throttle/crostini_active_window_throttle_observer.h b/chrome/browser/chromeos/crostini/throttle/crostini_active_window_throttle_observer.h
new file mode 100644
index 0000000..20c0143e
--- /dev/null
+++ b/chrome/browser/chromeos/crostini/throttle/crostini_active_window_throttle_observer.h
@@ -0,0 +1,32 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_CHROMEOS_CROSTINI_THROTTLE_CROSTINI_ACTIVE_WINDOW_THROTTLE_OBSERVER_H_
+#define CHROME_BROWSER_CHROMEOS_CROSTINI_THROTTLE_CROSTINI_ACTIVE_WINDOW_THROTTLE_OBSERVER_H_
+
+#include "base/macros.h"
+#include "chrome/browser/chromeos/window_throttle_observer_base.h"
+
+namespace crostini {
+
+// This class observes window activations and sets the state to active if the
+// currently active window is a Crostini window.
+class CrostiniActiveWindowThrottleObserver
+    : public chromeos::WindowThrottleObserverBase {
+ public:
+  CrostiniActiveWindowThrottleObserver();
+  ~CrostiniActiveWindowThrottleObserver() override = default;
+
+  // WindowThrottleObserverBase:
+  bool ProcessWindowActivation(ActivationReason reason,
+                               aura::Window* gained_active,
+                               aura::Window* lost_active) override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(CrostiniActiveWindowThrottleObserver);
+};
+
+}  // namespace crostini
+
+#endif  // CHROME_BROWSER_CHROMEOS_CROSTINI_THROTTLE_CROSTINI_ACTIVE_WINDOW_THROTTLE_OBSERVER_H_
diff --git a/chrome/browser/chromeos/crostini/throttle/crostini_active_window_throttle_observer_unittest.cc b/chrome/browser/chromeos/crostini/throttle/crostini_active_window_throttle_observer_unittest.cc
new file mode 100644
index 0000000..89129db
--- /dev/null
+++ b/chrome/browser/chromeos/crostini/throttle/crostini_active_window_throttle_observer_unittest.cc
@@ -0,0 +1,65 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/chromeos/crostini/throttle/crostini_active_window_throttle_observer.h"
+
+#include "ash/public/cpp/app_types.h"
+#include "base/test/task_environment.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/aura/client/aura_constants.h"
+#include "ui/aura/test/test_window_delegate.h"
+#include "ui/aura/test/test_windows.h"
+
+namespace crostini {
+
+class CrostiniActiveWindowThrottleObserverTest : public testing::Test {
+ public:
+  using testing::Test::Test;
+
+ protected:
+  CrostiniActiveWindowThrottleObserver* observer() { return &observer_; }
+
+ private:
+  base::test::SingleThreadTaskEnvironment task_environment_{
+      base::test::TaskEnvironment::MainThreadType::UI};
+  CrostiniActiveWindowThrottleObserver observer_;
+
+  DISALLOW_COPY_AND_ASSIGN(CrostiniActiveWindowThrottleObserverTest);
+};
+
+TEST_F(CrostiniActiveWindowThrottleObserverTest, TestConstructDestruct) {}
+
+TEST_F(CrostiniActiveWindowThrottleObserverTest, TestOnWindowActivated) {
+  aura::test::TestWindowDelegate dummy_delegate;
+  aura::Window* crostini_window = aura::test::CreateTestWindowWithDelegate(
+      &dummy_delegate, 1, gfx::Rect(), nullptr);
+  aura::Window* chrome_window = aura::test::CreateTestWindowWithDelegate(
+      &dummy_delegate, 2, gfx::Rect(), nullptr);
+  crostini_window->SetProperty(aura::client::kAppType,
+                               static_cast<int>(ash::AppType::CROSTINI_APP));
+  chrome_window->SetProperty(aura::client::kAppType,
+                             static_cast<int>(ash::AppType::BROWSER));
+
+  EXPECT_FALSE(observer()->active());
+
+  // Test observer is active for crostini window.
+  observer()->OnWindowActivated(
+      CrostiniActiveWindowThrottleObserver::ActivationReason::INPUT_EVENT,
+      crostini_window, chrome_window);
+  EXPECT_TRUE(observer()->active());
+
+  // Test observer is inactive for non-crostini window.
+  observer()->OnWindowActivated(
+      CrostiniActiveWindowThrottleObserver::ActivationReason::INPUT_EVENT,
+      chrome_window, crostini_window);
+  EXPECT_FALSE(observer()->active());
+
+  // Test observer is inactive for null gained_active window.
+  observer()->OnWindowActivated(
+      CrostiniActiveWindowThrottleObserver::ActivationReason::INPUT_EVENT,
+      nullptr, crostini_window);
+  EXPECT_FALSE(observer()->active());
+}
+
+}  // namespace crostini
diff --git a/chrome/browser/chromeos/crostini/throttle/crostini_throttle.cc b/chrome/browser/chromeos/crostini/throttle/crostini_throttle.cc
index bc41786..8147ea1 100644
--- a/chrome/browser/chromeos/crostini/throttle/crostini_throttle.cc
+++ b/chrome/browser/chromeos/crostini/throttle/crostini_throttle.cc
@@ -7,6 +7,7 @@
 #include "base/no_destructor.h"
 #include "chrome/browser/chromeos/concierge_helper_service.h"
 #include "chrome/browser/chromeos/crostini/crostini_util.h"
+#include "chrome/browser/chromeos/crostini/throttle/crostini_active_window_throttle_observer.h"
 #include "chrome/browser/profiles/profile.h"
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
 #include "components/keyed_service/content/browser_context_keyed_service_factory.h"
@@ -77,6 +78,7 @@
 CrostiniThrottle::CrostiniThrottle(content::BrowserContext* context)
     : ThrottleService(context),
       delegate_(std::make_unique<DefaultDelegateImpl>(context)) {
+  AddObserver(std::make_unique<CrostiniActiveWindowThrottleObserver>());
   StartObservers();
 }
 
diff --git a/chrome/browser/chromeos/preferences.cc b/chrome/browser/chromeos/preferences.cc
index 81d7ce9..7477548a 100644
--- a/chrome/browser/chromeos/preferences.cc
+++ b/chrome/browser/chromeos/preferences.cc
@@ -115,25 +115,6 @@
                     static_cast<int>(method));
 }
 
-// Whitelist synable preferences that may be registered after sync system init.
-void WhitelistLateRegistrationPrefsForSync(
-    user_prefs::PrefRegistrySyncable* registry) {
-  // These foreign syncable preferences are registered asynchronously by Ash,
-  // perhaps after sync system initialization. Whitelist these prefs so that any
-  // values obtained via sync before the prefs are registered will be stored.
-  const char* const kAshForeignSyncablePrefs[] = {
-      ash::prefs::kEnableAutoScreenLock,
-      ash::prefs::kEnableStylusTools,
-      ash::prefs::kLaunchPaletteOnEjectEvent,
-      ash::prefs::kMessageCenterLockScreenMode,
-      ash::prefs::kShelfAlignment,
-      ash::prefs::kShelfAutoHideBehavior,
-      ash::prefs::kTapDraggingEnabled,
-  };
-  for (const auto* pref : kAshForeignSyncablePrefs)
-    registry->WhitelistLateRegistrationPrefForSync(pref);
-}
-
 }  // namespace
 
 Preferences::Preferences()
@@ -184,8 +165,6 @@
 // static
 void Preferences::RegisterProfilePrefs(
     user_prefs::PrefRegistrySyncable* registry) {
-  WhitelistLateRegistrationPrefsForSync(registry);
-
   std::string hardware_keyboard_id;
   // TODO(yusukes): Remove the runtime hack.
   if (IsRunningAsSystemCompositor()) {
@@ -587,7 +566,6 @@
 
   registry->RegisterBooleanPref(prefs::kSettingsShowBrowserBanner, true);
   registry->RegisterBooleanPref(prefs::kSettingsShowOSBanner, true);
-  registry->RegisterBooleanPref(prefs::kSettingsShowAboutOSBanner, true);
 }
 
 void Preferences::InitUserPrefs(sync_preferences::PrefServiceSyncable* prefs) {
diff --git a/chrome/browser/chromeos/printing/history/print_job_history_cleaner.cc b/chrome/browser/chromeos/printing/history/print_job_history_cleaner.cc
new file mode 100644
index 0000000..b70e94c
--- /dev/null
+++ b/chrome/browser/chromeos/printing/history/print_job_history_cleaner.cc
@@ -0,0 +1,110 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/chromeos/printing/history/print_job_history_cleaner.h"
+
+#include "base/bind.h"
+#include "base/time/clock.h"
+#include "base/time/default_clock.h"
+#include "base/time/time.h"
+#include "base/timer/timer.h"
+#include "chrome/browser/chromeos/printing/history/print_job_database.h"
+#include "chrome/browser/chromeos/printing/history/print_job_info.pb.h"
+#include "chrome/common/pref_names.h"
+#include "components/prefs/pref_service.h"
+
+namespace chromeos {
+
+namespace {
+
+constexpr base::TimeDelta kPrintJobHistoryUpdateInterval =
+    base::TimeDelta::FromDays(1);
+
+// This "PrintJobHistoryExpirationPeriod" policy value stands for storing the
+// print job history indefinitely.
+constexpr int kPrintJobHistoryIndefinite = -1;
+
+// Returns true if |pref_service| has been initialized.
+bool IsPrefServiceInitialized(PrefService* pref_service) {
+  return pref_service->GetAllPrefStoresInitializationStatus() !=
+         PrefService::INITIALIZATION_STATUS_WAITING;
+}
+
+bool IsPrintJobExpired(const printing::proto::PrintJobInfo& print_job_info,
+                       base::Time now,
+                       base::TimeDelta print_job_history_expiration_period) {
+  base::Time completion_time =
+      base::Time::FromJsTime(print_job_info.completion_time());
+  return completion_time + print_job_history_expiration_period < now;
+}
+
+}  // namespace
+
+PrintJobHistoryCleaner::PrintJobHistoryCleaner(
+    PrintJobDatabase* print_job_database,
+    PrefService* pref_service)
+    : print_job_database_(print_job_database),
+      pref_service_(pref_service),
+      clock_(base::DefaultClock::GetInstance()),
+      timer_(std::make_unique<base::RepeatingTimer>()) {}
+
+PrintJobHistoryCleaner::~PrintJobHistoryCleaner() = default;
+
+void PrintJobHistoryCleaner::Start() {
+  timer_->Start(FROM_HERE, kPrintJobHistoryUpdateInterval, this,
+                &PrintJobHistoryCleaner::CleanUp);
+  CleanUp();
+}
+
+void PrintJobHistoryCleaner::CleanUp() {
+  if (IsPrefServiceInitialized(pref_service_)) {
+    OnPrefServiceInitialized(true);
+    return;
+  }
+  // Register for a callback that will be invoked when |pref_service_| is
+  // initialized.
+  pref_service_->AddPrefInitObserver(
+      base::BindOnce(&PrintJobHistoryCleaner::OnPrefServiceInitialized,
+                     weak_ptr_factory_.GetWeakPtr()));
+}
+
+void PrintJobHistoryCleaner::OverrideTimeForTesting(
+    const base::Clock* clock,
+    std::unique_ptr<base::RepeatingTimer> timer) {
+  clock_ = clock;
+  timer_ = std::move(timer);
+}
+
+void PrintJobHistoryCleaner::OnPrefServiceInitialized(bool success) {
+  if (!success || !print_job_database_->IsInitialized() ||
+      pref_service_->GetInteger(prefs::kPrintJobHistoryExpirationPeriod) ==
+          kPrintJobHistoryIndefinite) {
+    return;
+  }
+  print_job_database_->GetPrintJobs(
+      base::BindOnce(&PrintJobHistoryCleaner::OnPrintJobsRetrieved,
+                     weak_ptr_factory_.GetWeakPtr()));
+}
+
+void PrintJobHistoryCleaner::OnPrintJobsRetrieved(
+    bool success,
+    std::unique_ptr<std::vector<printing::proto::PrintJobInfo>>
+        print_job_infos) {
+  if (!success || !print_job_infos)
+    return;
+  std::vector<std::string> print_job_ids_to_remove;
+  base::TimeDelta print_job_history_expiration_period =
+      base::TimeDelta::FromDays(
+          pref_service_->GetInteger(prefs::kPrintJobHistoryExpirationPeriod));
+  for (const auto& print_job_info : *print_job_infos) {
+    if (IsPrintJobExpired(print_job_info, clock_->Now(),
+                          print_job_history_expiration_period)) {
+      print_job_ids_to_remove.push_back(print_job_info.id());
+    }
+  }
+  print_job_database_->DeletePrintJobs(print_job_ids_to_remove,
+                                       base::DoNothing());
+}
+
+}  // namespace chromeos
diff --git a/chrome/browser/chromeos/printing/history/print_job_history_cleaner.h b/chrome/browser/chromeos/printing/history/print_job_history_cleaner.h
new file mode 100644
index 0000000..f18076b
--- /dev/null
+++ b/chrome/browser/chromeos/printing/history/print_job_history_cleaner.h
@@ -0,0 +1,68 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_CHROMEOS_PRINTING_HISTORY_PRINT_JOB_HISTORY_CLEANER_H_
+#define CHROME_BROWSER_CHROMEOS_PRINTING_HISTORY_PRINT_JOB_HISTORY_CLEANER_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "chrome/browser/chromeos/printing/history/print_job_database.h"
+
+class PrefService;
+
+namespace base {
+class Clock;
+class RepeatingTimer;
+}  // namespace base
+
+namespace chromeos {
+
+class PrintJobDatabase;
+
+class PrintJobHistoryCleaner {
+ public:
+  // The default amount of time the metadata of completed print job is stored on
+  // the device.
+  static constexpr int kDefaultPrintJobHistoryExpirationPeriodDays = 90;
+
+  PrintJobHistoryCleaner(PrintJobDatabase* print_job_database,
+                         PrefService* pref_service);
+  ~PrintJobHistoryCleaner();
+
+  void Start();
+  void CleanUp();
+
+  // Overrides elements responsible for time progression to allow testing.
+  void OverrideTimeForTesting(const base::Clock* clock,
+                              std::unique_ptr<base::RepeatingTimer> timer);
+
+ private:
+  void OnPrefServiceInitialized(bool success);
+  void OnPrintJobsRetrieved(
+      bool success,
+      std::unique_ptr<std::vector<printing::proto::PrintJobInfo>>
+          print_job_infos);
+
+  // This object is owned by PrintJobHistoryService and outlives
+  // PrintJobHistoryCleaner.
+  PrintJobDatabase* print_job_database_;
+
+  PrefService* pref_service_;
+
+  // Points to the base::DefaultClock by default.
+  const base::Clock* clock_;
+
+  // Timer used to update |print_job_database_|.
+  std::unique_ptr<base::RepeatingTimer> timer_;
+
+  base::WeakPtrFactory<PrintJobHistoryCleaner> weak_ptr_factory_{this};
+
+  DISALLOW_COPY_AND_ASSIGN(PrintJobHistoryCleaner);
+};
+
+}  // namespace chromeos
+
+#endif  // CHROME_BROWSER_CHROMEOS_PRINTING_HISTORY_PRINT_JOB_HISTORY_CLEANER_H_
diff --git a/chrome/browser/chromeos/printing/history/print_job_history_cleaner_unittest.cc b/chrome/browser/chromeos/printing/history/print_job_history_cleaner_unittest.cc
new file mode 100644
index 0000000..c201dbb
--- /dev/null
+++ b/chrome/browser/chromeos/printing/history/print_job_history_cleaner_unittest.cc
@@ -0,0 +1,157 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/chromeos/printing/history/print_job_history_cleaner.h"
+#include "base/bind.h"
+#include "base/run_loop.h"
+#include "base/test/task_environment.h"
+#include "base/time/clock.h"
+#include "base/time/time.h"
+#include "base/timer/timer.h"
+#include "chrome/browser/chromeos/printing/history/print_job_history_service.h"
+#include "chrome/browser/chromeos/printing/history/print_job_info.pb.h"
+#include "chrome/browser/chromeos/printing/history/test_print_job_database.h"
+#include "chrome/common/pref_names.h"
+#include "components/prefs/testing_pref_service.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace chromeos {
+
+using printing::proto::PrintJobInfo;
+
+namespace {
+
+constexpr char kId1[] = "id1";
+constexpr char kId2[] = "id2";
+
+PrintJobInfo ConstructPrintJobInfo(const std::string& id,
+                                   const base::Time& completion_time) {
+  PrintJobInfo print_job_info;
+  print_job_info.set_id(id);
+  print_job_info.set_completion_time(
+      static_cast<int64_t>(completion_time.ToJsTime()));
+  return print_job_info;
+}
+
+}  // namespace
+
+class PrintJobHistoryCleanerTest : public ::testing::Test {
+ public:
+  PrintJobHistoryCleanerTest() = default;
+
+  void SetUp() override {
+    test_prefs_.SetInitializationCompleted();
+
+    print_job_database_ = std::make_unique<TestPrintJobDatabase>();
+    print_job_history_cleaner_ = std::make_unique<PrintJobHistoryCleaner>(
+        print_job_database_.get(), &test_prefs_);
+
+    auto timer = std::make_unique<base::RepeatingTimer>(
+        task_environment_.GetMockTickClock());
+    timer->SetTaskRunner(task_environment_.GetMainThreadTaskRunner());
+    print_job_history_cleaner_->OverrideTimeForTesting(
+        task_environment_.GetMockClock(), std::move(timer));
+
+    PrintJobHistoryService::RegisterProfilePrefs(test_prefs_.registry());
+  }
+
+  void TearDown() override {}
+
+ protected:
+  void SavePrintJob(const PrintJobInfo& print_job_info) {
+    base::RunLoop run_loop;
+    print_job_database_->SavePrintJob(
+        print_job_info,
+        base::BindOnce(&PrintJobHistoryCleanerTest::OnPrintJobSaved,
+                       base::Unretained(this), run_loop.QuitClosure()));
+    run_loop.Run();
+  }
+
+  void OnPrintJobSaved(base::RepeatingClosure run_loop_closure, bool success) {
+    EXPECT_TRUE(success);
+    run_loop_closure.Run();
+  }
+
+  std::vector<PrintJobInfo> GetPrintJobs() {
+    base::RunLoop run_loop;
+    print_job_database_->GetPrintJobs(
+        base::BindOnce(&PrintJobHistoryCleanerTest::OnPrintJobsRetrieved,
+                       base::Unretained(this), run_loop.QuitClosure()));
+    run_loop.Run();
+    return entries_;
+  }
+
+  void OnPrintJobsRetrieved(
+      base::RepeatingClosure run_loop_closure,
+      bool success,
+      std::unique_ptr<std::vector<PrintJobInfo>> entries) {
+    EXPECT_TRUE(success);
+    entries_ = *entries;
+    run_loop_closure.Run();
+  }
+
+  base::test::TaskEnvironment task_environment_{
+      base::test::TaskEnvironment::TimeSource::MOCK_TIME};
+  TestingPrefServiceSimple test_prefs_;
+  std::unique_ptr<PrintJobDatabase> print_job_database_;
+  std::unique_ptr<PrintJobHistoryCleaner> print_job_history_cleaner_;
+
+ private:
+  std::vector<PrintJobInfo> entries_;
+};
+
+TEST_F(PrintJobHistoryCleanerTest, CleanExpiredPrintJobs) {
+  // Set expiration period to 1 day.
+  test_prefs_.SetInteger(prefs::kPrintJobHistoryExpirationPeriod, 1);
+  print_job_database_->Initialize(base::DoNothing());
+
+  task_environment_.FastForwardBy(base::TimeDelta::FromDays(300));
+  PrintJobInfo print_job_info1 =
+      ConstructPrintJobInfo(kId1, task_environment_.GetMockClock()->Now());
+  SavePrintJob(print_job_info1);
+
+  task_environment_.FastForwardBy(base::TimeDelta::FromDays(1));
+  PrintJobInfo print_job_info2 =
+      ConstructPrintJobInfo(kId2, task_environment_.GetMockClock()->Now());
+  SavePrintJob(print_job_info2);
+
+  task_environment_.FastForwardBy(base::TimeDelta::FromHours(12));
+  print_job_history_cleaner_->Start();
+  task_environment_.RunUntilIdle();
+
+  std::vector<PrintJobInfo> entries = GetPrintJobs();
+  // Start() call should clear the first entry which is expected to expire.
+  EXPECT_EQ(1u, entries.size());
+  EXPECT_EQ(kId2, entries[0].id());
+
+  task_environment_.FastForwardBy(base::TimeDelta::FromDays(1));
+  task_environment_.RunUntilIdle();
+  entries = GetPrintJobs();
+  // The second entry is expected to be removed after cleaner ran again.
+  EXPECT_TRUE(entries.empty());
+}
+
+TEST_F(PrintJobHistoryCleanerTest, StorePrintJobHistoryIndefinite) {
+  // Set expiration period policy to store history indefinitely.
+  test_prefs_.SetInteger(prefs::kPrintJobHistoryExpirationPeriod, -1);
+  print_job_database_->Initialize(base::DoNothing());
+
+  task_environment_.FastForwardBy(base::TimeDelta::FromDays(300));
+  PrintJobInfo print_job_info1 =
+      ConstructPrintJobInfo(kId1, task_environment_.GetMockClock()->Now());
+  SavePrintJob(print_job_info1);
+
+  task_environment_.FastForwardBy(base::TimeDelta::FromDays(300));
+
+  print_job_history_cleaner_->Start();
+  task_environment_.RunUntilIdle();
+
+  std::vector<PrintJobInfo> entries = GetPrintJobs();
+  // Start() call shouldn't clear anything as according to pref we store history
+  // indefinitely.
+  EXPECT_EQ(1u, entries.size());
+  EXPECT_EQ(kId1, entries[0].id());
+}
+
+}  // namespace chromeos
diff --git a/chrome/browser/chromeos/printing/history/print_job_history_service.cc b/chrome/browser/chromeos/printing/history/print_job_history_service.cc
index 38fc8b2..63472ab9b 100644
--- a/chrome/browser/chromeos/printing/history/print_job_history_service.cc
+++ b/chrome/browser/chromeos/printing/history/print_job_history_service.cc
@@ -3,6 +3,9 @@
 // found in the LICENSE file.
 
 #include "chrome/browser/chromeos/printing/history/print_job_history_service.h"
+#include "chrome/browser/chromeos/printing/history/print_job_history_cleaner.h"
+#include "chrome/common/pref_names.h"
+#include "components/prefs/pref_registry_simple.h"
 
 namespace chromeos {
 
@@ -10,6 +13,14 @@
 
 PrintJobHistoryService::~PrintJobHistoryService() = default;
 
+// static
+void PrintJobHistoryService::RegisterProfilePrefs(
+    PrefRegistrySimple* registry) {
+  registry->RegisterIntegerPref(
+      prefs::kPrintJobHistoryExpirationPeriod,
+      PrintJobHistoryCleaner::kDefaultPrintJobHistoryExpirationPeriodDays);
+}
+
 void PrintJobHistoryService::AddObserver(
     PrintJobHistoryService::Observer* observer) {
   observers_.AddObserver(observer);
diff --git a/chrome/browser/chromeos/printing/history/print_job_history_service.h b/chrome/browser/chromeos/printing/history/print_job_history_service.h
index 9b13adf..c520b318 100644
--- a/chrome/browser/chromeos/printing/history/print_job_history_service.h
+++ b/chrome/browser/chromeos/printing/history/print_job_history_service.h
@@ -10,6 +10,8 @@
 #include "chrome/browser/chromeos/printing/history/print_job_database.h"
 #include "components/keyed_service/core/keyed_service.h"
 
+class PrefRegistrySimple;
+
 namespace chromeos {
 
 // This service is responsible for maintaining print job history.
@@ -24,6 +26,9 @@
   PrintJobHistoryService();
   ~PrintJobHistoryService() override;
 
+  // Register the print job history preferences with the |registry|.
+  static void RegisterProfilePrefs(PrefRegistrySimple* registry);
+
   // Retrieves all print jobs from the database.
   virtual void GetPrintJobs(
       PrintJobDatabase::GetPrintJobsCallback callback) = 0;
diff --git a/chrome/browser/chromeos/printing/history/print_job_history_service_factory.cc b/chrome/browser/chromeos/printing/history/print_job_history_service_factory.cc
index 6165f144..abe5717 100644
--- a/chrome/browser/chromeos/printing/history/print_job_history_service_factory.cc
+++ b/chrome/browser/chromeos/printing/history/print_job_history_service_factory.cc
@@ -49,7 +49,7 @@
       chromeos::CupsPrintJobManagerFactory::GetForBrowserContext(profile);
 
   return new PrintJobHistoryServiceImpl(std::move(print_job_database),
-                                        print_job_manager);
+                                        print_job_manager, profile->GetPrefs());
 }
 
 }  // namespace chromeos
diff --git a/chrome/browser/chromeos/printing/history/print_job_history_service_impl.cc b/chrome/browser/chromeos/printing/history/print_job_history_service_impl.cc
index 3ba6e47..07eea45b 100644
--- a/chrome/browser/chromeos/printing/history/print_job_history_service_impl.cc
+++ b/chrome/browser/chromeos/printing/history/print_job_history_service_impl.cc
@@ -10,17 +10,22 @@
 #include "base/metrics/histogram_macros.h"
 #include "chrome/browser/chromeos/printing/cups_print_job.h"
 #include "chrome/browser/chromeos/printing/history/print_job_info_proto_conversions.h"
+#include "components/prefs/pref_service.h"
 
 namespace chromeos {
 
 PrintJobHistoryServiceImpl::PrintJobHistoryServiceImpl(
     std::unique_ptr<PrintJobDatabase> print_job_database,
-    CupsPrintJobManager* print_job_manager)
+    CupsPrintJobManager* print_job_manager,
+    PrefService* pref_service)
     : print_job_database_(std::move(print_job_database)),
-      print_job_manager_(print_job_manager) {
+      print_job_manager_(print_job_manager),
+      print_job_history_cleaner_(print_job_database_.get(), pref_service) {
   DCHECK(print_job_manager_);
   print_job_manager_->AddObserver(this);
-  print_job_database_->Initialize(base::DoNothing());
+  print_job_database_->Initialize(
+      base::BindOnce(&PrintJobHistoryServiceImpl::OnPrintJobDatabaseInitialized,
+                     base::Unretained(this)));
 }
 
 PrintJobHistoryServiceImpl::~PrintJobHistoryServiceImpl() {
@@ -59,6 +64,11 @@
                      base::Unretained(this), print_job_info));
 }
 
+void PrintJobHistoryServiceImpl::OnPrintJobDatabaseInitialized(bool success) {
+  if (success)
+    print_job_history_cleaner_.Start();
+}
+
 void PrintJobHistoryServiceImpl::OnPrintJobSaved(
     const printing::proto::PrintJobInfo& print_job_info,
     bool success) {
diff --git a/chrome/browser/chromeos/printing/history/print_job_history_service_impl.h b/chrome/browser/chromeos/printing/history/print_job_history_service_impl.h
index cb2502478..349d351 100644
--- a/chrome/browser/chromeos/printing/history/print_job_history_service_impl.h
+++ b/chrome/browser/chromeos/printing/history/print_job_history_service_impl.h
@@ -7,8 +7,11 @@
 
 #include "chrome/browser/chromeos/printing/cups_print_job_manager.h"
 #include "chrome/browser/chromeos/printing/history/print_job_database.h"
+#include "chrome/browser/chromeos/printing/history/print_job_history_cleaner.h"
 #include "chrome/browser/chromeos/printing/history/print_job_history_service.h"
 
+class PrefService;
+
 namespace chromeos {
 
 class CupsPrintJobManager;
@@ -22,7 +25,8 @@
  public:
   PrintJobHistoryServiceImpl(
       std::unique_ptr<PrintJobDatabase> print_job_database,
-      CupsPrintJobManager* print_job_manager);
+      CupsPrintJobManager* print_job_manager,
+      PrefService* pref_service);
   ~PrintJobHistoryServiceImpl() override;
 
   // PrintJobHistoryService:
@@ -36,11 +40,14 @@
 
   void SavePrintJob(base::WeakPtr<CupsPrintJob> job);
 
+  void OnPrintJobDatabaseInitialized(bool success);
+
   void OnPrintJobSaved(const printing::proto::PrintJobInfo& print_job_info,
                        bool success);
 
   std::unique_ptr<PrintJobDatabase> print_job_database_;
   CupsPrintJobManager* print_job_manager_;
+  PrintJobHistoryCleaner print_job_history_cleaner_;
 
   DISALLOW_COPY_AND_ASSIGN(PrintJobHistoryServiceImpl);
 };
diff --git a/chrome/browser/chromeos/printing/history/print_job_history_service_impl_unittest.cc b/chrome/browser/chromeos/printing/history/print_job_history_service_impl_unittest.cc
index 37e5492..cedaf46 100644
--- a/chrome/browser/chromeos/printing/history/print_job_history_service_impl_unittest.cc
+++ b/chrome/browser/chromeos/printing/history/print_job_history_service_impl_unittest.cc
@@ -12,6 +12,8 @@
 #include "chrome/test/base/testing_browser_process.h"
 #include "chrome/test/base/testing_profile.h"
 #include "chrome/test/base/testing_profile_manager.h"
+#include "components/prefs/pref_service.h"
+#include "components/prefs/testing_pref_service.h"
 #include "content/public/test/browser_task_environment.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -34,7 +36,7 @@
         std::make_unique<TestPrintJobDatabase>();
     print_job_manager_ = std::make_unique<TestCupsPrintJobManager>(&profile_);
     print_job_history_service_ = std::make_unique<PrintJobHistoryServiceImpl>(
-        std::move(print_job_database), print_job_manager_.get());
+        std::move(print_job_database), print_job_manager_.get(), &test_prefs_);
   }
 
   void TearDown() override {
@@ -64,6 +66,7 @@
 
  private:
   TestingProfile profile_;
+  TestingPrefServiceSimple test_prefs_;
 };
 
 TEST_F(PrintJobHistoryServiceImplTest, SaveObservedCupsPrintJob) {
diff --git a/chrome/browser/data_reduction_proxy/data_reduction_proxy_browsertest.cc b/chrome/browser/data_reduction_proxy/data_reduction_proxy_browsertest.cc
index c20c78a..495b98b 100644
--- a/chrome/browser/data_reduction_proxy/data_reduction_proxy_browsertest.cc
+++ b/chrome/browser/data_reduction_proxy/data_reduction_proxy_browsertest.cc
@@ -35,6 +35,8 @@
 #include "components/data_reduction_proxy/proto/client_config.pb.h"
 #include "components/page_load_metrics/browser/page_load_metrics_test_waiter.h"
 #include "components/prefs/pref_service.h"
+#include "components/previews/core/previews_experiments.h"
+#include "components/previews/core/previews_features.h"
 #include "components/proxy_config/proxy_config_dictionary.h"
 #include "components/proxy_config/proxy_config_pref_names.h"
 #include "components/variations/variations_associated_data.h"
@@ -299,17 +301,27 @@
 
   void SetConfig(const ClientConfig& config) {
     config_ = config;
-    // Config is not fetched in the holdback group. So, return early.
-    if (data_reduction_proxy::params::IsIncludedInHoldbackFieldTrial())
+    // Config is not fetched if the holdback group is enabled and lite page
+    // redirect previews are not enabled. So, return early.
+    if (data_reduction_proxy::params::IsIncludedInHoldbackFieldTrial() &&
+        !previews::params::IsLitePageServerPreviewsEnabled()) {
       return;
+    }
 
     config_waiter_ = std::make_unique<ScopedConfigWaiter>(browser()->profile());
   }
 
   void WaitForConfig() {
-    // Config is not fetched in the holdback group. So, return early.
-    if (data_reduction_proxy::params::IsIncludedInHoldbackFieldTrial())
+    // Config is not fetched if the holdback group is enabled and lite page
+    // redirect previews are not enabled. So, return early.
+    if (data_reduction_proxy::params::IsIncludedInHoldbackFieldTrial() &&
+        !previews::params::IsLitePageServerPreviewsEnabled()) {
       return;
+    }
+    // Destructor of |config_waiter_| waits for the config fetch request to
+    // arrive. For that check to work correctly, |config_waiter_| should be
+    // non-null.
+    ASSERT_TRUE(config_waiter_ != nullptr);
     config_waiter_.reset();
   }
 
@@ -320,11 +332,14 @@
   base::test::ScopedFeatureList scoped_feature_list_;
 
  private:
+  // Called when |config_server_| receives a request for config fetch.
   std::unique_ptr<net::test_server::HttpResponse> GetConfigResponse(
       const net::test_server::HttpRequest& request) {
-    // Config should not be fetched when in holdback.
-    EXPECT_FALSE(
-        data_reduction_proxy::params::IsIncludedInHoldbackFieldTrial());
+    // Config should be fetched only when holdback is disabled or lite page
+    // redirect previews are enabled.
+    EXPECT_TRUE(
+        !data_reduction_proxy::params::IsIncludedInHoldbackFieldTrial() ||
+        previews::params::IsLitePageServerPreviewsEnabled());
 
     auto response = std::make_unique<net::test_server::BasicHttpResponse>();
     response->set_content(config_.SerializeAsString());
@@ -658,29 +673,38 @@
       1);
 }
 
-// Test that enabling the holdback disables the proxy.
-// Parameter is true if the data reduction proxy holdback should be enabled.
+// Test that enabling the holdback disables the proxy and that the client config
+// is fetched when lite page redirect preview is enabled.
+// First parameter is true if the data reduction proxy holdback should be
+// enabled. Second parameter is true if lite page redirect preview is enabled.
 class DataReductionProxyWithHoldbackBrowsertest
-    : public ::testing::WithParamInterface<bool>,
+    : public ::testing::WithParamInterface<std::tuple<bool, bool>>,
       public DataReductionProxyBrowsertest {
  public:
   DataReductionProxyWithHoldbackBrowsertest()
       : DataReductionProxyBrowsertest(),
-        data_reduction_proxy_holdback_enabled_(GetParam()) {}
+        data_reduction_proxy_holdback_enabled_(std::get<0>(GetParam())),
+        lite_page_redirect_previews_enabled_(std::get<1>(GetParam())) {}
 
   void SetUp() override {
     if (data_reduction_proxy_holdback_enabled_) {
       scoped_feature_list_.InitAndEnableFeature(
           data_reduction_proxy::features::kDataReductionProxyHoldback);
     }
+    if (lite_page_redirect_previews_enabled_) {
+      previews_lite_page_redirect_feature_list_.InitAndEnableFeature(
+          previews::features::kLitePageServerPreviews);
+    }
 
     InProcessBrowserTest::SetUp();
   }
 
   const bool data_reduction_proxy_holdback_enabled_;
+  const bool lite_page_redirect_previews_enabled_;
 
  private:
   base::test::ScopedFeatureList scoped_feature_list_;
+  base::test::ScopedFeatureList previews_lite_page_redirect_feature_list_;
 };
 
 IN_PROC_BROWSER_TEST_P(DataReductionProxyWithHoldbackBrowsertest,
@@ -697,6 +721,8 @@
   // A network change forces the config to be fetched.
   SimulateNetworkChange(network::mojom::ConnectionType::CONNECTION_3G);
 
+  // Ensure that the client config is fetched when lite page redirect preview is
+  // enabled or DRP holdback is disabled.
   WaitForConfig();
 
   // Load a webpage in holdback group as well. This ensures that while in
@@ -705,17 +731,19 @@
   // holdback is not enabled would trigger and cause the test to fail.
   ui_test_utils::NavigateToURL(browser(), GURL("http://does.not.resolve/foo"));
 
-  if (GetParam()) {
+  if (data_reduction_proxy_holdback_enabled_) {
     EXPECT_NE(GetBody(), kPrimaryResponse);
   } else {
     EXPECT_EQ(GetBody(), kPrimaryResponse);
   }
 }
 
-// Parameter is true if the data reduction proxy holdback should be enabled.
+// First parameter is true if the data reduction proxy holdback should be
+// enabled. Second parameter is true if lite page redirect preview is enabled.
 INSTANTIATE_TEST_SUITE_P(,
                          DataReductionProxyWithHoldbackBrowsertest,
-                         ::testing::Values(false, true));
+                         ::testing::Combine(::testing::Bool(),
+                                            ::testing::Bool()));
 
 class DataReductionProxyExpBrowsertest : public DataReductionProxyBrowsertest {
  public:
diff --git a/chrome/browser/extensions/api/downloads/downloads_api_browsertest.cc b/chrome/browser/extensions/api/downloads/downloads_api_browsertest.cc
index 2694d314..1bfce417 100644
--- a/chrome/browser/extensions/api/downloads/downloads_api_browsertest.cc
+++ b/chrome/browser/extensions/api/downloads/downloads_api_browsertest.cc
@@ -1900,31 +1900,31 @@
   GoOnTheRecord();
 
   static const char* const kUnsafeHeaders[] = {
-    "Accept-chArsEt",
-    "accept-eNcoding",
-    "coNNection",
-    "coNteNt-leNgth",
-    "cooKIE",
-    "cOOkie2",
-    "coNteNt-traNsfer-eNcodiNg",
-    "dAtE",
-    "ExpEcT",
-    "hOsT",
-    "kEEp-aLivE",
-    "rEfErEr",
-    "tE",
-    "trAilER",
-    "trANsfer-eNcodiNg",
-    "upGRAde",
-    "usER-agENt",
-    "viA",
-    "pRoxY-",
-    "sEc-",
-    "pRoxY-probably-not-evil",
-    "sEc-probably-not-evil",
-    "oRiGiN",
-    "Access-Control-Request-Headers",
-    "Access-Control-Request-Method",
+      "Accept-chArsEt",
+      "accept-eNcoding",
+      "coNNection",
+      "coNteNt-leNgth",
+      "cooKIE",
+      "cOOkie2",
+      "dAtE",
+      "DNT",
+      "ExpEcT",
+      "hOsT",
+      "kEEp-aLivE",
+      "rEfErEr",
+      "tE",
+      "trAilER",
+      "trANsfer-eNcodiNg",
+      "upGRAde",
+      "usER-agENt",
+      "viA",
+      "pRoxY-",
+      "sEc-",
+      "pRoxY-probably-not-evil",
+      "sEc-probably-not-evil",
+      "oRiGiN",
+      "Access-Control-Request-Headers",
+      "Access-Control-Request-Method",
   };
 
   for (size_t index = 0; index < base::size(kUnsafeHeaders); ++index) {
diff --git a/chrome/browser/extensions/api/settings_private/prefs_util.cc b/chrome/browser/extensions/api/settings_private/prefs_util.cc
index 5b3493a..52f8e5f5 100644
--- a/chrome/browser/extensions/api/settings_private/prefs_util.cc
+++ b/chrome/browser/extensions/api/settings_private/prefs_util.cc
@@ -486,8 +486,6 @@
       settings_api::PrefType::PREF_TYPE_BOOLEAN;
   (*s_whitelist)[::prefs::kSettingsShowOSBanner] =
       settings_api::PrefType::PREF_TYPE_BOOLEAN;
-  (*s_whitelist)[::prefs::kSettingsShowAboutOSBanner] =
-      settings_api::PrefType::PREF_TYPE_BOOLEAN;
 
   // Bluetooth & Internet settings.
   (*s_whitelist)[chromeos::kAllowBluetooth] =
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index 070a249..892a8203 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -814,6 +814,11 @@
     "expiry_milestone": 89
   },
   {
+    "name": "enable-accessibility-expose-aria-annotations",
+    "owners": [ "aleventhal@chromium.org", "//third_party/blink/renderer/modules/accessibility/OWNERS", "//ui/accessibility/OWNERS" ],
+    "expiry_milestone": 85
+  },
+  {
     "name": "enable-accessibility-expose-display-none",
     "owners": [ "adettenb@microsoft.com", "//third_party/blink/renderer/modules/accessibility/OWNERS", "//ui/accessibility/OWNERS" ],
     "expiry_milestone": 82
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 1b75d45..4336b77a 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -22,6 +22,12 @@
 const char kAcceleratedVideoEncodeDescription[] =
     "Hardware-accelerated video encode where available.";
 
+const char kAccessibilityExposeARIAAnnotationsName[] =
+    "Expose ARIA Annotation roles";
+const char kAccessibilityExposeARIAAnnotationsDescription[] =
+    "Expose annotation- prefixed roles from ARIA Annotations draft "
+    "specification at https://w3c.github.io/annotation-aria/.";
+
 const char kAccessibilityExposeDisplayNoneName[] =
     "Expose 'display: none' nodes for accessibility";
 const char kAccessibilityExposeDisplayNoneDescription[] =
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 62df3c7a..06b1e52 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -53,6 +53,9 @@
 extern const char kAcceleratedVideoEncodeName[];
 extern const char kAcceleratedVideoEncodeDescription[];
 
+extern const char kAccessibilityExposeARIAAnnotationsName[];
+extern const char kAccessibilityExposeARIAAnnotationsDescription[];
+
 extern const char kAccessibilityExposeDisplayNoneName[];
 extern const char kAccessibilityExposeDisplayNoneDescription[];
 
diff --git a/chrome/browser/lookalikes/safety_tips/reputation_service.cc b/chrome/browser/lookalikes/safety_tips/reputation_service.cc
index e5f8a7a5..7209553 100644
--- a/chrome/browser/lookalikes/safety_tips/reputation_service.cc
+++ b/chrome/browser/lookalikes/safety_tips/reputation_service.cc
@@ -70,6 +70,15 @@
   return true;
 }
 
+// TODO(crbug/984725): Implement Keyword Check
+bool ShouldTriggerSafetyTipFromKeywordInURL(
+    const GURL& url,
+    const DomainInfo& navigated_domain,
+    const std::vector<DomainInfo>& engaged_sites) {
+  // TODO(crbug/987754): Record metrics here.
+  return false;
+}
+
 // This factory helps construct and find the singleton ReputationService linked
 // to a Profile.
 class ReputationServiceFactory : public BrowserContextKeyedServiceFactory {
@@ -251,7 +260,15 @@
     return;
   }
 
-  // TODO(crbug/984725): 5. Additional client-side heuristics
+  // 5. Keyword heuristics.
+  if (ShouldTriggerSafetyTipFromKeywordInURL(url, navigated_domain,
+                                             engaged_sites)) {
+    std::move(callback).Run(security_state::SafetyTipStatus::kBadKeyword,
+                            IsIgnored(url), url, GURL());
+    return;
+  }
+
+  // TODO(crbug/984725): 6. Additional client-side heuristics.
   std::move(callback).Run(security_state::SafetyTipStatus::kNone,
                           IsIgnored(url), url, GURL());
 }
diff --git a/chrome/browser/lookalikes/safety_tips/reputation_web_contents_observer.cc b/chrome/browser/lookalikes/safety_tips/reputation_web_contents_observer.cc
index 24c87d33..10f8b41 100644
--- a/chrome/browser/lookalikes/safety_tips/reputation_web_contents_observer.cc
+++ b/chrome/browser/lookalikes/safety_tips/reputation_web_contents_observer.cc
@@ -5,6 +5,7 @@
 #include "chrome/browser/lookalikes/safety_tips/reputation_web_contents_observer.h"
 
 #include <string>
+#include <utility>
 
 #include "base/metrics/histogram_functions.h"
 #include "base/metrics/histogram_macros.h"
@@ -137,13 +138,12 @@
   UMA_HISTOGRAM_ENUMERATION("Security.SafetyTips.SafetyTipShown",
                             safety_tip_status);
 
-  if (safety_tip_status == security_state::SafetyTipStatus::kNone) {
+  if (safety_tip_status == security_state::SafetyTipStatus::kNone ||
+      safety_tip_status == security_state::SafetyTipStatus::kBadKeyword) {
     MaybeCallReputationCheckCallback();
     return;
   }
 
-  // TODO(crbug/987754): Record metrics here.
-
   if (user_ignored) {
     UMA_HISTOGRAM_ENUMERATION("Security.SafetyTips.SafetyTipIgnoredPageLoad",
                               safety_tip_status);
diff --git a/chrome/browser/lookalikes/safety_tips/safety_tip_ui_helper.cc b/chrome/browser/lookalikes/safety_tips/safety_tip_ui_helper.cc
index 238f63e..20ac19c35 100644
--- a/chrome/browser/lookalikes/safety_tips/safety_tip_ui_helper.cc
+++ b/chrome/browser/lookalikes/safety_tips/safety_tip_ui_helper.cc
@@ -66,6 +66,7 @@
           security_interstitials::common_string_util::GetFormattedHostName(
               url));
 #endif
+    case security_state::SafetyTipStatus::kBadKeyword:
     case security_state::SafetyTipStatus::kUnknown:
     case security_state::SafetyTipStatus::kNone:
       NOTREACHED();
@@ -87,6 +88,7 @@
           IDS_PAGE_INFO_SAFETY_TIP_LOOKALIKE_DESCRIPTION,
           security_interstitials::common_string_util::GetFormattedHostName(
               url));
+    case security_state::SafetyTipStatus::kBadKeyword:
     case security_state::SafetyTipStatus::kNone:
     case security_state::SafetyTipStatus::kUnknown:
       NOTREACHED();
@@ -106,6 +108,7 @@
     case security_state::SafetyTipStatus::kLookalike:
       return IDS_PAGE_INFO_SAFETY_TIP_LEAVE_BUTTON;
 #endif
+    case security_state::SafetyTipStatus::kBadKeyword:
     case security_state::SafetyTipStatus::kUnknown:
     case security_state::SafetyTipStatus::kNone:
       NOTREACHED();
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 63b2971c..583e1df 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
@@ -11,6 +11,7 @@
 
 #include "base/feature_list.h"
 #include "base/logging.h"
+#include "base/rand_util.h"
 #include "base/strings/string_util.h"
 #include "base/time/default_tick_clock.h"
 #include "chrome/browser/heavy_ad_intervention/heavy_ad_blocklist.h"
@@ -94,6 +95,11 @@
 AdsPageLoadMetricsObserver::AggregateFrameInfo::AggregateFrameInfo()
     : bytes(0), network_bytes(0), num_frames(0) {}
 
+int AdsPageLoadMetricsObserver::HeavyAdThresholdNoiseProvider::
+    GetNetworkThresholdNoiseForFrame() const {
+  return base::RandInt(0, kMaxNetworkThresholdNoiseBytes);
+}
+
 AdsPageLoadMetricsObserver::AdsPageLoadMetricsObserver(
     base::TickClock* clock,
     HeavyAdBlocklist* blocklist)
@@ -101,7 +107,9 @@
       clock_(clock ? clock : base::DefaultTickClock::GetInstance()),
       heavy_ad_blocklist_(blocklist),
       heavy_ad_blocklist_enabled_(
-          base::FeatureList::IsEnabled(features::kHeavyAdBlocklist)) {}
+          base::FeatureList::IsEnabled(features::kHeavyAdBlocklist)),
+      heavy_ad_threshold_noise_provider_(
+          std::make_unique<HeavyAdThresholdNoiseProvider>()) {}
 
 AdsPageLoadMetricsObserver::~AdsPageLoadMetricsObserver() = default;
 
@@ -119,9 +127,11 @@
   if (observer_manager)
     subresource_observer_.Add(observer_manager);
   main_frame_data_ =
-      std::make_unique<FrameData>(navigation_handle->GetFrameTreeNodeId());
+      std::make_unique<FrameData>(navigation_handle->GetFrameTreeNodeId(),
+                                  0 /* heavy_ad_network_threshold_noise */);
   aggregate_frame_data_ =
-      std::make_unique<FrameData>(navigation_handle->GetFrameTreeNodeId());
+      std::make_unique<FrameData>(navigation_handle->GetFrameTreeNodeId(),
+                                  0 /* heavy_ad_network_threshold_noise */);
   return CONTINUE_OBSERVING;
 }
 
@@ -192,9 +202,10 @@
 
   ADS_HISTOGRAM("HeavyAds.InterventionType2", UMA_HISTOGRAM_ENUMERATION,
                 FrameData::FrameVisibility::kAnyVisibility,
-                frame_data->heavy_ad_status());
+                frame_data->heavy_ad_status_with_noise());
   ADS_HISTOGRAM("HeavyAds.InterventionType2", UMA_HISTOGRAM_ENUMERATION,
-                frame_data->visibility(), frame_data->heavy_ad_status());
+                frame_data->visibility(),
+                frame_data->heavy_ad_status_with_noise());
 
   // Report intervention to the blocklist.
   if (auto* blocklist = GetHeavyAdBlocklist()) {
@@ -277,7 +288,9 @@
       previous_data->UpdateForNavigation(ad_host, frame_navigated);
       return;
     }
-    ad_frames_data_storage_.emplace_back(ad_id);
+    ad_frames_data_storage_.emplace_back(
+        ad_id,
+        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);
@@ -846,6 +859,9 @@
                   ad_frame_data.user_activation_status());
     ADS_HISTOGRAM("HeavyAds.ComputedType2", UMA_HISTOGRAM_ENUMERATION,
                   visibility, ad_frame_data.heavy_ad_status());
+    ADS_HISTOGRAM("HeavyAds.ComputedTypeWithThresholdNoise",
+                  UMA_HISTOGRAM_ENUMERATION, visibility,
+                  ad_frame_data.heavy_ad_status_with_noise());
   }
 }
 
diff --git a/chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer.h b/chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer.h
index 3823a91..1938b3d 100644
--- a/chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer.h
+++ b/chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer.h
@@ -57,6 +57,23 @@
     DISALLOW_COPY_AND_ASSIGN(AggregateFrameInfo);
   };
 
+  // Helper class that generates a random amount of noise to apply to thresholds
+  // for heavy ads. A different noise should be generated for each frame.
+  class HeavyAdThresholdNoiseProvider {
+   public:
+    HeavyAdThresholdNoiseProvider() = default;
+    virtual ~HeavyAdThresholdNoiseProvider() = default;
+
+    // Gets a random amount of noise to add to a threshold. The generated noise
+    // is uniform random over the range 0 to kMaxThresholdNoiseBytes. Virtual
+    // for testing.
+    virtual int GetNetworkThresholdNoiseForFrame() const;
+
+    // Maximum amount of additive noise to add to the network threshold to
+    // obscure cross origin resource sizes: 1303 KB.
+    static const int kMaxNetworkThresholdNoiseBytes = 1303 * 1024;
+  };
+
   explicit AdsPageLoadMetricsObserver(base::TickClock* clock = nullptr,
                                       HeavyAdBlocklist* blocklist = nullptr);
   ~AdsPageLoadMetricsObserver() override;
@@ -101,6 +118,11 @@
       content::RenderFrameHost* render_frame_host) override;
   void OnFrameDeleted(content::RenderFrameHost* render_frame_host) override;
 
+  void SetHeavyAdThresholdNoiseProviderForTesting(
+      std::unique_ptr<HeavyAdThresholdNoiseProvider> noise_provider) {
+    heavy_ad_threshold_noise_provider_ = std::move(noise_provider);
+  }
+
  private:
   // subresource_filter::SubresourceFilterObserver:
   void OnAdSubframeDetected(
@@ -228,6 +250,9 @@
   // Whether the heavy ad blocklist feature is enabled.
   const bool heavy_ad_blocklist_enabled_;
 
+  std::unique_ptr<HeavyAdThresholdNoiseProvider>
+      heavy_ad_threshold_noise_provider_;
+
   DISALLOW_COPY_AND_ASSIGN(AdsPageLoadMetricsObserver);
 };
 
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 c58668bb..11d79cd 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
@@ -77,6 +77,12 @@
     "Content-Type: text/html; charset=utf-8\r\n"
     "\r\n";
 
+// Use the maximum possible threshold so tests are deterministic.
+const int kMaxHeavyAdNetworkSize =
+    heavy_ad_thresholds::kMaxNetworkBytes +
+    AdsPageLoadMetricsObserver::HeavyAdThresholdNoiseProvider::
+        kMaxNetworkThresholdNoiseBytes;
+
 void LoadLargeResource(net::test_server::ControllableHttpResponse* response,
                        int bytes) {
   response->WaitForRequest();
@@ -891,22 +897,16 @@
   ui_test_utils::NavigateToURL(browser(), url);
 
   // Load a resource large enough to trigger the intervention.
-  LoadLargeResource(incomplete_resource_response.get(),
-                    heavy_ad_thresholds::kMaxNetworkBytes);
-
-  // Wait for the resource update to be received for the large resource.
-  waiter->AddMinimumNetworkBytesExpectation(
-      heavy_ad_thresholds::kMaxNetworkBytes);
-  waiter->Wait();
-
-  histogram_tester.ExpectUniqueSample(kHeavyAdInterventionTypeHistogramId,
-                                      FrameData::HeavyAdStatus::kNetwork, 1);
+  LoadLargeResource(incomplete_resource_response.get(), kMaxHeavyAdNetworkSize);
 
   // Wait for the intervention page navigation to finish on the frame.
   error_observer.WaitForNavigationFinished();
 
   // Check that the ad frame was navigated to the intervention page.
   EXPECT_FALSE(error_observer.last_navigation_succeeded());
+
+  histogram_tester.ExpectUniqueSample(kHeavyAdInterventionTypeHistogramId,
+                                      FrameData::HeavyAdStatus::kNetwork, 1);
 }
 
 // Check that when the heavy ad feature is disabled we don't navigate
@@ -928,12 +928,10 @@
   ui_test_utils::NavigateToURL(browser(), url);
 
   // Load a resource large enough to trigger the intervention.
-  LoadLargeResource(incomplete_resource_response.get(),
-                    heavy_ad_thresholds::kMaxNetworkBytes);
+  LoadLargeResource(incomplete_resource_response.get(), kMaxHeavyAdNetworkSize);
 
   // Wait for the resource update to be received for the large resource.
-  waiter->AddMinimumNetworkBytesExpectation(
-      heavy_ad_thresholds::kMaxNetworkBytes);
+  waiter->AddMinimumNetworkBytesExpectation(kMaxHeavyAdNetworkSize);
   waiter->Wait();
 
   // We can't check whether the navigation didn't occur because the error page
@@ -961,11 +959,10 @@
 
   // Load a resource not large enough to trigger the intervention.
   LoadLargeResource(incomplete_resource_response.get(),
-                    heavy_ad_thresholds::kMaxNetworkBytes / 2);
+                    kMaxHeavyAdNetworkSize / 2);
 
   // Wait for the resource update to be received for the large resource.
-  waiter->AddMinimumNetworkBytesExpectation(
-      heavy_ad_thresholds::kMaxNetworkBytes / 2);
+  waiter->AddMinimumNetworkBytesExpectation(kMaxHeavyAdNetworkSize / 2);
   waiter->Wait();
 
   histogram_tester.ExpectTotalCount(kHeavyAdInterventionTypeHistogramId, 0);
@@ -1009,14 +1006,7 @@
       "createAdFrame('/ads_observer/ad_with_incomplete_resource.html', '');"));
 
   // Load a resource large enough to trigger the intervention.
-  LoadLargeResource(large_resource_1.get(),
-                    heavy_ad_thresholds::kMaxNetworkBytes);
-  waiter->AddMinimumNetworkBytesExpectation(
-      heavy_ad_thresholds::kMaxNetworkBytes);
-  waiter->Wait();
-
-  histogram_tester.ExpectUniqueSample(kHeavyAdInterventionTypeHistogramId,
-                                      FrameData::HeavyAdStatus::kNetwork, 1);
+  LoadLargeResource(large_resource_1.get(), kMaxHeavyAdNetworkSize);
 
   // Wait for the intervention page navigation to finish on the frame.
   error_observer.WaitForNavigationFinished();
@@ -1024,15 +1014,21 @@
   // Check that the ad frame was navigated to the intervention page.
   EXPECT_FALSE(error_observer.last_navigation_succeeded());
 
+  histogram_tester.ExpectUniqueSample(kHeavyAdInterventionTypeHistogramId,
+                                      FrameData::HeavyAdStatus::kNetwork, 1);
+
   EXPECT_TRUE(ExecJs(
       web_contents,
       "createAdFrame('/ads_observer/ad_with_incomplete_resource.html', '');"));
 
+  // Use the current network bytes because the ad could have been unloaded
+  // before loading the entire large resource.
+  int64_t current_network_bytes = waiter->current_network_bytes();
+
   // Load a resource large enough to trigger the intervention.
-  LoadLargeResource(large_resource_2.get(),
-                    heavy_ad_thresholds::kMaxNetworkBytes);
-  waiter->AddMinimumNetworkBytesExpectation(
-      2 * heavy_ad_thresholds::kMaxNetworkBytes);
+  LoadLargeResource(large_resource_2.get(), kMaxHeavyAdNetworkSize);
+  waiter->AddMinimumNetworkBytesExpectation(current_network_bytes +
+                                            kMaxHeavyAdNetworkSize);
   waiter->Wait();
 
   // Check that the intervention did not trigger on this frame.
diff --git a/chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer_unittest.cc b/chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer_unittest.cc
index 1fee296..b6772343 100644
--- a/chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer_unittest.cc
+++ b/chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer_unittest.cc
@@ -80,6 +80,11 @@
 const char kNonAdUrl[] = "https://foo.com/";
 const char kNonAdUrlSameOrigin[] = "https://ads.com/foo";
 
+const int kMaxHeavyAdNetworkBytes =
+    heavy_ad_thresholds::kMaxNetworkBytes +
+    AdsPageLoadMetricsObserver::HeavyAdThresholdNoiseProvider::
+        kMaxNetworkThresholdNoiseBytes;
+
 // Asynchronously cancels the navigation at WillProcessResponse. Before
 // cancelling, simulates loading a main frame resource.
 class ResourceLoadingCancellingThrottle
@@ -135,6 +140,21 @@
   DISALLOW_COPY_AND_ASSIGN(ResourceLoadingCancellingThrottle);
 };
 
+// Mock noise provider which always gives a supplied value of noise for the
+// heavy ad intervention thresholds.
+class MockNoiseProvider
+    : public AdsPageLoadMetricsObserver::HeavyAdThresholdNoiseProvider {
+ public:
+  explicit MockNoiseProvider(int noise)
+      : HeavyAdThresholdNoiseProvider(), noise_(noise) {}
+  ~MockNoiseProvider() override = default;
+
+  int GetNetworkThresholdNoiseForFrame() const override { return noise_; }
+
+ private:
+  int noise_;
+};
+
 std::string SuffixedHistogram(const std::string& suffix) {
   return base::StringPrintf("PageLoad.Clients.Ads.%s", suffix.c_str());
 }
@@ -404,6 +424,12 @@
     clock_->SetNowTicks(base::TimeTicks::Now());
   }
 
+  void OverrideHeavyAdNoiseProvider(
+      std::unique_ptr<MockNoiseProvider> noise_provider) {
+    ads_observer_->SetHeavyAdThresholdNoiseProviderForTesting(
+        std::move(noise_provider));
+  }
+
   // Given the prefix of the CPU histogram to check, either "Cpu.FullPage" or
   // "Cpu.AdFrames.PerFrame", as well as the type, one of "" (for "FullPage"),
   // "Activated", or "Unactivated", along with the total pre and post cpu time
@@ -457,8 +483,10 @@
   base::HistogramTester histogram_tester_;
   ukm::TestAutoSetUkmRecorder test_ukm_recorder_;
   std::unique_ptr<page_load_metrics::PageLoadMetricsObserverTester> tester_;
+
   // The clock used by the ui::ScopedVisibilityTracker, assigned if non-null.
   std::unique_ptr<base::SimpleTestTickClock> clock_;
+
   // A pointer to the AdsPageLoadMetricsObserver used by the tests.
   AdsPageLoadMetricsObserver* ads_observer_ = nullptr;
 
@@ -1518,6 +1546,10 @@
   OverrideVisibilityTrackerWithMockClock();
 
   RenderFrameHost* main_frame = NavigateMainFrame(kNonAdUrl);
+
+  OverrideHeavyAdNoiseProvider(
+      std::make_unique<MockNoiseProvider>(0 /* network noise */));
+
   RenderFrameHost* ad_frame_none =
       CreateAndNavigateSubFrame(kAdUrl, main_frame);
   RenderFrameHost* ad_frame_net = CreateAndNavigateSubFrame(kAdUrl, main_frame);
@@ -1559,6 +1591,22 @@
   histogram_tester().ExpectBucketCount(
       SuffixedHistogram("HeavyAds.ComputedType2"),
       FrameData::HeavyAdStatus::kTotalCpu, 1);
+
+  histogram_tester().ExpectTotalCount(
+      SuffixedHistogram("HeavyAds.ComputedTypeWithThresholdNoise"), 4);
+  histogram_tester().ExpectBucketCount(
+      SuffixedHistogram("HeavyAds.ComputedTypeWithThresholdNoise"),
+      FrameData::HeavyAdStatus::kNone, 1);
+  histogram_tester().ExpectBucketCount(
+      SuffixedHistogram("HeavyAds.ComputedTypeWithThresholdNoise"),
+      FrameData::HeavyAdStatus::kNetwork, 1);
+  histogram_tester().ExpectBucketCount(
+      SuffixedHistogram("HeavyAds.ComputedTypeWithThresholdNoise"),
+      FrameData::HeavyAdStatus::kPeakCpu, 1);
+  histogram_tester().ExpectBucketCount(
+      SuffixedHistogram("HeavyAds.ComputedTypeWithThresholdNoise"),
+      FrameData::HeavyAdStatus::kTotalCpu, 1);
+
   histogram_tester().ExpectTotalCount(
       SuffixedHistogram("HeavyAds.InterventionType2"), 0);
 }
@@ -1566,9 +1614,11 @@
 TEST_F(AdsPageLoadMetricsObserverTest, HeavyAdNetworkUsage_InterventionFired) {
   base::test::ScopedFeatureList feature_list;
   feature_list.InitAndEnableFeature(features::kHeavyAdIntervention);
-  OverrideVisibilityTrackerWithMockClock();
 
   RenderFrameHost* main_frame = NavigateMainFrame(kNonAdUrl);
+
+  OverrideHeavyAdNoiseProvider(
+      std::make_unique<MockNoiseProvider>(0 /* network noise */));
   RenderFrameHost* ad_frame = CreateAndNavigateSubFrame(kAdUrl, main_frame);
 
   // Load just under the threshold amount of bytes.
@@ -1585,6 +1635,95 @@
       FrameData::HeavyAdStatus::kNetwork, 1);
 }
 
+TEST_F(AdsPageLoadMetricsObserverTest,
+       HeavyAdNetworkUsageWithNoise_InterventionFired) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndEnableFeature(features::kHeavyAdIntervention);
+
+  RenderFrameHost* main_frame = NavigateMainFrame(kNonAdUrl);
+
+  OverrideHeavyAdNoiseProvider(
+      std::make_unique<MockNoiseProvider>(2048 /* network noise */));
+  RenderFrameHost* ad_frame = CreateAndNavigateSubFrame(kAdUrl, main_frame);
+
+  // Load just under the threshold amount of bytes with noise included.
+  ResourceDataUpdate(ad_frame, ResourceCached::NOT_CACHED,
+                     (heavy_ad_thresholds::kMaxNetworkBytes / 1024) + 1);
+  histogram_tester().ExpectTotalCount(
+      SuffixedHistogram("HeavyAds.InterventionType2"), 0);
+
+  // Load enough bytes to meet the noised threshold criteria.
+  ResourceDataUpdate(ad_frame, ResourceCached::NOT_CACHED, 1);
+
+  histogram_tester().ExpectUniqueSample(
+      SuffixedHistogram("HeavyAds.InterventionType2"),
+      FrameData::HeavyAdStatus::kNetwork, 1);
+}
+
+TEST_F(AdsPageLoadMetricsObserverTest,
+       HeavyAdNetworkUsageLessThanNoisedThreshold_NotFired) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndEnableFeature(features::kHeavyAdIntervention);
+
+  RenderFrameHost* main_frame = NavigateMainFrame(kNonAdUrl);
+
+  OverrideHeavyAdNoiseProvider(
+      std::make_unique<MockNoiseProvider>(2048 /* network noise */));
+  RenderFrameHost* ad_frame = CreateAndNavigateSubFrame(kAdUrl, main_frame);
+
+  // Load network bytes that trip the heavy ad threshold without noise.
+  ResourceDataUpdate(ad_frame, ResourceCached::NOT_CACHED,
+                     heavy_ad_thresholds::kMaxNetworkBytes / 1024 + 1);
+  histogram_tester().ExpectTotalCount(
+      SuffixedHistogram("HeavyAds.InterventionType2"), 0);
+
+  // Navigate again to trigger histograms.
+  NavigateFrame(kNonAdUrl, main_frame);
+
+  histogram_tester().ExpectUniqueSample(
+      SuffixedHistogram("HeavyAds.ComputedType2"),
+      FrameData::HeavyAdStatus::kNetwork, 1);
+  histogram_tester().ExpectUniqueSample(
+      SuffixedHistogram("HeavyAds.ComputedTypeWithThresholdNoise"),
+      FrameData::HeavyAdStatus::kNone, 1);
+}
+
+TEST_F(AdsPageLoadMetricsObserverTest,
+       HeavyAdNetworkUsageLessThanNoisedThreshold_CpuTriggers) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndEnableFeature(features::kHeavyAdIntervention);
+  OverrideVisibilityTrackerWithMockClock();
+
+  RenderFrameHost* main_frame = NavigateMainFrame(kNonAdUrl);
+
+  OverrideHeavyAdNoiseProvider(
+      std::make_unique<MockNoiseProvider>(2048 /* network noise */));
+  RenderFrameHost* ad_frame = CreateAndNavigateSubFrame(kAdUrl, main_frame);
+
+  // Load network bytes that trip the heavy ad threshold without noise.
+  ResourceDataUpdate(ad_frame, ResourceCached::NOT_CACHED,
+                     heavy_ad_thresholds::kMaxNetworkBytes / 1024 + 1);
+  histogram_tester().ExpectTotalCount(
+      SuffixedHistogram("HeavyAds.InterventionType2"), 0);
+
+  // Verify the frame can still trip the CPU threshold.
+  UseCpuTimeUnderThreshold(ad_frame, base::TimeDelta::FromMilliseconds(
+                                         heavy_ad_thresholds::kMaxCpuTime + 1));
+  histogram_tester().ExpectUniqueSample(
+      SuffixedHistogram("HeavyAds.InterventionType2"),
+      FrameData::HeavyAdStatus::kTotalCpu, 1);
+
+  // Navigate again to trigger histograms.
+  NavigateFrame(kNonAdUrl, main_frame);
+
+  histogram_tester().ExpectUniqueSample(
+      SuffixedHistogram("HeavyAds.ComputedType2"),
+      FrameData::HeavyAdStatus::kNetwork, 1);
+  histogram_tester().ExpectUniqueSample(
+      SuffixedHistogram("HeavyAds.ComputedTypeWithThresholdNoise"),
+      FrameData::HeavyAdStatus::kTotalCpu, 1);
+}
+
 TEST_F(AdsPageLoadMetricsObserverTest, HeavyAdTotalCpuUsage_InterventionFired) {
   base::test::ScopedFeatureList feature_list;
   feature_list.InitAndEnableFeature(features::kHeavyAdIntervention);
@@ -1649,7 +1788,7 @@
 
   // Add enough data to trigger the intervention.
   ResourceDataUpdate(ad_frame, ResourceCached::NOT_CACHED,
-                     (heavy_ad_thresholds::kMaxNetworkBytes / 1024) + 1);
+                     (kMaxHeavyAdNetworkBytes / 1024) + 1);
 
   histogram_tester().ExpectTotalCount(
       SuffixedHistogram("HeavyAds.InterventionType2"), 0);
@@ -1713,6 +1852,9 @@
     blocklist()->AddEntry(GURL(kNonAdUrl).host(), true, 0);
 
   RenderFrameHost* main_frame = NavigateMainFrame(kNonAdUrl);
+
+  OverrideHeavyAdNoiseProvider(
+      std::make_unique<MockNoiseProvider>(0 /* network noise */));
   RenderFrameHost* ad_frame = CreateAndNavigateSubFrame(kAdUrl, main_frame);
 
   // Add enough data to trigger the intervention.
@@ -1733,6 +1875,9 @@
     blocklist()->AddEntry(GURL(kNonAdUrl).host(), true, 0);
 
   RenderFrameHost* main_frame = NavigateMainFrame(kNonAdUrl);
+
+  OverrideHeavyAdNoiseProvider(
+      std::make_unique<MockNoiseProvider>(0 /* network noise */));
   RenderFrameHost* ad_frame = CreateAndNavigateSubFrame(kAdUrl, main_frame);
 
   // Add enough data to trigger the intervention.
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 14c0ab5..fa14dba3 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
@@ -55,7 +55,8 @@
   return ResourceMimeType::kOther;
 }
 
-FrameData::FrameData(FrameTreeNodeId frame_tree_node_id)
+FrameData::FrameData(FrameTreeNodeId frame_tree_node_id,
+                     int heavy_ad_network_threshold_noise)
     : bytes_(0u),
       network_bytes_(0u),
       same_origin_bytes_(0u),
@@ -66,7 +67,9 @@
       is_display_none_(false),
       visibility_(FrameVisibility::kVisible),
       frame_size_(gfx::Size()),
-      heavy_ad_status_(HeavyAdStatus::kNone) {}
+      heavy_ad_status_(HeavyAdStatus::kNone),
+      heavy_ad_status_with_noise_(HeavyAdStatus::kNone),
+      heavy_ad_network_threshold_noise_(heavy_ad_network_threshold_noise) {}
 
 FrameData::~FrameData() = default;
 
@@ -184,11 +187,17 @@
 
 bool FrameData::MaybeTriggerHeavyAdIntervention() {
   if (user_activation_status_ == UserActivationStatus::kReceivedActivation ||
-      heavy_ad_status_ != HeavyAdStatus::kNone)
+      heavy_ad_status_with_noise_ != HeavyAdStatus::kNone)
     return false;
 
-  heavy_ad_status_ = ComputeHeavyAdStatus();
-  if (heavy_ad_status_ == HeavyAdStatus::kNone)
+  if (heavy_ad_status_ == HeavyAdStatus::kNone) {
+    heavy_ad_status_ =
+        ComputeHeavyAdStatus(false /* use_network_threshold_noise */);
+  }
+
+  heavy_ad_status_with_noise_ =
+      ComputeHeavyAdStatus(true /* use_network_threshold_noise */);
+  if (heavy_ad_status_with_noise_ == HeavyAdStatus::kNone)
     return false;
 
   // Only check if the feature is enabled once we have a heavy ad. This is done
@@ -305,7 +314,8 @@
           : FrameVisibility::kNonVisible;
 }
 
-FrameData::HeavyAdStatus FrameData::ComputeHeavyAdStatus() const {
+FrameData::HeavyAdStatus FrameData::ComputeHeavyAdStatus(
+    bool use_network_threshold_noise) const {
   // Check if the frame meets the peak CPU usage threshold.
   if (peak_windowed_cpu_percent_ >=
       heavy_ad_thresholds::kMaxPeakWindowedPercent) {
@@ -316,8 +326,12 @@
   if (GetTotalCpuUsage().InMilliseconds() >= heavy_ad_thresholds::kMaxCpuTime)
     return HeavyAdStatus::kTotalCpu;
 
-  // Check if the frame meets the network threshold.
-  if (network_bytes_ >= heavy_ad_thresholds::kMaxNetworkBytes)
+  size_t network_threshold =
+      heavy_ad_thresholds::kMaxNetworkBytes +
+      (use_network_threshold_noise ? heavy_ad_network_threshold_noise_ : 0);
+
+  // Check if the frame meets the network threshold, possible including noise.
+  if (network_bytes_ >= network_threshold)
     return HeavyAdStatus::kNetwork;
   return HeavyAdStatus::kNone;
 }
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 45eee23..0f85a8b 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
@@ -21,7 +21,8 @@
 // Maximum number of network bytes allowed to be loaded by a frame. These
 // numbers reflect the 99.9th percentile of the
 // PageLoad.Clients.Ads.Bytes.AdFrames.PerFrame.Network histogram on mobile and
-// desktop.
+// desktop. Additive noise is added to this threshold, see
+// AdsPageLoadMetricsObserver::HeavyAdThresholdNoiseProvider.
 const int kMaxNetworkBytes = 4.0 * 1024 * 1024;
 
 // CPU thresholds are selected from AdFrameLoad UKM, and are intended to target
@@ -117,7 +118,8 @@
   static ResourceMimeType GetResourceMimeType(
       const page_load_metrics::mojom::ResourceDataUpdatePtr& resource);
 
-  explicit FrameData(FrameTreeNodeId frame_tree_node_id);
+  explicit FrameData(FrameTreeNodeId frame_tree_node_id,
+                     int heavy_ad_network_threshold_noise);
   ~FrameData();
 
   // Update the metadata of this frame if it is being navigated.
@@ -227,6 +229,10 @@
 
   HeavyAdStatus heavy_ad_status() const { return heavy_ad_status_; }
 
+  HeavyAdStatus heavy_ad_status_with_noise() const {
+    return heavy_ad_status_with_noise_;
+  }
+
  private:
   // Time updates for the frame with a timestamp indicating when they arrived.
   // Used for windowed cpu load reporting.
@@ -242,7 +248,10 @@
 
   // Computes whether this frame meets the criteria for being a heavy frame for
   // the heavy ad intervention and returns the type of threshold hit if any.
-  HeavyAdStatus ComputeHeavyAdStatus() const;
+  // If |use_network_threshold_noise| is set,
+  // |heavy_ad_network_threshold_noise_| is added to the network threshold when
+  // computing the status.
+  HeavyAdStatus ComputeHeavyAdStatus(bool use_network_threshold_noise) const;
 
   // The most recently updated timing received for this frame.
   page_load_metrics::mojom::PageLoadTimingPtr timing_;
@@ -312,10 +321,19 @@
   MediaStatus media_status_ = MediaStatus::kNotPlayed;
 
   // Indicates whether or not this frame met the criteria for the heavy ad
-  // intervention. This should be not be set if the Heavy Ad Intervention is
-  // not enabled.
+  // intervention.
   HeavyAdStatus heavy_ad_status_;
 
+  // Same as |heavy_ad_status_| but uses additional additive noise for the
+  // network threshold. A frame can be considered a heavy ad by
+  // |heavy_ad_status_| but not |heavy_ad_status_with_noise_|. The noised
+  // threshold is used when determining whether to actually trigger the
+  // intervention.
+  HeavyAdStatus heavy_ad_status_with_noise_;
+
+  // Number of bytes of noise that should be added to the network threshold.
+  const int heavy_ad_network_threshold_noise_;
+
   DISALLOW_COPY_AND_ASSIGN(FrameData);
 };
 
diff --git a/chrome/browser/policy/configuration_policy_handler_list_factory.cc b/chrome/browser/policy/configuration_policy_handler_list_factory.cc
index b2ab51da..69bf848 100644
--- a/chrome/browser/policy/configuration_policy_handler_list_factory.cc
+++ b/chrome/browser/policy/configuration_policy_handler_list_factory.cc
@@ -1599,6 +1599,9 @@
   handlers->AddHandler(
       std::make_unique<PrintingBackgroundGraphicsDefaultPolicyHandler>());
   handlers->AddHandler(std::make_unique<PrintingSizeDefaultPolicyHandler>());
+  handlers->AddHandler(std::make_unique<IntRangePolicyHandler>(
+      key::kPrintJobHistoryExpirationPeriod,
+      prefs::kPrintJobHistoryExpirationPeriod, -1, INT_MAX, true));
   handlers->AddHandler(std::make_unique<SimpleSchemaValidatingPolicyHandler>(
       key::kNetworkFileSharesPreconfiguredShares,
       prefs::kNetworkFileSharesPreconfiguredShares, chrome_schema,
diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc
index 09949f2d..1f4ba93 100644
--- a/chrome/browser/prefs/browser_prefs.cc
+++ b/chrome/browser/prefs/browser_prefs.cc
@@ -284,6 +284,7 @@
 #include "chrome/browser/chromeos/power/power_metrics_reporter.h"
 #include "chrome/browser/chromeos/preferences.h"
 #include "chrome/browser/chromeos/printing/cups_printers_manager.h"
+#include "chrome/browser/chromeos/printing/history/print_job_history_service.h"
 #include "chrome/browser/chromeos/printing/synced_printers_manager.h"
 #include "chrome/browser/chromeos/release_notes/release_notes_storage.h"
 #include "chrome/browser/chromeos/resource_reporter/resource_reporter.h"
@@ -923,6 +924,7 @@
   chromeos::quick_unlock::FingerprintStorage::RegisterProfilePrefs(registry);
   chromeos::quick_unlock::PinStoragePrefs::RegisterProfilePrefs(registry);
   chromeos::Preferences::RegisterProfilePrefs(registry);
+  chromeos::PrintJobHistoryService::RegisterProfilePrefs(registry);
   chromeos::SyncedPrintersManager::RegisterProfilePrefs(registry);
   chromeos::parent_access::ParentAccessService::RegisterProfilePrefs(registry);
   chromeos::quick_unlock::RegisterProfilePrefs(registry);
diff --git a/chrome/browser/prerender/prerender_manager.cc b/chrome/browser/prerender/prerender_manager.cc
index a2e1d88d..a74ab970 100644
--- a/chrome/browser/prerender/prerender_manager.cc
+++ b/chrome/browser/prerender/prerender_manager.cc
@@ -78,10 +78,16 @@
 namespace {
 
 // Time interval at which periodic cleanups are performed.
-const int kPeriodicCleanupIntervalMs = 1000;
+constexpr base::TimeDelta kPeriodicCleanupInterval =
+    base::TimeDelta::FromMilliseconds(1000);
+
+// Time interval after which OnCloseWebContentsDeleter will schedule a
+// WebContents for deletion.
+constexpr base::TimeDelta kDeleteWithExtremePrejudice =
+    base::TimeDelta::FromSeconds(3);
 
 // Length of prerender history, for display in chrome://net-internals
-const int kHistoryLength = 100;
+constexpr int kHistoryLength = 100;
 
 // Check if |extra_headers| requested via chrome::NavigateParams::extra_headers
 // are the same as what the HTTP server saw when serving prerendered contents.
@@ -108,19 +114,19 @@
  public:
   OnCloseWebContentsDeleter(PrerenderManager* manager,
                             std::unique_ptr<WebContents> tab)
-      : manager_(manager), tab_(std::move(tab)), suppressed_dialog_(false) {
+      : manager_(manager), tab_(std::move(tab)) {
     tab_->SetDelegate(this);
     base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
         FROM_HERE,
         base::BindOnce(
             &OnCloseWebContentsDeleter::ScheduleWebContentsForDeletion,
-            AsWeakPtr(), true),
-        base::TimeDelta::FromSeconds(kDeleteWithExtremePrejudiceSeconds));
+            AsWeakPtr(), /*timeout=*/true),
+        kDeleteWithExtremePrejudice);
   }
 
   void CloseContents(WebContents* source) override {
     DCHECK_EQ(tab_.get(), source);
-    ScheduleWebContentsForDeletion(false);
+    ScheduleWebContentsForDeletion(/*timeout=*/false);
   }
 
   bool ShouldSuppressDialogs(WebContents* source) override {
@@ -132,8 +138,6 @@
   }
 
  private:
-  static const int kDeleteWithExtremePrejudiceSeconds = 3;
-
   void ScheduleWebContentsForDeletion(bool timeout) {
     UMA_HISTOGRAM_BOOLEAN("Prerender.TabContentsDeleterTimeout", timeout);
     UMA_HISTOGRAM_BOOLEAN("Prerender.TabContentsDeleterSuppressedDialog",
@@ -145,12 +149,12 @@
 
   PrerenderManager* const manager_;
   std::unique_ptr<WebContents> tab_;
-  bool suppressed_dialog_;
+  bool suppressed_dialog_ = false;
 
   DISALLOW_COPY_AND_ASSIGN(OnCloseWebContentsDeleter);
 };
 
-PrerenderManagerObserver::~PrerenderManagerObserver() {}
+PrerenderManagerObserver::~PrerenderManagerObserver() = default;
 
 // static
 PrerenderManager::PrerenderManagerMode PrerenderManager::mode_ =
@@ -198,12 +202,9 @@
 PrerenderManager::PrerenderManager(Profile* profile)
     : profile_(profile),
       prerender_contents_factory_(PrerenderContents::CreateFactory()),
-      prerender_history_(new PrerenderHistory(kHistoryLength)),
-      histograms_(new PrerenderHistograms()),
-      profile_network_bytes_(0),
-      last_recorded_profile_network_bytes_(0),
-      tick_clock_(base::DefaultTickClock::GetInstance()),
-      page_load_metric_observer_disabled_(false) {
+      prerender_history_(std::make_unique<PrerenderHistory>(kHistoryLength)),
+      histograms_(std::make_unique<PrerenderHistograms>()),
+      tick_clock_(base::DefaultTickClock::GetInstance()) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   last_prerender_start_time_ =
@@ -582,8 +583,9 @@
   for (const auto& prerender_data : active_prerenders_) {
     PrerenderContents* prerender_contents = prerender_data->contents();
     if (prerender_contents->Matches(url, session_storage_namespace) &&
-        prerender_contents->has_finished_loading())
+        prerender_contents->has_finished_loading()) {
       return true;
+    }
   }
   return false;
 }
@@ -716,13 +718,11 @@
     base::TimeTicks expiry_time)
     : manager_(manager),
       contents_(std::move(contents)),
-      handle_count_(0),
       expiry_time_(expiry_time) {
   DCHECK(contents_);
 }
 
-PrerenderManager::PrerenderData::~PrerenderData() {
-}
+PrerenderManager::PrerenderData::~PrerenderData() = default;
 
 void PrerenderManager::PrerenderData::OnHandleCreated(PrerenderHandle* handle) {
   DCHECK(contents_);
@@ -964,10 +964,9 @@
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   if (repeating_timer_.IsRunning())
     return;
-  repeating_timer_.Start(FROM_HERE,
-      base::TimeDelta::FromMilliseconds(kPeriodicCleanupIntervalMs),
-      this,
-      &PrerenderManager::PeriodicCleanup);
+
+  repeating_timer_.Start(FROM_HERE, kPeriodicCleanupInterval, this,
+                         &PrerenderManager::PeriodicCleanup);
 }
 
 void PrerenderManager::StopSchedulingPeriodicCleanups() {
@@ -1325,9 +1324,8 @@
 void PrerenderManager::AddPrerenderProcessHost(
     content::RenderProcessHost* process_host) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  DCHECK(prerender_process_hosts_.find(process_host) ==
-         prerender_process_hosts_.end());
-  prerender_process_hosts_.insert(process_host);
+  bool inserted = prerender_process_hosts_.insert(process_host).second;
+  DCHECK(inserted);
   process_host->AddObserver(this);
 }
 
@@ -1336,8 +1334,7 @@
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   // Isolate prerender processes to make the resource monitoring check more
   // accurate.
-  return (prerender_process_hosts_.find(process_host) ==
-          prerender_process_hosts_.end());
+  return !base::Contains(prerender_process_hosts_, process_host);
 }
 
 void PrerenderManager::RenderProcessHostDestroyed(
diff --git a/chrome/browser/prerender/prerender_manager.h b/chrome/browser/prerender/prerender_manager.h
index 346eb560..cda0b3fc 100644
--- a/chrome/browser/prerender/prerender_manager.h
+++ b/chrome/browser/prerender/prerender_manager.h
@@ -409,7 +409,7 @@
     // counting the ones that have called PrerenderData::OnHandleCanceled(). For
     // pending prerenders, this will always be 1, since the PrerenderManager
     // only merges handles of running prerenders.
-    int handle_count_;
+    int handle_count_ = 0;
 
     // The time when OnHandleNavigatedAway was called.
     base::TimeTicks abandon_time_;
@@ -603,16 +603,16 @@
   std::vector<std::unique_ptr<OnCloseWebContentsDeleter>>
       on_close_web_contents_deleters_;
 
-  std::unique_ptr<PrerenderHistory> prerender_history_;
+  const std::unique_ptr<PrerenderHistory> prerender_history_;
 
-  std::unique_ptr<PrerenderHistograms> histograms_;
+  const std::unique_ptr<PrerenderHistograms> histograms_;
 
   // The number of bytes transferred over the network for the profile this
   // PrerenderManager is attached to.
-  int64_t profile_network_bytes_;
+  int64_t profile_network_bytes_ = 0;
 
   // The value of profile_network_bytes_ that was last recorded.
-  int64_t last_recorded_profile_network_bytes_;
+  int64_t last_recorded_profile_network_bytes_ = 0;
 
   // Set of process hosts being prerendered.
   using PrerenderProcessSet = std::set<content::RenderProcessHost*>;
@@ -620,7 +620,7 @@
 
   const base::TickClock* tick_clock_;
 
-  bool page_load_metric_observer_disabled_;
+  bool page_load_metric_observer_disabled_ = false;
 
   std::vector<std::unique_ptr<PrerenderManagerObserver>> observers_;
 
diff --git a/chrome/browser/previews/previews_lite_page_redirect_browsertest.cc b/chrome/browser/previews/previews_lite_page_redirect_browsertest.cc
index 941e60c..327ee086 100644
--- a/chrome/browser/previews/previews_lite_page_redirect_browsertest.cc
+++ b/chrome/browser/previews/previews_lite_page_redirect_browsertest.cc
@@ -141,6 +141,8 @@
 
   virtual bool UseOptimizationGuideKeyedServiceImplementation() const = 0;
 
+  virtual bool ShouldEnableDRPHoldback() const = 0;
+
   enum PreviewsServerAction {
     // Previews server will respond with HTTP 200 OK, OFCL=60,
     // Content-Length=20.
@@ -321,6 +323,10 @@
       opt_guide_keyed_service_feature_list_.InitWithFeatures(
           {}, {optimization_guide::features::kOptimizationGuideKeyedService});
     }
+
+    drp_holdback_feature_list_.InitWithFeatureState(
+        data_reduction_proxy::features::kDataReductionProxyHoldback,
+        ShouldEnableDRPHoldback());
   }
 
   void SetUpOnMainThread() override {
@@ -895,6 +901,7 @@
   base::test::ScopedFeatureList scoped_feature_list_;
   base::test::ScopedFeatureList url_loader_feature_list_;
   base::test::ScopedFeatureList opt_guide_keyed_service_feature_list_;
+  base::test::ScopedFeatureList drp_holdback_feature_list_;
   std::unique_ptr<net::EmbeddedTestServer> previews_server_;
   std::unique_ptr<net::EmbeddedTestServer> https_server_;
   std::unique_ptr<net::EmbeddedTestServer> http_server_;
@@ -922,23 +929,27 @@
   base::OnceClosure waiting_for_report_closure_;
 };
 
-// Param is true if testing using the OptimizationGuideKeyedService
-// implementation.
+// First param is true if testing using the OptimizationGuideKeyedService
+// implementation. Second param is true if DRP holdback should be enabled.
 class PreviewsLitePageRedirectServerBrowserTest
     : public BasePreviewsLitePageRedirectServerBrowserTest,
-      public testing::WithParamInterface<bool> {
+      public ::testing::WithParamInterface<std::tuple<bool, bool>> {
  public:
   bool UseOptimizationGuideKeyedServiceImplementation() const override {
-    return GetParam();
+    return std::get<0>(GetParam());
+  }
+  bool ShouldEnableDRPHoldback() const override {
+    return std::get<1>(GetParam());
   }
 };
 
-// Param is true if testing using the OptimizationGuideKeyedService
-// implementation.
+// First param is true if testing using the OptimizationGuideKeyedService
+// implementation. Second param is true if DRP holdback should be
+// enabled.
 INSTANTIATE_TEST_SUITE_P(
     /* no prefix */,
     PreviewsLitePageRedirectServerBrowserTest,
-    testing::Bool());
+    ::testing::Combine(::testing::Bool(), ::testing::Bool()));
 
 // Previews InfoBar (which these tests trigger) does not work on Mac.
 // See https://crbug.com/782322 for detail.
@@ -1596,12 +1607,12 @@
   }
 };
 
-// Param is true if testing using the OptimizationGuideKeyedService
-// implementation.
+// First param is true if testing using the OptimizationGuideKeyedService
+// implementation. Second param is true if DRP holdback should be enabled.
 INSTANTIATE_TEST_SUITE_P(
     /* no prefix */,
     PreviewsLitePageRedirectServerTimeoutBrowserTest,
-    testing::Bool());
+    ::testing::Combine(::testing::Bool(), ::testing::Bool()));
 
 IN_PROC_BROWSER_TEST_P(PreviewsLitePageRedirectServerTimeoutBrowserTest,
                        DISABLE_ON_WIN_MAC_CHROMESOS(LitePagePreviewsTimeout)) {
@@ -1642,12 +1653,12 @@
   }
 };
 
-// Param is true if testing using the OptimizationGuideKeyedService
-// implementation.
+// First param is true if testing using the OptimizationGuideKeyedService
+// implementation. Second param is true if DRP holdback should be enabled.
 INSTANTIATE_TEST_SUITE_P(
     /* no prefix */,
     PreviewsLitePageRedirectServerBadServerBrowserTest,
-    testing::Bool());
+    ::testing::Combine(::testing::Bool(), ::testing::Bool()));
 
 IN_PROC_BROWSER_TEST_P(
     PreviewsLitePageRedirectServerBadServerBrowserTest,
@@ -1690,12 +1701,12 @@
   }
 };
 
-// Param is true if testing using the OptimizationGuideKeyedService
-// implementation.
+// First param is true if testing using the OptimizationGuideKeyedService
+// implementation. Second param is true if DRP holdback should be enabled.
 INSTANTIATE_TEST_SUITE_P(
     /* no prefix */,
     PreviewsLitePageRedirectServerDataSaverBrowserTest,
-    testing::Bool());
+    ::testing::Combine(::testing::Bool(), ::testing::Bool()));
 
 IN_PROC_BROWSER_TEST_P(
     PreviewsLitePageRedirectServerDataSaverBrowserTest,
@@ -1728,12 +1739,12 @@
   }
 };
 
-// Param is true if testing using the OptimizationGuideKeyedService
-// implementation.
+// First param is true if testing using the OptimizationGuideKeyedService
+// implementation. Second param is true if DRP holdback should be enabled.
 INSTANTIATE_TEST_SUITE_P(
     /* no prefix */,
     PreviewsLitePageRedirectServerNoDataSaverHeaderBrowserTest,
-    testing::Bool());
+    ::testing::Combine(::testing::Bool(), ::testing::Bool()));
 
 IN_PROC_BROWSER_TEST_P(
     PreviewsLitePageRedirectServerNoDataSaverHeaderBrowserTest,
@@ -1770,12 +1781,12 @@
   }
 };
 
-// Param is true if testing using the OptimizationGuideKeyedService
-// implementation.
+// First param is true if testing using the OptimizationGuideKeyedService
+// implementation. Second param is true if DRP holdback should be enabled.
 INSTANTIATE_TEST_SUITE_P(
     /* no prefix */,
     PreviewsLitePageRedirectNotificationDSEnabledBrowserTest,
-    testing::Bool());
+    ::testing::Combine(::testing::Bool(), ::testing::Bool()));
 
 IN_PROC_BROWSER_TEST_P(
     PreviewsLitePageRedirectNotificationDSEnabledBrowserTest,
@@ -1831,12 +1842,12 @@
   }
 };
 
-// Param is true if testing using the OptimizationGuideKeyedService
-// implementation.
+// First param is true if testing using the OptimizationGuideKeyedService
+// implementation. Second param is true if DRP holdback should be enabled.
 INSTANTIATE_TEST_SUITE_P(
     /* no prefix */,
     PreviewsLitePageRedirectDSDisabledBrowserTest,
-    testing::Bool());
+    ::testing::Combine(::testing::Bool(), ::testing::Bool()));
 
 IN_PROC_BROWSER_TEST_P(
     PreviewsLitePageRedirectDSDisabledBrowserTest,
@@ -1864,12 +1875,12 @@
   }
 };
 
-// Param is true if testing using the OptimizationGuideKeyedService
-// implementation.
+// First param is true if testing using the OptimizationGuideKeyedService
+// implementation. Second param is true if DRP holdback should be enabled.
 INSTANTIATE_TEST_SUITE_P(
     /* no prefix */,
     PreviewsLitePageRedirectControlBrowserTest,
-    testing::Bool());
+    ::testing::Combine(::testing::Bool(), ::testing::Bool()));
 
 IN_PROC_BROWSER_TEST_P(
     PreviewsLitePageRedirectControlBrowserTest,
@@ -1924,6 +1935,8 @@
     return false;
   }
 
+  bool ShouldEnableDRPHoldback() const override { return false; }
+
  private:
   base::test::ScopedFeatureList scoped_feature_list_;
 };
@@ -1999,12 +2012,12 @@
   }
 };
 
-// Param is true if testing using the OptimizationGuideKeyedService
-// implementation.
+// First param is true if testing using the OptimizationGuideKeyedService
+// implementation. Second param is true if DRP holdback should be enabled.
 INSTANTIATE_TEST_SUITE_P(
     /* no prefix */,
     PreviewsLitePageRedirectAndPageHintsBrowserTest,
-    testing::Bool());
+    ::testing::Combine(::testing::Bool(), ::testing::Bool()));
 
 // Regression test for crbug.com/954554.
 IN_PROC_BROWSER_TEST_P(
@@ -2223,12 +2236,12 @@
   base::test::ScopedFeatureList ukm_feature_list_;
 };
 
-// Param is true if testing using the OptimizationGuideKeyedService
-// implementation.
+// First param is true if testing using the OptimizationGuideKeyedService
+// implementation. Second param is true if DRP holdback should be enabled.
 INSTANTIATE_TEST_SUITE_P(
     /* no prefix */,
     CoinFlipHoldbackExperimentBrowserTest,
-    testing::Bool());
+    ::testing::Combine(::testing::Bool(), ::testing::Bool()));
 
 IN_PROC_BROWSER_TEST_P(CoinFlipHoldbackExperimentBrowserTest,
                        DISABLE_ON_WIN_MAC_CHROMESOS(NoPreviews_NoCoinFlip)) {
diff --git a/chrome/browser/resources/chromeos/assistant_optin/assistant_optin_flow.js b/chrome/browser/resources/chromeos/assistant_optin/assistant_optin_flow.js
index 19ad679..9f85f90 100644
--- a/chrome/browser/resources/chromeos/assistant_optin/assistant_optin_flow.js
+++ b/chrome/browser/resources/chromeos/assistant_optin/assistant_optin_flow.js
@@ -82,6 +82,7 @@
    */
   reloadContent: function(data) {
     this.voiceMatchEnforcedOff = data['voiceMatchEnforcedOff'];
+    this.voiceMatchDisabled = loadTimeData.getBoolean('voiceMatchDisabled');
     data['flowType'] = this.flowType;
     this.$['value-prop'].reloadContent(data);
     this.$['third-party'].reloadContent(data);
@@ -118,7 +119,7 @@
         this.showScreen(this.$['third-party']);
         break;
       case this.$['third-party']:
-        if (this.voiceMatchEnforcedOff) {
+        if (this.voiceMatchEnforcedOff || this.voiceMatchDisabled) {
           this.showScreen(this.$['get-more']);
         } else {
           this.showScreen(this.$['voice-match']);
diff --git a/chrome/browser/resources/local_ntp/customize.css b/chrome/browser/resources/local_ntp/customize.css
index ec34586..b8604522 100644
--- a/chrome/browser/resources/local_ntp/customize.css
+++ b/chrome/browser/resources/local_ntp/customize.css
@@ -665,8 +665,7 @@
   color: white;
   font-weight: 500;
   left: 16px;
-  max-width: 80%;
-  padding: 8px 8px 8px 8px;
+  padding: 8px;
   position: fixed;
   text-shadow: 0 0 16px rgba(0, 0, 0, .3);
   z-index: -1;
@@ -678,18 +677,30 @@
   right: 16px;
 }
 
-.attr-common {
+#custom-bg-attr a {
+  clear: both;
+  color: inherit;
+  float: left;
   font-size: 13px;
   height: 20px;
   line-height: 20px;
+  max-width: 50vw;
   overflow: hidden;
   text-overflow: ellipsis;
   vertical-align: middle;
   white-space: nowrap;
-  width: -webkit-fill-available;
 }
 
-.attr-common.attr-small {
+[dir=rtl] #custom-bg-attr a {
+  float: right;
+}
+
+#custom-bg-attr a[href=''] {
+  cursor: auto;
+  text-decoration: none;
+}
+
+#custom-bg-attr a ~ a {
   font-size: 11px;
 }
 
@@ -697,11 +708,6 @@
   background: rgba(var(--GG900-rgb), .1);
 }
 
-.attr-link {
-  display: inline-block;
-  text-decoration: underline;
-}
-
 #link-icon {
   background: url(icons/link.svg);
   background-size: 10px 10px;
diff --git a/chrome/browser/resources/local_ntp/customize.js b/chrome/browser/resources/local_ntp/customize.js
index d75d7a0..487827f 100644
--- a/chrome/browser/resources/local_ntp/customize.js
+++ b/chrome/browser/resources/local_ntp/customize.js
@@ -163,7 +163,6 @@
  */
 customize.CLASSES = {
   ATTR_SMALL: 'attr-small',
-  ATTR_COMMON: 'attr-common',
   ATTR_LINK: 'attr-link',
   COLLECTION_DIALOG: 'is-col-sel',
   COLLECTION_SELECTED: 'bg-selected',  // Highlight selected tile
@@ -358,25 +357,27 @@
 customize.setAttribution = function(
     attributionLine1, attributionLine2, attributionActionUrl) {
   const attributionBox = $(customize.IDS.ATTRIBUTIONS);
-  const attr1 = document.createElement('span');
+  const attr1 = document.createElement('a');
   attr1.id = customize.IDS.ATTR1;
-  const attr2 = document.createElement('span');
+  const attr2 = document.createElement('a');
   attr2.id = customize.IDS.ATTR2;
 
   if (attributionLine1 !== '') {
     // Shouldn't be changed from textContent for security assurances.
     attr1.textContent = attributionLine1;
-    attr1.classList.add(customize.CLASSES.ATTR_COMMON);
+    attr1.href = attributionActionUrl || '';
     $(customize.IDS.ATTRIBUTIONS).appendChild(attr1);
   }
+
   if (attributionLine2 !== '') {
     // Shouldn't be changed from textContent for security assurances.
     attr2.textContent = attributionLine2;
-    attr2.classList.add(customize.CLASSES.ATTR_SMALL);
-    attr2.classList.add(customize.CLASSES.ATTR_COMMON);
+    attr2.href = attributionActionUrl || '';
     attributionBox.appendChild(attr2);
   }
-  if (attributionActionUrl !== '') {
+
+  const hasActionUrl = attributionActionUrl !== '';
+  if (hasActionUrl) {
     const attr = (attributionLine2 !== '' ? attr2 : attr1);
     attr.classList.add(customize.CLASSES.ATTR_LINK);
 
@@ -388,24 +389,18 @@
     }
     attr.insertBefore(linkIcon, attr.firstChild);
 
-    attributionBox.classList.add(customize.CLASSES.ATTR_LINK);
-    attributionBox.href = attributionActionUrl;
-    attributionBox.onclick = function() {
-      ntpApiHandle.logEvent(
-          customize.LOG_TYPE.NTP_CUSTOMIZE_ATTRIBUTION_CLICKED);
+    attributionBox.onclick = e => {
+      if (attr1.contains(e.target) || attr2.contains(e.target)) {
+        ntpApiHandle.logEvent(
+            customize.LOG_TYPE.NTP_CUSTOMIZE_ATTRIBUTION_CLICKED);
+      }
     };
-    attributionBox.style.cursor = 'pointer';
   }
+  attributionBox.classList.toggle(customize.CLASSES.ATTR_LINK, hasActionUrl);
 };
 
 customize.clearAttribution = function() {
-  const attributions = $(customize.IDS.ATTRIBUTIONS);
-  attributions.removeAttribute('href');
-  attributions.className = '';
-  attributions.style.cursor = 'default';
-  while (attributions.firstChild) {
-    attributions.removeChild(attributions.firstChild);
-  }
+  $(customize.IDS.ATTRIBUTIONS).innerHTML = '';
 };
 
 customize.unselectTile = function() {
diff --git a/chrome/browser/resources/local_ntp/local_ntp.html b/chrome/browser/resources/local_ntp/local_ntp.html
index 5690800..d5522ba 100644
--- a/chrome/browser/resources/local_ntp/local_ntp.html
+++ b/chrome/browser/resources/local_ntp/local_ntp.html
@@ -121,7 +121,7 @@
       <span id="edit-bg-text">$i18n{customizeButton}</span>
     </div>
 
-    <a id="custom-bg-attr"></a>
+    <div id="custom-bg-attr"></div>
   </div>
 
   <dialog div id="edit-bg-dialog">
diff --git a/chrome/browser/resources/settings/a11y_page/captions_subpage.html b/chrome/browser/resources/settings/a11y_page/captions_subpage.html
index 2e56392..a894a919 100644
--- a/chrome/browser/resources/settings/a11y_page/captions_subpage.html
+++ b/chrome/browser/resources/settings/a11y_page/captions_subpage.html
@@ -10,16 +10,37 @@
     <style include="settings-shared">
       .preview-box {
         align-items: center;
-        border: var(--cr-separator-line);
-        border-radius: var(--cr-card-border-radius);
+        background-image:
+          url(chrome://theme/IDR_ACCESSIBILITY_CAPTIONS_PREVIEW_BACKGROUND);
+        background-position: center;
+        background-size: cover;
         display: flex;
         height: 112px;
         justify-content: center;
-        margin: var(--cr-section-padding);
+        margin: 0 var(--cr-section-padding) var(--cr-section-padding);
         text-align: center;
       }
     </style>
     <div class="settings-box first">
+      <h2 class="start">$i18n{captionsPreview}</h2>
+    </div>
+    <div class="preview-box">
+      <span style="
+          font-size:[[prefs.accessibility.captions.text_size.value]];
+          font-family:[[prefs.accessibility.captions.text_font.value]];
+          background-color: [[computeBackgroundColor_(
+              prefs.accessibility.captions.background_opacity.value,
+              prefs.accessibility.captions.background_color.value)]];
+          color: [[computeTextColor_(
+              prefs.accessibility.captions.text_opacity.value,
+              prefs.accessibility.captions.text_color.value)]];
+          text-shadow: [[prefs.accessibility.captions.text_shadow.value]];
+          padding: [[computePadding_(
+              prefs.accessibility.captions.text_size.value)]]">
+        $i18n{quickBrownFox}
+      </span>
+    </div>
+    <div class="settings-box continuation">
       <h2 class="start">$i18n{captionsSettings}</h2>
     </div>
     <div class="list-frame">
@@ -49,12 +70,11 @@
       </div>
       <div class="list-item underbar">
         <div class="start settings-box-text">$i18n{captionsTextOpacity}</div>
-        <settings-slider id="captionsTextOpacity"
-            ticks="[[textOpacityRange_]]"
-            label-min="$i18n{captionsOpacityMin}"
-            label-max="$i18n{captionsOpacityMax}"
-            pref="{{prefs.accessibility.captions.text_opacity}}">
-        </settings-slider>
+        <settings-dropdown-menu id="captionsTextOpacity"
+            label="$i18n{captionsTextOpacity}"
+            pref="{{prefs.accessibility.captions.text_opacity}}"
+            menu-options="[[textOpacityOptions_]]">
+        </settings-dropdown-menu>
       </div>
       <div class="list-item underbar">
         <div class="start settings-box-text">$i18n{captionsTextShadow}</div>
@@ -78,33 +98,13 @@
         <div class="start settings-box-text">
           $i18n{captionsBackgroundOpacity}
         </div>
-        <settings-slider id="captionsBackgroundOpacity"
-            ticks="[[textOpacityRange_]]"
-            label-min="$i18n{captionsOpacityMin}"
-            label-max="$i18n{captionsOpacityMax}"
-            pref="{{prefs.accessibility.captions.background_opacity}}">
-        </settings-slider>
+        <settings-dropdown-menu id="captionsBackgroundOpacity"
+            label="$i18n{captionsBackgroundOpacity}"
+            pref="{{prefs.accessibility.captions.background_opacity}}"
+            menu-options="[[backgroundOpacityOptions_]]">
+        </settings-dropdown-menu>
       </div>
     </div>
-    <div class="settings-box continuation">
-      <h2 class="start">$i18n{captionsPreview}</h2>
-    </div>
-    <div class="preview-box">
-      <span style="
-          font-size:[[prefs.accessibility.captions.text_size.value]];
-          font-family:[[prefs.accessibility.captions.text_font.value]];
-          background-color: [[computeBackgroundColor_(
-              prefs.accessibility.captions.background_opacity.value,
-              prefs.accessibility.captions.background_color.value)]];
-          color: [[computeTextColor_(
-              prefs.accessibility.captions.text_opacity.value,
-              prefs.accessibility.captions.text_color.value)]];
-          text-shadow: [[prefs.accessibility.captions.text_shadow.value]];
-          padding: [[computePadding_(
-              prefs.accessibility.captions.text_size.value)]]">
-        $i18n{quickBrownFox}
-      </span>
-    </div>
   </template>
   <script src="captions_subpage.js"></script>
 </dom-module>
diff --git a/chrome/browser/resources/settings/a11y_page/captions_subpage.js b/chrome/browser/resources/settings/a11y_page/captions_subpage.js
index ee0f580..ca625a3 100644
--- a/chrome/browser/resources/settings/a11y_page/captions_subpage.js
+++ b/chrome/browser/resources/settings/a11y_page/captions_subpage.js
@@ -9,21 +9,6 @@
 (function() {
 'use strict';
 
-
-/** @type {!Array<number>} */
-const TEXT_OPACITY_RANGE = [
-  0,  5,  10, 15, 20, 25, 30, 35, 40, 45, 50,
-  55, 60, 65, 70, 75, 80, 85, 90, 95, 100
-];
-
-/**
- * @param {!Array<number>} ticks
- * @return {!Array<!cr_slider.SliderTick>}
- */
-function ticksWithLabels(ticks) {
-  return ticks.map(x => ({label: `${x}`, value: x}));
-}
-
 Polymer({
   is: 'settings-captions',
 
@@ -36,34 +21,26 @@
     },
 
     /**
-     * List of fonts populated by the fonts browser proxy.
-     * @private {!DropdownMenuOptionList} */
-    textFontOptions_: Object,
-
-    /**
-     * Reasonable, text opacity range.
-     * @private {!Array<!cr_slider.SliderTick>}
-     */
-    textOpacityRange_: {
-      readOnly: true,
-      type: Array,
-      value: ticksWithLabels(TEXT_OPACITY_RANGE),
-    },
-
-    /**
-     * List of options for the text size drop-down menu.
+     * List of options for the background opacity drop-down menu.
      * @type {!DropdownMenuOptionList}
      */
-    textSizeOptions_: {
+    backgroundOpacityOptions_: {
       readOnly: true,
       type: Array,
       value: function() {
         return [
-          {value: '25%', name: loadTimeData.getString('verySmall')},
-          {value: '50%', name: loadTimeData.getString('small')},
-          {value: '', name: loadTimeData.getString('medium')}, // Default = 100%
-          {value: '150%', name: loadTimeData.getString('large')},
-          {value: '200%', name: loadTimeData.getString('veryLarge')},
+          {
+            value: 100, // Default
+            name: loadTimeData.getString('captionsOpacityOpaque')
+          },
+          {
+            value: 50,
+            name: loadTimeData.getString('captionsOpacitySemiTransparent')
+          },
+          {
+            value: 0,
+            name: loadTimeData.getString('captionsOpacityTransparent')
+          },
         ];
       },
     },
@@ -77,18 +54,30 @@
       type: Array,
       value: function() {
         return [
-          {value: '', name: loadTimeData.getString('captionsDefaultSetting')},
-          {value: '0,0,0', name: loadTimeData.getString('captionsColorBlack')},
+          {
+            value: '',
+            name: loadTimeData.getString('captionsDefaultSetting')
+          },
+          {
+            value: '0,0,0',
+            name: loadTimeData.getString('captionsColorBlack')
+          },
           {
             value: '255,255,255',
             name: loadTimeData.getString('captionsColorWhite')
           },
-          {value: '255,0,0', name: loadTimeData.getString('captionsColorRed')},
+          {
+            value: '255,0,0',
+            name: loadTimeData.getString('captionsColorRed')
+          },
           {
             value: '0,255,0',
             name: loadTimeData.getString('captionsColorGreen')
           },
-          {value: '0,0,255', name: loadTimeData.getString('captionsColorBlue')},
+          {
+            value: '0,0,255',
+            name: loadTimeData.getString('captionsColorBlue')
+          },
           {
             value: '255,255,0',
             name: loadTimeData.getString('captionsColorYellow')
@@ -106,6 +95,36 @@
     },
 
     /**
+     * List of fonts populated by the fonts browser proxy.
+     * @private {!DropdownMenuOptionList} */
+    textFontOptions_: Object,
+
+    /**
+     * List of options for the text opacity drop-down menu.
+     * @type {!DropdownMenuOptionList}
+     */
+    textOpacityOptions_: {
+      readOnly: true,
+      type: Array,
+      value: function() {
+        return [
+          {
+            value: 100, // Default
+            name: loadTimeData.getString('captionsOpacityOpaque')
+          },
+          {
+            value: 50,
+            name: loadTimeData.getString('captionsOpacitySemiTransparent')
+          },
+          {
+            value: 10,
+            name: loadTimeData.getString('captionsOpacityTransparent')
+          },
+        ];
+      },
+    },
+
+    /**
      * List of options for the text shadow drop-down menu.
      * @type {!DropdownMenuOptionList}
      */
@@ -135,6 +154,24 @@
         ];
       },
     },
+
+    /**
+     * List of options for the text size drop-down menu.
+     * @type {!DropdownMenuOptionList}
+     */
+    textSizeOptions_: {
+      readOnly: true,
+      type: Array,
+      value: function() {
+        return [
+          {value: '25%', name: loadTimeData.getString('verySmall')},
+          {value: '50%', name: loadTimeData.getString('small')},
+          {value: '', name: loadTimeData.getString('medium')}, // Default = 100%
+          {value: '150%', name: loadTimeData.getString('large')},
+          {value: '200%', name: loadTimeData.getString('veryLarge')},
+        ];
+      },
+    },
   },
 
   /** @private {?settings.FontsBrowserProxy} */
diff --git a/chrome/browser/resources/settings/about_page/BUILD.gn b/chrome/browser/resources/settings/about_page/BUILD.gn
index 178c30b..794b8aa 100644
--- a/chrome/browser/resources/settings/about_page/BUILD.gn
+++ b/chrome/browser/resources/settings/about_page/BUILD.gn
@@ -23,7 +23,6 @@
     ":about_page_browser_proxy",
     "..:lifetime_browser_proxy",
     "..:route",
-    "../prefs:prefs_behavior",
     "../settings_page:main_page_behavior",
     "../settings_page:settings_animated_pages",
     "//ui/webui/resources/js:assert",
diff --git a/chrome/browser/resources/settings/about_page/about_page.html b/chrome/browser/resources/settings/about_page/about_page.html
index 4571c6ee..aad7975d 100644
--- a/chrome/browser/resources/settings/about_page/about_page.html
+++ b/chrome/browser/resources/settings/about_page/about_page.html
@@ -4,7 +4,6 @@
 <link rel="import" href="../i18n_setup.html">
 <link rel="import" href="../icons.html">
 <link rel="import" href="../lifetime_browser_proxy.html">
-<link rel="import" href="../prefs/prefs_behavior.html">
 <link rel="import" href="../prefs/prefs.html">
 <link rel="import" href="../route.html">
 <link rel="import" href="../settings_page/main_page_behavior.html">
@@ -36,15 +35,6 @@
         --about-page-image-space: 10px;
       }
 
-      #aboutOSBanner {
-        @apply --cr-card-elevation;
-        align-items: center;
-        background-color: white;
-        border-radius: 2px;
-        display: flex;
-        margin-top: 21px;
-      }
-
       .info-section {
         margin-bottom: 12px;
       }
@@ -97,19 +87,6 @@
       }
 </if>
     </style>
-<if expr="chromeos">
-    <template is="dom-if" if="[[showAboutOSBanner_]]">
-      <div id="aboutOSBanner" class="settings-box">
-        <div class="start">
-          $i18nRaw{aboutOSBanner}
-        </div>
-        <cr-icon-button class="icon-clear"
-            aria-label="$i18n{clear}"
-            on-click="onAboutOSBannerClosed_">
-        </cr-icon-button>
-      </div>
-    </template>
-</if>
     <settings-section page-title="$i18n{aboutPageTitle}" section="about">
       <settings-animated-pages id="pages" section="about"
           focus-config="[[focusConfig_]]">
diff --git a/chrome/browser/resources/settings/about_page/about_page.js b/chrome/browser/resources/settings/about_page/about_page.js
index d606310..a5c5388f 100644
--- a/chrome/browser/resources/settings/about_page/about_page.js
+++ b/chrome/browser/resources/settings/about_page/about_page.js
@@ -15,7 +15,6 @@
     settings.MainPageBehavior,
     settings.RouteObserverBehavior,
     I18nBehavior,
-    PrefsBehavior,
   ],
 
   properties: {
@@ -44,13 +43,6 @@
 
     // <if expr="chromeos">
     /** @private */
-    showAboutOSBanner_: {
-      type: Boolean,
-      computed: 'computeShowAboutOSBanner_(' +
-          'prefs.settings.cros.show_about_os_banner.value)',
-    },
-
-    /** @private */
     hasCheckedForUpdates_: {
       type: Boolean,
       value: false,
@@ -569,21 +561,6 @@
    * @return {boolean}
    * @private
    */
-  computeShowAboutOSBanner_: function() {
-    // Show when SplitSettings is off and the user hasn't closed it.
-    return !this.showOsSettings_ && /** @type {boolean} */
-        (this.getPref('settings.cros.show_about_os_banner').value);
-  },
-
-  /** @private */
-  onAboutOSBannerClosed_: function() {
-    this.setPrefValue('settings.cros.show_about_os_banner', false);
-  },
-
-  /**
-   * @return {boolean}
-   * @private
-   */
   isRollback_: function() {
     assert(this.currentChannel_.length > 0);
     assert(this.targetChannel_.length > 0);
diff --git a/chrome/browser/resources/settings/google_assistant_page/google_assistant_page.js b/chrome/browser/resources/settings/google_assistant_page/google_assistant_page.js
index 5a91841..5237e01 100644
--- a/chrome/browser/resources/settings/google_assistant_page/google_assistant_page.js
+++ b/chrome/browser/resources/settings/google_assistant_page/google_assistant_page.js
@@ -196,6 +196,7 @@
     this.refreshDspHotwordState_();
 
     this.shouldShowVoiceMatchSettings_ =
+        !loadTimeData.getBoolean('voiceMatchDisabled') &&
         this.getPref('settings.voice_interaction.hotword.enabled.value') &&
         (this.getPref(
           'settings.voice_interaction.activity_control.consent_status.value') ==
diff --git a/chrome/browser/resources/settings/languages_page/edit_dictionary_page.html b/chrome/browser/resources/settings/languages_page/edit_dictionary_page.html
index c3732a62..a6905ca 100644
--- a/chrome/browser/resources/settings/languages_page/edit_dictionary_page.html
+++ b/chrome/browser/resources/settings/languages_page/edit_dictionary_page.html
@@ -56,9 +56,12 @@
             scroll-target="[[subpageScrollTarget]]">
           <template>
             <div class="list-item">
-              <div class="word text-elide">[[item]]</div>
+              <div id$="word[[index]]" class="word text-elide">[[item]]</div>
               <cr-icon-button class="icon-clear" on-click="onRemoveWordTap_"
-                  tabindex$="[[tabIndex]]"></cr-icon-button>
+                  tabindex$="[[tabIndex]]"
+                  title="$i18n{deleteDictionaryWordButton}"
+                  aria-describedby$="word[[index]]">
+              </cr-icon-button>
             </div>
           </template>
         </iron-list>
diff --git a/chrome/browser/resources/tab_strip/tab_list.js b/chrome/browser/resources/tab_strip/tab_list.js
index 0443a44..a0ccee46 100644
--- a/chrome/browser/resources/tab_strip/tab_list.js
+++ b/chrome/browser/resources/tab_strip/tab_list.js
@@ -46,17 +46,6 @@
     this.animationPromises = Promise.resolve();
 
     /**
-     * Attach and detach callbacks require async requests and therefore may
-     * cause race conditions in which the async requests complete after another
-     * event has been dispatched. Therefore, this object is necessary to keep
-     * track of the recent attached or detached state of each tab to ensure
-     * elements are not created when they should not be. A truthy value
-     * signifies the tab is attached to the current window.
-     * @private {!Object<number, boolean>}
-     */
-    this.attachmentStates_ = {};
-
-    /**
      * The TabElement that is currently being dragged.
      * @private {!TabElement|undefined}
      */
@@ -76,17 +65,11 @@
     /** @private {!TabsApiProxy} */
     this.tabsApi_ = TabsApiProxy.getInstance();
 
-    /** @private {!Object} */
-    this.tabsApiHandler_ = this.tabsApi_.callbackRouter;
-
     /** @private {!Element} */
     this.tabsContainerElement_ =
         /** @type {!Element} */ (
             this.shadowRoot.querySelector('#tabsContainer'));
 
-    /** @private {number} */
-    this.windowId_;
-
     addWebUIListener('theme-changed', () => this.fetchAndUpdateColors_());
     this.tabStripViewProxy_.observeThemeChanges();
 
@@ -113,39 +96,17 @@
 
   connectedCallback() {
     this.fetchAndUpdateColors_();
-    this.tabsApi_.getCurrentWindow().then((currentWindow) => {
-      this.windowId_ = currentWindow.id;
+    this.tabsApi_.getTabs().then(tabs => {
+      tabs.forEach(tab => this.onTabCreated_(tab));
+      this.moveOrScrollToActiveTab_();
 
-      // TODO(johntlee): currentWindow.tabs is guaranteed to be defined because
-      // `populate: true` is passed in as part of the arguments to the API.
-      // Once the closure compiler is able to type `assert` to return a truthy
-      // type even when being used with modules, the conditionals should be
-      // replaced with `assert` (b/138729777).
-      if (currentWindow.tabs) {
-        for (const tab of currentWindow.tabs) {
-          if (tab) {
-            this.onTabCreated_(tab);
-          }
-        }
-
-        this.moveOrScrollToActiveTab_();
-      }
-
-      this.tabsApiHandler_.onAttached.addListener(
-          (tabId, attachedInfo) => this.onTabAttached_(tabId, attachedInfo));
-      this.tabsApiHandler_.onActivated.addListener(
-          (activeInfo) => this.onTabActivated_(activeInfo));
-      this.tabsApiHandler_.onCreated.addListener(
-          (tab) => this.onTabCreated_(tab));
-      this.tabsApiHandler_.onDetached.addListener(
-          (tabId, detachInfo) => this.onTabDetached_(tabId, detachInfo));
-      this.tabsApiHandler_.onMoved.addListener(
-          (tabId, moveInfo) => this.onTabMoved_(tabId, moveInfo));
-      this.tabsApiHandler_.onRemoved.addListener(
-          (tabId, removeInfo) => this.onTabRemoved_(tabId, removeInfo));
-      this.tabsApiHandler_.onUpdated.addListener(
-          (tabId, changeInfo, tab) =>
-              this.onTabUpdated_(tabId, changeInfo, tab));
+      addWebUIListener('tab-created', tab => this.onTabCreated_(tab));
+      addWebUIListener(
+          'tab-moved', (tabId, newIndex) => this.onTabMoved_(tabId, newIndex));
+      addWebUIListener('tab-removed', tabId => this.onTabRemoved_(tabId));
+      addWebUIListener('tab-updated', tab => this.onTabUpdated_(tab));
+      addWebUIListener(
+          'tab-active-changed', tabId => this.onTabActivated_(tabId));
     });
   }
 
@@ -281,21 +242,17 @@
   }
 
   /**
-   * @param {!TabActivatedInfo} activeInfo
+   * @param {number} tabId
    * @private
    */
-  onTabActivated_(activeInfo) {
-    if (activeInfo.windowId !== this.windowId_) {
-      return;
-    }
-
+  onTabActivated_(tabId) {
     const previouslyActiveTab = this.getActiveTab_();
     if (previouslyActiveTab) {
       previouslyActiveTab.tab = /** @type {!Tab} */ (
           Object.assign({}, previouslyActiveTab.tab, {active: false}));
     }
 
-    const newlyActiveTab = this.findTabElement_(activeInfo.tabId);
+    const newlyActiveTab = this.findTabElement_(tabId);
     if (newlyActiveTab) {
       newlyActiveTab.tab = /** @type {!Tab} */ (
           Object.assign({}, newlyActiveTab.tab, {active: true}));
@@ -304,32 +261,10 @@
   }
 
   /**
-   * @param {number} tabId
-   * @param {!TabAttachedInfo} attachInfo
-   * @private
-   */
-  async onTabAttached_(tabId, attachInfo) {
-    if (attachInfo.newWindowId !== this.windowId_) {
-      return;
-    }
-
-    this.attachmentStates_[tabId] = true;
-    const tab = await this.tabsApi_.getTab(tabId);
-    if (this.attachmentStates_[tabId] && !this.findTabElement_(tabId)) {
-      const tabElement = this.createTabElement_(tab);
-      this.insertTabOrMoveTo_(tabElement, attachInfo.newPosition);
-    }
-  }
-
-  /**
    * @param {!Tab} tab
    * @private
    */
   onTabCreated_(tab) {
-    if (tab.windowId !== this.windowId_) {
-      return;
-    }
-
     const tabElement = this.createTabElement_(tab);
 
     if (tab.active && !tab.pinned &&
@@ -351,34 +286,13 @@
 
   /**
    * @param {number} tabId
-   * @param {!TabDetachedInfo} detachInfo
+   * @param {number} newIndex
    * @private
    */
-  onTabDetached_(tabId, detachInfo) {
-    if (detachInfo.oldWindowId !== this.windowId_) {
-      return;
-    }
-
-    this.attachmentStates_[tabId] = false;
-    const tabElement = this.findTabElement_(tabId);
-    if (tabElement) {
-      tabElement.remove();
-    }
-  }
-
-  /**
-   * @param {number} tabId
-   * @param {!TabMovedInfo} moveInfo
-   * @private
-   */
-  onTabMoved_(tabId, moveInfo) {
-    if (moveInfo.windowId !== this.windowId_) {
-      return;
-    }
-
+  onTabMoved_(tabId, newIndex) {
     const movedTab = this.findTabElement_(tabId);
     if (movedTab) {
-      this.insertTabOrMoveTo_(movedTab, moveInfo.toIndex);
+      this.insertTabOrMoveTo_(movedTab, newIndex);
       if (movedTab.tab.active) {
         this.scrollToTab_(movedTab);
       }
@@ -387,54 +301,34 @@
 
   /**
    * @param {number} tabId
-   * @param {!WindowRemoveInfo} removeInfo
    * @private
    */
-  onTabRemoved_(tabId, removeInfo) {
-    if (removeInfo.windowId !== this.windowId_) {
-      return;
-    }
-
+  onTabRemoved_(tabId) {
     const tabElement = this.findTabElement_(tabId);
     if (tabElement) {
-      this.addAnimationPromise_(new Promise(async resolve => {
-        await tabElement.slideOut();
-        resolve();
-      }));
+      this.addAnimationPromise_(tabElement.slideOut());
     }
   }
 
   /**
-   * @param {number} tabId
-   * @param {!Tab} changeInfo
    * @param {!Tab} tab
    * @private
    */
-  onTabUpdated_(tabId, changeInfo, tab) {
-    if (tab.windowId !== this.windowId_) {
+  onTabUpdated_(tab) {
+    const tabElement = this.findTabElement_(tab.id);
+    if (!tabElement) {
       return;
     }
 
-    const tabElement = this.findTabElement_(tabId);
-    if (tabElement) {
-      // While a tab may go in and out of a loading state, the Extensions API
-      // only dispatches |onUpdated| events up until the first time a tab
-      // reaches a non-loading state. Therefore, the UI should ignore any
-      // updates to a |status| of a tab unless the API specifically has
-      // dispatched an event indicating the status has changed.
-      if (!changeInfo.status) {
-        tab.status = tabElement.tab.status;
-      }
+    const previousTab = tabElement.tab;
+    tabElement.tab = tab;
 
-      tabElement.tab = tab;
-
-      if (changeInfo.pinned !== undefined) {
-        // If the tab is being pinned or unpinned, we need to move it to its new
-        // location
-        this.insertTabOrMoveTo_(tabElement, tab.index);
-        if (tab.active) {
-          this.scrollToTab_(tabElement);
-        }
+    if (previousTab.pinned !== tab.pinned) {
+      // If the tab is being pinned or unpinned, we need to move it to its new
+      // location
+      this.insertTabOrMoveTo_(tabElement, tab.index);
+      if (tab.active) {
+        this.scrollToTab_(tabElement);
       }
     }
   }
diff --git a/chrome/browser/resources/tab_strip/tabs_api_proxy.js b/chrome/browser/resources/tab_strip/tabs_api_proxy.js
index 31ce009..e685568 100644
--- a/chrome/browser/resources/tab_strip/tabs_api_proxy.js
+++ b/chrome/browser/resources/tab_strip/tabs_api_proxy.js
@@ -2,22 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {addSingletonGetter} from 'chrome://resources/js/cr.m.js';
+import {addSingletonGetter, sendWithPromise} from 'chrome://resources/js/cr.m.js';
 
 export class TabsApiProxy {
-  constructor() {
-    /** @type {!Object<string, !ChromeEvent>} */
-    this.callbackRouter = {
-      onActivated: chrome.tabs.onActivated,
-      onAttached: chrome.tabs.onAttached,
-      onCreated: chrome.tabs.onCreated,
-      onDetached: chrome.tabs.onDetached,
-      onMoved: chrome.tabs.onMoved,
-      onRemoved: chrome.tabs.onRemoved,
-      onUpdated: chrome.tabs.onUpdated,
-    };
-  }
-
   /**
    * @param {number} tabId
    * @return {!Promise<!Tab>}
@@ -29,26 +16,10 @@
   }
 
   /**
-   * @return {!Promise<!ChromeWindow>}
+   * @return {!Promise<!Array<!Tab>>}
    */
-  getCurrentWindow() {
-    const options = {
-      populate: true,           // populate window data with tabs data
-      windowTypes: ['normal'],  // prevent devtools from being returned
-    };
-    return new Promise(resolve => {
-      chrome.windows.getCurrent(options, currentWindow => {
-        resolve(currentWindow);
-      });
-    });
-  }
-
-  /**
-   * @param {number} tabId
-   * @return {!Promise<!Tab>}
-   */
-  getTab(tabId) {
-    return new Promise(resolve => chrome.tabs.get(tabId, resolve));
+  getTabs() {
+    return sendWithPromise('getTabs');
   }
 
   /**
diff --git a/chrome/browser/search/chrome_colors/chrome_colors_service.cc b/chrome/browser/search/chrome_colors/chrome_colors_service.cc
index 93ef357..3660f5b 100644
--- a/chrome/browser/search/chrome_colors/chrome_colors_service.cc
+++ b/chrome/browser/search/chrome_colors/chrome_colors_service.cc
@@ -114,6 +114,8 @@
   }
 }
 
-void ChromeColorsService::Shutdown() {}
+void ChromeColorsService::Shutdown() {
+  RevertThemeChangesWithReason(RevertReason::SHUTDOWN);
+}
 
 }  // namespace chrome_colors
diff --git a/chrome/browser/search/chrome_colors/chrome_colors_service.h b/chrome/browser/search/chrome_colors/chrome_colors_service.h
index ddba6a4..0df330fb 100644
--- a/chrome/browser/search/chrome_colors/chrome_colors_service.h
+++ b/chrome/browser/search/chrome_colors/chrome_colors_service.h
@@ -25,7 +25,8 @@
   SEARCH_PROVIDER_CHANGE = 1,
   TAB_CLOSED = 2,
   NAVIGATION = 3,
-  kMaxValue = NAVIGATION,
+  SHUTDOWN = 4,
+  kMaxValue = SHUTDOWN,
 };
 
 // Supports theme changes originating from the NTP customization menu. Users can
diff --git a/chrome/browser/ssl/ssl_error_assistant.cc b/chrome/browser/ssl/ssl_error_assistant.cc
index 3f8904c..615af721 100644
--- a/chrome/browser/ssl/ssl_error_assistant.cc
+++ b/chrome/browser/ssl/ssl_error_assistant.cc
@@ -318,10 +318,8 @@
   auto proto = std::make_unique<chrome_browser_ssl::SSLErrorAssistantConfig>();
   DCHECK(proto);
   ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
-  base::StringPiece data =
-      bundle.GetRawDataResource(IDR_SSL_ERROR_ASSISTANT_PB);
-  google::protobuf::io::ArrayInputStream stream(data.data(), data.size());
-  return proto->ParseFromZeroCopyStream(&stream) ? std::move(proto) : nullptr;
+  std::string data = bundle.DecompressDataResource(IDR_SSL_ERROR_ASSISTANT_PB);
+  return proto->ParseFromString(data) ? std::move(proto) : nullptr;
 }
 
 void SSLErrorAssistant::SetErrorAssistantProto(
diff --git a/chrome/browser/sync/test/integration/sync_test.cc b/chrome/browser/sync/test/integration/sync_test.cc
index 52325c6..0eaed71 100644
--- a/chrome/browser/sync/test/integration/sync_test.cc
+++ b/chrome/browser/sync/test/integration/sync_test.cc
@@ -930,6 +930,12 @@
 
 void SyncTest::OnWillCreateBrowserContextServices(
     content::BrowserContext* context) {
+  if (UsingExternalServers()) {
+    // DO NOTHING. External live sync servers use GCM to notify profiles of
+    // any invalidations in sync'ed data. No need to provide a testing
+    // factory the ProfileInvalidationProvider.
+    return;
+  }
   invalidation::ProfileInvalidationProviderFactory::GetInstance()
       ->SetTestingFactory(
           context,
diff --git a/chrome/browser/sync/wifi_configuration_sync_service_factory.cc b/chrome/browser/sync/wifi_configuration_sync_service_factory.cc
index 394b3eb..26767a8 100644
--- a/chrome/browser/sync/wifi_configuration_sync_service_factory.cc
+++ b/chrome/browser/sync/wifi_configuration_sync_service_factory.cc
@@ -15,9 +15,9 @@
 #include "components/sync/model/model_type_store_service.h"
 
 // static
-sync_wifi::WifiConfigurationSyncService*
+chromeos::sync_wifi::WifiConfigurationSyncService*
 WifiConfigurationSyncServiceFactory::GetForProfile(Profile* profile) {
-  return static_cast<sync_wifi::WifiConfigurationSyncService*>(
+  return static_cast<chromeos::sync_wifi::WifiConfigurationSyncService*>(
       GetInstance()->GetServiceForBrowserContext(profile, true));
 }
 
@@ -39,7 +39,7 @@
 
 KeyedService* WifiConfigurationSyncServiceFactory::BuildServiceInstanceFor(
     content::BrowserContext* context) const {
-  return new sync_wifi::WifiConfigurationSyncService(
+  return new chromeos::sync_wifi::WifiConfigurationSyncService(
       chrome::GetChannel(), ModelTypeStoreServiceFactory::GetForProfile(
                                 Profile::FromBrowserContext(context))
                                 ->GetStoreFactory());
@@ -47,6 +47,6 @@
 
 void WifiConfigurationSyncServiceFactory::RegisterProfilePrefs(
     user_prefs::PrefRegistrySyncable* registry) {
-  sync_wifi::PendingNetworkConfigurationTrackerImpl::RegisterProfilePrefs(
-      registry);
+  chromeos::sync_wifi::PendingNetworkConfigurationTrackerImpl::
+      RegisterProfilePrefs(registry);
 }
diff --git a/chrome/browser/sync/wifi_configuration_sync_service_factory.h b/chrome/browser/sync/wifi_configuration_sync_service_factory.h
index 84a7260..18056fb 100644
--- a/chrome/browser/sync/wifi_configuration_sync_service_factory.h
+++ b/chrome/browser/sync/wifi_configuration_sync_service_factory.h
@@ -17,14 +17,16 @@
 struct DefaultSingletonTraits;
 }  // namespace base
 
+namespace chromeos {
 namespace sync_wifi {
 class WifiConfigurationSyncService;
 }  // namespace sync_wifi
+}  // namespace chromeos
 
 class WifiConfigurationSyncServiceFactory
     : public BrowserContextKeyedServiceFactory {
  public:
-  static sync_wifi::WifiConfigurationSyncService* GetForProfile(
+  static chromeos::sync_wifi::WifiConfigurationSyncService* GetForProfile(
       Profile* profile);
   static WifiConfigurationSyncServiceFactory* GetInstance();
 
diff --git a/chrome/browser/themes/theme_service_factory.cc b/chrome/browser/themes/theme_service_factory.cc
index c3bc959..3f06577 100644
--- a/chrome/browser/themes/theme_service_factory.cc
+++ b/chrome/browser/themes/theme_service_factory.cc
@@ -6,6 +6,7 @@
 
 #include "base/logging.h"
 #include "build/build_config.h"
+#include "chrome/browser/extensions/extension_system_factory.h"
 #include "chrome/browser/profiles/incognito_helpers.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/themes/theme_service.h"
@@ -13,6 +14,7 @@
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
 #include "components/pref_registry/pref_registry_syncable.h"
 #include "components/prefs/pref_service.h"
+#include "extensions/browser/extension_prefs_factory.h"
 #include "extensions/browser/extension_registry.h"
 #include "extensions/browser/extension_registry_factory.h"
 
@@ -51,6 +53,8 @@
           "ThemeService",
           BrowserContextDependencyManager::GetInstance()) {
   DependsOn(extensions::ExtensionRegistryFactory::GetInstance());
+  DependsOn(extensions::ExtensionPrefsFactory::GetInstance());
+  DependsOn(extensions::ExtensionSystemFactory::GetInstance());
 }
 
 ThemeServiceFactory::~ThemeServiceFactory() {}
diff --git a/chrome/browser/ui/chrome_pages.cc b/chrome/browser/ui/chrome_pages.cc
index f2c5ac61..78646a96 100644
--- a/chrome/browser/ui/chrome_pages.cc
+++ b/chrome/browser/ui/chrome_pages.cc
@@ -95,11 +95,6 @@
 }
 
 #if defined(OS_CHROMEOS) && BUILDFLAG(GOOGLE_CHROME_BRANDING)
-void LaunchReleaseNotesInTab(Profile* profile) {
-  GURL url(BuildQueryString(profile));
-  auto displayer = std::make_unique<ScopedTabbedBrowserDisplayer>(profile);
-  ShowSingletonTab(displayer->browser(), url);
-}
 
 const std::string BuildQueryString(Profile* profile) {
   const std::string board_name = base::SysInfo::GetLsbReleaseBoard();
@@ -130,9 +125,13 @@
        ",", region, ",", language, ",", channel_name, ",", user_type});
   return query_string;
 }
-#endif
 
-#if defined(OS_CHROMEOS) && BUILDFLAG(GOOGLE_CHROME_BRANDING)
+void LaunchReleaseNotesInTab(Profile* profile) {
+  GURL url(BuildQueryString(profile));
+  auto displayer = std::make_unique<ScopedTabbedBrowserDisplayer>(profile);
+  ShowSingletonTab(displayer->browser(), url);
+}
+
 void LaunchReleaseNotesImpl(Profile* profile) {
   base::RecordAction(UserMetricsAction("ReleaseNotes.ShowReleaseNotes"));
   const extensions::Extension* extension =
@@ -150,6 +149,7 @@
   DVLOG(1) << "ReleaseNotes App Not Found";
   LaunchReleaseNotesInTab(profile);
 }
+
 #endif
 
 // Shows either the help app or the appropriate help page for |source|. If
diff --git a/chrome/browser/ui/libgtkui/BUILD.gn b/chrome/browser/ui/libgtkui/BUILD.gn
index f3f288f..26e8390 100644
--- a/chrome/browser/ui/libgtkui/BUILD.gn
+++ b/chrome/browser/ui/libgtkui/BUILD.gn
@@ -115,6 +115,8 @@
       "gtk_event_loop_x11.h",
     ]
 
+    defines += [ "USE_GTK_EVENT_LOOP_X11" ]
+
     deps += [
       "//ui/events/platform/x11",
       "//ui/gfx/x",
diff --git a/chrome/browser/ui/libgtkui/gtk_ui.cc b/chrome/browser/ui/libgtkui/gtk_ui.cc
index 4a3c627..860292e2 100644
--- a/chrome/browser/ui/libgtkui/gtk_ui.cc
+++ b/chrome/browser/ui/libgtkui/gtk_ui.cc
@@ -24,7 +24,6 @@
 #include "base/strings/string_split.h"
 #include "base/strings/stringprintf.h"
 #include "chrome/browser/themes/theme_properties.h"
-#include "chrome/browser/ui/libgtkui/gtk_event_loop_x11.h"
 #include "chrome/browser/ui/libgtkui/gtk_key_bindings_handler.h"
 #include "chrome/browser/ui/libgtkui/gtk_util.h"
 #include "chrome/browser/ui/libgtkui/input_method_context_impl_gtk.h"
@@ -72,6 +71,10 @@
 #include "chrome/browser/ui/libgtkui/settings_provider_gsettings.h"
 #endif
 
+#if defined(USE_GTK_EVENT_LOOP_X11)
+#include "chrome/browser/ui/libgtkui/gtk_event_loop_x11.h"
+#endif
+
 #if defined(USE_X11)
 #include "ui/gfx/x/x11.h"        // nogncheck
 #include "ui/gfx/x/x11_types.h"  // nogncheck
@@ -323,6 +326,19 @@
   }
 }
 
+#if defined(USE_GTK_EVENT_LOOP_X11)
+bool ShouldCreateGtkEventLoopX11() {
+#if defined(USE_OZONE)
+  // TODO(crbug.com/1002674): This is a temporary layering violation, supported
+  // during X11 migration to Ozone.
+  std::string ozone_platform{ui::OzonePlatform::GetPlatformName()};
+  return ozone_platform == "x11";
+#else
+  return true;
+#endif
+}
+#endif  // defined(USE_GTK_EVENT_LOOP_X11)
+
 }  // namespace
 
 GtkUi::GtkUi() {
@@ -403,8 +419,11 @@
 
   indicators_count = 0;
 
-  // Instantiate the singleton instance of GtkEventLoopX11.
-  GtkEventLoopX11::GetInstance();
+#if defined(USE_GTK_EVENT_LOOP_X11)
+  if (ShouldCreateGtkEventLoopX11())
+    // Instantiate the singleton instance of GtkEventLoopX11.
+    GtkEventLoopX11::GetInstance();
+#endif
 }
 
 bool GtkUi::GetTint(int id, color_utils::HSL* tint) const {
diff --git a/chrome/browser/ui/page_info/page_info_ui.cc b/chrome/browser/ui/page_info/page_info_ui.cc
index cad3cc0..8676068 100644
--- a/chrome/browser/ui/page_info/page_info_ui.cc
+++ b/chrome/browser/ui/page_info/page_info_ui.cc
@@ -35,7 +35,6 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/common/pref_names.h"
 #include "components/prefs/pref_service.h"
-#include "components/vector_icons/vector_icons.h"
 #include "media/base/media_switches.h"
 #include "ui/gfx/color_palette.h"
 #include "ui/gfx/color_utils.h"
@@ -288,6 +287,9 @@
     case security_state::SafetyTipStatus::kLookalike:
       // Lookalikes have their own strings, but they're suggestions, not
       // warnings, so we leave Page Info alone.
+    case security_state::SafetyTipStatus::kBadKeyword:
+      // Keyword safety tips are only used to collect metrics for now and are
+      // not visible to the user, so don't affect Page Info.
     case security_state::SafetyTipStatus::kNone:
     case security_state::SafetyTipStatus::kUnknown:
       break;
diff --git a/chrome/browser/ui/views/chrome_cleaner_reboot_dialog_win.cc b/chrome/browser/ui/views/chrome_cleaner_reboot_dialog_win.cc
index cfcf70a..164ef60 100644
--- a/chrome/browser/ui/views/chrome_cleaner_reboot_dialog_win.cc
+++ b/chrome/browser/ui/views/chrome_cleaner_reboot_dialog_win.cc
@@ -49,6 +49,8 @@
     : dialog_controller_(dialog_controller) {
   DCHECK(dialog_controller_);
 
+  DialogDelegate::set_draggable(true);
+
   set_margins(ChromeLayoutProvider::Get()->GetDialogInsetsForContentType(
       views::TEXT, views::TEXT));
 }
@@ -117,10 +119,6 @@
   return true;
 }
 
-bool ChromeCleanerRebootDialog::IsDialogDraggable() const {
-  return true;
-}
-
 void ChromeCleanerRebootDialog::HandleDialogInteraction(
     DialogInteractionResult result) {
   if (!dialog_controller_)
diff --git a/chrome/browser/ui/views/chrome_cleaner_reboot_dialog_win.h b/chrome/browser/ui/views/chrome_cleaner_reboot_dialog_win.h
index e982c308..3917b97 100644
--- a/chrome/browser/ui/views/chrome_cleaner_reboot_dialog_win.h
+++ b/chrome/browser/ui/views/chrome_cleaner_reboot_dialog_win.h
@@ -44,7 +44,6 @@
   bool Accept() override;
   bool Cancel() override;
   bool Close() override;
-  bool IsDialogDraggable() const override;
 
  private:
   enum class DialogInteractionResult {
diff --git a/chrome/browser/ui/views/extensions/extension_install_dialog_view.cc b/chrome/browser/ui/views/extensions/extension_install_dialog_view.cc
index 2016d13..108a124a 100644
--- a/chrome/browser/ui/views/extensions/extension_install_dialog_view.cc
+++ b/chrome/browser/ui/views/extensions/extension_install_dialog_view.cc
@@ -241,6 +241,7 @@
       handled_result_(false),
       install_button_enabled_(false) {
   DialogDelegate::set_default_button(ui::DIALOG_BUTTON_CANCEL);
+  DialogDelegate::set_draggable(true);
   set_close_on_deactivate(false);
   CreateContents();
 
@@ -396,13 +397,6 @@
   return true;
 }
 
-// parent_window() may be null if an upgrade permissions prompt is triggered
-// when launching via a desktop shortcut. In that case, there is no browser
-// window to move (which would move the dialog), so allow dragging in this case.
-bool ExtensionInstallDialogView::IsDialogDraggable() const {
-  return !parent_window();
-}
-
 int ExtensionInstallDialogView::GetDialogButtons() const {
   int buttons = prompt_->GetDialogButtons();
   // Simply having just an OK button is *not* supported. See comment on function
diff --git a/chrome/browser/ui/views/extensions/extension_install_dialog_view.h b/chrome/browser/ui/views/extensions/extension_install_dialog_view.h
index 72fa049..f020b12 100644
--- a/chrome/browser/ui/views/extensions/extension_install_dialog_view.h
+++ b/chrome/browser/ui/views/extensions/extension_install_dialog_view.h
@@ -60,7 +60,6 @@
   std::unique_ptr<views::View> CreateExtraView() override;
   bool Cancel() override;
   bool Accept() override;
-  bool IsDialogDraggable() const override;
   int GetDialogButtons() const override;
   base::string16 GetDialogButtonLabel(ui::DialogButton button) const override;
   bool IsDialogButtonEnabled(ui::DialogButton button) const override;
diff --git a/chrome/browser/ui/views/extensions/extensions_menu_view.cc b/chrome/browser/ui/views/extensions/extensions_menu_view.cc
index 23e288a..1c88c748 100644
--- a/chrome/browser/ui/views/extensions/extensions_menu_view.cc
+++ b/chrome/browser/ui/views/extensions/extensions_menu_view.cc
@@ -35,6 +35,15 @@
 
 constexpr gfx::Size ExtensionsMenuView::kExtensionsMenuIconSize;
 
+ExtensionsMenuView::ButtonListener::ButtonListener(Browser* browser)
+    : browser_(browser) {}
+
+void ExtensionsMenuView::ButtonListener::ButtonPressed(views::Button* sender,
+                                                       const ui::Event& event) {
+  DCHECK_EQ(sender->GetID(), EXTENSIONS_SETTINGS_ID);
+  chrome::ShowExtensions(browser_, std::string());
+}
+
 ExtensionsMenuView::ExtensionsMenuView(
     views::View* anchor_view,
     Browser* browser,
@@ -44,7 +53,8 @@
       browser_(browser),
       extensions_container_(extensions_container),
       model_(ToolbarActionsModel::Get(browser_->profile())),
-      model_observer_(this) {
+      model_observer_(this),
+      button_listener_(browser_) {
   model_observer_.Add(model_);
   set_margins(gfx::Insets(0));
 
@@ -61,12 +71,6 @@
   extensions_menu_items_.clear();
 }
 
-void ExtensionsMenuView::ButtonPressed(views::Button* sender,
-                                       const ui::Event& event) {
-  DCHECK_EQ(sender->GetID(), EXTENSIONS_SETTINGS_ID);
-  chrome::ShowExtensions(browser_, std::string());
-}
-
 base::string16 ExtensionsMenuView::GetWindowTitle() const {
   return l10n_util::GetStringUTF16(IDS_EXTENSIONS_MENU_TITLE);
 }
@@ -110,7 +114,7 @@
                             GetNativeTheme()->GetSystemColor(
                                 ui::NativeTheme::kColorId_DefaultIconColor)));
   auto footer = std::make_unique<HoverButton>(
-      this, std::move(icon_view),
+      &button_listener_, std::move(icon_view),
       l10n_util::GetStringUTF16(IDS_MANAGE_EXTENSION), base::string16());
   footer->SetID(EXTENSIONS_SETTINGS_ID);
   manage_extensions_button_for_testing_ = footer.get();
diff --git a/chrome/browser/ui/views/extensions/extensions_menu_view.h b/chrome/browser/ui/views/extensions/extensions_menu_view.h
index 85288c2..0090b8d 100644
--- a/chrome/browser/ui/views/extensions/extensions_menu_view.h
+++ b/chrome/browser/ui/views/extensions/extensions_menu_view.h
@@ -20,11 +20,9 @@
 class ExtensionsContainer;
 class ExtensionsMenuItemView;
 
-// This bubble view displays a list of user extensions.
-// TODO(pbos): Once there's more functionality in here (getting to
-// chrome://extensions, pinning, extension settings), update this comment.
-class ExtensionsMenuView : public views::ButtonListener,
-                           public views::BubbleDialogDelegateView,
+// This bubble view displays a list of user extensions and a button to get to
+// managing the user's extensions (chrome://extensions).
+class ExtensionsMenuView : public views::BubbleDialogDelegateView,
                            public ToolbarActionsModel::Observer {
  public:
   static constexpr gfx::Size kExtensionsMenuIconSize = gfx::Size(28, 28);
@@ -42,9 +40,6 @@
   static ExtensionsMenuView* GetExtensionsMenuViewForTesting();
   static std::unique_ptr<views::ImageView> CreateFixedSizeIconView();
 
-  // views::ButtonListener:
-  void ButtonPressed(views::Button* sender, const ui::Event& event) override;
-
   // views::BubbleDialogDelegateView:
   base::string16 GetWindowTitle() const override;
   bool ShouldShowCloseButton() const override;
@@ -78,6 +73,17 @@
   }
 
  private:
+  class ButtonListener : public views::ButtonListener {
+   public:
+    explicit ButtonListener(Browser* browser);
+
+    // views::ButtonListener:
+    void ButtonPressed(views::Button* sender, const ui::Event& event) override;
+
+   private:
+    Browser* const browser_;
+  };
+
   void Repopulate();
   std::unique_ptr<views::View> CreateExtensionButtonsContainer();
 
@@ -86,6 +92,7 @@
   ToolbarActionsModel* const model_;
   ScopedObserver<ToolbarActionsModel, ToolbarActionsModel::Observer>
       model_observer_;
+  ButtonListener button_listener_;
   std::vector<ExtensionsMenuItemView*> extensions_menu_items_;
 
   views::Button* manage_extensions_button_for_testing_ = nullptr;
diff --git a/chrome/browser/ui/views/policy/enterprise_startup_dialog_view.cc b/chrome/browser/ui/views/policy/enterprise_startup_dialog_view.cc
index 71f658b..e6a95be 100644
--- a/chrome/browser/ui/views/policy/enterprise_startup_dialog_view.cc
+++ b/chrome/browser/ui/views/policy/enterprise_startup_dialog_view.cc
@@ -71,6 +71,7 @@
 EnterpriseStartupDialogView::EnterpriseStartupDialogView(
     EnterpriseStartupDialog::DialogResultCallback callback)
     : callback_(std::move(callback)) {
+  DialogDelegate::set_draggable(true);
   SetBorder(views::CreateEmptyBorder(GetDialogInsets()));
   CreateDialogWidget(this, nullptr, nullptr)->Show();
 #if defined(OS_MACOSX)
@@ -162,10 +163,6 @@
   return Cancel();
 }
 
-bool EnterpriseStartupDialogView::IsDialogDraggable() const {
-  return true;
-}
-
 bool EnterpriseStartupDialogView::ShouldShowWindowTitle() const {
   return false;
 }
diff --git a/chrome/browser/ui/views/policy/enterprise_startup_dialog_view.h b/chrome/browser/ui/views/policy/enterprise_startup_dialog_view.h
index 391727d..e5b32003 100644
--- a/chrome/browser/ui/views/policy/enterprise_startup_dialog_view.h
+++ b/chrome/browser/ui/views/policy/enterprise_startup_dialog_view.h
@@ -45,7 +45,6 @@
   bool Accept() override;
   bool Cancel() override;
   bool Close() override;
-  bool IsDialogDraggable() const override;
   bool ShouldShowWindowTitle() const override;
   ui::ModalType GetModalType() const override;
   std::unique_ptr<views::View> CreateExtraView() override;
diff --git a/chrome/browser/ui/webui/chromeos/assistant_optin/assistant_optin_ui.cc b/chrome/browser/ui/webui/chromeos/assistant_optin/assistant_optin_ui.cc
index b4daf5db..c7100b01 100644
--- a/chrome/browser/ui/webui/chromeos/assistant_optin/assistant_optin_ui.cc
+++ b/chrome/browser/ui/webui/chromeos/assistant_optin/assistant_optin_ui.cc
@@ -21,6 +21,7 @@
 #include "chrome/grit/browser_resources.h"
 #include "chromeos/assistant/buildflags.h"
 #include "chromeos/services/assistant/public/cpp/assistant_prefs.h"
+#include "chromeos/services/assistant/public/features.h"
 #include "components/prefs/pref_service.h"
 #include "components/session_manager/core/session_manager.h"
 #include "content/public/browser/host_zoom_map.h"
@@ -81,7 +82,6 @@
   source->UseStringsJs();
   source->AddResourcePath("assistant_optin.js", IDR_ASSISTANT_OPTIN_JS);
   source->AddResourcePath("assistant_logo.png", IDR_ASSISTANT_LOGO_PNG);
-  source->AddBoolean("hotwordDspAvailable", chromeos::IsHotwordDspAvailable());
   source->SetDefaultResource(IDR_ASSISTANT_OPTIN_HTML);
   source->AddResourcePath("voice_match_animation.json",
                           IDR_ASSISTANT_VOICE_MATCH_ANIMATION);
diff --git a/chrome/browser/ui/webui/chromeos/login/assistant_optin_flow_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/assistant_optin_flow_screen_handler.cc
index 70a6b897..84fccba 100644
--- a/chrome/browser/ui/webui/chromeos/login/assistant_optin_flow_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/assistant_optin_flow_screen_handler.cc
@@ -144,6 +144,14 @@
               &AssistantOptInFlowScreenHandler::HandleFlowInitialized);
 }
 
+void AssistantOptInFlowScreenHandler::GetAdditionalParameters(
+    base::DictionaryValue* dict) {
+  dict->SetBoolean("hotwordDspAvailable", chromeos::IsHotwordDspAvailable());
+  dict->SetBoolean("voiceMatchDisabled",
+                   chromeos::assistant::features::IsVoiceMatchDisabled());
+  BaseScreenHandler::GetAdditionalParameters(dict);
+}
+
 void AssistantOptInFlowScreenHandler::Bind(AssistantOptInFlowScreen* screen) {
   BaseScreenHandler::SetBaseScreen(screen);
   screen_ = screen;
diff --git a/chrome/browser/ui/webui/chromeos/login/assistant_optin_flow_screen_handler.h b/chrome/browser/ui/webui/chromeos/login/assistant_optin_flow_screen_handler.h
index 3b7db00..a04c198 100644
--- a/chrome/browser/ui/webui/chromeos/login/assistant_optin_flow_screen_handler.h
+++ b/chrome/browser/ui/webui/chromeos/login/assistant_optin_flow_screen_handler.h
@@ -63,6 +63,7 @@
   void DeclareLocalizedValues(
       ::login::LocalizedValuesBuilder* builder) override;
   void RegisterMessages() override;
+  void GetAdditionalParameters(base::DictionaryValue* dict) override;
 
   // AssistantOptInFlowScreenView:
   void Bind(AssistantOptInFlowScreen* screen) override;
diff --git a/chrome/browser/ui/webui/print_preview/print_preview_handler_unittest.cc b/chrome/browser/ui/webui/print_preview/print_preview_handler_unittest.cc
index 4780210..6d72409 100644
--- a/chrome/browser/ui/webui/print_preview/print_preview_handler_unittest.cc
+++ b/chrome/browser/ui/webui/print_preview/print_preview_handler_unittest.cc
@@ -701,7 +701,7 @@
 
   // Simulate renderer responses: PageLayoutReady, PageCountReady,
   // PagePreviewReady, and OnPrintPreviewReady will be called in that order.
-  base::DictionaryValue layout;
+  base::Value layout(base::Value::Type::DICTIONARY);
   layout.SetDoubleKey(kSettingMarginTop, 34.0);
   layout.SetDoubleKey(kSettingMarginLeft, 34.0);
   layout.SetDoubleKey(kSettingMarginBottom, 34.0);
@@ -712,7 +712,8 @@
   layout.SetIntKey(kSettingPrintableAreaY, 17);
   layout.SetIntKey(kSettingPrintableAreaWidth, 578);
   layout.SetIntKey(kSettingPrintableAreaHeight, 734);
-  handler()->SendPageLayoutReady(layout, /*has_custom_page_size_style,=*/false,
+  handler()->SendPageLayoutReady(base::Value::AsDictionaryValue(layout),
+                                 /*has_custom_page_size_style,=*/false,
                                  preview_request_id);
 
   // Verify that page-layout-ready webUI event was fired.
diff --git a/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc b/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
index 52b2098..9952a599 100644
--- a/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
+++ b/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
@@ -208,8 +208,10 @@
     {"captionsTextColor", IDS_SETTINGS_CAPTIONS_TEXT_COLOR},
     {"captionsTextOpacity", IDS_SETTINGS_CAPTIONS_TEXT_OPACITY},
     {"captionsBackgroundOpacity", IDS_SETTINGS_CAPTIONS_BACKGROUND_OPACITY},
-    {"captionsOpacityMin", IDS_SETTINGS_CAPTIONS_OPACITY_MIN},
-    {"captionsOpacityMax", IDS_SETTINGS_CAPTIONS_OPACITY_MAX},
+    {"captionsOpacityOpaque", IDS_SETTINGS_CAPTIONS_OPACITY_OPAQUE},
+    {"captionsOpacitySemiTransparent",
+     IDS_SETTINGS_CAPTIONS_OPACITY_SEMI_TRANSPARENT},
+    {"captionsOpacityTransparent", IDS_SETTINGS_CAPTIONS_OPACITY_TRANSPARENT},
     {"captionsTextShadow", IDS_SETTINGS_CAPTIONS_TEXT_SHADOW},
     {"captionsTextShadowNone", IDS_SETTINGS_CAPTIONS_TEXT_SHADOW_NONE},
     {"captionsTextShadowRaised", IDS_SETTINGS_CAPTIONS_TEXT_SHADOW_RAISED},
@@ -439,7 +441,6 @@
     {"aboutCheckForUpdates", IDS_SETTINGS_ABOUT_PAGE_CHECK_FOR_UPDATES},
     {"aboutCurrentlyOnChannel", IDS_SETTINGS_ABOUT_PAGE_CURRENT_CHANNEL},
     {"aboutDetailedBuildInfo", IDS_SETTINGS_ABOUT_PAGE_DETAILED_BUILD_INFO},
-    {"aboutOSBanner", IDS_SETTINGS_ABOUT_OS_BANNER},
     {"aboutEndOfLifeTitle", IDS_SETTINGS_ABOUT_PAGE_END_OF_LIFE_TITLE},
     {"aboutRelaunchAndPowerwash",
      IDS_SETTINGS_ABOUT_PAGE_RELAUNCH_AND_POWERWASH},
@@ -1613,6 +1614,8 @@
      IDS_SETTINGS_LANGUAGES_ADD_DICTIONARY_WORD_DUPLICATE_ERROR},
     {"addDictionaryWordLengthError",
      IDS_SETTINGS_LANGUAGES_ADD_DICTIONARY_WORD_LENGTH_ERROR},
+    {"deleteDictionaryWordButton",
+     IDS_SETTINGS_LANGUAGES_DELETE_DICTIONARY_WORD_BUTTON},
     {"customDictionaryWords", IDS_SETTINGS_LANGUAGES_DICTIONARY_WORDS},
     {"noCustomDictionaryWordsFound",
      IDS_SETTINGS_LANGUAGES_DICTIONARY_WORDS_NONE},
@@ -2707,6 +2710,9 @@
 
   html_source->AddBoolean("hotwordDspAvailable",
                           chromeos::IsHotwordDspAvailable());
+  html_source->AddBoolean(
+      "voiceMatchDisabled",
+      chromeos::assistant::features::IsVoiceMatchDisabled());
 }
 #endif
 
diff --git a/chrome/browser/ui/webui/tab_strip/tab_strip_ui.cc b/chrome/browser/ui/webui/tab_strip/tab_strip_ui.cc
index 321ba9d..37b43a17 100644
--- a/chrome/browser/ui/webui/tab_strip/tab_strip_ui.cc
+++ b/chrome/browser/ui/webui/tab_strip/tab_strip_ui.cc
@@ -10,6 +10,7 @@
 #include <vector>
 
 #include "base/base64.h"
+#include "base/bind.h"
 #include "base/values.h"
 #include "chrome/browser/extensions/extension_tab_util.h"
 #include "chrome/browser/profiles/profile.h"
@@ -17,6 +18,8 @@
 #include "chrome/browser/themes/theme_service.h"
 #include "chrome/browser/themes/theme_service_factory.h"
 #include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/tabs/tab_network_state.h"
+#include "chrome/browser/ui/tabs/tab_renderer_data.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/browser/ui/tabs/tab_strip_model_observer.h"
 #include "chrome/browser/ui/webui/favicon_source.h"
@@ -25,6 +28,8 @@
 #include "chrome/grit/tab_strip_resources.h"
 #include "chrome/grit/tab_strip_resources_map.h"
 #include "components/favicon_base/favicon_url_parser.h"
+#include "content/public/browser/favicon_status.h"
+#include "content/public/browser/navigation_entry.h"
 #include "content/public/browser/url_data_source.h"
 #include "content/public/browser/web_ui_data_source.h"
 #include "content/public/browser/web_ui_message_handler.h"
@@ -66,23 +71,83 @@
   explicit TabStripUIHandler(Browser* browser)
       : browser_(browser),
         thumbnail_tracker_(base::Bind(&TabStripUIHandler::HandleThumbnailUpdate,
-                                      base::Unretained(this))) {
-    browser_->tab_strip_model()->AddObserver(this);
-  }
+                                      base::Unretained(this))) {}
   ~TabStripUIHandler() override = default;
 
+  void OnJavascriptAllowed() override {
+    browser_->tab_strip_model()->AddObserver(this);
+  }
+
   // TabStripModelObserver:
+  void OnTabStripModelChanged(
+      TabStripModel* tab_strip_model,
+      const TabStripModelChange& change,
+      const TabStripSelectionChange& selection) override {
+    if (tab_strip_model->empty())
+      return;
+
+    switch (change.type()) {
+      case TabStripModelChange::kInserted: {
+        for (const auto& contents : change.GetInsert()->contents) {
+          FireWebUIListener("tab-created",
+                            GetTabData(contents.contents, contents.index));
+        }
+        break;
+      }
+      case TabStripModelChange::kRemoved: {
+        for (const auto& contents : change.GetRemove()->contents) {
+          FireWebUIListener("tab-removed",
+                            base::Value(extensions::ExtensionTabUtil::GetTabId(
+                                contents.contents)));
+        }
+        break;
+      }
+      case TabStripModelChange::kMoved: {
+        auto* move = change.GetMove();
+        FireWebUIListener(
+            "tab-moved",
+            base::Value(extensions::ExtensionTabUtil::GetTabId(move->contents)),
+            base::Value(move->to_index));
+        break;
+      }
+
+      case TabStripModelChange::kReplaced:
+      case TabStripModelChange::kGroupChanged:
+      case TabStripModelChange::kSelectionOnly:
+        // Not yet implemented.
+        break;
+    }
+
+    if (selection.active_tab_changed()) {
+      content::WebContents* new_contents = selection.new_contents;
+      int index = selection.new_model.active();
+      if (new_contents && index != TabStripModel::kNoTab) {
+        FireWebUIListener(
+            "tab-active-changed",
+            base::Value(extensions::ExtensionTabUtil::GetTabId(new_contents)));
+      }
+    }
+  }
+
   void TabChangedAt(content::WebContents* contents,
                     int index,
                     TabChangeType change_type) override {
-    // TODO(crbug.com/1006946): re-fetch the TabRendererData using
-    // |TabRendererData::FromTabInModel()|.
+    FireWebUIListener("tab-updated", GetTabData(contents, index));
+  }
+
+  void TabPinnedStateChanged(TabStripModel* tab_strip_model,
+                             content::WebContents* contents,
+                             int index) override {
+    FireWebUIListener("tab-updated", GetTabData(contents, index));
   }
 
  protected:
   // content::WebUIMessageHandler:
   void RegisterMessages() override {
     web_ui()->RegisterMessageCallback(
+        "getTabs",
+        base::Bind(&TabStripUIHandler::HandleGetTabs, base::Unretained(this)));
+    web_ui()->RegisterMessageCallback(
         "getThemeColors", base::Bind(&TabStripUIHandler::HandleGetThemeColors,
                                      base::Unretained(this)));
     web_ui()->RegisterMessageCallback(
@@ -94,6 +159,45 @@
   }
 
  private:
+  base::DictionaryValue GetTabData(content::WebContents* contents, int index) {
+    base::DictionaryValue tab_data;
+
+    tab_data.SetBoolean("active",
+                        browser_->tab_strip_model()->active_index() == index);
+    tab_data.SetInteger("id", extensions::ExtensionTabUtil::GetTabId(contents));
+    tab_data.SetInteger("index", index);
+    tab_data.SetString("status", extensions::ExtensionTabUtil::GetTabStatusText(
+                                     contents->IsLoading()));
+
+    // TODO(johntlee): Replace with favicon from TabRendererData
+    content::NavigationEntry* visible_entry =
+        contents->GetController().GetVisibleEntry();
+    if (visible_entry && visible_entry->GetFavicon().valid) {
+      tab_data.SetString("favIconUrl", visible_entry->GetFavicon().url.spec());
+    }
+
+    TabRendererData tab_renderer_data =
+        TabRendererData::FromTabInModel(browser_->tab_strip_model(), index);
+    tab_data.SetBoolean("pinned", tab_renderer_data.pinned);
+    tab_data.SetString("title", tab_renderer_data.title);
+    tab_data.SetString("url", tab_renderer_data.visible_url.GetContent());
+    // TODO(johntlee): Add the rest of TabRendererData
+
+    return tab_data;
+  }
+
+  void HandleGetTabs(const base::ListValue* args) {
+    AllowJavascript();
+    const base::Value& callback_id = args->GetList()[0];
+
+    base::ListValue tabs;
+    TabStripModel* tab_strip_model = browser_->tab_strip_model();
+    for (int i = 0; i < tab_strip_model->count(); ++i) {
+      tabs.Append(GetTabData(tab_strip_model->GetWebContentsAt(i), i));
+    }
+    ResolveJavascriptCallback(callback_id, tabs);
+  }
+
   void HandleGetThemeColors(const base::ListValue* args) {
     AllowJavascript();
     const base::Value& callback_id = args->GetList()[0];
diff --git a/chrome/common/pref_names.cc b/chrome/common/pref_names.cc
index 6db1886..fb72bb9 100644
--- a/chrome/common/pref_names.cc
+++ b/chrome/common/pref_names.cc
@@ -1007,10 +1007,6 @@
 // settings that links to OS settings.
 const char kSettingsShowOSBanner[] = "settings.cros.show_os_banner";
 
-// Boolean user profile pref that determines whether to show a banner in browser
-// settings "About Chrome" that links to OS settings "About Chrome OS".
-const char kSettingsShowAboutOSBanner[] = "settings.cros.show_about_os_banner";
-
 // A JSON pref for controlling which USB devices are whitelisted for certain
 // urls to be used via the WebUSB API on the login screen.
 const char kDeviceLoginScreenWebUsbAllowDevicesForUrls[] =
@@ -1367,6 +1363,10 @@
 // to print server.
 const char kPrintingSendUsernameAndFilenameEnabled[] =
     "printing.send_username_and_filename_enabled";
+
+// Indicates how long print jobs metadata is stored on the device, in days.
+const char kPrintJobHistoryExpirationPeriod[] =
+    "printing.print_job_history_expiration_period";
 #endif  // OS_CHROMEOS
 
 // An integer pref specifying the fallback behavior for sites outside of content
diff --git a/chrome/common/pref_names.h b/chrome/common/pref_names.h
index 820bffe..c9990d3 100644
--- a/chrome/common/pref_names.h
+++ b/chrome/common/pref_names.h
@@ -319,7 +319,6 @@
 extern const char kLoginExtensionApiDataForNextLoginAttempt[];
 extern const char kSettingsShowBrowserBanner[];
 extern const char kSettingsShowOSBanner[];
-extern const char kSettingsShowAboutOSBanner[];
 extern const char kDeviceLoginScreenWebUsbAllowDevicesForUrls[];
 #endif  // defined(OS_CHROMEOS)
 extern const char kShowHomeButton[];
@@ -436,6 +435,7 @@
 extern const char kPrintingBackgroundGraphicsDefault[];
 extern const char kPrintingSizeDefault[];
 extern const char kPrintingSendUsernameAndFilenameEnabled[];
+extern const char kPrintJobHistoryExpirationPeriod[];
 #endif  // OS_CHROMEOS
 
 extern const char kDefaultSupervisedUserFilteringBehavior[];
diff --git a/chrome/installer/mac/sign_chrome.py b/chrome/installer/mac/sign_chrome.py
index 9d34f1e..2ccdfc99 100755
--- a/chrome/installer/mac/sign_chrome.py
+++ b/chrome/installer/mac/sign_chrome.py
@@ -70,7 +70,11 @@
     parser.add_argument(
         '--keychain', help='The keychain to load the identity from.')
     parser.add_argument(
-        '--identity', required=True, help='The identity to sign with.')
+        '--identity',
+        required=True,
+        help='The identity to sign everything but PKGs with.')
+    parser.add_argument(
+        '--installer-identity', help='The identity to sign PKGs with.')
     parser.add_argument(
         '--notary-user',
         help='The username used to authenticate to the Apple notary service.')
@@ -102,10 +106,15 @@
         'products and installer tools will be placed here.')
     parser.add_argument(
         '--disable-packaging',
-        dest='disable_packaging',
         action='store_true',
         help='Disable creating any packaging (.dmg/.pkg) specified by the '
         'configuration.')
+    parser.add_argument(
+        '--skip-brand',
+        dest='skip_brands',
+        action='append',
+        default=[],
+        help='Causes any distribution whose brand code matches to be skipped.')
 
     group = parser.add_mutually_exclusive_group(required=False)
     group.add_argument(
@@ -124,9 +133,10 @@
             parser.error('The --notary-user and --notary-password arguments '
                          'are required with --notarize.')
 
-    config = create_config((args.identity, args.keychain, args.notary_user,
-                            args.notary_password, args.notary_asc_provider),
-                           args.development)
+    config = create_config(
+        (args.identity, args.installer_identity, args.keychain,
+         args.notary_user, args.notary_password, args.notary_asc_provider),
+        args.development)
     paths = model.Paths(args.input, args.output, None)
 
     if not os.path.exists(paths.output):
@@ -136,7 +146,8 @@
         paths,
         config,
         disable_packaging=args.disable_packaging,
-        do_notarization=args.notarize)
+        do_notarization=args.notarize,
+        skip_brands=args.skip_brands)
 
 
 if __name__ == '__main__':
diff --git a/chrome/installer/mac/signing/commands.py b/chrome/installer/mac/signing/commands.py
index c6ec720..c3adf40 100644
--- a/chrome/installer/mac/signing/commands.py
+++ b/chrome/installer/mac/signing/commands.py
@@ -75,15 +75,20 @@
     """
     PlistContext is a context manager that reads a plist on entry, providing
     the contents as a dictionary. If |rewrite| is True, then the same dictionary
-    is re-serialized on exit.
+    is re-serialized on exit. If |create_new| is True, then the file is not read
+    but rather an empty dictionary is created.
     """
 
-    def __init__(self, plist_path, rewrite=False):
+    def __init__(self, plist_path, rewrite=False, create_new=False):
         self._path = plist_path
         self._rewrite = rewrite
+        self._create_new = create_new
 
     def __enter__(self):
-        self._plist = plistlib.readPlist(self._path)
+        if self._create_new:
+            self._plist = {}
+        else:
+            self._plist = plistlib.readPlist(self._path)
         return self._plist
 
     def __exit__(self, exc_type, exc_value, exc_tb):
diff --git a/chrome/installer/mac/signing/config.py b/chrome/installer/mac/signing/config.py
index 5578b050..019b2507 100644
--- a/chrome/installer/mac/signing/config.py
+++ b/chrome/installer/mac/signing/config.py
@@ -24,13 +24,14 @@
     There is a class hierarchy of CodeSignConfig objects, with the
     build_props_config.BuildPropsConfig containing injected variables from the
     build process. Configs for Chromium and Google Chrome subclass that to
-    control signing options further. ANd then derived configurations are
+    control signing options further. And then derived configurations are
     created for internal signing artifacts and when using |model.Distribution|
     objects.
     """
 
     def __init__(self,
                  identity,
+                 installer_identity=None,
                  keychain=None,
                  notary_user=None,
                  notary_password=None,
@@ -40,9 +41,16 @@
         constructor, which is found in the specified keychain.
 
         Args:
-            identity: The name of the code signing identity to use. This can
-                be any value that `codesign -s <identity>` accepts, like the
-                hex-encoded SHA1 hash of the certificate. Must not be None.
+            identity: The name of the code signing identity to use for non-PKG
+                files. This can be any value that `codesign -s <identity>`
+                accepts, like the hex-encoded SHA1 hash of the certificate. Must
+                not be None.
+            installer_identity: The name of the code signing identity to use for
+                PKG files. This will be passed as the parameter for the call to
+                `productbuild --sign <identity>`. Note that a hex-encoded SHA1
+                hash is not a valid option, as it is for |identity| above. The
+                common name of the cert will work. If there is any distribution
+                that is packaged in a PKG this must not be None.
             keychain: Optional path to the keychain file, in which the signing
                 |identity| will be searched for.
             notary_user: Optional string username that will be used to
@@ -56,6 +64,7 @@
         """
         assert identity
         self._identity = identity
+        self._installer_identity = installer_identity
         self._keychain = keychain
         self._notary_user = notary_user
         self._notary_password = notary_password
@@ -64,11 +73,18 @@
     @property
     def identity(self):
         """Returns the code signing identity that will be used to sign the
-        products.
+        products, everything but PKG files.
         """
         return self._identity
 
     @property
+    def installer_identity(self):
+        """Returns the code signing identity that will be used to sign the
+        PKG file products.
+        """
+        return self._installer_identity
+
+    @property
     def keychain(self):
         """Returns the filename of the keychain in which |identity| will be
         searched for. May be None.
diff --git a/chrome/installer/mac/signing/model.py b/chrome/installer/mac/signing/model.py
index 2c694d1..5a4c4bd2 100644
--- a/chrome/installer/mac/signing/model.py
+++ b/chrome/installer/mac/signing/model.py
@@ -265,7 +265,8 @@
                              self).packaging_basename
 
         return DistributionCodeSignConfig(
-            base_config.identity, base_config.keychain, base_config.notary_user,
+            base_config.identity, base_config.installer_identity,
+            base_config.keychain, base_config.notary_user,
             base_config.notary_password, base_config.notary_asc_provider)
 
 
diff --git a/chrome/installer/mac/signing/pipeline.py b/chrome/installer/mac/signing/pipeline.py
index f801c3b..58578b5 100644
--- a/chrome/installer/mac/signing/pipeline.py
+++ b/chrome/installer/mac/signing/pipeline.py
@@ -118,62 +118,133 @@
         notarize.staple(os.path.join(paths.work, part_path))
 
 
-def _package_and_sign_item(packager, paths, dist_config):
-    """Creates, signs, and verifies a packaged item (PKG/DMG).
+def _productbuild_requirements_path(paths, dist_config):
+    """Creates a requirements file for use by `productbuild`. This specifies
+    that an x64 machine is required, and copies the OS requirement from the copy
+    of Chrome being packaged.
 
     Args:
-        packager: A function that turns the .app into a packaged item and
-            returns the path to the packaged item. The function will be called
-            with (model.Paths, model.Distribution, config.CodeSignConfig).
         paths: A |model.Paths| object.
         dist_config: The |config.CodeSignConfig| object.
 
     Returns:
-        The path to the signed packaged file.
+        The path to the requirements file.
     """
-    dist = dist_config.distribution
+    requirements_path = os.path.join(paths.work,
+                                     '{}.req'.format(dist_config.app_product))
 
-    package_path = packager(paths, dist, dist_config)
+    app_plist_path = os.path.join(paths.work, dist_config.app_dir, 'Contents',
+                                  'Info.plist')
+    with commands.PlistContext(app_plist_path) as app_plist:
+        with commands.PlistContext(
+                requirements_path, rewrite=True,
+                create_new=True) as requirements:
+            requirements['os'] = [app_plist['LSMinimumSystemVersion']]
+            requirements['arch'] = ['x86_64']
 
-    # item_identifier is the PKG/DMG name but without the file extension. If a
-    # brand code is in use, use the actual brand code instead of the name
-    # fragment, to avoid leaking the association between brand codes and their
-    # meanings.
-    item_identifier = dist_config.packaging_basename
-    if dist.branding_code:
-        item_identifier = dist_config.packaging_basename.replace(
-            dist.packaging_name_fragment, dist.branding_code)
-
-    product = model.CodeSignedProduct(
-        package_path, item_identifier, sign_with_identifier=True)
-    signing.sign_part(paths, dist_config, product)
-    signing.verify_part(paths, product)
-
-    return package_path
+    return requirements_path
 
 
-def _package_pkg(paths, dist, config):
-    """Packages a Chrome application bundle into a PKG.
+def _package_and_sign_pkg(paths, dist_config):
+    """Packages, signs, and verifies a PKG for a signed build product.
 
     Args:
         paths: A |model.Paths| object.
-        dist: The |model.Distribution| for which the product was customized.
-        config: The |config.CodeSignConfig| object.
+        dist_config: The |config.CodeSignConfig| object.
 
     Returns:
-        A path to the produced PKG file.
+        The path to the signed PKG file.
     """
-    pkg_path = os.path.join(paths.output,
-                            '{}.pkg'.format(config.packaging_basename))
-    app_path = os.path.join(paths.work, config.app_dir)
+    assert dist_config.installer_identity
+
+    # There are two .pkg files to be built:
+    #   1. The inner component package (which is the one that can contain things
+    #      like postinstall scripts). This is built with `pkgbuild`.
+    #   2. The outer product archive (which is the installable thing that has
+    #      pre-install requirements). This is built with `productbuild`.
+
+    # The component package.
+
+    component_pkg_path = os.path.join(paths.work,
+                                      '{}.pkg'.format(dist_config.app_product))
+    app_path = os.path.join(paths.work, dist_config.app_dir)
 
     commands.run_command([
-        'pkgbuild', '--identifier', config.base_bundle_id, '--version',
-        config.version, '--component', app_path, '--install-location',
-        '/Applications', pkg_path
+        'pkgbuild', '--identifier', dist_config.base_bundle_id, '--version',
+        dist_config.version, '--component', app_path, '--install-location',
+        '/Applications', component_pkg_path
     ])
 
-    return pkg_path
+    # The product archive.
+
+    # There are two steps here. The first is to create the "distribution file"
+    # which describes the product archive. `productbuild` has a mode to generate
+    # such a file, with the optional input of a "requirements file" that
+    # describes the desired installation requirements of the product archive.
+    # Use this mode to generate a distribution file. Note that if, in the
+    # future, it's desired that the product archive have UI customization, then
+    # this distribution file will need to be hand-crafted. With any luck, this
+    # auto-generated distribution file continue to suffice.
+
+    requirements_path = _productbuild_requirements_path(paths, dist_config)
+
+    distribution_path = os.path.join(paths.work,
+                                     '{}.dist'.format(dist_config.app_product))
+    commands.run_command([
+        'productbuild', '--synthesize', '--product', requirements_path,
+        '--package', component_pkg_path, distribution_path
+    ])
+
+    # The second step is to actually create the product archive using
+    # `productbuild`.
+
+    product_pkg_path = os.path.join(
+        paths.output, '{}.pkg'.format(dist_config.packaging_basename))
+
+    command = [
+        'productbuild', '--distribution', distribution_path, '--package-path',
+        paths.work, '--sign', dist_config.installer_identity
+    ]
+    if dist_config.notary_user:
+        # Assume if the config has notary authentication information that the
+        # products will be notarized, which requires a secure timestamp.
+        command.append('--timestamp')
+    if dist_config.keychain:
+        command.extend(['--keychain', dist_config.keychain])
+    command.append(product_pkg_path)
+    commands.run_command(command)
+
+    return product_pkg_path
+
+
+def _package_and_sign_dmg(paths, dist_config):
+    """Packages, signs, and verifies a DMG for a signed build product.
+
+    Args:
+        paths: A |model.Paths| object.
+        dist_config: The |config.CodeSignConfig| object.
+
+    Returns:
+        The path to the signed DMG file.
+    """
+    dist = dist_config.distribution
+
+    dmg_path = _package_dmg(paths, dist, dist_config)
+
+    # dmg_identifier is like dmg_name but without the file extension. If a brand
+    # code is in use, use the actual brand code instead of the name fragment, to
+    # avoid leaking the association between brand codes and their meanings.
+    dmg_identifier = dist_config.packaging_basename
+    if dist.branding_code:
+        dmg_identifier = dist_config.packaging_basename.replace(
+            dist.packaging_name_fragment, dist.branding_code)
+
+    product = model.CodeSignedProduct(
+        dmg_path, dmg_identifier, sign_with_identifier=True)
+    signing.sign_part(paths, dist_config, product)
+    signing.verify_part(paths, product)
+
+    return dmg_path
 
 
 def _package_dmg(paths, dist, config):
@@ -290,7 +361,11 @@
     return dist_config.packaging_basename
 
 
-def sign_all(orig_paths, config, disable_packaging=False, do_notarization=True):
+def sign_all(orig_paths,
+             config,
+             disable_packaging=False,
+             do_notarization=True,
+             skip_brands=[]):
     """For each distribution in |config|, performs customization, signing, and
     DMG packaging and places the resulting signed DMG in |orig_paths.output|.
     The |paths.input| must contain the products to customize and sign.
@@ -306,6 +381,8 @@
             be stapled. If |package_dmg| is also True, the stapled application
             will be packaged in the DMG and then the DMG itself will be
             notarized and stapled.
+        skip_brands: A list of brand code strings. If a distribution has a brand
+            code in this list, that distribution will be skipped.
     """
     with commands.WorkDirectory(orig_paths) as notary_paths:
         # First, sign all the distributions and optionally submit the
@@ -313,6 +390,9 @@
         uuids_to_config = {}
         signed_frameworks = {}
         for dist in config.distributions:
+            if dist.branding_code in skip_brands:
+                continue
+
             with commands.WorkDirectory(orig_paths) as paths:
                 dist_config = dist.to_config(config)
                 do_packaging = (dist.package_as_dmg or
@@ -357,22 +437,23 @@
         if not disable_packaging:
             uuids_to_package_path = {}
             for dist in config.distributions:
+                if dist.branding_code in skip_brands:
+                    continue
+
                 dist_config = dist.to_config(config)
                 paths = orig_paths.replace_work(
                     os.path.join(notary_paths.work,
                                  _intermediate_work_dir_name(dist_config)))
 
                 if dist.package_as_dmg:
-                    dmg_path = _package_and_sign_item(_package_dmg, paths,
-                                                      dist_config)
+                    dmg_path = _package_and_sign_dmg(paths, dist_config)
 
                     if do_notarization:
                         uuid = notarize.submit(dmg_path, dist_config)
                         uuids_to_package_path[uuid] = dmg_path
 
                 if dist.package_as_pkg:
-                    pkg_path = _package_and_sign_item(_package_pkg, paths,
-                                                      dist_config)
+                    pkg_path = _package_and_sign_pkg(paths, dist_config)
 
                     if do_notarization:
                         uuid = notarize.submit(pkg_path, dist_config)
diff --git a/chrome/installer/mac/signing/pipeline_test.py b/chrome/installer/mac/signing/pipeline_test.py
index 82f2adb..68ca9796 100644
--- a/chrome/installer/mac/signing/pipeline_test.py
+++ b/chrome/installer/mac/signing/pipeline_test.py
@@ -18,6 +18,10 @@
 _get_work_dir.count = 0
 
 
+def _productbuild_requirements_path(p, d):
+    return '$W/App Product.req'
+
+
 def _get_adjacent_item(l, o):
     """Finds object |o| in collection |l| and returns the item at its index
     plus 1.
@@ -36,6 +40,8 @@
     'signing.signing',
     **{m: mock.DEFAULT for m in ('sign_part', 'sign_chrome', 'verify_part')})
 @mock.patch('signing.commands.tempfile.mkdtemp', _get_work_dir)
+@mock.patch('signing.pipeline._productbuild_requirements_path',
+            _productbuild_requirements_path)
 class TestPipelineHelpers(unittest.TestCase):
 
     def setUp(self):
@@ -250,10 +256,8 @@
         dist_config = dist.to_config(config)
 
         paths = self.paths.replace_work('$W')
-        self.assertEqual(
-            '$O/AppProduct-99.0.9999.99.dmg',
-            pipeline._package_and_sign_item(pipeline._package_dmg, paths,
-                                            dist_config))
+        self.assertEqual('$O/AppProduct-99.0.9999.99.dmg',
+                         pipeline._package_and_sign_dmg(paths, dist_config))
 
         manager.assert_has_calls([
             mock.call.make_dir('$W/empty'),
@@ -284,27 +288,43 @@
         dist_config = dist.to_config(config)
 
         paths = self.paths.replace_work('$W')
-        self.assertEqual(
-            '$O/AppProduct-99.0.9999.99.pkg',
-            pipeline._package_and_sign_item(pipeline._package_pkg, paths,
-                                            dist_config))
+        self.assertEqual('$O/AppProduct-99.0.9999.99.pkg',
+                         pipeline._package_and_sign_pkg(paths, dist_config))
 
         manager.assert_has_calls([
             mock.call.run_command(mock.ANY),
-            mock.call.sign_part(paths, dist_config, mock.ANY),
-            mock.call.verify_part(paths, mock.ANY)
+            mock.call.run_command(mock.ANY),
+            mock.call.run_command(mock.ANY)
         ])
 
-        run_command = [
+        run_commands = [
             call for call in manager.mock_calls if call[0] == 'run_command'
-        ][0]
-        pkgbuild_args = run_command[1][0]
+        ]
+        pkgbuild_args = run_commands[0][1][0]
+        productbuild_synthesize_args = run_commands[1][1][0]
+        productbuild_args = run_commands[2][1][0]
 
         self.assertEqual('test.signing.bundle_id',
                          _get_adjacent_item(pkgbuild_args, '--identifier'))
         self.assertEqual('99.0.9999.99',
                          _get_adjacent_item(pkgbuild_args, '--version'))
 
+        self.assertTrue('--synthesize' in productbuild_synthesize_args)
+        self.assertEqual(
+            '$W/App Product.req',
+            _get_adjacent_item(productbuild_synthesize_args, '--product'))
+        self.assertEqual(
+            '$W/App Product.pkg',
+            _get_adjacent_item(productbuild_synthesize_args, '--package'))
+
+        self.assertEqual(
+            '$W/App Product.dist',
+            _get_adjacent_item(productbuild_args, '--distribution'))
+        self.assertEqual(
+            '$W', _get_adjacent_item(productbuild_args, '--package-path'))
+        self.assertEqual('[INSTALLER-IDENTITY]',
+                         _get_adjacent_item(productbuild_args, '--sign'))
+
     def test_package_and_sign_dmg_branding(self, **kwargs):
         manager = mock.Mock()
         for attr in kwargs:
@@ -319,10 +339,8 @@
         dist_config = dist.to_config(config)
 
         paths = self.paths.replace_work('$W')
-        self.assertEqual(
-            '$O/AppProduct-99.0.9999.99-ForCows.dmg',
-            pipeline._package_and_sign_item(pipeline._package_dmg, paths,
-                                            dist_config))
+        self.assertEqual('$O/AppProduct-99.0.9999.99-ForCows.dmg',
+                         pipeline._package_and_sign_dmg(paths, dist_config))
 
         manager.assert_has_calls([
             mock.call.make_dir('$W/empty'),
@@ -357,27 +375,43 @@
         dist_config = dist.to_config(config)
 
         paths = self.paths.replace_work('$W')
-        self.assertEqual(
-            '$O/AppProduct-99.0.9999.99-ForCows.pkg',
-            pipeline._package_and_sign_item(pipeline._package_pkg, paths,
-                                            dist_config))
+        self.assertEqual('$O/AppProduct-99.0.9999.99-ForCows.pkg',
+                         pipeline._package_and_sign_pkg(paths, dist_config))
 
         manager.assert_has_calls([
             mock.call.run_command(mock.ANY),
-            mock.call.sign_part(paths, dist_config, mock.ANY),
-            mock.call.verify_part(paths, mock.ANY)
+            mock.call.run_command(mock.ANY),
+            mock.call.run_command(mock.ANY)
         ])
 
-        run_command = [
+        run_commands = [
             call for call in manager.mock_calls if call[0] == 'run_command'
-        ][0]
-        pkgbuild_args = run_command[1][0]
+        ]
+        pkgbuild_args = run_commands[0][1][0]
+        productbuild_synthesize_args = run_commands[1][1][0]
+        productbuild_args = run_commands[2][1][0]
 
         self.assertEqual('test.signing.bundle_id',
                          _get_adjacent_item(pkgbuild_args, '--identifier'))
         self.assertEqual('99.0.9999.99',
                          _get_adjacent_item(pkgbuild_args, '--version'))
 
+        self.assertTrue('--synthesize' in productbuild_synthesize_args)
+        self.assertEqual(
+            '$W/App Product.req',
+            _get_adjacent_item(productbuild_synthesize_args, '--product'))
+        self.assertEqual(
+            '$W/App Product.pkg',
+            _get_adjacent_item(productbuild_synthesize_args, '--package'))
+
+        self.assertEqual(
+            '$W/App Product.dist',
+            _get_adjacent_item(productbuild_args, '--distribution'))
+        self.assertEqual(
+            '$W', _get_adjacent_item(productbuild_args, '--package-path'))
+        self.assertEqual('[INSTALLER-IDENTITY]',
+                         _get_adjacent_item(productbuild_args, '--sign'))
+
     def test_package_dmg_no_customize(self, **kwargs):
         dist = model.Distribution()
         config = test_config.TestConfig()
@@ -524,7 +558,8 @@
     'signing.pipeline', **{
         m: mock.DEFAULT
         for m in ('_customize_and_sign_chrome', '_staple_chrome',
-                  '_package_and_sign_item', '_package_installer_tools')
+                  '_package_and_sign_dmg', '_package_and_sign_pkg',
+                  '_package_installer_tools')
     })
 @mock.patch('signing.commands.tempfile.mkdtemp', _get_work_dir)
 class TestSignAll(unittest.TestCase):
@@ -544,14 +579,8 @@
         kwargs['wait_for_results'].side_effect = [
             iter([app_uuid]), iter([dmg_uuid])
         ]
-
-        def package_and_sign_side_effect(packager, paths, dist_config):
-            if packager == pipeline._package_dmg:
-                return '$O/AppProduct-99.0.9999.99.dmg'
-            assert False
-
         kwargs[
-            '_package_and_sign_item'].side_effect = package_and_sign_side_effect
+            '_package_and_sign_dmg'].return_value = '$O/AppProduct-99.0.9999.99.dmg'
 
         class Config(test_config.TestConfig):
 
@@ -588,8 +617,7 @@
                 mock.ANY),
 
             # Make the DMG.
-            mock.call._package_and_sign_item(pipeline._package_dmg, mock.ANY,
-                                             mock.ANY),
+            mock.call._package_and_sign_dmg(mock.ANY, mock.ANY),
 
             # Notarize the DMG.
             mock.call.submit('$O/AppProduct-99.0.9999.99.dmg', mock.ANY),
@@ -614,14 +642,8 @@
         kwargs['wait_for_results'].side_effect = [
             iter([app_uuid]), iter([pkg_uuid])
         ]
-
-        def package_and_sign_side_effect(packager, paths, dist_config):
-            if packager == pipeline._package_pkg:
-                return '$O/AppProduct-99.0.9999.99.pkg'
-            assert False
-
         kwargs[
-            '_package_and_sign_item'].side_effect = package_and_sign_side_effect
+            '_package_and_sign_pkg'].return_value = '$O/AppProduct-99.0.9999.99.pkg'
 
         class Config(test_config.TestConfig):
 
@@ -658,8 +680,7 @@
                 mock.ANY),
 
             # Make the DMG.
-            mock.call._package_and_sign_item(pipeline._package_pkg, mock.ANY,
-                                             mock.ANY),
+            mock.call._package_and_sign_pkg(mock.ANY, mock.ANY),
 
             # Notarize the DMG.
             mock.call.submit('$O/AppProduct-99.0.9999.99.pkg', mock.ANY),
@@ -685,16 +706,10 @@
         kwargs['wait_for_results'].side_effect = [
             iter([app_uuid]), iter([dmg_uuid, pkg_uuid])
         ]
-
-        def package_and_sign_side_effect(packager, paths, dist_config):
-            if packager == pipeline._package_dmg:
-                return '$O/AppProduct-99.0.9999.99.dmg'
-            if packager == pipeline._package_pkg:
-                return '$O/AppProduct-99.0.9999.99.pkg'
-            assert False
-
         kwargs[
-            '_package_and_sign_item'].side_effect = package_and_sign_side_effect
+            '_package_and_sign_dmg'].return_value = '$O/AppProduct-99.0.9999.99.dmg'
+        kwargs[
+            '_package_and_sign_pkg'].return_value = '$O/AppProduct-99.0.9999.99.pkg'
 
         class Config(test_config.TestConfig):
 
@@ -731,13 +746,11 @@
                 mock.ANY),
 
             # Make the DMG, and submit for notarization.
-            mock.call._package_and_sign_item(pipeline._package_dmg, mock.ANY,
-                                             mock.ANY),
+            mock.call._package_and_sign_dmg(mock.ANY, mock.ANY),
             mock.call.submit('$O/AppProduct-99.0.9999.99.dmg', mock.ANY),
 
             # Make the PKG, and submit for notarization.
-            mock.call._package_and_sign_item(pipeline._package_pkg, mock.ANY,
-                                             mock.ANY),
+            mock.call._package_and_sign_pkg(mock.ANY, mock.ANY),
             mock.call.submit('$O/AppProduct-99.0.9999.99.pkg', mock.ANY),
 
             # Wait for notarization results.
@@ -810,8 +823,7 @@
             mock.call.shutil.rmtree('$W_2'),
 
             # Make the DMG.
-            mock.call._package_and_sign_item(pipeline._package_dmg, mock.ANY,
-                                             mock.ANY),
+            mock.call._package_and_sign_dmg(mock.ANY, mock.ANY),
             mock.call.shutil.rmtree('$W_1'),
 
             # Package the installer tools.
@@ -894,24 +906,19 @@
             mock.call.shutil.rmtree('$W_5'),
 
             # Packaging and signing.
-            mock.call._package_and_sign_item(
-                pipeline._package_dmg,
+            mock.call._package_and_sign_dmg(
                 self.paths.replace_work('$W_1/AppProduct-99.0.9999.99'),
                 mock.ANY),
-            mock.call._package_and_sign_item(
-                pipeline._package_dmg,
+            mock.call._package_and_sign_dmg(
                 self.paths.replace_work(
                     '$W_1/AppProduct-99.0.9999.99-ForCows-MOO'), mock.ANY),
-            mock.call._package_and_sign_item(
-                pipeline._package_pkg,
+            mock.call._package_and_sign_pkg(
                 self.paths.replace_work(
                     '$W_1/AppProduct-99.0.9999.99-ForDogs-ARF'), mock.ANY),
-            mock.call._package_and_sign_item(
-                pipeline._package_dmg,
+            mock.call._package_and_sign_dmg(
                 self.paths.replace_work(
                     '$W_1/AppProduct-99.0.9999.99-ForDogcows-MOOF'), mock.ANY),
-            mock.call._package_and_sign_item(
-                pipeline._package_pkg,
+            mock.call._package_and_sign_pkg(
                 self.paths.replace_work(
                     '$W_1/AppProduct-99.0.9999.99-ForDogcows-MOOF'), mock.ANY),
             mock.call.shutil.rmtree('$W_1'),
diff --git a/chrome/installer/mac/signing/signing_test.py b/chrome/installer/mac/signing/signing_test.py
index 72a0f01..cf109090 100644
--- a/chrome/installer/mac/signing/signing_test.py
+++ b/chrome/installer/mac/signing/signing_test.py
@@ -120,7 +120,7 @@
         ])
 
     def test_sign_part_no_keychain(self, run_command):
-        config = test_config.TestConfig('[IDENTITY]', None)
+        config = test_config.TestConfig(identity='[IDENTITY]', keychain=None)
         part = model.CodeSignedProduct('Test.app', 'test.signing.app')
         signing.sign_part(self.paths, config, part)
         run_command.assert_called_once_with([
diff --git a/chrome/installer/mac/signing/test_config.py b/chrome/installer/mac/signing/test_config.py
index 089ae428..902942c 100644
--- a/chrome/installer/mac/signing/test_config.py
+++ b/chrome/installer/mac/signing/test_config.py
@@ -9,12 +9,14 @@
 
     def __init__(self,
                  identity='[IDENTITY]',
+                 installer_identity='[INSTALLER-IDENTITY]',
                  keychain='[KEYCHAIN]',
                  notary_user='[NOTARY-USER]',
                  notary_password='[NOTARY-PASSWORD]',
                  notary_asc_provider=None):
-        super(TestConfig, self).__init__(identity, keychain, notary_user,
-                                         notary_password, notary_asc_provider)
+        super(TestConfig,
+              self).__init__(identity, installer_identity, keychain,
+                             notary_user, notary_password, notary_asc_provider)
 
     @property
     def app_product(self):
diff --git a/chrome/services/file_util/public/cpp/BUILD.gn b/chrome/services/file_util/public/cpp/BUILD.gn
index 7c7aff4..66f8577 100644
--- a/chrome/services/file_util/public/cpp/BUILD.gn
+++ b/chrome/services/file_util/public/cpp/BUILD.gn
@@ -43,6 +43,7 @@
     deps = [
       ":cpp",
       "//chrome/services/file_util",
+      "//content/test:test_support",
       "//crypto:platform",
       "//skia",
       "//testing/gmock",
diff --git a/chrome/test/chromedriver/test/run_py_tests.py b/chrome/test/chromedriver/test/run_py_tests.py
index 18f7ed3..b3d7522 100755
--- a/chrome/test/chromedriver/test/run_py_tests.py
+++ b/chrome/test/chromedriver/test/run_py_tests.py
@@ -978,7 +978,13 @@
       "parameters": {"pointerType": "touch"},
       "id": "pointer2"}]})
     self._driver.PerformActions(actions)
-    events = self._driver.ExecuteScript('return window.events')
+    for _ in range(5):
+      events = self._driver.ExecuteScript('return window.events')
+      if len(events) == 4:
+        break
+      # Wait 10 ms for the event handler to be executed.
+      time.sleep(0.01)
+
     self.assertEquals(4, len(events))
     self.assertEquals("touchstart", events[0]['type'])
     self.assertEquals("touchstart", events[1]['type'])
diff --git a/chrome/test/data/local_ntp/customize_menu_browsertest.js b/chrome/test/data/local_ntp/customize_menu_browsertest.js
index 21b86e5d..86908c3 100644
--- a/chrome/test/data/local_ntp/customize_menu_browsertest.js
+++ b/chrome/test/data/local_ntp/customize_menu_browsertest.js
@@ -348,7 +348,6 @@
   assertFalse(document.body.classList.contains('alternate-logo'));
   assertEquals(null, $(test.customizeMenu.IDS.CUSTOM_BG_ATTR_LINE1));
   assertEquals(null, $(test.customizeMenu.IDS.CUSTOM_BG_ATTR_LINE2));
-  assertEquals('', $(test.customizeMenu.IDS.CUSTOM_BG_ATTR).href);
 
   // Select a background and check that correct styling and attributes are
   // applied to the page.
@@ -363,7 +362,10 @@
       $(test.customizeMenu.IDS.CUSTOM_BG_ATTR_LINE2).innerText);
   assertEquals(
       image_tile.dataset.attributionActionUrl,
-      $(test.customizeMenu.IDS.CUSTOM_BG_ATTR).href);
+      $(test.customizeMenu.IDS.CUSTOM_BG_ATTR_LINE1).href);
+  assertEquals(
+      image_tile.dataset.attributionActionUrl,
+      $(test.customizeMenu.IDS.CUSTOM_BG_ATTR_LINE2).href);
 };
 
 /**
diff --git a/chrome/test/data/local_ntp/local_ntp_browsertest.html b/chrome/test/data/local_ntp/local_ntp_browsertest.html
index 509b700..0390bd6 100644
--- a/chrome/test/data/local_ntp/local_ntp_browsertest.html
+++ b/chrome/test/data/local_ntp/local_ntp_browsertest.html
@@ -114,7 +114,7 @@
         <span id="edit-bg-text">$i18n{customizeButton}</span>
       </div>
 
-      <a id="custom-bg-attr"></a>
+      <div id="custom-bg-attr"></div>
     </div>
 
     <dialog div id="edit-bg-dialog" class="customize-dialog">
diff --git a/chrome/test/data/policy/policy_test_cases.json b/chrome/test/data/policy/policy_test_cases.json
index 6ee7351..8013d96c 100644
--- a/chrome/test/data/policy/policy_test_cases.json
+++ b/chrome/test/data/policy/policy_test_cases.json
@@ -411,6 +411,14 @@
     ]
   },
 
+  "PrintJobHistoryExpirationPeriod": {
+    "os": ["chromeos"],
+    "test_policy": { "PrintJobHistoryExpirationPeriod": 90 },
+    "pref_mappings": [
+      { "pref": "printing.print_job_history_expiration_period" }
+    ]
+  },
+
   "CloudPrintSubmitEnabled": {
     "os": ["win", "mac", "linux"],
     "test_policy": { "CloudPrintSubmitEnabled": false },
diff --git a/chrome/test/data/webui/settings/a11y/edit_dictionary_a11y_test.js b/chrome/test/data/webui/settings/a11y/edit_dictionary_a11y_test.js
index 1752b29..4ca0271 100644
--- a/chrome/test/data/webui/settings/a11y/edit_dictionary_a11y_test.js
+++ b/chrome/test/data/webui/settings/a11y/edit_dictionary_a11y_test.js
@@ -2,9 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-/**
- * @fileoverview Define accessibility tests for the EDIT_DICTIONARY route.
- */
+/** @fileoverview Define accessibility tests for the EDIT_DICTIONARY route.  */
 
 // Disable since the EDIT_DICTIONARY route does not exist on Mac.
 GEN('#if !defined(OS_MACOSX)');
@@ -14,18 +12,49 @@
   'settings_accessibility_test.js',
 ]);
 
-AccessibilityTest.define('SettingsAccessibilityTest', {
+// eslint-disable-next-line no-var
+var EditDictionaryA11yTest = class extends SettingsAccessibilityTest {
+  /** @override */
+  get extraLibraries() {
+    return super.extraLibraries.concat([
+      '../../fake_chrome_event.js',
+      '../../test_browser_proxy.js',
+      '../../test_util.js',
+      '../fake_language_settings_private.js',
+      '../fake_settings_private.js',
+    ]);
+  }
+};
+
+function getDictionaryPage() {
+  const settingsUI = document.querySelector('settings-ui');
+  assertTrue(!!settingsUI);
+  const settingsMain = settingsUI.$.main;
+  assertTrue(!!settingsMain);
+  const settingsBasicPage = settingsMain.$$('settings-basic-page');
+  assertTrue(!!settingsBasicPage);
+  const languagesPage = settingsBasicPage.$$('settings-languages-page');
+  assertTrue(!!languagesPage);
+  const dictionaryPage = languagesPage.$$('settings-edit-dictionary-page');
+  assertTrue(!!dictionaryPage);
+  return dictionaryPage;
+}
+
+AccessibilityTest.define('EditDictionaryA11yTest', {
   /** @override */
   name: 'EDIT_DICTIONARY',
+
   /** @override */
-  axeOptions: SettingsAccessibilityTest.axeOptions,
-  /** @override */
-  setup: function() {
-    settings.navigateTo(settings.routes.EDIT_DICTIONARY);
-    Polymer.dom.flush();
-  },
-  /** @override */
-  tests: {'Accessible with No Changes': function() {}},
+  axeOptions: Object.assign({}, SettingsAccessibilityTest.axeOptions, {
+    'rules': Object.assign({}, SettingsAccessibilityTest.axeOptions.rules, {
+      // TODO(crbug.com/1012370): Disable because of timeout in CFI build.
+      'hidden-content': {enabled: false},
+    }),
+  }),
+
+  /** @type {settings.FakeLanguageSettingsPrivate} */
+  languageSettingsPrivate_: null,
+
   /** @override */
   violationFilter:
       Object.assign({}, SettingsAccessibilityTest.violationFilter, {
@@ -39,7 +68,39 @@
           // TODO(crbug.com/808276): remove this exception when bug is fixed.
           return nodeResult.element.getAttribute('tabindex') == '0';
         },
-      })
+      }),
+
+  /** @override */
+  setup: async function() {
+    // Don't replace b/c each test case needs to use the same fake.
+    if (!this.languageSettingsPrivate_) {
+      this.languageSettingsPrivate_ =
+          new settings.FakeLanguageSettingsPrivate(),
+      settings.languageSettingsPrivateApiForTest =
+          this.languageSettingsPrivate_;
+    }
+
+    settings.navigateTo(settings.routes.EDIT_DICTIONARY);
+    Polymer.dom.flush();
+    await test_util.flushTasks();
+  },
+
+  /** @override */
+  tests: {
+    'Accessible with No Changes': function() {
+      const dictionaryPage = getDictionaryPage();
+      assertFalse(!!dictionaryPage.$$('#list'));
+    },
+
+    'Load data to list': function() {
+      const dictionaryPage = getDictionaryPage();
+
+      this.languageSettingsPrivate_.addSpellcheckWord('one');
+
+      assertTrue(!!dictionaryPage.$$('#list'));
+      assertEquals(1, dictionaryPage.$$('#list').items.length);
+    },
+  },
 });
 
-GEN('#endif // !defined(OS_MACOSX)');
+GEN('#endif  // !defined(OS_MACOSX)');
diff --git a/chrome/test/data/webui/tab_strip/tab_list_test.js b/chrome/test/data/webui/tab_strip/tab_list_test.js
index 3f5cb4c..33b06432 100644
--- a/chrome/test/data/webui/tab_strip/tab_list_test.js
+++ b/chrome/test/data/webui/tab_strip/tab_list_test.js
@@ -55,37 +55,31 @@
   let testTabStripViewProxy;
   let testTabsApiProxy;
 
-  const currentWindow = {
-    id: 1001,
-    tabs: [
-      {
-        active: true,
-        id: 0,
-        index: 0,
-        title: 'Tab 1',
-        windowId: 1001,
-      },
-      {
-        active: false,
-        id: 1,
-        index: 1,
-        title: 'Tab 2',
-        windowId: 1001,
-      },
-      {
-        active: false,
-        id: 2,
-        index: 2,
-        title: 'Tab 3',
-        windowId: 1001,
-      },
-    ],
-  };
+  const tabs = [
+    {
+      active: true,
+      id: 0,
+      index: 0,
+      title: 'Tab 1',
+    },
+    {
+      active: false,
+      id: 1,
+      index: 1,
+      title: 'Tab 2',
+    },
+    {
+      active: false,
+      id: 2,
+      index: 2,
+      title: 'Tab 3',
+    },
+  ];
 
   function pinTabAt(tab, index) {
     const changeInfo = {index: index, pinned: true};
     const updatedTab = Object.assign({}, tab, changeInfo);
-    callbackRouter.onUpdated.dispatchEvent(tab.id, changeInfo, updatedTab);
+    webUIListenerCallback('tab-updated', updatedTab);
   }
 
   function getUnpinnedTabs() {
@@ -101,7 +95,7 @@
     document.body.innerHTML = '';
 
     testTabsApiProxy = new TestTabsApiProxy();
-    testTabsApiProxy.setCurrentWindow(currentWindow);
+    testTabsApiProxy.setTabs(tabs);
     TabsApiProxy.instance_ = testTabsApiProxy;
     callbackRouter = testTabsApiProxy.callbackRouter;
 
@@ -115,7 +109,7 @@
     tabList = document.createElement('tabstrip-tab-list');
     document.body.appendChild(tabList);
 
-    return testTabsApiProxy.whenCalled('getCurrentWindow');
+    return testTabsApiProxy.whenCalled('getTabs');
   });
 
   teardown(() => {
@@ -142,8 +136,8 @@
 
   test('creates a tab element for each tab', () => {
     const tabElements = getUnpinnedTabs();
-    assertEquals(currentWindow.tabs.length, tabElements.length);
-    currentWindow.tabs.forEach((tab, index) => {
+    assertEquals(tabs.length, tabElements.length);
+    tabs.forEach((tab, index) => {
       assertEquals(tabElements[index].tab, tab);
     });
   });
@@ -153,22 +147,20 @@
       id: 3,
       index: 3,
       title: 'New tab',
-      windowId: currentWindow.id,
     };
-    callbackRouter.onCreated.dispatchEvent(appendedTab);
+    webUIListenerCallback('tab-created', appendedTab);
     let tabElements = getUnpinnedTabs();
-    assertEquals(currentWindow.tabs.length + 1, tabElements.length);
-    assertEquals(tabElements[currentWindow.tabs.length].tab, appendedTab);
+    assertEquals(tabs.length + 1, tabElements.length);
+    assertEquals(tabElements[tabs.length].tab, appendedTab);
 
     const prependedTab = {
       id: 4,
       index: 0,
       title: 'New tab',
-      windowId: currentWindow.id,
     };
-    callbackRouter.onCreated.dispatchEvent(prependedTab);
+    webUIListenerCallback('tab-created', prependedTab);
     tabElements = getUnpinnedTabs();
-    assertEquals(currentWindow.tabs.length + 2, tabElements.length);
+    assertEquals(tabs.length + 2, tabElements.length);
     assertEquals(tabElements[0].tab, prependedTab);
   });
 
@@ -178,44 +170,25 @@
       id: 3,
       index: 3,
       title: 'New tab',
-      windowId: currentWindow.id,
     };
-    callbackRouter.onCreated.dispatchEvent(newActiveTab);
+    webUIListenerCallback('tab-created', newActiveTab);
     const [tabId, newIndex] = await testTabsApiProxy.whenCalled('moveTab');
     assertEquals(tabId, newActiveTab.id);
     assertEquals(newIndex, 0);
   });
 
-  test(
-      'does not add a new tab element when a tab is added in a different ' +
-          'window',
-      () => {
-        const newTab = {
-          index: 3,
-          title: 'New tab',
-          windowId: currentWindow.id + 1,
-        };
-        callbackRouter.onCreated.dispatchEvent(newTab);
-        const tabElements = getUnpinnedTabs();
-        assertEquals(currentWindow.tabs.length, tabElements.length);
-      });
-
   test('removes a tab when tab is removed from current window', async () => {
-    const tabToRemove = currentWindow.tabs[0];
-    callbackRouter.onRemoved.dispatchEvent(tabToRemove.id, {
-      windowId: currentWindow.id,
-    });
+    const tabToRemove = tabs[0];
+    webUIListenerCallback('tab-removed', tabToRemove.id);
     await tabList.animationPromises;
-    assertEquals(currentWindow.tabs.length - 1, getUnpinnedTabs().length);
+    assertEquals(tabs.length - 1, getUnpinnedTabs().length);
   });
 
   test('updates a tab with new tab data when a tab is updated', () => {
-    const tabToUpdate = currentWindow.tabs[0];
+    const tabToUpdate = tabs[0];
     const changeInfo = {title: 'A new title'};
     const updatedTab = Object.assign({}, tabToUpdate, changeInfo);
-    callbackRouter.onUpdated.dispatchEvent(
-        tabToUpdate.id, changeInfo, updatedTab);
-
+    webUIListenerCallback('tab-updated', updatedTab);
     const tabElements = getUnpinnedTabs();
     assertEquals(tabElements[0].tab, updatedTab);
   });
@@ -224,21 +197,17 @@
     const tabElements = getUnpinnedTabs();
 
     // Mock activating the 2nd tab
-    callbackRouter.onActivated.dispatchEvent({
-      tabId: currentWindow.tabs[1].id,
-      windowId: currentWindow.id,
-    });
+    webUIListenerCallback('tab-active-changed', tabs[1].id);
     assertFalse(tabElements[0].tab.active);
     assertTrue(tabElements[1].tab.active);
     assertFalse(tabElements[2].tab.active);
   });
 
   test('adds a pinned tab to its designated container', () => {
-    callbackRouter.onCreated.dispatchEvent({
+    webUIListenerCallback('tab-created', {
       index: 0,
       title: 'New pinned tab',
       pinned: true,
-      windowId: currentWindow.id,
     });
     const pinnedTabElements = getPinnedTabs();
     assertEquals(pinnedTabElements.length, 1);
@@ -246,10 +215,11 @@
   });
 
   test('moves pinned tabs to designated containers', () => {
-    const tabToPin = currentWindow.tabs[1];
+    const tabToPin = tabs[1];
     const changeInfo = {index: 0, pinned: true};
     let updatedTab = Object.assign({}, tabToPin, changeInfo);
-    callbackRouter.onUpdated.dispatchEvent(tabToPin.id, changeInfo, updatedTab);
+    webUIListenerCallback('tab-updated', updatedTab);
+
     let pinnedTabElements = getPinnedTabs();
     assertEquals(pinnedTabElements.length, 1);
     assertTrue(pinnedTabElements[0].tab.pinned);
@@ -260,7 +230,8 @@
     changeInfo.index = 0;
     changeInfo.pinned = false;
     updatedTab = Object.assign({}, updatedTab, changeInfo);
-    callbackRouter.onUpdated.dispatchEvent(tabToPin.id, changeInfo, updatedTab);
+    webUIListenerCallback('tab-updated', updatedTab);
+
     const unpinnedTabElements = getUnpinnedTabs();
     assertEquals(getPinnedTabs().length, 0);
     assertEquals(unpinnedTabElements.length, 3);
@@ -269,11 +240,9 @@
 
   test('moves tab elements when tabs move', () => {
     const tabElementsBeforeMove = getUnpinnedTabs();
-    const tabToMove = currentWindow.tabs[0];
-    callbackRouter.onMoved.dispatchEvent(tabToMove.id, {
-      toIndex: 2,
-      windowId: currentWindow.id,
-    });
+    const tabToMove = tabs[0];
+    webUIListenerCallback('tab-moved', tabToMove.id, 2);
+
     const tabElementsAfterMove = getUnpinnedTabs();
     assertEquals(tabElementsBeforeMove[0], tabElementsAfterMove[2]);
     assertEquals(tabElementsBeforeMove[1], tabElementsAfterMove[0]);
@@ -301,10 +270,7 @@
     // The 2nd tab should be off-screen to the right, so activating it should
     // scroll so that the element's right edge is aligned with the screen's
     // right edge
-    callbackRouter.onActivated.dispatchEvent({
-      tabId: currentWindow.tabs[1].id,
-      windowId: currentWindow.id,
-    });
+    webUIListenerCallback('tab-active-changed', tabs[1].id);
     let activeTab = getUnpinnedTabs()[1];
     await tabList.animationPromises;
     assertEquals(
@@ -315,77 +281,15 @@
     // The 1st tab should be now off-screen to the left, so activating it should
     // scroll so that the element's left edge is aligned with the screen's
     // left edge
-    callbackRouter.onActivated.dispatchEvent({
-      tabId: currentWindow.tabs[0].id,
-      windowId: currentWindow.id,
-    });
+    webUIListenerCallback('tab-active-changed', tabs[0].id);
     activeTab = getUnpinnedTabs()[0];
     await tabList.animationPromises;
     assertEquals(fakeScroller.scrollLeft, activeTab.offsetLeft - scrollPadding);
   });
 
-  test('attaching a tab creates a new tab element', async () => {
-    const attachedTab = {
-      index: 2,
-      id: 9001,
-      title: 'My new tab',
-      windowId: currentWindow.id,
-    };
-    testTabsApiProxy.setTab(attachedTab);
-    callbackRouter.onAttached.dispatchEvent(attachedTab.id, {
-      newPosition: attachedTab.index,
-      newWindowId: attachedTab.windowId,
-    });
-
-    const tabId = await testTabsApiProxy.whenCalled('getTab');
-    assertEquals(tabId, attachedTab.id);
-    assertEquals(getUnpinnedTabs().length, currentWindow.tabs.length + 1);
-    assertEquals(getUnpinnedTabs()[attachedTab.index].tab, attachedTab);
-  });
-
-  test('detaching a tab removes the tab element', () => {
-    const detachedTab = currentWindow.tabs[1];
-    callbackRouter.onDetached.dispatchEvent(detachedTab.id, {
-      oldPosition: 1,
-      oldWindowId: currentWindow.id,
-    });
-    assertEquals(getUnpinnedTabs().length, currentWindow.tabs.length - 1);
-  });
-
-  test(
-      'respects the last attached/detached event when multiple events are ' +
-          'dispatched for the same tab',
-      async () => {
-        const attachedTab = {
-          index: 2,
-          id: 9001,
-          title: 'My new tab',
-          windowId: currentWindow.id,
-        };
-        testTabsApiProxy.setTab(attachedTab);
-        callbackRouter.onAttached.dispatchEvent(attachedTab.id, {
-          newPosition: attachedTab.index,
-          newWindowId: attachedTab.windowId,
-        });
-        callbackRouter.onDetached.dispatchEvent(attachedTab.id, {
-          oldPosition: attachedTab.index,
-          oldWindowId: attachedTab.windowId,
-        });
-        callbackRouter.onAttached.dispatchEvent(attachedTab.id, {
-          newPosition: attachedTab.index,
-          newWindowId: attachedTab.windowId,
-        });
-        callbackRouter.onDetached.dispatchEvent(attachedTab.id, {
-          oldPosition: attachedTab.index,
-          oldWindowId: attachedTab.windowId,
-        });
-        await testTabsApiProxy.whenCalled('getTab');
-        assertEquals(getUnpinnedTabs().length, currentWindow.tabs.length);
-      });
-
   test('dragstart sets a drag image offset by the event coordinates', () => {
     // Drag and drop only works for pinned tabs
-    currentWindow.tabs.forEach(pinTabAt);
+    tabs.forEach(pinTabAt);
 
     const draggedTab = getPinnedTabs()[0];
     const mockDataTransfer = new MockDataTransfer();
@@ -408,7 +312,7 @@
 
   test('dragover moves tabs', async () => {
     // Drag and drop only works for pinned tabs
-    currentWindow.tabs.forEach(pinTabAt);
+    tabs.forEach(pinTabAt);
 
     const draggedIndex = 0;
     const dragOverIndex = 1;
@@ -435,7 +339,7 @@
     dragOverTab.dispatchEvent(dragOverEvent);
     assertEquals(dragOverEvent.dataTransfer.dropEffect, 'move');
     const [tabId, newIndex] = await testTabsApiProxy.whenCalled('moveTab');
-    assertEquals(tabId, currentWindow.tabs[draggedIndex].id);
+    assertEquals(tabId, tabs[draggedIndex].id);
     assertEquals(newIndex, dragOverIndex);
   });
 
@@ -443,10 +347,7 @@
       'when the tab strip closes, the active tab should move to the start',
       async () => {
         // Mock activating the 2nd tab
-        callbackRouter.onActivated.dispatchEvent({
-          tabId: currentWindow.tabs[1].id,
-          windowId: currentWindow.id,
-        });
+        webUIListenerCallback('tab-active-changed', tabs[1].id);
         testTabsApiProxy.resetResolver('moveTab');
 
         // Mock tab strip going from visible to hidden
@@ -454,7 +355,7 @@
         document.dispatchEvent(new Event('visibilitychange'));
 
         const [moveId, newIndex] = await testTabsApiProxy.whenCalled('moveTab');
-        assertEquals(moveId, currentWindow.tabs[1].id);
+        assertEquals(moveId, tabs[1].id);
         assertEquals(newIndex, 0);
       });
 });
diff --git a/chrome/test/data/webui/tab_strip/test_tabs_api_proxy.js b/chrome/test/data/webui/tab_strip/test_tabs_api_proxy.js
index d377ffb..4dfeafe 100644
--- a/chrome/test/data/webui/tab_strip/test_tabs_api_proxy.js
+++ b/chrome/test/data/webui/tab_strip/test_tabs_api_proxy.js
@@ -4,45 +4,17 @@
 
 import {TestBrowserProxy} from 'chrome://test/test_browser_proxy.m.js';
 
-class EventDispatcher {
-  constructor() {
-    this.eventListeners_ = [];
-  }
-
-  addListener(callback) {
-    this.eventListeners_.push(callback);
-  }
-
-  dispatchEvent() {
-    this.eventListeners_.forEach((callback) => {
-      callback(...arguments);
-    });
-  }
-}
-
 export class TestTabsApiProxy extends TestBrowserProxy {
   constructor() {
     super([
       'activateTab',
       'closeTab',
-      'getCurrentWindow',
-      'getTab',
+      'getTabs',
       'moveTab',
       'trackThumbnailForTab',
     ]);
 
-    this.callbackRouter = {
-      onActivated: new EventDispatcher(),
-      onAttached: new EventDispatcher(),
-      onCreated: new EventDispatcher(),
-      onDetached: new EventDispatcher(),
-      onMoved: new EventDispatcher(),
-      onRemoved: new EventDispatcher(),
-      onUpdated: new EventDispatcher(),
-    };
-
-    this.currentWindow_;
-    this.tab_;
+    this.tabs_;
   }
 
   activateTab(tabId) {
@@ -55,14 +27,9 @@
     return Promise.resolve();
   }
 
-  getCurrentWindow() {
-    this.methodCalled('getCurrentWindow');
-    return Promise.resolve(this.currentWindow_);
-  }
-
-  getTab(tabId) {
-    this.methodCalled('getTab', tabId);
-    return Promise.resolve(this.tab_);
+  getTabs() {
+    this.methodCalled('getTabs');
+    return Promise.resolve(this.tabs_.slice());
   }
 
   moveTab(tabId, newIndex) {
@@ -70,12 +37,8 @@
     return Promise.resolve();
   }
 
-  setCurrentWindow(currentWindow) {
-    this.currentWindow_ = currentWindow;
-  }
-
-  setTab(tab) {
-    this.tab_ = tab;
+  setTabs(tabs) {
+    this.tabs_ = tabs;
   }
 
   trackThumbnailForTab(tabId) {
diff --git a/chromecast/browser/cast_web_contents_impl.cc b/chromecast/browser/cast_web_contents_impl.cc
index 2ad9379..5b63dea 100644
--- a/chromecast/browser/cast_web_contents_impl.cc
+++ b/chromecast/browser/cast_web_contents_impl.cc
@@ -658,8 +658,6 @@
 }
 
 void CastWebContentsImpl::NotifyPageState() {
-  if (!delegate_)
-    return;
   // Don't notify if the page state didn't change.
   if (last_state_ == page_state_)
     return;
diff --git a/chromecast/browser/cast_web_view_default.cc b/chromecast/browser/cast_web_view_default.cc
index e929575..52f0dc1e 100644
--- a/chromecast/browser/cast_web_view_default.cc
+++ b/chromecast/browser/cast_web_view_default.cc
@@ -7,6 +7,7 @@
 #include <utility>
 
 #include "base/logging.h"
+#include "base/memory/ptr_util.h"
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/threading/thread_task_runner_handle.h"
@@ -53,7 +54,8 @@
     const CreateParams& params,
     CastWebContentsManager* web_contents_manager,
     content::BrowserContext* browser_context,
-    scoped_refptr<content::SiteInstance> site_instance)
+    scoped_refptr<content::SiteInstance> site_instance,
+    std::unique_ptr<shell::CastContentWindow> cast_content_window)
     : web_contents_manager_(web_contents_manager),
       browser_context_(browser_context),
       site_instance_(std::move(site_instance)),
@@ -62,7 +64,9 @@
       log_prefix_(params.log_prefix),
       web_contents_(CreateWebContents(browser_context_, site_instance_)),
       cast_web_contents_(web_contents_.get(), params.web_contents_params),
-      window_(shell::CastContentWindow::Create(params.window_params)),
+      window_(cast_content_window
+                  ? std::move(cast_content_window)
+                  : shell::CastContentWindow::Create(params.window_params)),
       resize_window_when_navigation_starts_(true) {
   DCHECK(delegate_);
   DCHECK(web_contents_manager_);
diff --git a/chromecast/browser/cast_web_view_default.h b/chromecast/browser/cast_web_view_default.h
index 66de3de..1fa1d39 100644
--- a/chromecast/browser/cast_web_view_default.h
+++ b/chromecast/browser/cast_web_view_default.h
@@ -34,10 +34,12 @@
                            content::WebContentsDelegate {
  public:
   // |web_contents_manager| and |browser_context| should outlive this object.
-  CastWebViewDefault(const CreateParams& params,
-                     CastWebContentsManager* web_contents_manager,
-                     content::BrowserContext* browser_context,
-                     scoped_refptr<content::SiteInstance> site_instance);
+  CastWebViewDefault(
+      const CreateParams& params,
+      CastWebContentsManager* web_contents_manager,
+      content::BrowserContext* browser_context,
+      scoped_refptr<content::SiteInstance> site_instance,
+      std::unique_ptr<shell::CastContentWindow> cast_content_window = nullptr);
   ~CastWebViewDefault() override;
 
   // CastWebView implementation:
diff --git a/chromecast/media/BUILD.gn b/chromecast/media/BUILD.gn
index ec0444e6..6dc9f3e7 100644
--- a/chromecast/media/BUILD.gn
+++ b/chromecast/media/BUILD.gn
@@ -6,6 +6,22 @@
 import("//media/media_options.gni")
 import("//testing/test.gni")
 
+declare_args() {
+  if (is_cast_desktop_build) {
+    # Desktop builds should set cast_static_media_lib_target explicitly if
+    # desired.
+    cast_static_media_lib_target = ""
+  } else if (is_cast_audio_only) {
+    cast_static_media_lib_target =
+        "//chromecast/media/cma/backend/alsa:libcast_media_1.0_audio"
+  } else if (enable_video_with_mixed_audio) {
+    cast_static_media_lib_target =
+        "//chromecast/media/cma/backend/video:libcast_media_1.0_avsync"
+  } else {
+    cast_static_media_lib_target = ""
+  }
+}
+
 group("media") {
   public_deps = [
     "//chromecast/media/audio",
@@ -27,14 +43,22 @@
     deps = [
       "//chromecast/media/cma/backend/fuchsia:media_backend",
     ]
+  } else if (cast_static_media_lib_target != "") {
+    deps = [
+      "$cast_static_media_lib_target",
+    ]
+  } else if (is_cast_desktop_build) {
+    deps = [
+      "//chromecast/media/cma/backend/desktop",
+    ]
   } else {
     deps = [
       "//chromecast/media/cma/backend:libcast_media_1.0",
     ]
+  }
 
-    if (cast_volume_control_in_avsettings) {
-      deps += [ "//chromecast/media/avsettings:libcast_avsettings_1.0" ]
-    }
+  if (cast_volume_control_in_avsettings) {
+    deps += [ "//chromecast/media/avsettings:libcast_avsettings_1.0" ]
   }
 }
 
diff --git a/chromecast/media/cma/BUILD.gn b/chromecast/media/cma/BUILD.gn
index bf49cf6..4020907d 100644
--- a/chromecast/media/cma/BUILD.gn
+++ b/chromecast/media/cma/BUILD.gn
@@ -43,7 +43,7 @@
     "//ui/gfx/geometry",
   ]
 
-  if (!enable_video_with_mixed_audio) {
+  if (is_cast_audio_only) {
     deps += [ "//chromecast/media/cma/backend:null_video" ]
   }
 }
@@ -77,16 +77,6 @@
 
   if (enable_video_with_mixed_audio) {
     defines = [ "ENABLE_VIDEO_WITH_MIXED_AUDIO" ]
-
-    # libvideodecoderformixer is linked here for the
-    # VideoDecoderForMixer::InitializeGraphicsForTesting symbol. Currently only
-    # alsa platforms do anything useful in this initialization, so otherwise
-    # just link in a dummy.
-    if (use_alsa) {
-      libs = [ "videodecoderformixer" ]
-    } else {
-      sources += [ "backend/dummy_initialize_graphics_for_testing.cc" ]
-    }
   }
 
   data = [
diff --git a/chromecast/media/cma/backend/BUILD.gn b/chromecast/media/cma/backend/BUILD.gn
index 9765466..65fe625 100644
--- a/chromecast/media/cma/backend/BUILD.gn
+++ b/chromecast/media/cma/backend/BUILD.gn
@@ -51,20 +51,6 @@
 # libcast_media_1.0.so. This target is only used to build executables
 # with correct linkage information.
 cast_shared_library("libcast_media_1.0") {
-  if (is_cast_desktop_build) {
-    deps = [
-      "//chromecast/media/cma/backend/desktop",
-    ]
-  } else {
-    deps = [
-      ":dummy",
-    ]
-  }
-}
-
-# Dummy implementation of media backend used on chromecast devices.
-# Must not depend on anything outside //chromecast/public.
-cast_source_set("dummy") {
   sources = [
     "cast_media_dummy.cc",
   ]
diff --git a/chromecast/media/cma/backend/alsa/BUILD.gn b/chromecast/media/cma/backend/alsa/BUILD.gn
index 52aaaad..0daf87d2 100644
--- a/chromecast/media/cma/backend/alsa/BUILD.gn
+++ b/chromecast/media/cma/backend/alsa/BUILD.gn
@@ -10,7 +10,7 @@
 # Alsa must be used for these targets to build.
 assert(use_alsa)
 
-cast_shared_library("libcast_media_1.0_audio") {
+cast_source_set("libcast_media_1.0_audio") {
   sources = [
     "cast_media_shlib.cc",
   ]
diff --git a/chromecast/media/cma/backend/alsa/cast_media_shlib.cc b/chromecast/media/cma/backend/alsa/cast_media_shlib.cc
index 9484e41..fdb58dd 100644
--- a/chromecast/media/cma/backend/alsa/cast_media_shlib.cc
+++ b/chromecast/media/cma/backend/alsa/cast_media_shlib.cc
@@ -4,16 +4,9 @@
 
 #include <alsa/asoundlib.h>
 
-#include "base/at_exit.h"
 #include "base/command_line.h"
-#include "base/files/scoped_file.h"
 #include "base/logging.h"
-#include "base/single_thread_task_runner.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "chromecast/base/init_command_line_shlib.h"
-#include "chromecast/base/task_runner_impl.h"
 #include "chromecast/media/cma/backend/media_pipeline_backend_for_mixer.h"
-#include "chromecast/media/cma/backend/stream_mixer.h"
 #include "chromecast/public/cast_media_shlib.h"
 #include "chromecast/public/graphics_types.h"
 #include "chromecast/public/video_plane.h"
@@ -81,19 +74,9 @@
 
 DefaultVideoPlane* g_video_plane = nullptr;
 
-base::AtExitManager g_at_exit_manager;
-
-std::unique_ptr<base::ThreadTaskRunnerHandle> g_thread_task_runner_handle;
-
 }  // namespace
 
 void CastMediaShlib::Initialize(const std::vector<std::string>& argv) {
-  // Sets logging to display process and thread ID.
-  logging::SetLogItems(true, true, false, false);
-  chromecast::InitCommandLineShlib(argv);
-
-  g_video_plane = new DefaultVideoPlane();
-
   InitializeAlsaControls();
   ::media::InitializeMediaLibrary();
 }
@@ -111,29 +94,17 @@
 
   delete g_video_plane;
   g_video_plane = nullptr;
-
-  g_thread_task_runner_handle.reset();
 }
 
 VideoPlane* CastMediaShlib::GetVideoPlane() {
+  if (!g_video_plane) {
+    g_video_plane = new DefaultVideoPlane();
+  }
   return g_video_plane;
 }
 
 MediaPipelineBackend* CastMediaShlib::CreateMediaPipelineBackend(
     const MediaPipelineDeviceParams& params) {
-  // Set up the static reference in base::ThreadTaskRunnerHandle::Get
-  // for the media thread in this shared library.  We can extract the
-  // SingleThreadTaskRunner passed in from cast_shell for this.
-  if (!base::ThreadTaskRunnerHandle::IsSet()) {
-    DCHECK(!g_thread_task_runner_handle);
-    const scoped_refptr<base::SingleThreadTaskRunner> task_runner =
-        static_cast<TaskRunnerImpl*>(params.task_runner)->runner();
-    DCHECK(task_runner->BelongsToCurrentThread());
-    g_thread_task_runner_handle.reset(
-        new base::ThreadTaskRunnerHandle(task_runner));
-  }
-
-  // TODO(cleichner): Implement MediaSyncType in MediaPipelineDeviceAlsa.
   return new MediaPipelineBackendForMixer(params);
 }
 
diff --git a/chromecast/media/cma/backend/desktop/cast_media_desktop.cc b/chromecast/media/cma/backend/desktop/cast_media_desktop.cc
index 18359e8b..b4c989a 100644
--- a/chromecast/media/cma/backend/desktop/cast_media_desktop.cc
+++ b/chromecast/media/cma/backend/desktop/cast_media_desktop.cc
@@ -4,15 +4,10 @@
 
 #include <memory>
 
-#include "base/single_thread_task_runner.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "build/build_config.h"
-#include "chromecast/base/task_runner_impl.h"
 #include "chromecast/media/cma/backend/desktop/media_pipeline_backend_desktop.h"
 #include "chromecast/public/cast_media_shlib.h"
 #include "chromecast/public/graphics_types.h"
 #include "chromecast/public/media/media_capabilities_shlib.h"
-#include "chromecast/public/media/media_pipeline_device_params.h"
 #include "chromecast/public/video_plane.h"
 
 namespace chromecast {
@@ -27,7 +22,6 @@
 };
 
 DesktopVideoPlane* g_video_plane = nullptr;
-base::ThreadTaskRunnerHandle* g_thread_task_runner_handle = nullptr;
 
 }  // namespace
 
@@ -38,8 +32,6 @@
 void CastMediaShlib::Finalize() {
   delete g_video_plane;
   g_video_plane = nullptr;
-  delete g_thread_task_runner_handle;
-  g_thread_task_runner_handle = nullptr;
 }
 
 VideoPlane* CastMediaShlib::GetVideoPlane() {
@@ -48,17 +40,6 @@
 
 MediaPipelineBackend* CastMediaShlib::CreateMediaPipelineBackend(
     const MediaPipelineDeviceParams& params) {
-  // Set up the static reference in base::ThreadTaskRunnerHandle::Get
-  // for the media thread in this shared library.  We can extract the
-  // SingleThreadTaskRunner passed in from cast_shell for this.
-  if (!base::ThreadTaskRunnerHandle::IsSet()) {
-    DCHECK(!g_thread_task_runner_handle);
-    const scoped_refptr<base::SingleThreadTaskRunner> task_runner =
-        static_cast<TaskRunnerImpl*>(params.task_runner)->runner();
-    DCHECK(task_runner->BelongsToCurrentThread());
-    g_thread_task_runner_handle = new base::ThreadTaskRunnerHandle(task_runner);
-  }
-
   return new MediaPipelineBackendDesktop();
 }
 
diff --git a/chromecast/media/cma/backend/dummy_initialize_graphics_for_testing.cc b/chromecast/media/cma/backend/dummy_initialize_graphics_for_testing.cc
deleted file mode 100644
index df6928f..0000000
--- a/chromecast/media/cma/backend/dummy_initialize_graphics_for_testing.cc
+++ /dev/null
@@ -1,15 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chromecast/media/cma/backend/video_decoder_for_mixer.h"
-
-namespace chromecast {
-namespace media {
-
-CHROMECAST_EXPORT void VideoDecoderForMixer::InitializeGraphicsForTesting() {
-  // No initialization required.
-}
-
-}  // namespace media
-}  // namespace chromecast
diff --git a/chromecast/media/cma/backend/fuchsia/cast_media_shlib_fuchsia.cc b/chromecast/media/cma/backend/fuchsia/cast_media_shlib_fuchsia.cc
index f89d162f..a518568 100644
--- a/chromecast/media/cma/backend/fuchsia/cast_media_shlib_fuchsia.cc
+++ b/chromecast/media/cma/backend/fuchsia/cast_media_shlib_fuchsia.cc
@@ -4,21 +4,11 @@
 
 #include "chromecast/public/cast_media_shlib.h"
 
-#include "base/at_exit.h"
-#include "base/command_line.h"
-#include "base/files/scoped_file.h"
-#include "base/logging.h"
-#include "base/single_thread_task_runner.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "chromecast/base/init_command_line_shlib.h"
-#include "chromecast/base/task_runner_impl.h"
 #include "chromecast/media/cma/backend/media_pipeline_backend_for_mixer.h"
 #include "chromecast/media/cma/backend/stream_mixer.h"
 #include "chromecast/public/graphics_types.h"
-#include "chromecast/public/media/media_pipeline_device_params.h"
 #include "chromecast/public/video_plane.h"
 #include "media/base/media.h"
-#include "media/base/media_switches.h"
 
 namespace chromecast {
 namespace media {
@@ -36,7 +26,6 @@
 };
 
 DefaultVideoPlane* g_video_plane = nullptr;
-base::ThreadTaskRunnerHandle* g_thread_task_runner_handle = nullptr;
 
 }  // namespace
 
@@ -52,9 +41,6 @@
 void CastMediaShlib::Finalize() {
   delete g_video_plane;
   g_video_plane = nullptr;
-
-  delete g_thread_task_runner_handle;
-  g_thread_task_runner_handle = nullptr;
 }
 
 VideoPlane* CastMediaShlib::GetVideoPlane() {
@@ -63,18 +49,6 @@
 
 MediaPipelineBackend* CastMediaShlib::CreateMediaPipelineBackend(
     const MediaPipelineDeviceParams& params) {
-  // Set up the static reference in base::ThreadTaskRunnerHandle::Get()
-  // for the media thread in this shared library. We can extract the
-  // SingleThreadTaskRunner passed in from cast_shell for this.
-  if (!base::ThreadTaskRunnerHandle::IsSet()) {
-    DCHECK(!g_thread_task_runner_handle);
-    const scoped_refptr<base::SingleThreadTaskRunner> task_runner =
-        static_cast<TaskRunnerImpl*>(params.task_runner)->runner();
-
-    DCHECK(task_runner->BelongsToCurrentThread());
-    g_thread_task_runner_handle = new base::ThreadTaskRunnerHandle(task_runner);
-  }
-
   return new MediaPipelineBackendForMixer(params);
 }
 
diff --git a/chromecast/media/cma/backend/video/BUILD.gn b/chromecast/media/cma/backend/video/BUILD.gn
index 1e9a84cfa..8795474 100644
--- a/chromecast/media/cma/backend/video/BUILD.gn
+++ b/chromecast/media/cma/backend/video/BUILD.gn
@@ -20,18 +20,6 @@
   ]
 }
 
-cast_source_set("task_runner_lifetime_handler") {
-  sources = [
-    "task_runner_lifetime_handler.cc",
-    "task_runner_lifetime_handler.h",
-  ]
-  deps = [
-    "//base",
-    "//chromecast/base",
-    "//chromecast/public",
-  ]
-}
-
 cast_source_set("cast_media_shlib_common") {
   sources = [
     "cast_media_shlib_common.cc",
@@ -48,7 +36,6 @@
   ]
   deps = [
     ":cast_media_shlib_common",
-    ":task_runner_lifetime_handler",
     "//base",
     "//chromecast/base",
     "//chromecast/media/cma/backend:for_mixer_audio",
@@ -56,11 +43,11 @@
   ]
 }
 
-cast_shared_library("libcast_media_1.0_avsync") {
+cast_source_set("libcast_media_1.0_avsync") {
   deps = [
     ":av_sync_video",
     ":cast_media_shlib_default",
-    "//chromecast/base:base",
+    "//chromecast/base",
     "//chromecast/media/cma/backend:for_mixer_audio",
     "//chromecast/public/media",
     "//media",
diff --git a/chromecast/media/cma/backend/video/cast_media_shlib_common.cc b/chromecast/media/cma/backend/video/cast_media_shlib_common.cc
index ef0c335..392e7f0 100644
--- a/chromecast/media/cma/backend/video/cast_media_shlib_common.cc
+++ b/chromecast/media/cma/backend/video/cast_media_shlib_common.cc
@@ -2,16 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "base/at_exit.h"
 #include "chromecast/public/media/media_capabilities_shlib.h"
 
 namespace chromecast {
 namespace media {
 
-namespace {
-base::AtExitManager g_at_exit_manager;
-}  // namespace
-
 bool MediaCapabilitiesShlib::IsSupportedAudioConfig(const AudioConfig& config) {
   switch (config.codec) {
     case kCodecPCM:
diff --git a/chromecast/media/cma/backend/video/cast_media_shlib_default.cc b/chromecast/media/cma/backend/video/cast_media_shlib_default.cc
index 8082c36..90601747 100644
--- a/chromecast/media/cma/backend/video/cast_media_shlib_default.cc
+++ b/chromecast/media/cma/backend/video/cast_media_shlib_default.cc
@@ -4,27 +4,16 @@
 
 #include "chromecast/public/cast_media_shlib.h"
 
-#include "base/single_thread_task_runner.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "chromecast/base/task_runner_impl.h"
 #include "chromecast/media/cma/backend/media_pipeline_backend_for_mixer.h"
-#include "chromecast/media/cma/backend/video/task_runner_lifetime_handler.h"
 
 namespace chromecast {
 namespace media {
 
 MediaPipelineBackend* CastMediaShlib::CreateMediaPipelineBackend(
     const MediaPipelineDeviceParams& params) {
-  TaskRunnerLifetimeHandler::SetTaskRunnerHandle(
-      static_cast<TaskRunnerImpl*>(params.task_runner));
-
   return new MediaPipelineBackendForMixer(params);
 }
 
-void CastMediaShlib::Finalize() {
-  TaskRunnerLifetimeHandler::ResetTaskRunnerHandle();
-}
-
 bool CastMediaShlib::SupportsMediaClockRateChange() {
   return false;
 }
diff --git a/chromecast/media/cma/backend/video/task_runner_lifetime_handler.cc b/chromecast/media/cma/backend/video/task_runner_lifetime_handler.cc
deleted file mode 100644
index 08e9259..0000000
--- a/chromecast/media/cma/backend/video/task_runner_lifetime_handler.cc
+++ /dev/null
@@ -1,39 +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.
-
-#include "chromecast/media/cma/backend/video/task_runner_lifetime_handler.h"
-
-#include "base/logging.h"
-#include "base/single_thread_task_runner.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "chromecast/base/task_runner_impl.h"
-
-namespace chromecast {
-namespace media {
-
-std::unique_ptr<base::ThreadTaskRunnerHandle> g_thread_task_runner_handle;
-
-// static
-void TaskRunnerLifetimeHandler::SetTaskRunnerHandle(
-    const TaskRunnerImpl* task_runner_in) {
-  // Set up the static reference in base::ThreadTaskRunnerHandle::Get
-  // for the media thread in this shared library.  We can extract the
-  // SingleThreadTaskRunner passed in from cast_shell for this.
-  if (!base::ThreadTaskRunnerHandle::IsSet()) {
-    DCHECK(!g_thread_task_runner_handle);
-    const scoped_refptr<base::SingleThreadTaskRunner> task_runner =
-        task_runner_in->runner();
-    DCHECK(task_runner->BelongsToCurrentThread());
-    g_thread_task_runner_handle.reset(
-        new base::ThreadTaskRunnerHandle(task_runner));
-  }
-}
-
-// static
-void TaskRunnerLifetimeHandler::ResetTaskRunnerHandle() {
-  g_thread_task_runner_handle.reset();
-}
-
-}  // namespace media
-}  // namespace chromecast
diff --git a/chromecast/media/cma/backend/video/task_runner_lifetime_handler.h b/chromecast/media/cma/backend/video/task_runner_lifetime_handler.h
deleted file mode 100644
index e2f3a26..0000000
--- a/chromecast/media/cma/backend/video/task_runner_lifetime_handler.h
+++ /dev/null
@@ -1,29 +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 CHROMECAST_MEDIA_CMA_BACKEND_VIDEO_TASK_RUNNER_LIFETIME_HANDLER_H_
-#define CHROMECAST_MEDIA_CMA_BACKEND_VIDEO_TASK_RUNNER_LIFETIME_HANDLER_H_
-
-namespace chromecast {
-class TaskRunnerImpl;
-
-namespace media {
-
-// This helper class is used to handle the lifetime of a task runner that needs
-// to stay alive for a set amount of time while other compontents are using it.
-class TaskRunnerLifetimeHandler {
- public:
-  // Sets the task runner whose lifetime this class will handle.
-  // TaskRunnerLifetimeHandler will create a handle to this task_runner, and
-  // keep the handle alive until ResetTaskRunnerHandle is called.
-  static void SetTaskRunnerHandle(const TaskRunnerImpl* task_runner_in);
-
-  // Resets the task runner handle.
-  static void ResetTaskRunnerHandle();
-};
-
-}  // namespace media
-}  // namespace chromecast
-
-#endif  // CHROMECAST_MEDIA_CMA_BACKEND_VIDEO_TASK_RUNNER_LIFETIME_HANDLER_H_
diff --git a/chromeos/components/sync_wifi/network_identifier.cc b/chromeos/components/sync_wifi/network_identifier.cc
index f9d1314..a6dcb4c4 100644
--- a/chromeos/components/sync_wifi/network_identifier.cc
+++ b/chromeos/components/sync_wifi/network_identifier.cc
@@ -11,6 +11,10 @@
 #include "components/sync/protocol/model_type_state.pb.h"
 #include "third_party/cros_system_api/dbus/shill/dbus-constants.h"
 
+namespace chromeos {
+
+namespace sync_wifi {
+
 namespace {
 
 const char kDelimeter[] = "_";
@@ -30,8 +34,6 @@
 
 }  // namespace
 
-namespace sync_wifi {
-
 // static
 NetworkIdentifier NetworkIdentifier::FromProto(
     const sync_pb::WifiConfigurationSpecificsData& specifics) {
@@ -76,3 +78,5 @@
 }
 
 }  // namespace sync_wifi
+
+}  // namespace chromeos
diff --git a/chromeos/components/sync_wifi/network_identifier.h b/chromeos/components/sync_wifi/network_identifier.h
index 8a9813d..3d0cc96 100644
--- a/chromeos/components/sync_wifi/network_identifier.h
+++ b/chromeos/components/sync_wifi/network_identifier.h
@@ -7,14 +7,14 @@
 
 #include <string>
 
-namespace chromeos {
-class NetworkState;
-}
-
 namespace sync_pb {
 class WifiConfigurationSpecificsData;
 }
 
+namespace chromeos {
+
+class NetworkState;
+
 namespace sync_wifi {
 
 // A unique identifier for synced networks which contains the properties
@@ -55,4 +55,6 @@
 
 }  // namespace sync_wifi
 
+}  // namespace chromeos
+
 #endif  // CHROMEOS_COMPONENTS_SYNC_WIFI_NETWORK_IDENTIFIER_H_
diff --git a/chromeos/components/sync_wifi/network_identifier_unittest.cc b/chromeos/components/sync_wifi/network_identifier_unittest.cc
index 5cd9411..aef500bd 100644
--- a/chromeos/components/sync_wifi/network_identifier_unittest.cc
+++ b/chromeos/components/sync_wifi/network_identifier_unittest.cc
@@ -13,6 +13,8 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/cros_system_api/dbus/shill/dbus-constants.h"
 
+namespace chromeos {
+
 namespace sync_wifi {
 
 namespace {
@@ -65,3 +67,5 @@
 }
 
 }  // namespace sync_wifi
+
+}  // namespace chromeos
diff --git a/chromeos/components/sync_wifi/pending_network_configuration_tracker.h b/chromeos/components/sync_wifi/pending_network_configuration_tracker.h
index 76113a4..2268387b 100644
--- a/chromeos/components/sync_wifi/pending_network_configuration_tracker.h
+++ b/chromeos/components/sync_wifi/pending_network_configuration_tracker.h
@@ -12,6 +12,8 @@
 #include "chromeos/components/sync_wifi/pending_network_configuration_update.h"
 #include "components/sync/protocol/wifi_configuration_specifics.pb.h"
 
+namespace chromeos {
+
 namespace sync_wifi {
 
 class NetworkIdentifier;
@@ -56,4 +58,6 @@
 
 }  // namespace sync_wifi
 
+}  // namespace chromeos
+
 #endif  // CHROMEOS_COMPONENTS_SYNC_WIFI_PENDING_NETWORK_CONFIGURATION_TRACKER_H_
diff --git a/chromeos/components/sync_wifi/pending_network_configuration_tracker_impl.cc b/chromeos/components/sync_wifi/pending_network_configuration_tracker_impl.cc
index 8ca38c66..b54f1d4 100644
--- a/chromeos/components/sync_wifi/pending_network_configuration_tracker_impl.cc
+++ b/chromeos/components/sync_wifi/pending_network_configuration_tracker_impl.cc
@@ -10,6 +10,10 @@
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/pref_service.h"
 
+namespace chromeos {
+
+namespace sync_wifi {
+
 namespace {
 
 const char kPendingNetworkConfigurationsPref[] =
@@ -18,15 +22,15 @@
 const char kCompletedAttemptsKey[] = "CompletedAttempts";
 const char kSpecificsKey[] = "Specifics";
 
-std::string GeneratePath(const sync_wifi::NetworkIdentifier& id,
+std::string GeneratePath(const NetworkIdentifier& id,
                          const std::string& subkey) {
   return base::StringPrintf("%s.%s", id.SerializeToString().c_str(),
                             subkey.c_str());
 }
 
-sync_wifi::PendingNetworkConfigurationUpdate ConvertToPendingUpdate(
+PendingNetworkConfigurationUpdate ConvertToPendingUpdate(
     base::Value* dict,
-    const sync_wifi::NetworkIdentifier& id) {
+    const NetworkIdentifier& id) {
   std::string* change_guid = dict->FindStringKey(kChangeGuidKey);
   base::Optional<sync_pb::WifiConfigurationSpecificsData> specifics;
   std::string* specifics_string = dict->FindStringKey(kSpecificsKey);
@@ -41,14 +45,12 @@
   DCHECK(change_guid);
   DCHECK(completed_attempts);
 
-  return sync_wifi::PendingNetworkConfigurationUpdate(
-      id, *change_guid, specifics, completed_attempts.value());
+  return PendingNetworkConfigurationUpdate(id, *change_guid, specifics,
+                                           completed_attempts.value());
 }
 
 }  // namespace
 
-namespace sync_wifi {
-
 // static
 void PendingNetworkConfigurationTrackerImpl::RegisterProfilePrefs(
     PrefRegistrySimple* registry) {
@@ -122,3 +124,5 @@
 }
 
 }  // namespace sync_wifi
+
+}  // namespace chromeos
diff --git a/chromeos/components/sync_wifi/pending_network_configuration_tracker_impl.h b/chromeos/components/sync_wifi/pending_network_configuration_tracker_impl.h
index 0ea876d..ddfbec7 100644
--- a/chromeos/components/sync_wifi/pending_network_configuration_tracker_impl.h
+++ b/chromeos/components/sync_wifi/pending_network_configuration_tracker_impl.h
@@ -12,6 +12,8 @@
 class PrefRegistrySimple;
 class PrefService;
 
+namespace chromeos {
+
 namespace sync_wifi {
 
 // Keeps track of in flight updates to the local network stack and persists
@@ -19,7 +21,7 @@
 class PendingNetworkConfigurationTrackerImpl
     : public PendingNetworkConfigurationTracker {
  public:
-  PendingNetworkConfigurationTrackerImpl(PrefService* pref_service);
+  explicit PendingNetworkConfigurationTrackerImpl(PrefService* pref_service);
   ~PendingNetworkConfigurationTrackerImpl() override;
 
   // Registers preferences used by this class in the provided |registry|.
@@ -49,4 +51,6 @@
 
 }  // namespace sync_wifi
 
+}  // namespace chromeos
+
 #endif  // CHROMEOS_COMPONENTS_SYNC_WIFI_PENDING_NETWORK_CONFIGURATION_TRACKER_IMPL_H_
diff --git a/chromeos/components/sync_wifi/pending_network_configuration_tracker_impl_unittest.cc b/chromeos/components/sync_wifi/pending_network_configuration_tracker_impl_unittest.cc
index 5fc1f68..1af7a02 100644
--- a/chromeos/components/sync_wifi/pending_network_configuration_tracker_impl_unittest.cc
+++ b/chromeos/components/sync_wifi/pending_network_configuration_tracker_impl_unittest.cc
@@ -14,6 +14,10 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/cros_system_api/dbus/shill/dbus-constants.h"
 
+namespace chromeos {
+
+namespace sync_wifi {
+
 namespace {
 
 const char kFredSsid[] = "Fred";
@@ -27,8 +31,6 @@
 
 }  // namespace
 
-namespace sync_wifi {
-
 class PendingNetworkConfigurationTrackerImplTest : public testing::Test {
  public:
   PendingNetworkConfigurationTrackerImplTest() = default;
@@ -198,3 +200,5 @@
 }
 
 }  // namespace sync_wifi
+
+}  // namespace chromeos
diff --git a/chromeos/components/sync_wifi/pending_network_configuration_update.cc b/chromeos/components/sync_wifi/pending_network_configuration_update.cc
index 705843f..010cfe90 100644
--- a/chromeos/components/sync_wifi/pending_network_configuration_update.cc
+++ b/chromeos/components/sync_wifi/pending_network_configuration_update.cc
@@ -4,6 +4,8 @@
 
 #include "chromeos/components/sync_wifi/pending_network_configuration_update.h"
 
+namespace chromeos {
+
 namespace sync_wifi {
 
 PendingNetworkConfigurationUpdate::PendingNetworkConfigurationUpdate(
@@ -27,3 +29,5 @@
 }
 
 }  // namespace sync_wifi
+
+}  // namespace chromeos
diff --git a/chromeos/components/sync_wifi/pending_network_configuration_update.h b/chromeos/components/sync_wifi/pending_network_configuration_update.h
index 446a34c8..062ce1f9 100644
--- a/chromeos/components/sync_wifi/pending_network_configuration_update.h
+++ b/chromeos/components/sync_wifi/pending_network_configuration_update.h
@@ -13,6 +13,8 @@
 #include "chromeos/components/sync_wifi/network_identifier.h"
 #include "components/sync/protocol/wifi_configuration_specifics.pb.h"
 
+namespace chromeos {
+
 namespace sync_wifi {
 
 // Represents a change to the local network stack which hasn't been saved yet,
@@ -55,4 +57,6 @@
 
 }  // namespace sync_wifi
 
+}  // namespace chromeos
+
 #endif  // CHROMEOS_COMPONENTS_SYNC_WIFI_PENDING_NETWORK_CONFIGURATION_UPDATE_H_
diff --git a/chromeos/components/sync_wifi/synced_network_updater.h b/chromeos/components/sync_wifi/synced_network_updater.h
index ee95744..863a4ac 100644
--- a/chromeos/components/sync_wifi/synced_network_updater.h
+++ b/chromeos/components/sync_wifi/synced_network_updater.h
@@ -13,6 +13,8 @@
 class WifiConfigurationSpecificsData;
 }
 
+namespace chromeos {
+
 namespace sync_wifi {
 
 class NetworkIdentifier;
@@ -35,4 +37,6 @@
 
 }  // namespace sync_wifi
 
+}  // namespace chromeos
+
 #endif  // CHROMEOS_COMPONENTS_SYNC_WIFI_SYNCED_NETWORK_UPDATER_H_
diff --git a/chromeos/components/sync_wifi/test_data_generator.cc b/chromeos/components/sync_wifi/test_data_generator.cc
index 5ef91b8..775bda6 100644
--- a/chromeos/components/sync_wifi/test_data_generator.cc
+++ b/chromeos/components/sync_wifi/test_data_generator.cc
@@ -7,6 +7,8 @@
 #include "components/sync/protocol/wifi_configuration_specifics.pb.h"
 #include "third_party/cros_system_api/dbus/shill/dbus-constants.h"
 
+namespace chromeos {
+
 namespace sync_wifi {
 
 NetworkIdentifier GeneratePskNetworkId(const std::string& ssid) {
@@ -42,3 +44,5 @@
 }
 
 }  // namespace sync_wifi
+
+}  // namespace chromeos
diff --git a/chromeos/components/sync_wifi/test_data_generator.h b/chromeos/components/sync_wifi/test_data_generator.h
index 328c2d24..848b08a 100644
--- a/chromeos/components/sync_wifi/test_data_generator.h
+++ b/chromeos/components/sync_wifi/test_data_generator.h
@@ -7,6 +7,8 @@
 
 #include "components/sync/protocol/wifi_configuration_specifics.pb.h"
 
+namespace chromeos {
+
 namespace sync_wifi {
 
 class NetworkIdentifier;
@@ -21,4 +23,6 @@
 
 }  // namespace sync_wifi
 
+}  // namespace chromeos
+
 #endif  // CHROMEOS_COMPONENTS_SYNC_WIFI_TEST_DATA_GENERATOR_H_
diff --git a/chromeos/components/sync_wifi/wifi_configuration_bridge.cc b/chromeos/components/sync_wifi/wifi_configuration_bridge.cc
index 35adc7a..ef284c6 100644
--- a/chromeos/components/sync_wifi/wifi_configuration_bridge.cc
+++ b/chromeos/components/sync_wifi/wifi_configuration_bridge.cc
@@ -22,6 +22,8 @@
 #include "components/sync/model/mutable_data_batch.h"
 #include "components/sync/protocol/model_type_state.pb.h"
 
+namespace chromeos {
+
 namespace sync_wifi {
 
 namespace {
@@ -198,3 +200,5 @@
 }
 
 }  // namespace sync_wifi
+
+}  // namespace chromeos
diff --git a/chromeos/components/sync_wifi/wifi_configuration_bridge.h b/chromeos/components/sync_wifi/wifi_configuration_bridge.h
index 0877aec9..491fa9d 100644
--- a/chromeos/components/sync_wifi/wifi_configuration_bridge.h
+++ b/chromeos/components/sync_wifi/wifi_configuration_bridge.h
@@ -23,6 +23,8 @@
 class ModelTypeChangeProcessor;
 }  // namespace syncer
 
+namespace chromeos {
+
 namespace sync_wifi {
 
 // Receives updates to network configurations from the Chrome sync back end and
@@ -85,4 +87,6 @@
 
 }  // namespace sync_wifi
 
+}  // namespace chromeos
+
 #endif  // CHROMEOS_COMPONENTS_SYNC_WIFI_WIFI_CONFIGURATION_BRIDGE_H_
diff --git a/chromeos/components/sync_wifi/wifi_configuration_bridge_unittest.cc b/chromeos/components/sync_wifi/wifi_configuration_bridge_unittest.cc
index 6f6d707..f263deb 100644
--- a/chromeos/components/sync_wifi/wifi_configuration_bridge_unittest.cc
+++ b/chromeos/components/sync_wifi/wifi_configuration_bridge_unittest.cc
@@ -24,6 +24,8 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+namespace chromeos {
+
 namespace sync_wifi {
 
 namespace {
@@ -266,3 +268,5 @@
 }  // namespace
 
 }  // namespace sync_wifi
+
+}  // namespace chromeos
diff --git a/chromeos/components/sync_wifi/wifi_configuration_sync_service.cc b/chromeos/components/sync_wifi/wifi_configuration_sync_service.cc
index ea2a1f0..f11023d6 100644
--- a/chromeos/components/sync_wifi/wifi_configuration_sync_service.cc
+++ b/chromeos/components/sync_wifi/wifi_configuration_sync_service.cc
@@ -13,6 +13,8 @@
 #include "components/sync/model/model_type_store.h"
 #include "components/sync/model_impl/client_tag_based_model_type_processor.h"
 
+namespace chromeos {
+
 namespace sync_wifi {
 
 WifiConfigurationSyncService::WifiConfigurationSyncService(
@@ -33,4 +35,6 @@
   return bridge_->change_processor()->GetControllerDelegate();
 }
 
-}  // namespace sync_wifi
\ No newline at end of file
+}  // namespace sync_wifi
+
+}  // namespace chromeos
diff --git a/chromeos/components/sync_wifi/wifi_configuration_sync_service.h b/chromeos/components/sync_wifi/wifi_configuration_sync_service.h
index a3b2c671c..6fd2818 100644
--- a/chromeos/components/sync_wifi/wifi_configuration_sync_service.h
+++ b/chromeos/components/sync_wifi/wifi_configuration_sync_service.h
@@ -18,6 +18,8 @@
 class ModelTypeControllerDelegate;
 }  // namespace syncer
 
+namespace chromeos {
+
 namespace sync_wifi {
 
 class WifiConfigurationBridge;
@@ -41,4 +43,6 @@
 
 }  // namespace sync_wifi
 
+}  // namespace chromeos
+
 #endif  // CHROMEOS_COMPONENTS_SYNC_WIFI_WIFI_CONFIGURATION_SYNC_SERVICE_H_
diff --git a/chromeos/dbus/update_engine_client.cc b/chromeos/dbus/update_engine_client.cc
index 17482bd..55e9b8a 100644
--- a/chromeos/dbus/update_engine_client.cc
+++ b/chromeos/dbus/update_engine_client.cc
@@ -462,7 +462,7 @@
     VLOG(1) << "Eol date received: " << status.eol_date();
 
     EolInfo eol_info;
-    if (status.eol_date() > -1) {
+    if (status.eol_date() > 0) {
       eol_info.eol_date = base::Time::UnixEpoch() +
                           base::TimeDelta::FromDays(status.eol_date());
     }
diff --git a/chromeos/services/assistant/assistant_manager_service_impl.cc b/chromeos/services/assistant/assistant_manager_service_impl.cc
index 5738c94..b1debae 100644
--- a/chromeos/services/assistant/assistant_manager_service_impl.cc
+++ b/chromeos/services/assistant/assistant_manager_service_impl.cc
@@ -1198,7 +1198,8 @@
         assistant_client::InternalOptions::UserCredentialMode::SIGNED_OUT);
   }
 
-  internal_options->EnableRequireVoiceMatchVerification();
+  if (!features::IsVoiceMatchDisabled())
+    internal_options->EnableRequireVoiceMatchVerification();
 
   assistant_manager_internal->SetOptions(*internal_options, [](bool success) {
     DVLOG(2) << "set options: " << success;
diff --git a/chromeos/services/assistant/assistant_settings_manager_impl.cc b/chromeos/services/assistant/assistant_settings_manager_impl.cc
index 3471ace4..a5b4467 100644
--- a/chromeos/services/assistant/assistant_settings_manager_impl.cc
+++ b/chromeos/services/assistant/assistant_settings_manager_impl.cc
@@ -176,7 +176,8 @@
   DCHECK(main_task_runner()->RunsTasksInCurrentSequence());
 
   if (assistant_state()->allowed_state() !=
-      ash::mojom::AssistantAllowedState::ALLOWED) {
+          ash::mojom::AssistantAllowedState::ALLOWED ||
+      features::IsVoiceMatchDisabled()) {
     return;
   }
 
diff --git a/chromeos/services/assistant/public/features.cc b/chromeos/services/assistant/public/features.cc
index 588eee7a..ac8437e 100644
--- a/chromeos/services/assistant/public/features.cc
+++ b/chromeos/services/assistant/public/features.cc
@@ -72,6 +72,10 @@
     "AssistantEnableMediaSessionIntegration",
     base::FEATURE_DISABLED_BY_DEFAULT};
 
+// Disable voice match for test purpose.
+const base::Feature kDisableVoiceMatch{"DisableVoiceMatch",
+                                       base::FEATURE_DISABLED_BY_DEFAULT};
+
 int GetProactiveSuggestionsMaxWidth() {
   return kAssistantProactiveSuggestionsMaxWidth.Get();
 }
@@ -144,6 +148,10 @@
   return base::FeatureList::IsEnabled(kAssistantWarmerWelcomeFeature);
 }
 
+bool IsVoiceMatchDisabled() {
+  return base::FeatureList::IsEnabled(kDisableVoiceMatch);
+}
+
 }  // namespace features
 }  // namespace assistant
 }  // namespace chromeos
diff --git a/chromeos/services/assistant/public/features.h b/chromeos/services/assistant/public/features.h
index 5a37623..b5fce1e 100644
--- a/chromeos/services/assistant/public/features.h
+++ b/chromeos/services/assistant/public/features.h
@@ -127,6 +127,9 @@
 
 COMPONENT_EXPORT(ASSISTANT_SERVICE_PUBLIC) bool IsWarmerWelcomeEnabled();
 
+COMPONENT_EXPORT(ASSISTANT_SERVICE_PUBLIC)
+bool IsVoiceMatchDisabled();
+
 }  // namespace features
 }  // namespace assistant
 }  // namespace chromeos
diff --git a/components/certificate_transparency/data/log_list.json b/components/certificate_transparency/data/log_list.json
index 46df1d8..8b3da9b6 100644
--- a/components/certificate_transparency/data/log_list.json
+++ b/components/certificate_transparency/data/log_list.json
@@ -11,7 +11,6 @@
           "log_id": "Y/Lbzeg7zCzPC3KEJ1drM6SNYXePvXWmOLHHaFRL2I0=",
           "key": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEI3MQm+HzXvaYa2mVlhB4zknbtAT8cSxakmBoJcBKGqGwYS0bhxSpuvABM1kdBTDpQhXnVdcq+LSiukXJRpGHVg==",
           "url": "https://ct.googleapis.com/logs/argon2019/",
-          "dns": "argon2019.ct.googleapis.com",
           "mmd": 86400,
           "state": {
             "usable": {
@@ -28,7 +27,6 @@
           "log_id": "sh4FzIuizYogTodm+Su5iiUgZ2va+nDnsklTLe+LkF4=",
           "key": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE6Tx2p1yKY4015NyIYvdrk36es0uAc1zA4PQ+TGRY+3ZjUTIYY9Wyu+3q/147JG4vNVKLtDWarZwVqGkg6lAYzA==",
           "url": "https://ct.googleapis.com/logs/argon2020/",
-          "dns": "argon2020.ct.googleapis.com",
           "mmd": 86400,
           "state": {
             "usable": {
@@ -45,7 +43,6 @@
           "log_id": "9lyUL9F3MCIUVBgIMJRWjuNNExkzv98MLyALzE7xZOM=",
           "key": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAETeBmZOrzZKo4xYktx9gI2chEce3cw/tbr5xkoQlmhB18aKfsxD+MnILgGNl0FOm0eYGilFVi85wLRIOhK8lxKw==",
           "url": "https://ct.googleapis.com/logs/argon2021/",
-          "dns": "argon2021.ct.googleapis.com",
           "mmd": 86400,
           "state": {
             "usable": {
@@ -62,7 +59,6 @@
           "log_id": "KXm+8J45OSHwVnOfY6V35b5XfZxgCvj5TV0mXCVdx4Q=",
           "key": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEeIPc6fGmuBg6AJkv/z7NFckmHvf/OqmjchZJ6wm2qN200keRDg352dWpi7CHnSV51BpQYAj1CQY5JuRAwrrDwg==",
           "url": "https://ct.googleapis.com/logs/argon2022/",
-          "dns": "argon2022.ct.googleapis.com",
           "mmd": 86400,
           "state": {
             "qualified": {
@@ -79,7 +75,6 @@
           "log_id": "6D7Q2j71BjUy51covIlryQPTy9ERa+zraeF3fW0GvW4=",
           "key": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE0JCPZFJOQqyEti5M8j13ALN3CAVHqkVM4yyOcKWCu2yye5yYeqDpEXYoALIgtM3TmHtNlifmt+4iatGwLpF3eA==",
           "url": "https://ct.googleapis.com/logs/argon2023/",
-          "dns": "argon2023.ct.googleapis.com",
           "mmd": 86400,
           "state": {
             "qualified": {
@@ -96,7 +91,6 @@
           "log_id": "CEEUmABxUywWGQRgvPxH/cJlOvopLHKzf/hjrinMyfA=",
           "key": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE/XyDwqzXL9i2GTjMYkqaEyiRL0Dy9sHq/BTebFdshbvCaXXEh6mjUK0Yy+AsDcI4MpzF1l7Kded2MD5zi420gA==",
           "url": "https://ct.googleapis.com/logs/xenon2019/",
-          "dns": "xenon2019.ct.googleapis.com",
           "mmd": 86400,
           "state": {
             "usable": {
@@ -113,7 +107,6 @@
           "log_id": "B7dcG+V9aP/xsMYdIxXHuuZXfFeUt2ruvGE6GmnTohw=",
           "key": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEZU75VqjyzSTgFZKAnWg1QeYfFFIRZTMK7q3kWWZsmHhQdrBYnHRZ3OA4kUeUx0JN+xX+dSgt1ruqUhhl7jOvmw==",
           "url": "https://ct.googleapis.com/logs/xenon2020/",
-          "dns": "xenon2020.ct.googleapis.com",
           "mmd": 86400,
           "state": {
             "usable": {
@@ -130,7 +123,6 @@
           "log_id": "fT7y+I//iFVoJMLAyp5SiXkrxQ54CX8uapdomX4i8Nc=",
           "key": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAER+1MInu8Q39BwDZ5Rp9TwXhwm3ktvgJzpk/r7dDgGk7ZacMm3ljfcoIvP1E72T8jvyLT1bvdapylajZcTH6W5g==",
           "url": "https://ct.googleapis.com/logs/xenon2021/",
-          "dns": "xenon2021.ct.googleapis.com",
           "mmd": 86400,
           "state": {
             "usable": {
@@ -147,7 +139,6 @@
           "log_id": "RqVV63X6kSAwtaKJafTzfREsQXS+/Um4havy/HD+bUc=",
           "key": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE+WS9FSxAYlCVEzg8xyGwOrmPonoV14nWjjETAIdZvLvukPzIWBMKv6tDNlQjpIHNrUcUt1igRPpqoKDXw2MeKw==",
           "url": "https://ct.googleapis.com/logs/xenon2022/",
-          "dns": "xenon2022.ct.googleapis.com",
           "mmd": 86400,
           "state": {
             "usable": {
@@ -164,7 +155,6 @@
           "log_id": "rfe++nz/EMiLnT2cHj4YarRnKV3PsQwkyoWGNOvcgoo=",
           "key": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEchY+C+/vzj5g3ZXLY3q5qY1Kb2zcYYCmRV4vg6yU84WI0KV00HuO/8XuQqLwLZPjwtCymeLhQunSxgAnaXSuzg==",
           "url": "https://ct.googleapis.com/logs/xenon2023/",
-          "dns": "xenon2023.ct.googleapis.com",
           "mmd": 86400,
           "state": {
             "qualified": {
@@ -181,7 +171,6 @@
           "log_id": "aPaY+B9kgr46jO65KB1M/HFRXWeT1ETRCmesu09P+8Q=",
           "key": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE1/TMabLkDpCjiupacAlP7xNi0I1JYP8bQFAHDG1xhtolSY1l4QgNRzRrvSe8liE+NPWHdjGxfx3JhTsN9x8/6Q==",
           "url": "https://ct.googleapis.com/aviator/",
-          "dns": "aviator.ct.googleapis.com",
           "mmd": 86400,
           "state": {
             "readonly": {
@@ -198,7 +187,6 @@
           "log_id": "KTxRllTIOWW6qlD8WAfUt2+/WHopctykwwz05UVH9Hg=",
           "key": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAETtK8v7MICve56qTHHDhhBOuV4IlUaESxZryCfk9QbG9co/CqPvTsgPDbCpp6oFtyAHwlDhnvr7JijXRD9Cb2FA==",
           "url": "https://ct.googleapis.com/icarus/",
-          "dns": "icarus.ct.googleapis.com",
           "mmd": 86400,
           "state": {
             "usable": {
@@ -211,7 +199,6 @@
           "log_id": "pLkJkLQYWBSHuxOizGdwCjw1mAT5G9+443fNDsgN3BA=",
           "key": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEfahLEimAoz2t01p3uMziiLOl/fHTDM0YDOhBRuiBARsV4UvxG2LdNgoIGLrtCzWE0J5APC2em4JlvR8EEEFMoA==",
           "url": "https://ct.googleapis.com/pilot/",
-          "dns": "pilot.ct.googleapis.com",
           "mmd": 86400,
           "state": {
             "usable": {
@@ -224,7 +211,6 @@
           "log_id": "7ku9t3XOYLrhQmkfq+GeZqMPfl+wctiDAMR7iXqo/cs=",
           "key": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEIFsYyDzBi7MxCAC/oJBXK7dHjG+1aLCOkHjpoHPqTyghLpzA9BYbqvnV16mAw04vUjyYASVGJCUoI3ctBcJAeg==",
           "url": "https://ct.googleapis.com/rocketeer/",
-          "dns": "rocketeer.ct.googleapis.com",
           "mmd": 86400,
           "state": {
             "usable": {
@@ -237,7 +223,6 @@
           "log_id": "u9nfvB+KcbWTlCOXqpJ7RzhXlQqrUugakJZkNo4e0YU=",
           "key": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEEmyGDvYXsRJsNyXSrYc9DjHsIa2xzb4UR7ZxVoV6mrc9iZB7xjI6+NrOiwH+P/xxkRmOFG6Jel20q37hTh58rA==",
           "url": "https://ct.googleapis.com/skydiver/",
-          "dns": "skydiver.ct.googleapis.com",
           "mmd": 86400,
           "state": {
             "usable": {
@@ -263,7 +248,6 @@
           "log_id": "dH7agzGtMxCRIZzOJU9CcMK//V5CIAjGNzV55hB7zFY=",
           "key": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEkZHz1v5r8a9LmXSMegYZAg4UW+Ug56GtNfJTDNFZuubEJYgWf4FcC5D+ZkYwttXTDSo4OkanG9b3AI4swIQ28g==",
           "url": "https://ct.cloudflare.com/logs/nimbus2019/",
-          "dns": "cloudflare-nimbus2019.ct.googleapis.com",
           "mmd": 86400,
           "state": {
             "usable": {
@@ -280,7 +264,6 @@
           "log_id": "Xqdz+d9WwOe1Nkh90EngMnqRmgyEoRIShBh1loFxRVg=",
           "key": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE01EAhx4o0zPQrXTcYjgCt4MVFsT0Pwjzb1RwrM0lhWDlxAYPP6/gyMCXNkOn/7KFsjL7rwk78tHMpY8rXn8AYg==",
           "url": "https://ct.cloudflare.com/logs/nimbus2020/",
-          "dns": "cloudflare-nimbus2020.ct.googleapis.com",
           "mmd": 86400,
           "state": {
             "usable": {
@@ -297,7 +280,6 @@
           "log_id": "RJRlLrDuzq/EQAfYqP4owNrmgr7YyzG1P9MzlrW2gag=",
           "key": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAExpon7ipsqehIeU1bmpog9TFo4Pk8+9oN8OYHl1Q2JGVXnkVFnuuvPgSo2Ep+6vLffNLcmEbxOucz03sFiematg==",
           "url": "https://ct.cloudflare.com/logs/nimbus2021/",
-          "dns": "cloudflare-nimbus2021.ct.googleapis.com",
           "mmd": 86400,
           "state": {
             "usable": {
@@ -314,7 +296,6 @@
           "log_id": "QcjKsd8iRkoQxqE6CUKHXk4xixsD6+tLx2jwkGKWBvY=",
           "key": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAESLJHTlAycmJKDQxIv60pZG8g33lSYxYpCi5gteI6HLevWbFVCdtZx+m9b+0LrwWWl/87mkNN6xE0M4rnrIPA/w==",
           "url": "https://ct.cloudflare.com/logs/nimbus2022/",
-          "dns": "cloudflare-nimbus2022.ct.googleapis.com",
           "mmd": 86400,
           "state": {
             "qualified": {
@@ -331,7 +312,6 @@
           "log_id": "ejKMVNi3LbYg6jjgUh7phBZwMhOFTTvSK8E6V6NS61I=",
           "key": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEi/8tkhjLRp0SXrlZdTzNkTd6HqmcmXiDJz3fAdWLgOhjmv4mohvRhwXul9bgW0ODgRwC9UGAgH/vpGHPvIS1qA==",
           "url": "https://ct.cloudflare.com/logs/nimbus2023/",
-          "dns": "cloudflare-nimbus2023.ct.googleapis.com",
           "mmd": 86400,
           "state": {
             "qualified": {
@@ -356,7 +336,6 @@
           "log_id": "VhQGmi/XwuzT9eG9RLI+x0Z2ubyZEVzA75SYVdaJ0N0=",
           "key": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEAkbFvhu7gkAW6MHSrBlpE1n4+HCFRkC5OLAjgqhkTH+/uzSfSl8ois8ZxAD2NgaTZe1M9akhYlrYkes4JECs6A==",
           "url": "https://ct1.digicert-ct.com/log/",
-          "dns": "digicert.ct.googleapis.com",
           "mmd": 86400,
           "state": {
             "usable": {
@@ -369,7 +348,6 @@
           "log_id": "h3W/51l8+IxDmV+9827/Vo1HVjb/SrVgwbTq/16ggw8=",
           "key": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEzF05L2a4TH/BLgOhNKPoioYCrkoRxvcmajeb8Dj4XQmNY+gxa4Zmz3mzJTwe33i0qMVp+rfwgnliQ/bM/oFmhA==",
           "url": "https://ct2.digicert-ct.com/log/",
-          "dns": "digicert2.ct.googleapis.com",
           "mmd": 86400,
           "state": {
             "usable": {
@@ -382,7 +360,6 @@
           "log_id": "4mlLribo6UAJ6IYbtjuD1D7n/nSI+6SPKJMBnd3x2/4=",
           "key": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEkZd/ow8X+FSVWAVSf8xzkFohcPph/x6pS1JHh7g1wnCZ5y/8Hk6jzJxs6t3YMAWz2CPd4VkCdxwKexGhcFxD9A==",
           "url": "https://yeti2019.ct.digicert.com/log/",
-          "dns": "digicert-yeti2019.ct.googleapis.com",
           "mmd": 86400,
           "state": {
             "usable": {
@@ -399,7 +376,6 @@
           "log_id": "8JWkWfIA0YJAEC0vk4iOrUv+HUfjmeHQNKawqKqOsnM=",
           "key": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEURAG+Zo0ac3n37ifZKUhBFEV6jfcCzGIRz3tsq8Ca9BP/5XUHy6ZiqsPaAEbVM0uI3Tm9U24RVBHR9JxDElPmg==",
           "url": "https://yeti2020.ct.digicert.com/log/",
-          "dns": "digicert-yeti2020.ct.googleapis.com",
           "mmd": 86400,
           "state": {
             "usable": {
@@ -416,7 +392,6 @@
           "log_id": "XNxDkv7mq0VEsV6a1FbmEDf71fpH3KFzlLJe5vbHDso=",
           "key": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE6J4EbcpIAl1+AkSRsbhoY5oRTj3VoFfaf1DlQkfi7Rbe/HcjfVtrwN8jaC+tQDGjF+dqvKhWJAQ6Q6ev6q9Mew==",
           "url": "https://yeti2021.ct.digicert.com/log/",
-          "dns": "digicert-yeti2021.ct.googleapis.com",
           "mmd": 86400,
           "state": {
             "usable": {
@@ -433,7 +408,6 @@
           "log_id": "IkVFB1lVJFaWP6Ev8fdthuAjJmOtwEt/XcaDXG7iDwI=",
           "key": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEn/jYHd77W1G1+131td5mEbCdX/1v/KiYW5hPLcOROvv+xA8Nw2BDjB7y+RGyutD2vKXStp/5XIeiffzUfdYTJg==",
           "url": "https://yeti2022.ct.digicert.com/log/",
-          "dns": "digicert-yeti2022.ct.googleapis.com",
           "mmd": 86400,
           "state": {
             "usable": {
@@ -450,7 +424,6 @@
           "log_id": "Nc8ZG7+xbFe/D61MbULLu7YnICZR6j/hKu+oA8M71kw=",
           "key": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEfQ0DsdWYitzwFTvG3F4Nbj8Nv5XIVYzQpkyWsU4nuSYlmcwrAp6m092fsdXEw6w1BAeHlzaqrSgNfyvZaJ9y0Q==",
           "url": "https://yeti2023.ct.digicert.com/log/",
-          "dns": "digicert-yeti2023.ct.googleapis.com",
           "mmd": 86400,
           "state": {
             "qualified": {
@@ -467,7 +440,6 @@
           "log_id": "/kRhCLHQGreKYsz+q2qysrq/86va2ApNizDfLQAIgww=",
           "key": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEX+0nudCKImd7QCtelhMrDW0OXni5RE10tiiClZesmrwUk2iHLCoTHHVV+yg5D4n/rxCRVyRhikPpVDOLMLxJaA==",
           "url": "https://nessie2019.ct.digicert.com/log/",
-          "dns": "digicert-nessie2019.ct.googleapis.com",
           "mmd": 86400,
           "state": {
             "usable": {
@@ -484,7 +456,6 @@
           "log_id": "xlKg7EjOs/yrFwmSxDqHQTMJ6ABlomJSQBujNioXxWU=",
           "key": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE4hHIyMVIrR9oShgbQMYEk8WX1lmkfFKB448Gn93KbsZnnwljDHY6MQqEnWfKGgMOq0gh3QK48c5ZB3UKSIFZ4g==",
           "url": "https://nessie2020.ct.digicert.com/log/",
-          "dns": "digicert-nessie2020.ct.googleapis.com",
           "mmd": 86400,
           "state": {
             "usable": {
@@ -501,7 +472,6 @@
           "log_id": "7sCV7o1yZA+S48O5G8cSo2lqCXtLahoUOOZHssvtxfk=",
           "key": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE9o7AiwrbGBIX6Lnc47I6OfLMdZnRzKoP5u072nBi6vpIOEooktTi1gNwlRPzGC2ySGfuc1xLDeaA/wSFGgpYFg==",
           "url": "https://nessie2021.ct.digicert.com/log/",
-          "dns": "digicert-nessie2021.ct.googleapis.com",
           "mmd": 86400,
           "state": {
             "usable": {
@@ -518,7 +488,6 @@
           "log_id": "UaOw9f0BeZxWbbg3eI8MpHrMGyfL956IQpoN/tSLBeU=",
           "key": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEJyTdaAMoy/5jvg4RR019F2ihEV1McclBKMe2okuX7MCv/C87v+nxsfz1Af+p+0lADGMkmNd5LqZVqxbGvlHYcQ==",
           "url": "https://nessie2022.ct.digicert.com/log/",
-          "dns": "digicert-nessie2022.ct.googleapis.com",
           "mmd": 86400,
           "state": {
             "usable": {
@@ -535,7 +504,6 @@
           "log_id": "s3N3B+GEUPhjhtYFqdwRCUp5LbFnDAuH3PADDnk2pZo=",
           "key": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEEXu8iQwSCRSf2CbITGpUpBtFVt8+I0IU0d1C36Lfe1+fbwdaI0Z5FktfM2fBoI1bXBd18k2ggKGYGgdZBgLKTg==",
           "url": "https://nessie2023.ct.digicert.com/log/",
-          "dns": "digicert-nessie2023.ct.googleapis.com",
           "mmd": 86400,
           "state": {
             "qualified": {
@@ -552,7 +520,6 @@
           "log_id": "3esdK3oNT6Ygi4GtgWhwfi6OnQHVXIiNPRHEzbbsvsw=",
           "key": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEluqsHEYMG1XcDfy1lCdGV0JwOmkY4r87xNuroPS2bMBTP01CEDPwWJePa75y9CrsHEKqAy8afig1dpkIPSEUhg==",
           "url": "https://ct.ws.symantec.com/",
-          "dns": "symantec.ct.googleapis.com",
           "mmd": 86400,
           "state": {
             "retired": {
@@ -565,7 +532,6 @@
           "log_id": "vHjh38X2PGhGSTNNoQ+hXwl5aSAJwIG08/aRfz7ZuKU=",
           "key": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE6pWeAv/u8TNtS4e8zf0ZF2L/lNPQWQc/Ai0ckP7IRzA78d0NuBEMXR2G3avTK0Zm+25ltzv9WWis36b4ztIYTQ==",
           "url": "https://vega.ws.symantec.com/",
-          "dns": "symantec-vega.ct.googleapis.com",
           "mmd": 86400,
           "state": {
             "retired": {
@@ -578,7 +544,6 @@
           "log_id": "FZcEiNe5l6Bb61JRKt7o0ui0oxZSZBIan6v71fha2T8=",
           "key": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEowJkhCK7JewN47zCyYl93UXQ7uYVhY/Z5xcbE4Dq7bKFN61qxdglnfr0tPNuFiglN+qjN2Syxwv9UeXBBfQOtQ==",
           "url": "https://sirius.ws.symantec.com/",
-          "dns": "symantec-sirius.ct.googleapis.com",
           "mmd": 86400,
           "state": {
             "retired": {
@@ -671,7 +636,6 @@
           "log_id": "AwGd8/2FppqOvR+sxtqbpz5Gl3T+d/V5/FoIuDKMHWs=",
           "key": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEjicnerZVCXTrbEuUhGW85BXx6lrYfA43zro/bAna5ymW00VQb94etBzSg4j/KS/Oqf/fNN51D8DMGA2ULvw3AQ==",
           "url": "https://ctlog-gen2.api.venafi.com/",
-          "dns": "venafi2.ct.googleapis.com",
           "mmd": 86400,
           "state": {
             "readonly": {
@@ -736,7 +700,6 @@
           "log_id": "VYHUwhaQNgFK6gubVzxT8MDkOHhwJQgXL6OqHQcT0ww=",
           "key": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE8m/SiQ8/xfiHHqtls9m7FyOMBg4JVZY9CgiixXGz0akvKD6DEL8S0ERmFe9U4ZiA0M4kbT5nmuk3I85Sk4bagA==",
           "url": "https://sabre.ct.comodo.com/",
-          "dns": "comodo-sabre.ct.googleapis.com",
           "mmd": 86400,
           "state": {
             "usable": {
@@ -749,7 +712,6 @@
           "log_id": "b1N2rDHwMRnYmQCkURX/dxUcEdkCwQApBo2yCJo32RM=",
           "key": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE7+R9dC4VFbbpuyOL+yy14ceAmEf7QGlo/EmtYU6DRzwat43f/3swtLr/L8ugFOOt1YU/RFmMjGCL17ixv66MZw==",
           "url": "https://mammoth.ct.comodo.com/",
-          "dns": "comodo-mammoth.ct.googleapis.com",
           "mmd": 86400,
           "state": {
             "usable": {
@@ -758,6 +720,78 @@
           }
         }
       ]
+    },
+    {
+      "name": "Let's Encrypt",
+      "email": [
+        "sre@letsencrypt.org"
+      ],
+      "logs": [
+        {
+          "description": "Let's Encrypt 'Oak2019' log",
+          "log_id": "ZZszUPQ7EsxepatOx2XT/ebIgkN3d3jnIAP56yuMMSk=",
+          "key": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEFkqNKRuZ+Z8IOsnNJrUZ8gwp+KKGOdQrJ/HKhSadK/SJuoCc9+dxQ7awpmWIMr9SKcQeG5uRzG1kVSyFN4Wfcw==",
+          "url": "https://oak.ct.letsencrypt.org/2019/",
+          "mmd": 86400,
+          "state": {
+            "qualified": {
+              "timestamp": "2019-10-02T00:00:00Z"
+            }
+          },
+          "temporal_interval": {
+            "start_inclusive": "2019-01-01T00:00:00Z",
+            "end_exclusive": "2020-01-07T00:00:00Z"
+          }
+        },
+        {
+          "description": "Let's Encrypt 'Oak2020' log",
+          "log_id": "5xLysDd+GmL7jskMYYTx6ns3y1YdESZb8+DzS/JBVG4=",
+          "key": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEfzb42Zdr/h7hgqgDCo1vrNJqGqbcUvJGJEER9DDqp19W/wFSB0l166hD+U5cAXchpH8ZkBNUuvOHS0OnJ4oJrQ==",
+          "url": "https://oak.ct.letsencrypt.org/2020/",
+          "mmd": 86400,
+          "state": {
+            "qualified": {
+              "timestamp": "2019-10-02T00:00:00Z"
+            }
+          },
+          "temporal_interval": {
+            "start_inclusive": "2020-01-01T00:00:00Z",
+            "end_exclusive": "2021-01-07T00:00:00Z"
+          }
+        },
+        {
+          "description": "Let's Encrypt 'Oak2021' log",
+          "log_id": "lCC8Ho7VjWyIcx+CiyIsDdHaTV5sT5Q9YdtOL1hNosI=",
+          "key": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAELsYzGMNwo8rBIlaklBIdmD2Ofn6HkfrjK0Ukz1uOIUC6Lm0jTITCXhoIdjs7JkyXnwuwYiJYiH7sE1YeKu8k9w==",
+          "url": "https://oak.ct.letsencrypt.org/2021/",
+          "mmd": 86400,
+          "state": {
+            "qualified": {
+              "timestamp": "2019-10-02T00:00:00Z"
+            }
+          },
+          "temporal_interval": {
+            "start_inclusive": "2021-01-01T00:00:00Z",
+            "end_exclusive": "2022-01-07T00:00:00Z"
+          }
+        },
+        {
+          "description": "Let's Encrypt 'Oak2022' log",
+          "log_id": "36Veq2iCTx9sre64X04+WurNohKkal6OOxLAIERcKnM=",
+          "key": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEhjyxDVIjWt5u9sB/o2S8rcGJ2pdZTGA8+IpXhI/tvKBjElGE5r3de4yAfeOPhqTqqc+o7vPgXnDgu/a9/B+RLg==",
+          "url": "https://oak.ct.letsencrypt.org/2022/",
+          "mmd": 86400,
+          "state": {
+            "qualified": {
+              "timestamp": "2019-10-02T00:00:00Z"
+            }
+          },
+          "temporal_interval": {
+            "start_inclusive": "2022-01-01T00:00:00Z",
+            "end_exclusive": "2023-01-07T00:00:00Z"
+          }
+        }
+      ]
     }
   ]
 }
\ No newline at end of file
diff --git a/components/cronet/host_cache_persistence_manager_unittest.cc b/components/cronet/host_cache_persistence_manager_unittest.cc
index 7f57e01..b908197 100644
--- a/components/cronet/host_cache_persistence_manager_unittest.cc
+++ b/components/cronet/host_cache_persistence_manager_unittest.cc
@@ -45,14 +45,14 @@
   // not the full contents, since the tests in this file are only intended
   // to test that writes happen when they're supposed to, not serialization
   // correctness.
-  void CheckPref(size_t size) {
+  void CheckPref(size_t expected_size) {
     const base::Value* value = pref_service_->GetUserPref(kPrefName);
-    base::ListValue list;
+    base::Value list(base::Value::Type::LIST);
     if (value)
-      list = base::ListValue(value->GetList());
+      list = base::Value(value->GetList());
     net::HostCache temp_cache(10);
-    temp_cache.RestoreFromListValue(list);
-    ASSERT_EQ(size, temp_cache.size());
+    temp_cache.RestoreFromListValue(base::Value::AsListValue(list));
+    ASSERT_EQ(expected_size, temp_cache.size());
   }
 
   // Generates a temporary HostCache with a few entries and uses it to
diff --git a/components/data_reduction_proxy/DEPS b/components/data_reduction_proxy/DEPS
index 3f3525b..a5bfa08c 100644
--- a/components/data_reduction_proxy/DEPS
+++ b/components/data_reduction_proxy/DEPS
@@ -2,6 +2,7 @@
   "+components/data_use_measurement/core",
   "+components/pref_registry",
   "+components/prefs",
+  "+components/previews/core",
   "+components/variations",
   "+crypto",
   "+google_apis",
diff --git a/components/data_reduction_proxy/core/browser/BUILD.gn b/components/data_reduction_proxy/core/browser/BUILD.gn
index 498deb0..4bc7f032 100644
--- a/components/data_reduction_proxy/core/browser/BUILD.gn
+++ b/components/data_reduction_proxy/core/browser/BUILD.gn
@@ -56,6 +56,7 @@
       "//components/data_use_measurement/core:ascriber",
       "//components/pref_registry",
       "//components/prefs",
+      "//components/previews/core:core",
       "//components/variations",
       "//components/variations/net",
       "//content/public/browser",
@@ -91,6 +92,7 @@
     "//components/data_use_measurement/core:ascriber",
     "//components/pref_registry",
     "//components/prefs",
+    "//components/previews/core:core",
     "//components/variations",
     "//components/variations/net",
     "//content/public/browser",
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client.cc
index 84da0c1..c11118d 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client.cc
@@ -33,6 +33,7 @@
 #include "components/data_reduction_proxy/core/common/data_reduction_proxy_switches.h"
 #include "components/data_reduction_proxy/proto/client_config.pb.h"
 #include "components/data_use_measurement/core/data_use_user_data.h"
+#include "components/previews/core/previews_experiments.h"
 #include "components/variations/net/variations_http_headers.h"
 #include "net/base/host_port_pair.h"
 #include "net/base/load_flags.h"
@@ -178,7 +179,8 @@
   DCHECK(config);
   DCHECK(service);
   DCHECK(config_service_url_.is_valid());
-  DCHECK(!params::IsIncludedInHoldbackFieldTrial());
+  DCHECK(!params::IsIncludedInHoldbackFieldTrial() ||
+         previews::params::IsLitePageServerPreviewsEnabled());
 
   const base::CommandLine& command_line =
       *base::CommandLine::ForCurrentProcess();
@@ -431,7 +433,8 @@
 
 void DataReductionProxyConfigServiceClient::RetrieveRemoteConfig() {
   DCHECK(thread_checker_.CalledOnValidThread());
-  DCHECK(!params::IsIncludedInHoldbackFieldTrial());
+  DCHECK(!params::IsIncludedInHoldbackFieldTrial() ||
+         previews::params::IsLitePageServerPreviewsEnabled());
 
   CreateClientConfigRequest request;
   std::string serialized_request;
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.cc
index 7eb3ed1..e1ad07b 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.cc
@@ -159,7 +159,8 @@
   RegenerateRequestHeaderValue();
 }
 
-void DataReductionProxyRequestOptions::SetKey(const std::string& key) {
+void DataReductionProxyRequestOptions::SetKeyForTesting(
+    const std::string& key) {
   DCHECK(thread_checker_.CalledOnValidThread());
   if(!key.empty()) {
     key_ = key;
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.h b/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.h
index e2254b2..6b07e18 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.h
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.h
@@ -79,7 +79,7 @@
   // this class has been constructed. Android WebView is a platform that does
   // this. The caller needs to make sure |this| pointer is valid when
   // SetKey is called.
-  void SetKey(const std::string& key);
+  void SetKeyForTesting(const std::string& key);
 
   // Sets the credentials for sending to the Data Reduction Proxy.
   void SetSecureSession(const std::string& secure_session);
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options_unittest.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options_unittest.cc
index 0086bf7..e57b6b3f 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options_unittest.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options_unittest.cc
@@ -195,7 +195,7 @@
   test_context_->RunUntilIdle();
 
   // Now set a key.
-  request_options()->SetKey(kTestKey2);
+  request_options()->SetKeyForTesting(kTestKey2);
 
   // Write headers.
   VerifyExpectedHeader(expected_header, kPageIdValue);
@@ -211,7 +211,7 @@
 
   // Now set an empty key. The auth handler should ignore that, and the key
   // remains |kTestKey|.
-  request_options()->SetKey(std::string());
+  request_options()->SetKeyForTesting(std::string());
   VerifyExpectedHeader(expected_header, kPageIdValue);
 }
 
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_service.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_service.cc
index 54eebd8..f3cd6f4 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_service.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_service.cc
@@ -34,6 +34,7 @@
 #include "components/data_reduction_proxy/proto/data_store.pb.h"
 #include "components/data_use_measurement/core/data_use_measurement.h"
 #include "components/prefs/pref_service.h"
+#include "components/previews/core/previews_experiments.h"
 #include "mojo/public/cpp/bindings/remote.h"
 
 namespace data_reduction_proxy {
@@ -93,7 +94,8 @@
   // It is safe to use base::Unretained here, since it gets executed
   // synchronously on the UI thread, and |this| outlives the caller (since the
   // caller is owned by |this|.
-  if (!params::IsIncludedInHoldbackFieldTrial()) {
+  if (!params::IsIncludedInHoldbackFieldTrial() ||
+      previews::params::IsLitePageServerPreviewsEnabled()) {
     config_client_ = std::make_unique<DataReductionProxyConfigServiceClient>(
         GetBackoffPolicy(), request_options_.get(), raw_mutable_config,
         config_.get(), this, network_connection_tracker_,
@@ -441,6 +443,9 @@
 }
 
 void DataReductionProxyService::UpdateCustomProxyConfig() {
+  if (params::IsIncludedInHoldbackFieldTrial())
+    return;
+
   network::mojom::CustomProxyConfigPtr config = CreateCustomProxyConfig(
       !base::FeatureList::IsEnabled(
           features::kDataReductionProxyDisableProxyFailedWarmup),
@@ -506,6 +511,9 @@
 void DataReductionProxyService::StoreSerializedConfig(
     const std::string& serialized_config) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(!params::IsIncludedInHoldbackFieldTrial() ||
+         previews::params::IsLitePageServerPreviewsEnabled());
+
   SetStringPref(prefs::kDataReductionProxyConfig, serialized_config);
   SetInt64Pref(prefs::kDataReductionProxyLastConfigRetrievalTime,
                (base::Time::Now() - base::Time()).InMicroseconds());
diff --git a/components/history/core/browser/BUILD.gn b/components/history/core/browser/BUILD.gn
index a9e0f52..f02b61b 100644
--- a/components/history/core/browser/BUILD.gn
+++ b/components/history/core/browser/BUILD.gn
@@ -170,6 +170,7 @@
     "//components/test/data/history/history.38.sql",
     "//components/test/data/history/history.39.sql",
     "//components/test/data/history/history.40.sql",
+    "//components/test/data/history/history.41.sql",
     "//components/test/data/history/thumbnail_wild/Favicons.corrupt_meta.disable",
     "//components/test/data/history/thumbnail_wild/Favicons.v2.init.sql",
     "//components/test/data/history/thumbnail_wild/Favicons.v3.init.sql",
diff --git a/components/history/core/browser/history_backend_db_unittest.cc b/components/history/core/browser/history_backend_db_unittest.cc
index 17fb57f..f5e7e10 100644
--- a/components/history/core/browser/history_backend_db_unittest.cc
+++ b/components/history/core/browser/history_backend_db_unittest.cc
@@ -27,6 +27,7 @@
 #include "base/bind.h"
 #include "base/format_macros.h"
 #include "base/guid.h"
+#include "base/i18n/case_conversion.h"
 #include "base/run_loop.h"
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
@@ -36,6 +37,7 @@
 #include "components/history/core/browser/download_row.h"
 #include "components/history/core/browser/history_constants.h"
 #include "components/history/core/browser/history_database.h"
+#include "components/history/core/browser/keyword_search_term.h"
 #include "components/history/core/browser/page_usage_data.h"
 #include "components/history/core/test/history_backend_db_base_test.h"
 #include "components/history/core/test/test_history_database.h"
@@ -1699,6 +1701,44 @@
   EXPECT_FALSE(visit_row.incremented_omnibox_typed_score);
 }
 
+// Tests that the migration code correctly replaces the lower_term column in the
+// keyword search terms table which normalized_term which contains the
+// normalized search term during migration to version 42.
+TEST_F(HistoryBackendDBTest, MigrateKeywordSearchTerms) {
+  ASSERT_NO_FATAL_FAILURE(CreateDBVersion(41));
+
+  const KeywordID keyword_id = 12;
+  const URLID url_id = 34;
+  const base::string16 term = base::ASCIIToUTF16("WEEKLY  NEWS  ");
+  const base::string16 lower_term = base::i18n::ToLower(term);
+  const base::string16 normalized_term =
+      base::CollapseWhitespace(lower_term, false);
+
+  sql::Database db;
+  ASSERT_TRUE(db.Open(history_dir_.Append(kHistoryFilename)));
+  sql::Statement insert_statement(
+      db.GetUniqueStatement("INSERT INTO keyword_search_terms (keyword_id, "
+                            "url_id, lower_term, term) VALUES (?,?,?,?)"));
+  insert_statement.BindInt64(0, keyword_id);
+  insert_statement.BindInt64(1, url_id);
+  insert_statement.BindString16(2, lower_term);
+  insert_statement.BindString16(3, term);
+  ASSERT_TRUE(insert_statement.Run());
+
+  // Re-open the db, triggering migration.
+  CreateBackendAndDatabase();
+
+  // The version should have been updated.
+  ASSERT_GE(HistoryDatabase::GetCurrentVersion(), 42);
+
+  history::KeywordSearchTermRow keyword_search_term_row;
+  ASSERT_TRUE(db_->GetKeywordSearchTermRow(url_id, &keyword_search_term_row));
+  EXPECT_EQ(keyword_id, keyword_search_term_row.keyword_id);
+  EXPECT_EQ(url_id, keyword_search_term_row.url_id);
+  EXPECT_EQ(term, keyword_search_term_row.term);
+  EXPECT_EQ(normalized_term, keyword_search_term_row.normalized_term);
+}
+
 // Test to verify the left-over typed_url sync metadata gets cleared correctly
 // during migration to version 41.
 TEST_F(HistoryBackendDBTest, MigrateTypedURLLeftoverMetadata) {
diff --git a/components/history/core/browser/history_database.cc b/components/history/core/browser/history_database.cc
index 55e8c4e..8eb12b9 100644
--- a/components/history/core/browser/history_database.cc
+++ b/components/history/core/browser/history_database.cc
@@ -37,7 +37,7 @@
 // Current version number. We write databases at the "current" version number,
 // but any previous version that can read the "compatible" one can make do with
 // our database without *too* many bad effects.
-const int kCurrentVersionNumber = 41;
+const int kCurrentVersionNumber = 42;
 const int kCompatibleVersionNumber = 16;
 const char kEarlyExpirationThresholdKey[] = "early_expiration_threshold";
 
@@ -129,7 +129,6 @@
       !InitSegmentTables() || !InitSyncTable())
     return LogInitFailure(InitStep::CREATE_TABLES);
   CreateMainURLIndex();
-  CreateKeywordSearchTermsIndices();
 
   // TODO(benjhayden) Remove at some point.
   meta_table_.DeleteKey("next_download_id");
@@ -295,7 +294,6 @@
   if (!InitSegmentTables())
     return false;
 
-  CreateKeywordSearchTermsIndices();
   return true;
 }
 
@@ -586,6 +584,13 @@
     meta_table_.SetVersionNumber(cur_version);
   }
 
+  if (cur_version == 41) {
+    if (!MigrateKeywordsSearchTermsLowerTermColumn())
+      return LogMigrationFailure(41);
+    cur_version++;
+    meta_table_.SetVersionNumber(cur_version);
+  }
+
   // =========================       ^^ new migration code goes here ^^
   // ADDING NEW MIGRATION CODE
   // =========================
diff --git a/components/history/core/browser/in_memory_database.cc b/components/history/core/browser/in_memory_database.cc
index fe13033..2d2d229a 100644
--- a/components/history/core/browser/in_memory_database.cc
+++ b/components/history/core/browser/in_memory_database.cc
@@ -54,7 +54,6 @@
   // InitDB doesn't create the index so in the disk-loading case, it can be
   // added afterwards.
   CreateMainURLIndex();
-  CreateKeywordSearchTermsIndices();
   return true;
 }
 
@@ -139,7 +138,6 @@
   // Index the table, this is faster than creating the index first and then
   // inserting into it.
   CreateMainURLIndex();
-  CreateKeywordSearchTermsIndices();
 
   return true;
 }
diff --git a/components/history/core/browser/keyword_search_term.cc b/components/history/core/browser/keyword_search_term.cc
index 08c0239..59634aa 100644
--- a/components/history/core/browser/keyword_search_term.cc
+++ b/components/history/core/browser/keyword_search_term.cc
@@ -13,6 +13,9 @@
 
 KeywordSearchTermRow::KeywordSearchTermRow() : keyword_id(0), url_id(0) {}
 
+KeywordSearchTermRow::KeywordSearchTermRow(const KeywordSearchTermRow& other) =
+    default;
+
 KeywordSearchTermRow::~KeywordSearchTermRow() {}
 
 }  // namespace history
diff --git a/components/history/core/browser/keyword_search_term.h b/components/history/core/browser/keyword_search_term.h
index 4630364f..8afc36a 100644
--- a/components/history/core/browser/keyword_search_term.h
+++ b/components/history/core/browser/keyword_search_term.h
@@ -19,6 +19,8 @@
   ~KeywordSearchTermVisit();
 
   base::string16 term;  // The search term that was used.
+  base::string16 normalized_term;  // The search term, in lower case and with
+                                   // extra whitespaces collapsed.
   int visits;  // The visit count.
   base::Time time;  // The time of the most recent visit.
 };
@@ -26,11 +28,14 @@
 // Used for URLs that have a search term associated with them.
 struct KeywordSearchTermRow {
   KeywordSearchTermRow();
+  KeywordSearchTermRow(const KeywordSearchTermRow& other);
   ~KeywordSearchTermRow();
 
   KeywordID keyword_id;  // ID of the keyword.
   URLID url_id;  // ID of the url.
   base::string16 term;  // The search term that was used.
+  base::string16 normalized_term;  // The search term, in lower case and with
+                                   // extra whitespaces collapsed.
 };
 
 }  // namespace history
diff --git a/components/history/core/browser/url_database.cc b/components/history/core/browser/url_database.cc
index 286fc9f..3b85ce6 100644
--- a/components/history/core/browser/url_database.cc
+++ b/components/history/core/browser/url_database.cc
@@ -71,6 +71,55 @@
   i->set_hidden(s.ColumnInt(6) != 0);
 }
 
+bool URLDatabase::MigrateKeywordsSearchTermsLowerTermColumn() {
+  // Create a temporary keyword search terms table.
+  if (!GetDB().Execute(
+          "CREATE TABLE temp_keyword_search_terms ("
+          "keyword_id INTEGER NOT NULL,"  // ID of the TemplateURL.
+          "url_id INTEGER NOT NULL,"      // ID of the url.
+          "term LONGVARCHAR NOT NULL,"    // The actual search term.
+          // The search term, in lower case, and with whitespaces collapsed.
+          "normalized_term LONGVARCHAR NOT NULL)")) {
+    return false;
+  }
+
+  // Extract rows from the keyword search terms table, convert lower_term to
+  // normalized_term, and insert them into the temporary table.
+  sql::Statement select_statement(
+      GetDB().GetCachedStatement(SQL_FROM_HERE,
+                                 "SELECT keyword_id, url_id, lower_term, term "
+                                 "FROM keyword_search_terms"));
+  while (select_statement.Step()) {
+    sql::Statement insert_statement(GetDB().GetCachedStatement(
+        SQL_FROM_HERE,
+        "INSERT INTO temp_keyword_search_terms "
+        "(keyword_id, url_id, term, normalized_term) VALUES (?,?,?,?)"));
+    insert_statement.BindInt64(0, select_statement.ColumnInt64(0));
+    insert_statement.BindInt64(1, select_statement.ColumnInt64(1));
+    insert_statement.BindString16(2, select_statement.ColumnString16(3));
+    insert_statement.BindString16(
+        3, base::CollapseWhitespace(select_statement.ColumnString16(2), false));
+    if (!insert_statement.Run())
+      return false;
+  }
+  if (!select_statement.Succeeded())
+    return false;
+
+  // Replace the keyword search terms table with the temporary one.
+  if (!GetDB().Execute("DROP TABLE keyword_search_terms"))
+    return false;
+  if (!GetDB().Execute("ALTER TABLE temp_keyword_search_terms RENAME TO "
+                       "keyword_search_terms")) {
+    return false;
+  }
+
+  // Index the table, this is faster than creating the index first and then
+  // inserting into it.
+  CreateKeywordSearchTermsIndices();
+
+  return true;
+}
+
 bool URLDatabase::GetURLRow(URLID url_id, URLRow* info) {
   // TODO(brettw) We need check for empty URLs to handle the case where
   // there are old URLs in the database that are empty that got in before
@@ -412,12 +461,16 @@
 bool URLDatabase::InitKeywordSearchTermsTable() {
   has_keyword_search_terms_ = true;
   if (!GetDB().DoesTableExist("keyword_search_terms")) {
-    if (!GetDB().Execute("CREATE TABLE keyword_search_terms ("
-        "keyword_id INTEGER NOT NULL,"      // ID of the TemplateURL.
-        "url_id INTEGER NOT NULL,"          // ID of the url.
-        "lower_term LONGVARCHAR NOT NULL,"  // The search term, in lower case.
-        "term LONGVARCHAR NOT NULL)"))      // The actual search term.
+    if (!GetDB().Execute(
+            "CREATE TABLE keyword_search_terms ("
+            "keyword_id INTEGER NOT NULL,"  // ID of the TemplateURL.
+            "url_id INTEGER NOT NULL,"      // ID of the url.
+            "term LONGVARCHAR NOT NULL,"    // The actual search term.
+            // The search term, in lower case, and with whitespaces collapsed.
+            "normalized_term LONGVARCHAR NOT NULL)") ||
+        !CreateKeywordSearchTermsIndices()) {
       return false;
+    }
   }
   return true;
 }
@@ -426,7 +479,7 @@
   // For searching.
   if (!GetDB().Execute(
           "CREATE INDEX IF NOT EXISTS keyword_search_terms_index1 ON "
-          "keyword_search_terms (keyword_id, lower_term)")) {
+          "keyword_search_terms (keyword_id, normalized_term)")) {
     return false;
   }
 
@@ -468,21 +521,25 @@
   if (!exist_statement.Succeeded())
     return false;
 
-  sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE,
-      "INSERT INTO keyword_search_terms (keyword_id, url_id, lower_term, term) "
-      "VALUES (?,?,?,?)"));
+  sql::Statement statement(GetDB().GetCachedStatement(
+      SQL_FROM_HERE,
+      "INSERT INTO keyword_search_terms (keyword_id, url_id, term, "
+      "normalized_term) VALUES (?,?,?,?)"));
   statement.BindInt64(0, keyword_id);
   statement.BindInt64(1, url_id);
-  statement.BindString16(2, base::i18n::ToLower(term));
-  statement.BindString16(3, term);
+  statement.BindString16(2, term);
+  statement.BindString16(
+      3, base::i18n::ToLower(base::CollapseWhitespace(term, false)));
   return statement.Run();
 }
 
 bool URLDatabase::GetKeywordSearchTermRow(URLID url_id,
                                           KeywordSearchTermRow* row) {
   DCHECK(url_id);
-  sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE,
-      "SELECT keyword_id, term FROM keyword_search_terms WHERE url_id=?"));
+  sql::Statement statement(
+      GetDB().GetCachedStatement(SQL_FROM_HERE,
+                                 "SELECT keyword_id, term, normalized_term "
+                                 "FROM keyword_search_terms WHERE url_id=?"));
   statement.BindInt64(0, url_id);
 
   if (!statement.Step())
@@ -492,6 +549,7 @@
     row->url_id = url_id;
     row->keyword_id = statement.ColumnInt64(0);
     row->term = statement.ColumnString16(1);
+    row->normalized_term = statement.ColumnString16(2);
   }
   return true;
 }
@@ -499,8 +557,10 @@
 bool URLDatabase::GetKeywordSearchTermRows(
     const base::string16& term,
     std::vector<KeywordSearchTermRow>* rows) {
-  sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE,
-      "SELECT keyword_id, url_id FROM keyword_search_terms WHERE term=?"));
+  sql::Statement statement(
+      GetDB().GetCachedStatement(SQL_FROM_HERE,
+                                 "SELECT keyword_id, url_id, normalized_term "
+                                 "FROM keyword_search_terms WHERE term=?"));
   statement.BindString16(0, term);
 
   if (!statement.is_valid())
@@ -511,6 +571,7 @@
     row.url_id = statement.ColumnInt64(1);
     row.keyword_id = statement.ColumnInt64(0);
     row.term = term;
+    row.normalized_term = statement.ColumnInt64(2);
     rows->push_back(row);
   }
   return true;
@@ -538,20 +599,24 @@
     return;
 
   DCHECK(!prefix.empty());
-  sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE,
+  sql::Statement statement(GetDB().GetCachedStatement(
+      SQL_FROM_HERE,
       "SELECT DISTINCT kv.term, u.visit_count, u.last_visit_time "
       "FROM keyword_search_terms kv "
       "JOIN urls u ON kv.url_id = u.id "
-      "WHERE kv.keyword_id = ? AND kv.lower_term >= ? AND kv.lower_term < ? "
+      "WHERE kv.keyword_id = ? AND kv.normalized_term >= ? AND "
+      "kv.normalized_term < ? "
       "ORDER BY u.last_visit_time DESC LIMIT ?"));
 
-  // NOTE: Keep this ToLower() call in sync with search_provider.cc.
-  base::string16 lower_prefix = base::i18n::ToLower(prefix);
+  // NOTE: Keep these CollapseWhitespace() and ToLower() calls in sync with
+  // search_provider.cc.
+  base::string16 normalized_prefix =
+      base::CollapseWhitespace(base::i18n::ToLower(prefix), false);
   // This magic gives us a prefix search.
-  base::string16 next_prefix = lower_prefix;
+  base::string16 next_prefix = normalized_prefix;
   next_prefix.back() = next_prefix.back() + 1;
   statement.BindInt64(0, keyword_id);
-  statement.BindString16(1, lower_prefix);
+  statement.BindString16(1, normalized_prefix);
   statement.BindString16(2, next_prefix);
   statement.BindInt(3, max_count);
 
@@ -564,6 +629,38 @@
   }
 }
 
+std::vector<KeywordSearchTermVisit>
+URLDatabase::GetMostRecentKeywordSearchTerms(KeywordID keyword_id,
+                                             int max_count) {
+  // NOTE: the keyword_id can be zero if on first run the user does a query
+  // before the TemplateURLService has finished loading. As the chances of this
+  // occurring are small, we ignore it.
+  if (!keyword_id)
+    return {};
+
+  sql::Statement statement(GetDB().GetCachedStatement(
+      SQL_FROM_HERE,
+      "SELECT DISTINCT "
+      "kv.term, kv.normalized_term, u.visit_count, u.last_visit_time "
+      "FROM keyword_search_terms kv JOIN urls u ON kv.url_id = u.id "
+      "WHERE kv.keyword_id = ? "
+      "ORDER BY u.last_visit_time DESC LIMIT ?"));
+
+  statement.BindInt64(0, keyword_id);
+  statement.BindInt(1, max_count);
+
+  std::vector<KeywordSearchTermVisit> visits;
+  while (statement.Step()) {
+    KeywordSearchTermVisit visit;
+    visit.term = statement.ColumnString16(0);
+    visit.normalized_term = statement.ColumnString16(1);
+    visit.visits = statement.ColumnInt(2);
+    visit.time = base::Time::FromInternalValue(statement.ColumnInt64(3));
+    visits.push_back(visit);
+  }
+  return visits;
+}
+
 bool URLDatabase::DeleteKeywordSearchTerm(const base::string16& term) {
   sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE,
       "DELETE FROM keyword_search_terms WHERE term=?"));
diff --git a/components/history/core/browser/url_database.h b/components/history/core/browser/url_database.h
index e7eabb9..af4977c0 100644
--- a/components/history/core/browser/url_database.h
+++ b/components/history/core/browser/url_database.h
@@ -225,6 +225,11 @@
       int max_count,
       std::vector<KeywordSearchTermVisit>* matches);
 
+  // Returns up to max_count of the most recent search terms.
+  std::vector<KeywordSearchTermVisit> GetMostRecentKeywordSearchTerms(
+      KeywordID keyword_id,
+      int max_count);
+
   // Deletes all searches matching |term|.
   bool DeleteKeywordSearchTerm(const base::string16& term);
 
@@ -299,6 +304,11 @@
   // this class implements these functions to return its objects.
   virtual sql::Database& GetDB() = 0;
 
+  // Replaces the lower_term column in the keyword search terms table with
+  // normalized_term which contains the search term, in lower case, and with
+  // whitespaces collapsed for migration to version 42.
+  bool MigrateKeywordsSearchTermsLowerTermColumn();
+
  private:
   // True if InitKeywordSearchTermsTable() has been invoked. Not all subclasses
   // have keyword search terms.
diff --git a/components/history/core/browser/url_database_unittest.cc b/components/history/core/browser/url_database_unittest.cc
index 23aa7c6..a1c42ef2 100644
--- a/components/history/core/browser/url_database_unittest.cc
+++ b/components/history/core/browser/url_database_unittest.cc
@@ -185,15 +185,22 @@
 
   // Add a keyword visit.
   KeywordID keyword_id = 100;
-  base::string16 keyword = base::UTF8ToUTF16("visit");
+  base::string16 keyword = base::UTF8ToUTF16(" VISIT ");
+  base::string16 normalized_keyword = base::UTF8ToUTF16("visit");
   ASSERT_TRUE(SetKeywordSearchTermsForURL(url_id, keyword_id, keyword));
 
   // Make sure we get it back.
   std::vector<KeywordSearchTermVisit> matches;
-  GetMostRecentKeywordSearchTerms(keyword_id, keyword, 10, &matches);
+  GetMostRecentKeywordSearchTerms(keyword_id, base::UTF8ToUTF16("vi"), 10,
+                                  &matches);
   ASSERT_EQ(1U, matches.size());
   ASSERT_EQ(keyword, matches[0].term);
 
+  auto zero_prefix_matches = GetMostRecentKeywordSearchTerms(keyword_id, 10);
+  ASSERT_EQ(1U, zero_prefix_matches.size());
+  ASSERT_EQ(keyword, zero_prefix_matches[0].term);
+  ASSERT_EQ(normalized_keyword, zero_prefix_matches[0].normalized_term);
+
   KeywordSearchTermRow keyword_search_term_row;
   ASSERT_TRUE(GetKeywordSearchTermRow(url_id, &keyword_search_term_row));
   EXPECT_EQ(keyword_id, keyword_search_term_row.keyword_id);
diff --git a/components/optimization_guide/optimization_guide_features.cc b/components/optimization_guide/optimization_guide_features.cc
index 1c58883..5254694 100644
--- a/components/optimization_guide/optimization_guide_features.cc
+++ b/components/optimization_guide/optimization_guide_features.cc
@@ -41,10 +41,6 @@
 const base::Feature kOptimizationHintsExperiments{
     "OptimizationHintsExperiments", base::FEATURE_DISABLED_BY_DEFAULT};
 
-// Provides slow page triggering parameters.
-const base::Feature kSlowPageTriggering{"PreviewsSlowPageTriggering",
-                                        base::FEATURE_DISABLED_BY_DEFAULT};
-
 // Enables fetching optimization hints from a remote Optimization Guide Service.
 const base::Feature kOptimizationHintsFetching{
     "OptimizationHintsFetching", base::FEATURE_DISABLED_BY_DEFAULT};
diff --git a/components/optimization_guide/optimization_guide_features.h b/components/optimization_guide/optimization_guide_features.h
index 457dc0c..78b262108 100644
--- a/components/optimization_guide/optimization_guide_features.h
+++ b/components/optimization_guide/optimization_guide_features.h
@@ -20,7 +20,6 @@
 extern const base::Feature kOptimizationHints;
 extern const base::Feature kOptimizationHintsExperiments;
 constexpr char kOptimizationHintsExperimentNameParam[] = "experiment_name";
-extern const base::Feature kSlowPageTriggering;
 extern const base::Feature kOptimizationHintsFetching;
 extern const base::Feature kOptimizationGuideKeyedService;
 
diff --git a/components/password_manager/core/browser/password_reuse_detection_manager.cc b/components/password_manager/core/browser/password_reuse_detection_manager.cc
index e106684b..928b0ee 100644
--- a/components/password_manager/core/browser/password_reuse_detection_manager.cc
+++ b/components/password_manager/core/browser/password_reuse_detection_manager.cc
@@ -90,28 +90,32 @@
     const std::vector<std::string>& matching_domains,
     int saved_passwords) {
   reuse_on_this_page_was_found_ = true;
-  std::unique_ptr<BrowserSavePasswordProgressLogger> logger;
   metrics_util::PasswordType reused_password_type = GetReusedPasswordType(
       reused_protected_password_hash, matching_domains.size());
 
   if (password_manager_util::IsLoggingActive(client_)) {
-    logger.reset(
-        new BrowserSavePasswordProgressLogger(client_->GetLogManager()));
+    BrowserSavePasswordProgressLogger logger(client_->GetLogManager());
     std::vector<std::string> domains_to_log(matching_domains);
-    if (reused_password_type ==
-        metrics_util::PasswordType::PRIMARY_ACCOUNT_PASSWORD) {
-      domains_to_log.push_back("CHROME SYNC PASSWORD");
-    } else if (reused_password_type ==
-               metrics_util::PasswordType::OTHER_GAIA_PASSWORD) {
-      domains_to_log.push_back("OTHER GAIA PASSWORD");
-    } else if (reused_password_type ==
-               metrics_util::PasswordType::ENTERPRISE_PASSWORD) {
-      domains_to_log.push_back("ENTERPRISE PASSWORD");
+    switch (reused_password_type) {
+      case metrics_util::PasswordType::PRIMARY_ACCOUNT_PASSWORD:
+        domains_to_log.push_back("CHROME SYNC PASSWORD");
+        break;
+      case metrics_util::PasswordType::OTHER_GAIA_PASSWORD:
+        domains_to_log.push_back("OTHER GAIA PASSWORD");
+        break;
+      case metrics_util::PasswordType::ENTERPRISE_PASSWORD:
+        domains_to_log.push_back("ENTERPRISE PASSWORD");
+        break;
+      case metrics_util::PasswordType::SAVED_PASSWORD:
+        domains_to_log.push_back("SAVED PASSWORD");
+        break;
+      default:
+        break;
     }
-    // TODO(nparker): Implement LogList() to log all domains in one call.
+
     for (const auto& domain : domains_to_log) {
-      logger->LogString(BrowserSavePasswordProgressLogger::STRING_REUSE_FOUND,
-                        domain);
+      logger.LogString(BrowserSavePasswordProgressLogger::STRING_REUSE_FOUND,
+                       domain);
     }
   }
 
diff --git a/components/policy/resources/policy_templates.json b/components/policy/resources/policy_templates.json
index 71415b0..01eb77e 100644
--- a/components/policy/resources/policy_templates.json
+++ b/components/policy/resources/policy_templates.json
@@ -969,6 +969,7 @@
         'PrintingBackgroundGraphicsDefault',
         'PrintingSizeDefault',
         'PrintingSendUsernameAndFilenameEnabled',
+        'PrintJobHistoryExpirationPeriod',
         'CloudPrintSubmitEnabled',
         'DisablePrintPreview',
         'PrintHeaderFooter',
@@ -2399,6 +2400,28 @@
       Setting this policy to true also disables printers that use protocols other than IPPS, USB, or IPP-over-USB since username and filename shouldn't be sent over the network openly.''',
     },
     {
+      'name': 'PrintJobHistoryExpirationPeriod',
+      'owners': ['nikitapodguzov@chromium.org'],
+      'type': 'int',
+      'schema': { 'type': 'integer', 'minimum': -1 },
+      'supported_on': ['chrome_os:79-'],
+      'features': {
+        'dynamic_refresh': True,
+        'per_profile': True,
+      },
+      'example_value': 90,
+      'id': 625,
+      'caption': '''Set the time period in days for storing print jobs metadata''',
+      'tags': [],
+      'desc': '''This policy controls how long print jobs metadata is stored on the device, in days.
+
+      When this policy is set to a value of -1, the print jobs metadata is stored indefinitely. When this policy is set to a value of 0, the print jobs metadata is not stored at all. When this policy is set to any other value, it specifies the period of time during which the metadata of completed print jobs is stored on the device.
+
+      If not set, the default period of 90 days is used for <ph name="PRODUCT_OS_NAME">$2<ex>Google Chrome OS</ex></ph> devices.
+
+      The policy value should be specified in days.''',
+    },
+    {
       'name': 'ForceSafeSearch',
       'owners': ['sergiu@chromium.org', 'igorcov@chromium.org'],
       'type': 'main',
@@ -19115,6 +19138,6 @@
   ],
   'placeholders': [],
   'deleted_policy_ids': [412, 546, 562, 569],
-  'highest_id_currently_used': 624,
+  'highest_id_currently_used': 625,
   'highest_atomic_group_id_currently_used': 38
 }
diff --git a/components/previews/DEPS b/components/previews/DEPS
index ba623643..03135a1 100644
--- a/components/previews/DEPS
+++ b/components/previews/DEPS
@@ -3,5 +3,6 @@
   # allowances of //content dependencies as appropriate.
   "-components",
   "-components/data_reduction_proxy/",
+  "+components/data_reduction_proxy/core/browser/",
   "-components/previews/content",
 ]
\ No newline at end of file
diff --git a/components/safe_browsing/android/java/src/org/chromium/components/safe_browsing/SafeBrowsingApiHandler.java b/components/safe_browsing/android/java/src/org/chromium/components/safe_browsing/SafeBrowsingApiHandler.java
index ba751e3..f12deca7 100644
--- a/components/safe_browsing/android/java/src/org/chromium/components/safe_browsing/SafeBrowsingApiHandler.java
+++ b/components/safe_browsing/android/java/src/org/chromium/components/safe_browsing/SafeBrowsingApiHandler.java
@@ -82,8 +82,5 @@
      *
      * @return true if the uri is found in the corresponding allowlist. Otherwise, false.
      */
-    // TODO(xinghuilu@): remove default once downstream implementation is patched
-    default boolean startAllowlistLookup(String uri, int threatType) {
-        return false;
-    }
+    public boolean startAllowlistLookup(String uri, int threatType);
 }
diff --git a/components/security_state/core/features.cc b/components/security_state/core/features.cc
index 98312b9..961cee5 100644
--- a/components/security_state/core/features.cc
+++ b/components/security_state/core/features.cc
@@ -13,6 +13,7 @@
 const char kMarkHttpAsParameterDangerous[] = "dangerous";
 const char kMarkHttpAsParameterWarningAndDangerousOnFormEdits[] =
     "warning-and-dangerous-on-form-edits";
+const char kMarkHttpAsParameterDangerWarning[] = "danger-warning";
 
 const base::Feature kLegacyTLSWarnings{"LegacyTLSWarnings",
                                        base::FEATURE_DISABLED_BY_DEFAULT};
diff --git a/components/security_state/core/features.h b/components/security_state/core/features.h
index 2c97912..1e7f4721 100644
--- a/components/security_state/core/features.h
+++ b/components/security_state/core/features.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 COMPONENTS_SECURITY_STATE_FEATURES_H_
-#define COMPONENTS_SECURITY_STATE_FEATURES_H_
+#ifndef COMPONENTS_SECURITY_STATE_CORE_FEATURES_H_
+#define COMPONENTS_SECURITY_STATE_CORE_FEATURES_H_
 
 #include "base/feature_list.h"
 
@@ -17,6 +17,8 @@
 // - 'warning-and-dangerous-on-form-edits': Show a Not Secure warning on all
 //   http:// pages, and treat them as actively dangerous when the user edits
 //   form fields
+// - 'danger-warning': Show a grey triangle icon instead of the info icon on all
+//   http:// pages.
 extern const base::Feature kMarkHttpAsFeature;
 
 // The parameter name which controls the warning treatment.
@@ -25,6 +27,7 @@
 // The different parameter values, described above.
 extern const char kMarkHttpAsParameterDangerous[];
 extern const char kMarkHttpAsParameterWarningAndDangerousOnFormEdits[];
+extern const char kMarkHttpAsParameterDangerWarning[];
 
 // This feature enables security warning UI treatments for sites that use legacy
 // TLS version (TLS 1.0 or 1.1).
@@ -33,4 +36,4 @@
 }  // namespace features
 }  // namespace security_state
 
-#endif  // COMPONENTS_SECURITY_STATE_FEATURES_H_
+#endif  // COMPONENTS_SECURITY_STATE_CORE_FEATURES_H_
diff --git a/components/security_state/core/security_state.cc b/components/security_state/core/security_state.cc
index 506e35d..8b38fe1 100644
--- a/components/security_state/core/security_state.cc
+++ b/components/security_state/core/security_state.cc
@@ -82,7 +82,10 @@
       return "SafetyTip_BadReputation";
     case security_state::SafetyTipStatus::kLookalike:
       return "SafetyTip_Lookalike";
-  };
+    case security_state::SafetyTipStatus::kBadKeyword:
+      NOTREACHED();
+      return std::string();
+  }
   NOTREACHED();
   return std::string();
 }
diff --git a/components/security_state/core/security_state.h b/components/security_state/core/security_state.h
index 4448f5c..0260f4a 100644
--- a/components/security_state/core/security_state.h
+++ b/components/security_state/core/security_state.h
@@ -120,9 +120,12 @@
   kNone = 1,
   // The current page triggered a Safety Tip because it was bad reputation.
   kBadReputation = 2,
-  // The current page trigged a Safety Tip because it had a lookalike URL.
+  // The current page triggered a Safety Tip because it had a lookalike URL.
   kLookalike = 3,
-  kMaxValue = kLookalike,
+  // The current page triggered a Safety Tip because a suspicious keyword was
+  // found in its hostname.
+  kBadKeyword = 4,
+  kMaxValue = kBadKeyword,
 };
 
 // Contains the security state relevant to computing the SecurityLevel
diff --git a/components/test/data/history/history.41.sql b/components/test/data/history/history.41.sql
new file mode 100644
index 0000000..439c7bb
--- /dev/null
+++ b/components/test/data/history/history.41.sql
@@ -0,0 +1,29 @@
+PRAGMA foreign_keys=OFF;
+BEGIN TRANSACTION;
+CREATE TABLE meta(key LONGVARCHAR NOT NULL UNIQUE PRIMARY KEY, value LONGVARCHAR);
+INSERT INTO meta VALUES('mmap_status','-1');
+INSERT INTO meta VALUES('version','41');
+INSERT INTO meta VALUES('last_compatible_version','16');
+CREATE TABLE urls(id INTEGER PRIMARY KEY AUTOINCREMENT,url LONGVARCHAR,title LONGVARCHAR,visit_count INTEGER DEFAULT 0 NOT NULL,typed_count INTEGER DEFAULT 0 NOT NULL,last_visit_time INTEGER NOT NULL,hidden INTEGER DEFAULT 0 NOT NULL);
+CREATE TABLE visits(id INTEGER PRIMARY KEY,url INTEGER NOT NULL,visit_time INTEGER NOT NULL,from_visit INTEGER,transition INTEGER DEFAULT 0 NOT NULL,segment_id INTEGER,visit_duration INTEGER DEFAULT 0 NOT NULL,incremented_omnibox_typed_score BOOLEAN DEFAULT FALSE NOT NULL);
+CREATE TABLE visit_source(id INTEGER PRIMARY KEY,source INTEGER NOT NULL);
+CREATE TABLE keyword_search_terms (keyword_id INTEGER NOT NULL,url_id INTEGER NOT NULL,lower_term LONGVARCHAR NOT NULL,term LONGVARCHAR NOT NULL);
+CREATE TABLE downloads (id INTEGER PRIMARY KEY,guid VARCHAR NOT NULL,current_path LONGVARCHAR NOT NULL,target_path LONGVARCHAR NOT NULL,start_time INTEGER NOT NULL,received_bytes INTEGER NOT NULL,total_bytes INTEGER NOT NULL,state INTEGER NOT NULL,danger_type INTEGER NOT NULL,interrupt_reason INTEGER NOT NULL,hash BLOB NOT NULL,end_time INTEGER NOT NULL,opened INTEGER NOT NULL,last_access_time INTEGER NOT NULL,transient INTEGER NOT NULL,referrer VARCHAR NOT NULL,site_url VARCHAR NOT NULL,tab_url VARCHAR NOT NULL,tab_referrer_url VARCHAR NOT NULL,http_method VARCHAR NOT NULL,by_ext_id VARCHAR NOT NULL,by_ext_name VARCHAR NOT NULL,etag VARCHAR NOT NULL,last_modified VARCHAR NOT NULL,mime_type VARCHAR(255) NOT NULL,original_mime_type VARCHAR(255) NOT NULL);
+CREATE TABLE downloads_url_chains (id INTEGER NOT NULL,chain_index INTEGER NOT NULL,url LONGVARCHAR NOT NULL, PRIMARY KEY (id, chain_index) );
+CREATE TABLE downloads_slices (download_id INTEGER NOT NULL,offset INTEGER NOT NULL,received_bytes INTEGER NOT NULL,finished INTEGER NOT NULL DEFAULT 0,PRIMARY KEY (download_id, offset) );
+CREATE TABLE segments (id INTEGER PRIMARY KEY,name VARCHAR,url_id INTEGER NON NULL);
+CREATE TABLE segment_usage (id INTEGER PRIMARY KEY,segment_id INTEGER NOT NULL,time_slot INTEGER NOT NULL,visit_count INTEGER DEFAULT 0 NOT NULL);
+CREATE TABLE typed_url_sync_metadata (storage_key INTEGER PRIMARY KEY NOT NULL,value BLOB);
+DELETE FROM sqlite_sequence;
+CREATE INDEX visits_url_index ON visits (url);
+CREATE INDEX visits_from_index ON visits (from_visit);
+CREATE INDEX visits_time_index ON visits (visit_time);
+CREATE INDEX segments_name ON segments(name);
+CREATE INDEX segments_url_id ON segments(url_id);
+CREATE INDEX segment_usage_time_slot_segment_id ON segment_usage(time_slot, segment_id);
+CREATE INDEX segments_usage_seg_id ON segment_usage(segment_id);
+CREATE INDEX urls_url_index ON urls (url);
+CREATE INDEX keyword_search_terms_index1 ON keyword_search_terms (keyword_id, lower_term);
+CREATE INDEX keyword_search_terms_index2 ON keyword_search_terms (url_id);
+CREATE INDEX keyword_search_terms_index3 ON keyword_search_terms (term);
+COMMIT;
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index 61de0417..5a30d88c 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -9,6 +9,7 @@
 import("//build/config/ui.gni")
 import("//chromeos/assistant/assistant.gni")
 import("//components/ui_devtools/devtools.gni")
+import("//content/common/features.gni")
 import("//gpu/vulkan/features.gni")
 import("//media/media_options.gni")
 import("//net/features.gni")
@@ -2073,8 +2074,7 @@
 
   # Desktop/Window/WebContents screen capture implementations, conditionally
   # built depending on the available implementations for each platform.
-  if (is_linux || is_mac || is_win) {
-    defines += [ "ENABLE_SCREEN_CAPTURE=1" ]
+  if (enable_screen_capture && !is_android) {
     sources += [
       "media/capture/desktop_capture_device.cc",
       "media/capture/desktop_capture_device.h",
@@ -2438,10 +2438,7 @@
     if (enable_vulkan) {
       deps += [ "//gpu/vulkan/init" ]
     }
-    defines += [
-      "APPCACHE_USE_SIMPLE_CACHE",
-      "ENABLE_SCREEN_CAPTURE=1",
-    ]
+    defines += [ "APPCACHE_USE_SIMPLE_CACHE" ]
     libs += [ "jnigraphics" ]
   } else {
     # Not Android.
diff --git a/content/browser/accessibility/browser_accessibility_com_win.cc b/content/browser/accessibility/browser_accessibility_com_win.cc
index 3beb9da9..8a61098 100644
--- a/content/browser/accessibility/browser_accessibility_com_win.cc
+++ b/content/browser/accessibility/browser_accessibility_com_win.cc
@@ -492,20 +492,7 @@
     LONG y,
     IA2CoordinateType coord_type,
     LONG* offset) {
-  WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_OFFSET_AT_POINT);
-  AddAccessibilityModeFlags(kScreenReaderAndHTMLAccessibilityModes |
-                            ui::AXMode::kInlineTextBoxes);
-  if (!owner())
-    return E_FAIL;
-
-  if (!offset)
-    return E_INVALIDARG;
-
-  // TODO(dmazzoni): implement this. We're returning S_OK for now so that
-  // screen readers still return partially accurate results rather than
-  // completely failing.
-  *offset = 0;
-  return S_OK;
+  return AXPlatformNodeWin::get_offsetAtPoint(x, y, coord_type, offset);
 }
 
 IFACEMETHODIMP BrowserAccessibilityComWin::scrollSubstringTo(
diff --git a/content/browser/accessibility/dump_accessibility_browsertest_base.cc b/content/browser/accessibility/dump_accessibility_browsertest_base.cc
index aebc90ec..1a5c754b 100644
--- a/content/browser/accessibility/dump_accessibility_browsertest_base.cc
+++ b/content/browser/accessibility/dump_accessibility_browsertest_base.cc
@@ -108,6 +108,10 @@
 
   // Enable exposing "display: none" nodes to the browser process for testing.
   enabled_features.emplace_back(
+      features::kEnableAccessibilityExposeARIAAnnotations);
+
+  // Enable exposing ARIA Annotation roles.
+  enabled_features.emplace_back(
       features::kEnableAccessibilityExposeDisplayNone);
 
   // TODO(dmazzoni): DumpAccessibilityTree expectations are based on the
diff --git a/content/browser/cache_storage/legacy/legacy_cache_storage_manager.cc b/content/browser/cache_storage/legacy/legacy_cache_storage_manager.cc
index 537640fe..268a1e9 100644
--- a/content/browser/cache_storage/legacy/legacy_cache_storage_manager.cc
+++ b/content/browser/cache_storage/legacy/legacy_cache_storage_manager.cc
@@ -70,7 +70,11 @@
   // have a modified time older than the index's modified time.  Modifying
   // the index should update the directories time as well.  Therefore we
   // should be guaranteed that the time is equal here.
-  DCHECK_EQ(base_path_time, index_time);
+  //
+  // In practice, though, there can be a few microseconds difference on
+  // some operating systems so we can't do an exact DCHECK here.  Instead
+  // we do a fuzzy DCHECK allowing some microseconds difference.
+  DCHECK_LE((index_time - base_path_time).magnitude().InMicroseconds(), 10);
 
   int64_t storage_size = 0;
   for (int i = 0, max = index.cache_size(); i < max; ++i) {
diff --git a/content/browser/loader/file_url_loader_factory.cc b/content/browser/loader/file_url_loader_factory.cc
index d5c926c..b676584 100644
--- a/content/browser/loader/file_url_loader_factory.cc
+++ b/content/browser/loader/file_url_loader_factory.cc
@@ -436,6 +436,10 @@
              LinkFollowingPolicy link_following_policy,
              std::unique_ptr<FileURLLoaderObserver> observer,
              scoped_refptr<net::HttpResponseHeaders> extra_response_headers) {
+    // ClusterFuzz depends on the following VLOG to get resource dependencies.
+    // See crbug.com/715656.
+    VLOG(1) << "FileURLLoader::Start: " << request.url;
+
     network::ResourceResponseHead head;
     head.request_start = base::TimeTicks::Now();
     head.response_start = base::TimeTicks::Now();
diff --git a/content/browser/permissions/permission_service_context.cc b/content/browser/permissions/permission_service_context.cc
index 921cc41..c3ab8129 100644
--- a/content/browser/permissions/permission_service_context.cc
+++ b/content/browser/permissions/permission_service_context.cc
@@ -28,6 +28,8 @@
     observer_.set_disconnect_handler(base::BindOnce(
         &PermissionSubscription::OnConnectionError, base::Unretained(this)));
   }
+  PermissionSubscription(const PermissionSubscription&) = delete;
+  PermissionSubscription& operator=(const PermissionSubscription&) = delete;
 
   ~PermissionSubscription() {
     DCHECK_NE(id_, 0);
@@ -50,7 +52,7 @@
   void set_id(int id) { id_ = id; }
 
  private:
-  PermissionServiceContext* context_;
+  PermissionServiceContext* const context_;
   mojo::Remote<blink::mojom::PermissionObserver> observer_;
   int id_ = 0;
 };
@@ -110,16 +112,16 @@
       PermissionControllerImpl::FromBrowserContext(browser_context)
           ->SubscribePermissionStatusChange(
               permission_type, render_frame_host_, requesting_origin,
-              base::Bind(&PermissionSubscription::OnPermissionStatusChanged,
-                         base::Unretained(subscription.get())));
+              base::BindRepeating(
+                  &PermissionSubscription::OnPermissionStatusChanged,
+                  base::Unretained(subscription.get())));
   subscription->set_id(subscription_id);
   subscriptions_[subscription_id] = std::move(subscription);
 }
 
 void PermissionServiceContext::ObserverHadConnectionError(int subscription_id) {
-  auto it = subscriptions_.find(subscription_id);
-  DCHECK(it != subscriptions_.end());
-  subscriptions_.erase(it);
+  size_t erased = subscriptions_.erase(subscription_id);
+  DCHECK_EQ(1u, erased);
 }
 
 void PermissionServiceContext::RenderFrameHostChanged(
@@ -168,8 +170,4 @@
                         : GURL();
 }
 
-RenderFrameHost* PermissionServiceContext::render_frame_host() const {
-  return render_frame_host_;
-}
-
-} // namespace content
+}  // namespace content
diff --git a/content/browser/permissions/permission_service_context.h b/content/browser/permissions/permission_service_context.h
index c54f763f..4f93be5 100644
--- a/content/browser/permissions/permission_service_context.h
+++ b/content/browser/permissions/permission_service_context.h
@@ -5,7 +5,9 @@
 #ifndef CONTENT_BROWSER_PERMISSIONS_PERMISSION_SERVICE_CONTEXT_H_
 #define CONTENT_BROWSER_PERMISSIONS_PERMISSION_SERVICE_CONTEXT_H_
 
-#include "base/macros.h"
+#include <memory>
+#include <unordered_map>
+
 #include "content/common/content_export.h"
 #include "content/public/browser/permission_type.h"
 #include "content/public/browser/web_contents_observer.h"
@@ -32,6 +34,8 @@
  public:
   explicit PermissionServiceContext(RenderFrameHost* render_frame_host);
   explicit PermissionServiceContext(RenderProcessHost* render_process_host);
+  PermissionServiceContext(const PermissionServiceContext&) = delete;
+  PermissionServiceContext& operator=(const PermissionServiceContext&) = delete;
   ~PermissionServiceContext() override;
 
   void CreateService(
@@ -55,7 +59,7 @@
 
   GURL GetEmbeddingOrigin() const;
 
-  RenderFrameHost* render_frame_host() const;
+  RenderFrameHost* render_frame_host() const { return render_frame_host_; }
   RenderProcessHost* render_process_host() const {
     return render_process_host_;
   }
@@ -69,15 +73,13 @@
   void FrameDeleted(RenderFrameHost* render_frame_host) override;
   void DidFinishNavigation(NavigationHandle* navigation_handle) override;
 
-  void CloseBindings(RenderFrameHost*);
+  void CloseBindings(RenderFrameHost* render_frame_host);
 
-  RenderFrameHost* render_frame_host_;
-  RenderProcessHost* render_process_host_;
+  RenderFrameHost* const render_frame_host_;
+  RenderProcessHost* const render_process_host_;
   mojo::UniqueReceiverSet<blink::mojom::PermissionService> services_;
   std::unordered_map<int, std::unique_ptr<PermissionSubscription>>
       subscriptions_;
-
-  DISALLOW_COPY_AND_ASSIGN(PermissionServiceContext);
 };
 
 }  // namespace content
diff --git a/content/browser/renderer_host/dwrite_font_lookup_table_builder_win.cc b/content/browser/renderer_host/dwrite_font_lookup_table_builder_win.cc
index 0f0914a..c920205 100644
--- a/content/browser/renderer_host/dwrite_font_lookup_table_builder_win.cc
+++ b/content/browser/renderer_host/dwrite_font_lookup_table_builder_win.cc
@@ -385,6 +385,8 @@
         FROM_HERE, base::BlockingType::MAY_BLOCK);
 
     outstanding_family_results_ = collection_->GetFontFamilyCount();
+    family_results_empty_ = 0;
+    family_results_non_empty_ = 0;
     UMA_HISTOGRAM_CUSTOM_COUNTS(
         "DirectWrite.Fonts.Proxy.FamilyCountIndexingStart",
         outstanding_family_results_, 1, 5000, 50);
@@ -400,9 +402,8 @@
          base::ThreadPolicy::MUST_USE_FOREGROUND,
          base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
         base::BindOnce(
-            &DWriteFontLookupTableBuilder::ExtractPathAndNamesFromFamily,
-            collection_, family_index, start_time_table_build_,
-            slow_down_mode_for_testing_,
+            &ExtractPathAndNamesFromFamily, collection_, family_index,
+            start_time_table_build_, slow_down_mode_for_testing_,
             OptionalOrNullptr(hang_event_for_testing_), IndexingTimeout()),
         base::BindOnce(&DWriteFontLookupTableBuilder::
                            AppendFamilyResultAndFinalizeIfNeeded,
@@ -554,6 +555,11 @@
   if (font_table_built_.IsSignaled())
     return;
 
+  if (!family_result.size())
+    family_results_empty_++;
+  else
+    family_results_non_empty_++;
+
   for (const FontFileWithUniqueNames& font_of_family : family_result) {
     blink::FontUniqueNameTable_UniqueFont* added_unique_font =
         font_unique_name_table_->add_fonts();
@@ -597,6 +603,13 @@
   UMA_HISTOGRAM_BOOLEAN("DirectWrite.Fonts.Proxy.TableBuildTimedOut",
                         timed_out);
 
+  int empty_family_results_percentage =
+      round(((family_results_empty_ * 1.0f) /
+             (family_results_empty_ + family_results_non_empty_)) *
+            100.0);
+  UMA_HISTOGRAM_PERCENTAGE("DirectWrite.Fonts.Proxy.EmptyFamilyResultsRatio",
+                           empty_family_results_percentage);
+
   unsigned num_font_files = font_unique_name_table->fonts_size();
 
   blink::FontTableMatcher::SortUniqueNameTableForSearch(
diff --git a/content/browser/renderer_host/dwrite_font_lookup_table_builder_win.h b/content/browser/renderer_host/dwrite_font_lookup_table_builder_win.h
index 4d23f8ac..3597b811 100644
--- a/content/browser/renderer_host/dwrite_font_lookup_table_builder_win.h
+++ b/content/browser/renderer_host/dwrite_font_lookup_table_builder_win.h
@@ -201,6 +201,8 @@
   Microsoft::WRL::ComPtr<IDWriteFactory3> factory3_;
   SlowDownMode slow_down_mode_for_testing_ = SlowDownMode::kNoSlowdown;
   uint32_t outstanding_family_results_ = 0;
+  uint32_t family_results_non_empty_ = 0;
+  uint32_t family_results_empty_ = 0;
   base::TimeTicks start_time_table_ready_;
   base::TimeTicks start_time_table_build_;
   base::FilePath cache_directory_;
diff --git a/content/browser/renderer_host/media/in_process_launched_video_capture_device.cc b/content/browser/renderer_host/media/in_process_launched_video_capture_device.cc
index 7d9cb11..a9c0ad6 100644
--- a/content/browser/renderer_host/media/in_process_launched_video_capture_device.cc
+++ b/content/browser/renderer_host/media/in_process_launched_video_capture_device.cc
@@ -9,10 +9,12 @@
 #include "base/bind.h"
 #include "base/bind_helpers.h"
 #include "base/metrics/histogram_macros.h"
+#include "build/build_config.h"
+#include "content/common/buildflags.h"
 #include "content/public/browser/browser_thread.h"
 #include "media/media_buildflags.h"
 
-#if defined(ENABLE_SCREEN_CAPTURE) && !defined(OS_ANDROID)
+#if BUILDFLAG(ENABLE_SCREEN_CAPTURE) && !defined(OS_ANDROID)
 #include "content/browser/media/capture/desktop_capture_device.h"
 #endif
 
diff --git a/content/browser/renderer_host/media/in_process_video_capture_device_launcher.cc b/content/browser/renderer_host/media/in_process_video_capture_device_launcher.cc
index 8541327..c9ba78a 100644
--- a/content/browser/renderer_host/media/in_process_video_capture_device_launcher.cc
+++ b/content/browser/renderer_host/media/in_process_video_capture_device_launcher.cc
@@ -15,6 +15,7 @@
 #include "build/build_config.h"
 #include "content/browser/renderer_host/media/in_process_launched_video_capture_device.h"
 #include "content/browser/renderer_host/media/video_capture_controller.h"
+#include "content/common/buildflags.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/desktop_media_id.h"
@@ -29,7 +30,7 @@
 #include "media/capture/video/video_frame_receiver_on_task_runner.h"
 #include "third_party/blink/public/common/mediastream/media_stream_request.h"
 
-#if defined(ENABLE_SCREEN_CAPTURE)
+#if BUILDFLAG(ENABLE_SCREEN_CAPTURE)
 #include "content/browser/media/capture/desktop_capture_device_uma_types.h"
 #if defined(OS_ANDROID)
 #include "content/browser/media/capture/screen_capture_device_android.h"
@@ -40,7 +41,7 @@
 #endif
 #include "content/browser/media/capture/desktop_capture_device.h"
 #endif  // defined(OS_ANDROID)
-#endif  // defined(ENABLE_SCREEN_CAPTURE)
+#endif  // BUILDFLAG(ENABLE_SCREEN_CAPTURE)
 
 #if defined(OS_CHROMEOS)
 #include "content/browser/gpu/chromeos/video_capture_dependencies.h"
@@ -140,7 +141,7 @@
       break;
     }
 
-#if defined(ENABLE_SCREEN_CAPTURE)
+#if BUILDFLAG(ENABLE_SCREEN_CAPTURE)
 #if !defined(OS_ANDROID)
     case blink::mojom::MediaStreamType::GUM_TAB_VIDEO_CAPTURE:
       start_capture_closure = base::BindOnce(
@@ -221,7 +222,7 @@
           std::move(after_start_capture_callback));
       break;
     }
-#endif  // defined(ENABLE_SCREEN_CAPTURE)
+#endif  // BUILDFLAG(ENABLE_SCREEN_CAPTURE)
 
     default: {
       NOTIMPLEMENTED();
@@ -329,7 +330,7 @@
   std::move(result_callback).Run(std::move(video_capture_device));
 }
 
-#if defined(ENABLE_SCREEN_CAPTURE)
+#if BUILDFLAG(ENABLE_SCREEN_CAPTURE)
 
 #if !defined(OS_ANDROID)
 void InProcessVideoCaptureDeviceLauncher::DoStartTabCaptureOnDeviceThread(
@@ -407,7 +408,7 @@
   std::move(result_callback).Run(std::move(video_capture_device));
 }
 
-#endif  // defined(ENABLE_SCREEN_CAPTURE)
+#endif  // BUILDFLAG(ENABLE_SCREEN_CAPTURE)
 
 void InProcessVideoCaptureDeviceLauncher::
     DoStartFakeDisplayCaptureOnDeviceThread(
diff --git a/content/browser/renderer_host/media/video_capture_manager_unittest.cc b/content/browser/renderer_host/media/video_capture_manager_unittest.cc
index ef523692..53f7440f 100644
--- a/content/browser/renderer_host/media/video_capture_manager_unittest.cc
+++ b/content/browser/renderer_host/media/video_capture_manager_unittest.cc
@@ -24,6 +24,7 @@
 #include "content/browser/renderer_host/media/video_capture_controller_event_handler.h"
 #include "content/browser/screenlock_monitor/screenlock_monitor.h"
 #include "content/browser/screenlock_monitor/screenlock_monitor_source.h"
+#include "content/common/buildflags.h"
 #include "content/public/browser/desktop_media_id.h"
 #include "content/public/test/browser_task_environment.h"
 #include "media/capture/video/fake_video_capture_device_factory.h"
@@ -907,7 +908,7 @@
   vcm_->UnregisterListener(listener_.get());
 }
 
-#if defined(ENABLE_SCREEN_CAPTURE) && !defined(OS_ANDROID)
+#if BUILDFLAG(ENABLE_SCREEN_CAPTURE) && !defined(OS_ANDROID)
 // Try to open, start a desktop capture device, and confirm it's closed on
 // ScreenLocked event on desktop platforms.
 TEST_F(VideoCaptureManagerTest, DesktopCaptureDeviceClosedOnScreenlock) {
@@ -941,7 +942,7 @@
   base::RunLoop().RunUntilIdle();
   vcm_->UnregisterListener(listener_.get());
 }
-#endif  // ENABLE_SCREEN_CAPTURE && !OS_ANDROID
+#endif  // ENABLE_SCREEN_CAPTURE && !defined(OS_ANDROID)
 
 // TODO(mcasas): Add a test to check consolidation of the supported formats
 // provided by the device when http://crbug.com/323913 is closed.
diff --git a/content/browser/renderer_host/render_widget_host_input_event_router.cc b/content/browser/renderer_host/render_widget_host_input_event_router.cc
index 23956a4..9842e5a 100644
--- a/content/browser/renderer_host/render_widget_host_input_event_router.cc
+++ b/content/browser/renderer_host/render_widget_host_input_event_router.cc
@@ -1974,7 +1974,7 @@
 
 void RenderWidgetHostInputEventRouter::SetAutoScrollInProgress(
     bool is_autoscroll_in_progress) {
-  event_targeter_->set_is_auto_scroll_in_progress(is_autoscroll_in_progress);
+  event_targeter_->SetIsAutoScrollInProgress(is_autoscroll_in_progress);
 }
 
 }  // namespace content
diff --git a/content/browser/renderer_host/render_widget_host_input_event_router_unittest.cc b/content/browser/renderer_host/render_widget_host_input_event_router_unittest.cc
index ee6a030..dc07831 100644
--- a/content/browser/renderer_host/render_widget_host_input_event_router_unittest.cc
+++ b/content/browser/renderer_host/render_widget_host_input_event_router_unittest.cc
@@ -909,4 +909,29 @@
                                    view_root_.get());
 }
 
+// Input events get latched to a target when middle click autoscroll is in
+// progress. This tests enusres that autoscroll latched target state is cleared
+// when the view, input events are latched to is destroyed.
+TEST_F(RenderWidgetHostInputEventRouterTest,
+       EnsureAutoScrollLatchedTargetIsCleared) {
+  ChildViewState child = MakeChildView(view_root_.get());
+
+  // Simulate middle click mouse event.
+  blink::WebMouseEvent mouse_event(
+      blink::WebInputEvent::kMouseDown, blink::WebInputEvent::kNoModifiers,
+      blink::WebInputEvent::GetStaticTimeStampForTests());
+  mouse_event.button = blink::WebPointerProperties::Button::kMiddle;
+
+  view_root_->SetHittestResult(child.view.get(), false);
+  RenderWidgetTargeter* targeter = rwhier()->GetRenderWidgetTargeterForTests();
+  rwhier()->RouteMouseEvent(view_root_.get(), &mouse_event,
+                            ui::LatencyInfo(ui::SourceEventType::MOUSE));
+  // Set middle click autoscroll in progress to true.
+  rwhier()->SetAutoScrollInProgress(true);
+  // Destroy the view/target, middle click autoscroll is latched to.
+  rwhier()->OnRenderWidgetHostViewBaseDestroyed(child.view.get());
+
+  EXPECT_FALSE(targeter->is_auto_scroll_in_progress());
+}
+
 }  // namespace content
diff --git a/content/browser/renderer_host/render_widget_targeter.cc b/content/browser/renderer_host/render_widget_targeter.cc
index af789f4c..6e121183 100644
--- a/content/browser/renderer_host/render_widget_targeter.cc
+++ b/content/browser/renderer_host/render_widget_targeter.cc
@@ -290,12 +290,25 @@
 
 void RenderWidgetTargeter::ViewWillBeDestroyed(RenderWidgetHostViewBase* view) {
   unresponsive_views_.erase(view);
+
+  if (is_autoscroll_in_progress_ && middle_click_result_.view == view) {
+    SetIsAutoScrollInProgress(false);
+  }
 }
 
 bool RenderWidgetTargeter::HasEventsPendingDispatch() const {
   return request_in_flight_ || !requests_.empty();
 }
 
+void RenderWidgetTargeter::SetIsAutoScrollInProgress(
+    bool autoscroll_in_progress) {
+  is_autoscroll_in_progress_ = autoscroll_in_progress;
+
+  // If middle click autoscroll ends, reset |middle_click_result_|.
+  if (!autoscroll_in_progress)
+    middle_click_result_ = RenderWidgetTargetResult();
+}
+
 void RenderWidgetTargeter::QueryClientInternal(
     RenderWidgetHostViewBase* target,
     const gfx::PointF& target_location,
diff --git a/content/browser/renderer_host/render_widget_targeter.h b/content/browser/renderer_host/render_widget_targeter.h
index 4b82293..058f01c 100644
--- a/content/browser/renderer_host/render_widget_targeter.h
+++ b/content/browser/renderer_host/render_widget_targeter.h
@@ -134,13 +134,7 @@
     return request_in_flight_.has_value();
   }
 
-  void set_is_auto_scroll_in_progress(bool autoscroll_in_progress) {
-    is_autoscroll_in_progress_ = autoscroll_in_progress;
-
-    // If middle click autoscroll ends, reset |middle_click_result_|.
-    if (!autoscroll_in_progress)
-      middle_click_result_ = RenderWidgetTargetResult();
-  }
+  void SetIsAutoScrollInProgress(bool autoscroll_in_progress);
 
   bool is_auto_scroll_in_progress() const { return is_autoscroll_in_progress_; }
 
diff --git a/content/browser/webui/web_ui_security_browsertest.cc b/content/browser/webui/web_ui_security_browsertest.cc
new file mode 100644
index 0000000..65448ac
--- /dev/null
+++ b/content/browser/webui/web_ui_security_browsertest.cc
@@ -0,0 +1,224 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/hash/hash.h"
+#include "base/memory/ref_counted_memory.h"
+#include "base/path_service.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/threading/thread_restrictions.h"
+#include "content/browser/child_process_security_policy_impl.h"
+#include "content/browser/frame_host/frame_tree_node.h"
+#include "content/browser/web_contents/web_contents_impl.h"
+#include "content/browser/webui/web_ui_controller_factory_registry.h"
+#include "content/public/browser/web_ui.h"
+#include "content/public/browser/web_ui_controller.h"
+#include "content/public/browser/web_ui_data_source.h"
+#include "content/public/common/bindings_policy.h"
+#include "content/public/common/content_paths.h"
+#include "content/public/test/browser_test_utils.h"
+#include "content/public/test/content_browser_test.h"
+#include "content/public/test/content_browser_test_utils.h"
+#include "content/public/test/test_frame_navigation_observer.h"
+#include "content/public/test/test_utils.h"
+#include "content/shell/browser/shell.h"
+#include "content/test/content_browser_test_utils_internal.h"
+#include "net/base/url_util.h"
+#include "url/gurl.h"
+
+namespace content {
+
+namespace {
+
+void GetResource(const std::string& id,
+                 const WebUIDataSource::GotDataCallback& callback) {
+  base::ScopedAllowBlockingForTesting allow_blocking;
+
+  std::string contents;
+  base::FilePath path;
+  CHECK(base::PathService::Get(content::DIR_TEST_DATA, &path));
+  path = path.AppendASCII(id.substr(0, id.find("?")));
+  CHECK(base::ReadFileToString(path, &contents)) << path.value();
+
+  base::RefCountedString* ref_contents = new base::RefCountedString;
+  ref_contents->data() = contents;
+  callback.Run(ref_contents);
+}
+
+struct WebUIControllerConfig {
+  int bindings = BINDINGS_POLICY_MOJO_WEB_UI;
+  std::string child_src = "child-src 'self' chrome://web-ui-subframe/;";
+  bool disable_xfo = false;
+};
+
+class TestWebUIController : public WebUIController {
+ public:
+  TestWebUIController(WebUI* web_ui,
+                      const GURL& base_url,
+                      const WebUIControllerConfig& config)
+      : WebUIController(web_ui) {
+    web_ui->SetBindings(config.bindings);
+
+    WebUIDataSource* data_source = WebUIDataSource::Create(base_url.host());
+    data_source->SetRequestFilter(
+        base::BindRepeating([](const std::string& path) { return true; }),
+        base::BindRepeating(&GetResource));
+
+    if (!config.child_src.empty())
+      data_source->OverrideContentSecurityPolicyChildSrc(config.child_src);
+
+    if (config.disable_xfo)
+      data_source->DisableDenyXFrameOptions();
+
+    WebUIDataSource::Add(web_ui->GetWebContents()->GetBrowserContext(),
+                         data_source);
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(TestWebUIController);
+};
+
+class TestWebUIControllerFactory : public WebUIControllerFactory {
+ public:
+  TestWebUIControllerFactory() {}
+
+  std::unique_ptr<WebUIController> CreateWebUIControllerForURL(
+      WebUI* web_ui,
+      const GURL& url) override {
+    if (!url.SchemeIs(kChromeUIScheme))
+      return nullptr;
+
+    WebUIControllerConfig config;
+    if (url.has_query()) {
+      std::string value;
+      bool has_value = net::GetValueForKeyInQuery(url, "bindings", &value);
+      if (has_value)
+        EXPECT_TRUE(base::StringToInt(value, &(config.bindings)));
+
+      has_value = net::GetValueForKeyInQuery(url, "noxfo", &value);
+      if (has_value && value == "true")
+        config.disable_xfo = true;
+    }
+
+    return std::make_unique<TestWebUIController>(web_ui, url, config);
+  }
+
+  WebUI::TypeID GetWebUIType(BrowserContext* browser_context,
+                             const GURL& url) override {
+    if (!url.SchemeIs(kChromeUIScheme))
+      return WebUI::kNoWebUI;
+
+    return reinterpret_cast<WebUI::TypeID>(base::Hash(url.host()));
+  }
+
+  bool UseWebUIForURL(BrowserContext* browser_context,
+                      const GURL& url) override {
+    return GetWebUIType(browser_context, url) != WebUI::kNoWebUI;
+  }
+  bool UseWebUIBindingsForURL(BrowserContext* browser_context,
+                              const GURL& url) override {
+    return GetWebUIType(browser_context, url) != WebUI::kNoWebUI;
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(TestWebUIControllerFactory);
+};
+
+}  // namespace
+
+class WebUISecurityTest : public ContentBrowserTest {
+ public:
+  WebUISecurityTest() { WebUIControllerFactory::RegisterFactory(&factory_); }
+
+  ~WebUISecurityTest() override {
+    WebUIControllerFactory::UnregisterFactoryForTesting(&factory_);
+  }
+
+  TestWebUIControllerFactory* factory() { return &factory_; }
+
+ private:
+  TestWebUIControllerFactory factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(WebUISecurityTest);
+};
+
+// Loads a WebUI which does not have any bindings.
+IN_PROC_BROWSER_TEST_F(WebUISecurityTest, NoBindings) {
+  GURL test_url(GetWebUIURL("web-ui/title1.html?bindings=0"));
+  EXPECT_TRUE(NavigateToURL(shell(), test_url));
+
+  EXPECT_FALSE(ChildProcessSecurityPolicyImpl::GetInstance()->HasWebUIBindings(
+      shell()->web_contents()->GetMainFrame()->GetProcess()->GetID()));
+}
+
+// Loads a WebUI which has WebUI bindings.
+IN_PROC_BROWSER_TEST_F(WebUISecurityTest, WebUIBindings) {
+  GURL test_url(GetWebUIURL("web-ui/title1.html?bindings=" +
+                            base::NumberToString(BINDINGS_POLICY_WEB_UI)));
+  EXPECT_TRUE(NavigateToURL(shell(), test_url));
+
+  EXPECT_TRUE(ChildProcessSecurityPolicyImpl::GetInstance()->HasWebUIBindings(
+      shell()->web_contents()->GetMainFrame()->GetProcess()->GetID()));
+}
+
+// Loads a WebUI which has Mojo bindings.
+IN_PROC_BROWSER_TEST_F(WebUISecurityTest, MojoBindings) {
+  GURL test_url(GetWebUIURL("web-ui/title1.html?bindings=" +
+                            base::NumberToString(BINDINGS_POLICY_MOJO_WEB_UI)));
+  EXPECT_TRUE(NavigateToURL(shell(), test_url));
+
+  EXPECT_TRUE(ChildProcessSecurityPolicyImpl::GetInstance()->HasWebUIBindings(
+      shell()->web_contents()->GetMainFrame()->GetProcess()->GetID()));
+}
+
+// Loads a WebUI which has both WebUI and Mojo bindings.
+IN_PROC_BROWSER_TEST_F(WebUISecurityTest, WebUIAndMojoBindings) {
+  GURL test_url(GetWebUIURL("web-ui/title1.html?bindings=" +
+                            base::NumberToString(BINDINGS_POLICY_WEB_UI |
+                                                 BINDINGS_POLICY_MOJO_WEB_UI)));
+  EXPECT_TRUE(NavigateToURL(shell(), test_url));
+
+  EXPECT_TRUE(ChildProcessSecurityPolicyImpl::GetInstance()->HasWebUIBindings(
+      shell()->web_contents()->GetMainFrame()->GetProcess()->GetID()));
+}
+
+// Verify that a WebUI can add a subframe for its own WebUI.
+// Note: This works by accident, since the main frame WebUI will respond to
+// the navigation request for the subframe. See https://crbug.com/713313.
+IN_PROC_BROWSER_TEST_F(WebUISecurityTest, WebUISameSiteSubframe) {
+  // TODO(nasko): Remove the noxfo=true parameter when https://crbug.com/713313
+  // is fixed. It is required for now, since currently WebUIs are not created
+  // for subframes, so the main frame WebUI object will respond to the
+  // navigation request for the subframe, which means we need to disable
+  // X-Frame-Options on it.
+  GURL test_url(GetWebUIURL("web-ui/page_with_blank_iframe.html?noxfo=true"));
+  EXPECT_TRUE(NavigateToURL(shell(), test_url));
+
+  FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
+                            ->GetFrameTree()
+                            ->root();
+  EXPECT_EQ(1U, root->child_count());
+
+  TestFrameNavigationObserver observer(root->child_at(0));
+  GURL subframe_url(GetWebUIURL("web-ui/title1.html?noxfo=true"));
+  NavigateFrameToURL(root->child_at(0), subframe_url);
+
+  EXPECT_TRUE(observer.last_navigation_succeeded());
+  EXPECT_EQ(subframe_url, observer.last_committed_url());
+  EXPECT_EQ(root->current_frame_host()->GetSiteInstance(),
+            root->child_at(0)->current_frame_host()->GetSiteInstance());
+  EXPECT_EQ(
+      GetWebUIURL("web-ui"),
+      root->child_at(0)->current_frame_host()->GetSiteInstance()->GetSiteURL());
+
+  // TODO(nasko): The subframe should have its own WebUI object, so the
+  // following expectation should be inverted once https://crbug.com/713313 is
+  // fixed.
+  EXPECT_EQ(nullptr, root->child_at(0)->current_frame_host()->web_ui());
+  EXPECT_NE(root->current_frame_host()->web_ui(),
+            root->child_at(0)->current_frame_host()->web_ui());
+}
+
+}  // namespace content
diff --git a/content/child/runtime_features.cc b/content/child/runtime_features.cc
index c729bed..b8018f5 100644
--- a/content/child/runtime_features.cc
+++ b/content/child/runtime_features.cc
@@ -315,6 +315,10 @@
   if (base::FeatureList::IsEnabled(features::kFeaturePolicyForSandbox))
     WebRuntimeFeatures::EnableFeaturePolicyForSandbox(true);
 
+  WebRuntimeFeatures::EnableAccessibilityExposeARIAAnnotations(
+      base::FeatureList::IsEnabled(
+          features::kEnableAccessibilityExposeARIAAnnotations));
+
   WebRuntimeFeatures::EnableAccessibilityExposeDisplayNone(
       base::FeatureList::IsEnabled(
           features::kEnableAccessibilityExposeDisplayNone));
diff --git a/content/common/BUILD.gn b/content/common/BUILD.gn
index 0a5b5c5..7070753 100644
--- a/content/common/BUILD.gn
+++ b/content/common/BUILD.gn
@@ -24,6 +24,7 @@
   flags = [
     "USE_EXTERNAL_POPUP_MENU=$use_external_popup_menu",
     "ALLOW_CRITICAL_MEMORY_PRESSURE_HANDLING_IN_FOREGROUND=$allow_critical_memory_pressure_handling_in_foreground",
+    "ENABLE_SCREEN_CAPTURE=$enable_screen_capture",
   ]
 }
 
diff --git a/content/common/features.gni b/content/common/features.gni
index b1bcb83..13874c3 100644
--- a/content/common/features.gni
+++ b/content/common/features.gni
@@ -12,3 +12,5 @@
   # false, critical memory pressure is treated like moderate pressure in foreground).
   allow_critical_memory_pressure_handling_in_foreground = is_chromecast
 }
+
+enable_screen_capture = is_linux || is_mac || is_win || is_android
diff --git a/content/gpu/gpu_child_thread.cc b/content/gpu/gpu_child_thread.cc
index e21a7d5..2d2128f2 100644
--- a/content/gpu/gpu_child_thread.cc
+++ b/content/gpu/gpu_child_thread.cc
@@ -331,7 +331,7 @@
       gpu_service->gpu_channel_manager()->gpu_driver_bug_workarounds(),
       gpu_service->gpu_feature_info(),
       gpu_service->media_gpu_channel_manager()->AsWeakPtr(),
-      std::move(overlay_factory_cb)));
+      gpu_service->gpu_memory_buffer_factory(), std::move(overlay_factory_cb)));
 
   if (GetContentClient()->gpu()) {  // NULL in tests.
     GetContentClient()->gpu()->GpuServiceInitialized(
diff --git a/content/gpu/gpu_service_factory.cc b/content/gpu/gpu_service_factory.cc
index 4b8a936..cc4ac25 100644
--- a/content/gpu/gpu_service_factory.cc
+++ b/content/gpu/gpu_service_factory.cc
@@ -26,6 +26,7 @@
     const gpu::GpuDriverBugWorkarounds& gpu_workarounds,
     const gpu::GpuFeatureInfo& gpu_feature_info,
     base::WeakPtr<media::MediaGpuChannelManager> media_gpu_channel_manager,
+    gpu::GpuMemoryBufferFactory* gpu_memory_buffer_factory,
     media::AndroidOverlayMojoFactoryCB android_overlay_factory_cb) {
 #if BUILDFLAG(ENABLE_MOJO_MEDIA_IN_GPU_PROCESS)
   gpu_preferences_ = gpu_preferences;
@@ -33,6 +34,7 @@
   gpu_feature_info_ = gpu_feature_info;
   task_runner_ = base::ThreadTaskRunnerHandle::Get();
   media_gpu_channel_manager_ = std::move(media_gpu_channel_manager);
+  gpu_memory_buffer_factory_ = gpu_memory_buffer_factory;
   android_overlay_factory_cb_ = std::move(android_overlay_factory_cb);
 #endif
 }
@@ -70,8 +72,8 @@
     FactoryCallback factory = base::BindOnce(
         &media::CreateGpuMediaService, std::move(request), gpu_preferences_,
         gpu_workarounds_, gpu_feature_info_, task_runner_,
-        media_gpu_channel_manager_, android_overlay_factory_cb_,
-        std::move(cdm_proxy_factory_cb));
+        media_gpu_channel_manager_, gpu_memory_buffer_factory_,
+        android_overlay_factory_cb_, std::move(cdm_proxy_factory_cb));
     task_runner->PostTask(
         FROM_HERE, base::BindOnce(
                        [](FactoryCallback factory) {
diff --git a/content/gpu/gpu_service_factory.h b/content/gpu/gpu_service_factory.h
index cb4314b..1c15091d 100644
--- a/content/gpu/gpu_service_factory.h
+++ b/content/gpu/gpu_service_factory.h
@@ -16,6 +16,10 @@
 #include "media/mojo/buildflags.h"
 #include "services/service_manager/public/mojom/service.mojom.h"
 
+namespace gpu {
+class GpuMemoryBufferFactory;
+}
+
 namespace media {
 class MediaGpuChannelManager;
 }
@@ -30,6 +34,7 @@
       const gpu::GpuDriverBugWorkarounds& gpu_workarounds,
       const gpu::GpuFeatureInfo& gpu_feature_info,
       base::WeakPtr<media::MediaGpuChannelManager> media_gpu_channel_manager,
+      gpu::GpuMemoryBufferFactory* gpu_memory_buffer_factory,
       media::AndroidOverlayMojoFactoryCB android_overlay_factory_cb);
   ~GpuServiceFactory();
 
@@ -46,6 +51,8 @@
   scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
   base::WeakPtr<media::MediaGpuChannelManager> media_gpu_channel_manager_;
   media::AndroidOverlayMojoFactoryCB android_overlay_factory_cb_;
+  // Indirectly owned by GpuChildThread.
+  gpu::GpuMemoryBufferFactory* gpu_memory_buffer_factory_;
   gpu::GpuPreferences gpu_preferences_;
   gpu::GpuDriverBugWorkarounds gpu_workarounds_;
   gpu::GpuFeatureInfo gpu_feature_info_;
diff --git a/content/public/common/content_features.cc b/content/public/common/content_features.cc
index 3a065d7..63372ca 100644
--- a/content/public/common/content_features.cc
+++ b/content/public/common/content_features.cc
@@ -5,6 +5,10 @@
 #include "content/public/common/content_features.h"
 #include "build/build_config.h"
 
+#if defined(OS_WIN)
+#include "base/win/windows_version.h"
+#endif
+
 namespace features {
 
 // All features in alphabetical order.
@@ -819,6 +823,10 @@
 #if defined(OS_ANDROID) || defined(OS_CHROMEOS)
   return VideoCaptureServiceConfiguration::kEnabledForBrowserProcess;
 #else
+#if defined(OS_WIN)
+  if (base::win::GetVersion() <= base::win::Version::WIN7)
+    return VideoCaptureServiceConfiguration::kEnabledForBrowserProcess;
+#endif
   return base::FeatureList::IsEnabled(
              features::kRunVideoCaptureServiceInBrowserProcess)
              ? VideoCaptureServiceConfiguration::kEnabledForBrowserProcess
diff --git a/content/renderer/render_view_impl.cc b/content/renderer/render_view_impl.cc
index 4f79fb0..72f8ac2 100644
--- a/content/renderer/render_view_impl.cc
+++ b/content/renderer/render_view_impl.cc
@@ -534,7 +534,6 @@
   OnSetRendererPrefs(*params->renderer_preferences);
 
   GetContentClient()->renderer()->RenderViewCreated(this);
-  page_zoom_level_ = 0;
 
   nav_state_sync_timer_.SetTaskRunner(task_runner);
 
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index 0bc6ad9..eff69d8 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -10,6 +10,7 @@
 import("//build/config/jumbo.gni")
 import("//build/config/ui.gni")
 import("//build/nocompile.gni")
+import("//content/common/features.gni")
 import("//media/media_options.gni")
 import("//mojo/public/tools/bindings/mojom.gni")
 import("//net/features.gni")
@@ -1087,6 +1088,7 @@
     "../browser/webrtc/webrtc_webcam_browsertest.cc",
     "../browser/webrtc/webrtc_webcam_browsertest.h",
     "../browser/webui/web_ui_mojo_browsertest.cc",
+    "../browser/webui/web_ui_security_browsertest.cc",
     "../browser/worker_host/worker_browsertest.cc",
     "../browser/worker_network_isolation_key_browsertest.cc",
     "../child/webthemeengine_impl_default_browsertest.cc",
@@ -2046,6 +2048,7 @@
     "//content/browser/service_worker:service_worker_proto",
     "//content/browser/speech/proto",
     "//content/child:for_content_tests",
+    "//content/common:buildflags",
     "//content/gpu",
     "//content/public/browser",
     "//content/public/child",
@@ -2201,9 +2204,9 @@
   }
 
   # Screen capture unit tests.
-  if (is_linux || is_mac || is_win) {
-    defines += [ "ENABLE_SCREEN_CAPTURE=1" ]
+  if (enable_screen_capture && !is_android) {
     deps += [
+      "//content/common:buildflags",
       "//media/capture:test_support",
       "//third_party/libyuv",
       "//third_party/webrtc/modules/desktop_capture",
diff --git a/content/test/data/page_with_blank_iframe.html b/content/test/data/page_with_blank_iframe.html
new file mode 100644
index 0000000..f3d1538
--- /dev/null
+++ b/content/test/data/page_with_blank_iframe.html
@@ -0,0 +1,7 @@
+<html>
+<head></head>
+<body>
+  <p>This page has blank iframe. Yay for iframes!
+  <p><iframe id="test_iframe"></iframe>
+</body>
+</html>
diff --git a/courgette/disassembler_elf_32_arm.cc b/courgette/disassembler_elf_32_arm.cc
index 6c8a841..b4fbf3f0 100644
--- a/courgette/disassembler_elf_32_arm.cc
+++ b/courgette/disassembler_elf_32_arm.cc
@@ -524,7 +524,7 @@
       // 0        | 0          1
       // 0        | 1          0
       // 1        | 1          1
-      on_32bit = (~(on_32bit ^ (op_size == 4))) != 0;
+      on_32bit = (on_32bit == (op_size == 4));
     } else {
       // Move 2 bytes at a time, but track 32-bit boundaries
       p += 2;
diff --git a/device/vr/android/gvr/gvr_delegate.cc b/device/vr/android/gvr/gvr_delegate.cc
index 9b596da..a09b837 100644
--- a/device/vr/android/gvr/gvr_delegate.cc
+++ b/device/vr/android/gvr/gvr_delegate.cc
@@ -151,6 +151,22 @@
   pose->angular_velocity =
       GetAngularVelocityFromPoses(*head_mat_ptr, head_mat_2, epsilon_seconds);
 
+  // The position is emulated unless the current tracking status is 6DoF and is
+  // not still initializing or invalid.
+  pose->emulated_position = true;
+  gvr::Properties props = gvr_api->GetCurrentProperties();
+  gvr::Value head_tracking_status;
+  if (props.Get(GVR_PROPERTY_TRACKING_STATUS, &head_tracking_status)) {
+    bool has_6dof =
+        !!(head_tracking_status.fl & GVR_TRACKING_STATUS_FLAG_HAS_6DOF);
+    bool initialized =
+        !(head_tracking_status.fl & GVR_TRACKING_STATUS_FLAG_INITIALIZING);
+    bool valid = !(head_tracking_status.fl & GVR_TRACKING_STATUS_FLAG_INVALID);
+    if (has_6dof && initialized && valid) {
+      pose->emulated_position = false;
+    }
+  }
+
   return pose;
 }
 
diff --git a/docs/commit_checklist.md b/docs/commit_checklist.md
index b67430a..7e9fa367 100644
--- a/docs/commit_checklist.md
+++ b/docs/commit_checklist.md
@@ -1,8 +1,11 @@
 # Commit Checklist for Chromium Workflow
 
-Here is a helpful checklist to go through before uploading change lists (CLs)
-on Gerrit. This checklist is designed to be streamlined. See [contributing to
-Chromium][contributing] for a more thorough reference.
+Here is a helpful checklist to go through before uploading change lists (CLs) on
+Gerrit, which is the code review platform for the Chromium project. This
+checklist is designed to be streamlined. See
+[contributing to Chromium][contributing] for a more thorough reference. The
+intended audience is software engineers who are unfamiliar with contributing to
+the Chromium project.
 
 [TOC]
 
@@ -48,14 +51,14 @@
 
 ## 7. Check over your changes
 
-Run `git upstream-diff` to check over all of the changes you've made from
-the most recent checkpoint on the remote repository.
+Run `git upstream-diff` to check over all of the changes you've made from the
+most recent checkpoint on the remote repository.
 
 ## 8. Stage relevant files for commit
 
 Run `git add <path_to_file>` for all of the relevant files you've modified.
-Unlike other version-control systems such as svn, you have to specifically
-`git add` the files you want to commit before calling `git commit`.
+Unlike other version-control systems such as svn, you have to specifically `git
+add` the files you want to commit before calling `git commit`.
 
 ## 9. Commit your changes
 
@@ -67,20 +70,22 @@
 If you have many commits on your current branch, and you want to avoid some
 nasty commit-by-commit merge conflicts in the next step, it is recommended to
 squash your commits into a single commit. This is done by running `git rebase -i
-origin/master`. You should see a list of commits, each commit starting with the
-word "pick". Make sure the first commit says "pick" and change the rest from
-"pick" to "squash". This will squash each commit into the previous commit,
-which will continue until each commit is squashed into the first commit.
+<upstream-branch>`. The upstream branch is usually origin/master, but check `git
+branch -vv` to make sure. After running the git rebase command, you should see a
+list of commits, each commit starting with the word "pick". Make sure the first
+commit says "pick" and change the rest from "pick" to "squash". This will squash
+each commit into the previous commit, which will continue until each commit is
+squashed into the first commit.
 
 ## 11. Rebase your local repository
 
 Run `git rebase-update`. This command updates all of your local branches with
 remote changes that have landed since you started development work, which
 could've been a while ago. It also deletes any branches that match the remote
-repository, such as after the CL associated with that branch had merged. You
-may run into rebase conflicts which should be manually fixed before proceeding
-with `git rebase --continue`. Rebasing prevents unintended changes from creeping
-into your CL.
+repository, such as after the CL associated with that branch had merged. You may
+run into rebase conflicts which should be manually fixed before proceeding with
+`git rebase --continue`. Rebasing prevents unintended changes from creeping into
+your CL.
 
 Note that rebasing has the potential to break your build, so you might want to
 try re-building afterwards.
@@ -89,17 +94,17 @@
 
 Run `git cl upload`. Some useful options include:
 
-* `--cq-dry-run` (or `-d`) will set the patchset to do a CQ Dry Run.
-* `-r <chromium_username>` will add reviewers.
-* `-b <bug_number>` automatically populates the bug reference line of the commit
-message.
+*   `--cq-dry-run` (or `-d`) will set the patchset to do a CQ Dry Run.
+*   `-r <chromium_username>` will add reviewers.
+*   `-b <bug_number>` automatically populates the bug reference line of the
+    commit message.
 
 ## 13. Check the CL again in Gerrit
 
 Run `git cl web` to go to the Gerrit URL associated with the current branch.
-Open the latest patch set and verify that all of the uploaded files are
-correct. Click `Expand All` to check over all of the individual line-by-line
-changes again.
+Open the latest patch set and verify that all of the uploaded files are correct.
+Click `Expand All` to check over all of the individual line-by-line changes
+again.
 
 ## 14. Make sure all auto-regression tests pass
 
@@ -111,10 +116,11 @@
 
 Click `Find Owners` or run `git cl owners` to find file owners to review your
 code and instruct them about which parts you want them to focus on. Add anyone
-else you think should review your code. For your CL to land, you need an
-approval from an owner for each file you've changed, unless you are an owner of
-some files, in which case you don't need separate owner approval for those
-files.
+else you think should review your code. The blame functionality in Code Search
+is a good way to identify reviewers who may be familiar with the parts of code
+your CL touches. For your CL to land, you need an approval from an owner for
+each file you've changed, unless you are an owner of some files, in which case
+you don't need separate owner approval for those files.
 
 ## 16. Implement feedback from your reviewers
 
diff --git a/docs/mojo_and_services.md b/docs/mojo_and_services.md
index 173b142..1ba48d2 100644
--- a/docs/mojo_and_services.md
+++ b/docs/mojo_and_services.md
@@ -186,7 +186,7 @@
   }
 
  private:
-  mojo::Receiver<example::mojom::PingResponder> Receiver_;
+  mojo::Receiver<example::mojom::PingResponder> receiver_;
 
   DISALLOW_COPY_AND_ASSIGN(PingResponderImpl);
 };
diff --git a/extensions/BUILD.gn b/extensions/BUILD.gn
index 28572161..0dc76dc 100644
--- a/extensions/BUILD.gn
+++ b/extensions/BUILD.gn
@@ -296,6 +296,7 @@
     "//components/dom_distiller/content/browser",
     "//components/dom_distiller/core:test_support",
     "//components/guest_view/browser:test_support",
+    "//components/printing/common",
     "//components/resources",
     "//components/strings",
     "//components/sync",
diff --git a/extensions/DEPS b/extensions/DEPS
index 077597e..4df158a 100644
--- a/extensions/DEPS
+++ b/extensions/DEPS
@@ -40,4 +40,7 @@
   ".*(test|test_util)\.(cc|h)$": [
     "+content/public/test",
   ],
+  "mime_handler_view_browsertest.cc": [
+    "+components/printing/common",
+  ],
 }
diff --git a/extensions/browser/api/webcam_private/v4l2_webcam.cc b/extensions/browser/api/webcam_private/v4l2_webcam.cc
index db95f8a..6c4906d 100644
--- a/extensions/browser/api/webcam_private/v4l2_webcam.cc
+++ b/extensions/browser/api/webcam_private/v4l2_webcam.cc
@@ -21,19 +21,45 @@
 
 // GUID of the Extension Unit for Logitech CC3300e motor control:
 // {212de5ff-3080-2c4e-82d9-f587d00540bd}
-#define UVC_GUID_LOGITECH_CC3000E_MOTORS          \
- {0x21, 0x2d, 0xe5, 0xff, 0x30, 0x80, 0x2c, 0x4e, \
-  0x82, 0xd9, 0xf5, 0x87, 0xd0, 0x05, 0x40, 0xbd}
+#define UVC_GUID_LOGITECH_CC3000E_MOTORS                                    \
+  {                                                                         \
+    0x21, 0x2d, 0xe5, 0xff, 0x30, 0x80, 0x2c, 0x4e, 0x82, 0xd9, 0xf5, 0x87, \
+        0xd0, 0x05, 0x40, 0xbd                                              \
+  }
+
+// GUID of the Extension Unit for AVER XU1 motor control:
+// {cb666936-6e26-a047-8451-96ecf60330d6}
+#define UVC_GUID_AVER                                                       \
+  {                                                                         \
+    0xcb, 0x66, 0x69, 0x36, 0x6e, 0x26, 0xa0, 0x47, 0x84, 0x51, 0x96, 0xec, \
+        0xf6, 0x03, 0x30, 0xd6                                              \
+  }
 
 #define LOGITECH_MOTORCONTROL_PANTILT_CMD 2
+#define AVER_UVCX_UCAM_PRESET 0x12
 
 namespace {
 const int kLogitechMenuIndexGoHome = 2;
+const int kAverMenuIndexGoHome = 1;
 
 const uvc_menu_info kLogitechCmdMenu[] = {
   {1, "Set Preset"}, {2, "Get Preset"}, {3, "Go Home"}
 };
 
+// Preset 0 is equivalent to HOME in Aver cameras.
+const uvc_menu_info kAverPresetMenu[] = {
+    /*0*/ {0x0000, "Set Preset 0"},  /*1*/ {0x0001, "Restore preset 0"},
+    /*2*/ {0x0100, "Set Preset 1"},  /*3*/ {0x0101, "Restore preset 1"},
+    /*4*/ {0x0200, "Set Preset 2"},  /*5*/ {0x0202, "Restore preset 2"},
+    /*6*/ {0x0300, "Set Preset 3"},  /*7*/ {0x0303, "Restore preset 3"},
+    /*8*/ {0x0400, "Set Preset 4"},  /*9*/ {0x0404, "Restore preset 4"},
+    /*10*/ {0x0500, "Set Preset 5"}, /*11*/ {0x0505, "Restore preset 5"},
+    /*12*/ {0x0600, "Set Preset 6"}, /*13*/ {0x0606, "Restore preset 6"},
+    /*14*/ {0x0700, "Set Preset 7"}, /*15*/ {0x0707, "Restore preset 7"},
+    /*16*/ {0x0800, "Set Preset 8"}, /*17*/ {0x0808, "Restore preset 8"},
+    /*18*/ {0x0900, "Set Preset 9"}, /*19*/ {0x0909, "Restore preset 9"},
+};
+
 const uvc_xu_control_mapping kLogitechCmdMapping = {
     V4L2_CID_PANTILT_CMD,
     "Pan/Tilt Go",
@@ -69,6 +95,19 @@
     UVC_CTRL_DATA_TYPE_SIGNED,
 };
 
+const uvc_xu_control_mapping kAverCmdMapping = {
+    V4L2_CID_PANTILT_CMD,
+    "PTZ Preset",
+    UVC_GUID_AVER,
+    AVER_UVCX_UCAM_PRESET,
+    24,
+    0,
+    V4L2_CTRL_TYPE_MENU,
+    UVC_CTRL_DATA_TYPE_ENUM,
+    const_cast<uvc_menu_info*>(kAverPresetMenu),
+    base::size(kAverPresetMenu),
+};
+
 }  // namespace
 
 namespace extensions {
@@ -94,6 +133,14 @@
   return res >= 0 || errno == EEXIST || errno == EFAULT;
 }
 
+bool V4L2Webcam::EnsureAverCommandsMapped() {
+  int res = HANDLE_EINTR(ioctl(fd_.get(), UVCIOC_CTRL_MAP, &kAverCmdMapping));
+  // If mapping is successful or it's already mapped, this is an Aver camera.
+  // NOTE: On success, occasionally EFAULT is returned.  On a real error,
+  // ENOMEM, EPERM, EINVAL, or EOVERFLOW should be returned.
+  return res >= 0 || errno == EEXIST || errno == EFAULT;
+}
+
 bool V4L2Webcam::SetWebcamParameter(int fd, uint32_t control_id, int value) {
   // Try to map the V4L2 control to the Logitech extension unit. If the
   // connected camera does not implement these extension unit this will just
@@ -251,6 +298,12 @@
         callback.Run(false);
         return;
       }
+    } else if (EnsureAverCommandsMapped()) {
+      if (!SetWebcamParameter(fd_.get(), V4L2_CID_PANTILT_CMD,
+                              kAverMenuIndexGoHome)) {
+        callback.Run(false);
+        return;
+      }
     } else {
       if (pan) {
         struct v4l2_control v4l2_ctrl = {V4L2_CID_PAN_RESET};
diff --git a/extensions/browser/api/webcam_private/v4l2_webcam.h b/extensions/browser/api/webcam_private/v4l2_webcam.h
index 78cc35e..6aeb6a3 100644
--- a/extensions/browser/api/webcam_private/v4l2_webcam.h
+++ b/extensions/browser/api/webcam_private/v4l2_webcam.h
@@ -22,6 +22,7 @@
  private:
   ~V4L2Webcam() override;
   bool EnsureLogitechCommandsMapped();
+  bool EnsureAverCommandsMapped();
   static bool SetWebcamParameter(int fd, uint32_t control_id, int value);
   static bool GetWebcamParameter(int fd,
                                  uint32_t control_id,
diff --git a/extensions/browser/guest_view/mime_handler_view/mime_handler_view_browsertest.cc b/extensions/browser/guest_view/mime_handler_view/mime_handler_view_browsertest.cc
index afd9b5ab..c8e9ac99 100644
--- a/extensions/browser/guest_view/mime_handler_view/mime_handler_view_browsertest.cc
+++ b/extensions/browser/guest_view/mime_handler_view/mime_handler_view_browsertest.cc
@@ -21,6 +21,7 @@
 #include "components/app_modal/javascript_app_modal_dialog.h"
 #include "components/app_modal/native_app_modal_dialog.h"
 #include "components/guest_view/browser/test_guest_view_manager.h"
+#include "components/printing/common/print_messages.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/navigation_handle.h"
 #include "content/public/browser/web_contents_observer.h"
@@ -220,6 +221,60 @@
   EXPECT_EQ(1U, gv_manager->num_guests_created());
 }
 
+#if BUILDFLAG(ENABLE_PRINT_PREVIEW)
+class PrintPreviewWaiter : public content::BrowserMessageFilter {
+ public:
+  PrintPreviewWaiter() : BrowserMessageFilter(PrintMsgStart) {}
+
+  bool OnMessageReceived(const IPC::Message& message) override {
+    IPC_BEGIN_MESSAGE_MAP(PrintPreviewWaiter, message)
+      IPC_MESSAGE_HANDLER(PrintHostMsg_DidStartPreview, OnDidStartPreview)
+    IPC_END_MESSAGE_MAP()
+    return false;
+  }
+
+  void OnDidStartPreview(const PrintHostMsg_DidStartPreview_Params& params,
+                         const PrintHostMsg_PreviewIds& ids) {
+    // Expect that there is at least one page.
+    did_load_ = true;
+    run_loop_.Quit();
+
+    EXPECT_TRUE(params.page_count >= 1);
+  }
+
+  void Wait() {
+    if (!did_load_)
+      run_loop_.Run();
+  }
+
+ private:
+  ~PrintPreviewWaiter() override = default;
+
+  bool did_load_ = false;
+  base::RunLoop run_loop_;
+};
+
+IN_PROC_BROWSER_TEST_P(MimeHandlerViewCrossProcessTest, EmbeddedThenPrint) {
+  RunTest("test_embedded.html");
+  ui_test_utils::NavigateToURL(browser(), GURL(url::kAboutBlankURL));
+  auto* gv_manager = GetGuestViewManager();
+  gv_manager->WaitForAllGuestsDeleted();
+  EXPECT_EQ(1U, gv_manager->num_guests_created());
+
+  // Verify that print dialog comes up.
+  auto* web_contents = browser()->tab_strip_model()->GetActiveWebContents();
+  auto* main_frame = web_contents->GetMainFrame();
+  auto print_preview_waiter = base::MakeRefCounted<PrintPreviewWaiter>();
+  web_contents->GetMainFrame()->GetProcess()->AddFilter(
+      print_preview_waiter.get());
+  // Use setTimeout() to prevent ExecuteScript() from blocking on the print
+  // dialog.
+  ASSERT_TRUE(content::ExecuteScript(
+      main_frame, "setTimeout(function() { window.print(); }, 0)"));
+  print_preview_waiter->Wait();
+}
+#endif  // BUILDFLAG(ENABLE_PRINT_PREVIEW)
+
 // This test start with an <object> that has a content frame. Then the content
 // frame (plugin frame) is navigated to a cross-origin target page. After the
 // navigation is completed, the <object> is set to render MimeHandlerView by
diff --git a/extensions/renderer/guest_view/mime_handler_view/mime_handler_view_container_manager.cc b/extensions/renderer/guest_view/mime_handler_view/mime_handler_view_container_manager.cc
index 92d4ab0..82819a8 100644
--- a/extensions/renderer/guest_view/mime_handler_view/mime_handler_view_container_manager.cc
+++ b/extensions/renderer/guest_view/mime_handler_view/mime_handler_view_container_manager.cc
@@ -99,6 +99,8 @@
       // This should translate into a same document navigation.
       return true;
     }
+    // Make sure we're not eligible to self-delete.
+    DCHECK(!internal_id_.empty());
     // If there is already a MHVFC for this |plugin_element|, destroy it.
     RemoveFrameContainerForReason(old_frame_container,
                                   UMAType::kRemoveFrameContainerUpdatePlugin);
@@ -268,6 +270,15 @@
   if (it == frame_containers_.cend())
     return false;
   frame_containers_.erase(it);
+
+  if (!frame_containers_.size() && internal_id_.empty()) {
+    // There are no frame containers left, and we're not serving a full-page
+    // MimeHandlerView, so we remove ourselves from the map.
+    auto& map = *GetRenderFrameMap();
+    map.erase(std::remove_if(map.begin(), map.end(), [this](const auto& iter) {
+      return iter.second.get() == this;
+    }));
+  }
   return true;
 }
 
diff --git a/extensions/renderer/guest_view/mime_handler_view/mime_handler_view_container_manager.h b/extensions/renderer/guest_view/mime_handler_view/mime_handler_view_container_manager.h
index 965b92c57..095be86 100644
--- a/extensions/renderer/guest_view/mime_handler_view/mime_handler_view_container_manager.h
+++ b/extensions/renderer/guest_view/mime_handler_view/mime_handler_view_container_manager.h
@@ -80,6 +80,8 @@
       v8::Isolate* isolate);
   // Removes the |frame_container| from |frame_containers_| and destroys it. The
   // |reason| is emitted for UMA.
+  // Note: Calling this function may delete |this| if we are removing the last
+  // frame container.
   void RemoveFrameContainerForReason(
       MimeHandlerViewFrameContainer* frame_container,
       MimeHandlerViewUMATypes::Type reason);
@@ -100,6 +102,9 @@
   void LoadEmptyPage(const GURL& resource_url) override;
   void CreateBeforeUnloadControl(
       CreateBeforeUnloadControlCallback callback) override;
+
+  // Note: Calling this function may delete |this| if we are destroying the last
+  // frame container.
   void DestroyFrameContainer(int32_t element_instance_id) override;
   void DidLoad(int32_t mime_handler_view_guest_element_instance_id,
                const GURL& resource_url) override;
@@ -111,6 +116,8 @@
   bool IsEmbedded() const override;
   bool IsResourceAccessibleBySource() const override;
 
+  // Note: Calling this function may delete |this| if we are removing the last
+  // frame container.
   bool RemoveFrameContainer(MimeHandlerViewFrameContainer* frame_container);
   // mime_handler::BeforeUnloadControl implementation.
   void SetShowBeforeUnloadDialog(
@@ -137,7 +144,7 @@
   // Used to match against plugin elements that request a scriptable object. The
   // one that matches is the one inserted in the HTML string injected by the
   // MimeHandlerViewAttachHelper (and hence requires a scriptable object to for
-  // postMessage purposes).
+  // postMessage purposes). This will only be non-empty for full-page MHV.
   std::string internal_id_;
   // The plugin element that is managed by MimeHandlerViewContainerManager.
   blink::WebElement plugin_element_;
diff --git a/google_apis/gaia/gaia_constants.cc b/google_apis/gaia/gaia_constants.cc
index 336f2075..93cba997 100644
--- a/google_apis/gaia/gaia_constants.cc
+++ b/google_apis/gaia/gaia_constants.cc
@@ -69,7 +69,7 @@
     "https://www.googleapis.com/auth/auditrecording-pa";
 
 // OAuth scope for access to clear cut logs.
-const char kClearCutOAuth2Scope[] = "https://wwww.googleapis.com/auth/cclog";
+const char kClearCutOAuth2Scope[] = "https://www.googleapis.com/auth/cclog";
 
 // Used to mint uber auth tokens when needed.
 const char kGaiaSid[] = "sid";
diff --git a/gpu/command_buffer/service/webgpu_decoder_impl.cc b/gpu/command_buffer/service/webgpu_decoder_impl.cc
index cd723db..28dde0f 100644
--- a/gpu/command_buffer/service/webgpu_decoder_impl.cc
+++ b/gpu/command_buffer/service/webgpu_decoder_impl.cc
@@ -636,8 +636,7 @@
 
   TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("gpu.dawn"),
                "WebGPUDecoderImpl::HandleDawnCommands", "bytes", size);
-  std::vector<char> commands(shm_commands, shm_commands + size);
-  if (!wire_server_->HandleCommands(commands.data(), size)) {
+  if (!wire_server_->HandleCommands(shm_commands, size)) {
     NOTREACHED();
     return error::kLostContext;
   }
diff --git a/gpu/config/gpu_driver_bug_list.json b/gpu/config/gpu_driver_bug_list.json
index e1ced2f..9561d72 100644
--- a/gpu/config/gpu_driver_bug_list.json
+++ b/gpu/config/gpu_driver_bug_list.json
@@ -3356,6 +3356,19 @@
       "features": [
         "prefer_draw_to_copy"
       ]
+    },
+    {
+      "id": 313,
+      "description": "Context lost recovery often fails on PowerVR on CrOS.",
+      "cr_bugs": [1010121],
+      "os": {
+        "type": "chromeos"
+      },
+      "gl_vendor": "Imagination.*",
+      "gl_renderer": "PowerVR.*",
+      "features": [
+        "exit_on_context_lost"
+      ]
     }
   ]
 }
diff --git a/gpu/ipc/common/gpu_client_ids.h b/gpu/ipc/common/gpu_client_ids.h
index 4189674c..56302c3 100644
--- a/gpu/ipc/common/gpu_client_ids.h
+++ b/gpu/ipc/common/gpu_client_ids.h
@@ -22,6 +22,11 @@
 // namespace for these shaders.
 constexpr int32_t kGrShaderCacheClientId = -2;
 
+// The ID used by PlatformVideoFramePool for video decoding buffer allocation
+// in the GPU process. These are never accessed on the host side so we can use
+// the same client ID for all clients.
+constexpr int32_t kPlatformVideoFramePoolClientId = -3;
+
 inline bool IsReservedClientId(int32_t client_id) {
   return client_id < 0;
 }
diff --git a/headless/test/data/protocol/sessions/headless-session-basics-expected.txt b/headless/test/data/protocol/sessions/headless-session-basics-expected.txt
new file mode 100644
index 0000000..f117232
--- /dev/null
+++ b/headless/test/data/protocol/sessions/headless-session-basics-expected.txt
@@ -0,0 +1,3 @@
+Tests headless session basics.
+
+SUCCESS: Target.createTarget returned sessionId.
diff --git a/headless/test/data/protocol/sessions/headless-session-basics.js b/headless/test/data/protocol/sessions/headless-session-basics.js
new file mode 100644
index 0000000..313eeda
--- /dev/null
+++ b/headless/test/data/protocol/sessions/headless-session-basics.js
@@ -0,0 +1,36 @@
+// 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.
+
+(async function(testRunner) {
+  testRunner.log('Tests headless session basics.\n');
+
+  // HeadlessDevToolsSession handles Target.createTarget. So, it's responsible
+  // for adding the sessionId into its return value. This test uses the
+  // low-level browser protocol connection, attached to a browser target,
+  // and creates a browser context. In the subsequent Target.createTarget
+  // call, we observe that the return value carries the expected sessionId.
+
+  testRunner.browserP().Target.attachToBrowserTarget();
+  const sessionId =
+        (await testRunner.browserP().Target.onceAttachedToTarget())
+        .params.sessionId;
+
+  const session = new TestRunner.Session(testRunner, sessionId);
+
+  const browserContextId =
+        (await session.protocol.Target.createBrowserContext()).browserContextId;
+
+  const returnValue = (await session.protocol.Target.createTarget({
+    'browserContextId': browserContextId,
+    'enableBeginFrameControl': true, 'height': 1, 'url': 'about:blank',
+    'width': 1}));
+
+  // If HeadlessDevToolsSession fails to put the sessionId into the response,
+  // this log line will be missing.
+  if (returnValue.sessionId && returnValue.sessionId.length > 0) {
+    testRunner.log('SUCCESS: Target.createTarget returned sessionId.\n');
+  }
+
+  testRunner.completeTest();
+})
diff --git a/headless/test/headless_protocol_browsertest.cc b/headless/test/headless_protocol_browsertest.cc
index 9838b71..e984333 100644
--- a/headless/test/headless_protocol_browsertest.cc
+++ b/headless/test/headless_protocol_browsertest.cc
@@ -264,6 +264,9 @@
 #undef MAYBE_VirtualTimeTimerOrder
 #undef MAYBE_VirtualTimeTimerSuspend
 
+HEADLESS_PROTOCOL_TEST(HeadlessSessionBasicsTest,
+                       "sessions/headless-session-basics.js")
+
 class HeadlessProtocolCompositorBrowserTest
     : public HeadlessProtocolBrowserTest {
  public:
diff --git a/infra/config/README.md b/infra/config/README.md
index c64edf4..a599304f 100644
--- a/infra/config/README.md
+++ b/infra/config/README.md
@@ -11,8 +11,10 @@
 https://luci-config.appspot.com/#/projects/chromium .
 
 The configuration files are currently in the process of being migrated to
-lucicfg/starlark. If a hand-written configuration file is still present
-alongside this file, you can modify that file directly.
+lucicfg/starlark. See
+https://chromium.googlesource.com/infra/luci/luci-go/+/HEAD/lucicfg/doc/README.md
+for more information on lucicfg/starlark. If a hand-written configuration file
+is still present alongside this file, you can modify that file directly.
 
 The remainder of the configuration files are generated by starlark. The starlark
 configuration is rooted in main.star and dev.star, which execute other starlark
diff --git a/infra/config/dev.star b/infra/config/dev.star
index 6b5cfc3..136dc5d7 100755
--- a/infra/config/dev.star
+++ b/infra/config/dev.star
@@ -1,4 +1,6 @@
 #!/usr/bin/env lucicfg
+# See https://chromium.googlesource.com/infra/luci/luci-go/+/HEAD/lucicfg/doc/README.md
+# for information on starlark/lucicfg
 
 # Tell lucicfg what files it is allowed to touch
 lucicfg.config(
@@ -16,7 +18,28 @@
 # TODO(https://crbug.com/1011908) Migrate the configuration in these files to starlark
 [lucicfg.emit(dest = f, data = io.read_file(f)) for f in (
     'cr-buildbucket-dev.cfg',
-    'luci-logdog-dev.cfg',
     'luci-milo-dev.cfg',
     'luci-scheduler-dev.cfg',
 )]
+
+luci.project(
+    name = 'chromium',
+    logdog = 'luci-logdog-dev.appspot.com',
+    acls = [
+        acl.entry(
+            roles = [
+                acl.LOGDOG_READER,
+                acl.PROJECT_CONFIGS_READER,
+            ],
+            groups = 'all',
+        ),
+        acl.entry(
+            roles = acl.LOGDOG_WRITER,
+            groups = 'luci-logdog-chromium-dev-writers',
+        ),
+    ],
+)
+
+luci.logdog(
+    gs_bucket = 'chromium-luci-logdog',
+)
diff --git a/infra/config/generated/luci-logdog-dev.cfg b/infra/config/generated/luci-logdog-dev.cfg
index 7057d02..d95b2d69 100644
--- a/infra/config/generated/luci-logdog-dev.cfg
+++ b/infra/config/generated/luci-logdog-dev.cfg
@@ -1,16 +1,9 @@
-# Copyright (c) 2015 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
+# Auto-generated by lucicfg.
+# Do not modify manually.
 #
-# For the schema of this file and documentation, see ProjectConfig message in
-# https://luci-config.appspot.com/schemas/projects:luci-logdog.cfg
+# For the schema of this file, see ProjectConfig message:
+#   https://luci-config.appspot.com/schemas/projects:luci-logdog.cfg
 
-# Auth groups who can read log streams.
 reader_auth_groups: "all"
-# Auth groups who can register and emit new log streams.
 writer_auth_groups: "luci-logdog-chromium-dev-writers"
-
-# The base Google Storage archival path for this project.
-#
-# Archived LogDog logs will be written to this bucket/path.
 archive_gs_bucket: "chromium-luci-logdog"
diff --git a/infra/config/generated/luci-logdog.cfg b/infra/config/generated/luci-logdog.cfg
index 29302eb..adc75be 100644
--- a/infra/config/generated/luci-logdog.cfg
+++ b/infra/config/generated/luci-logdog.cfg
@@ -1,16 +1,9 @@
-# Copyright (c) 2015 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
+# Auto-generated by lucicfg.
+# Do not modify manually.
 #
-# For the schema of this file and documentation, see ProjectConfig message in
-# https://luci-config.appspot.com/schemas/projects:luci-logdog.cfg
+# For the schema of this file, see ProjectConfig message:
+#   https://luci-config.appspot.com/schemas/projects:luci-logdog.cfg
 
-# Auth groups who can read log streams.
 reader_auth_groups: "all"
-# Auth groups who can register and emit new log streams.
 writer_auth_groups: "luci-logdog-chromium-writers"
-
-# The base Google Storage archival path for this project.
-#
-# Archived LogDog logs will be written to this bucket/path.
 archive_gs_bucket: "chromium-luci-logdog"
diff --git a/infra/config/generated/project.cfg b/infra/config/generated/project.cfg
index 531f167..185a4a55 100644
--- a/infra/config/generated/project.cfg
+++ b/infra/config/generated/project.cfg
@@ -1,5 +1,8 @@
-# For the schema of this file and documentation, see ProjectCfg message in
-# https://luci-config.appspot.com/schemas/projects:project.cfg
+# Auto-generated by lucicfg.
+# Do not modify manually.
+#
+# For the schema of this file, see ProjectCfg message:
+#   https://luci-config.appspot.com/schemas/projects:project.cfg
 
 name: "chromium"
-access: "group:all" # public
+access: "group:all"
diff --git a/infra/config/luci-logdog-dev.cfg b/infra/config/luci-logdog-dev.cfg
deleted file mode 100644
index 7057d02..0000000
--- a/infra/config/luci-logdog-dev.cfg
+++ /dev/null
@@ -1,16 +0,0 @@
-# Copyright (c) 2015 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-#
-# For the schema of this file and documentation, see ProjectConfig message in
-# https://luci-config.appspot.com/schemas/projects:luci-logdog.cfg
-
-# Auth groups who can read log streams.
-reader_auth_groups: "all"
-# Auth groups who can register and emit new log streams.
-writer_auth_groups: "luci-logdog-chromium-dev-writers"
-
-# The base Google Storage archival path for this project.
-#
-# Archived LogDog logs will be written to this bucket/path.
-archive_gs_bucket: "chromium-luci-logdog"
diff --git a/infra/config/luci-logdog.cfg b/infra/config/luci-logdog.cfg
deleted file mode 100644
index 29302eb..0000000
--- a/infra/config/luci-logdog.cfg
+++ /dev/null
@@ -1,16 +0,0 @@
-# Copyright (c) 2015 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-#
-# For the schema of this file and documentation, see ProjectConfig message in
-# https://luci-config.appspot.com/schemas/projects:luci-logdog.cfg
-
-# Auth groups who can read log streams.
-reader_auth_groups: "all"
-# Auth groups who can register and emit new log streams.
-writer_auth_groups: "luci-logdog-chromium-writers"
-
-# The base Google Storage archival path for this project.
-#
-# Archived LogDog logs will be written to this bucket/path.
-archive_gs_bucket: "chromium-luci-logdog"
diff --git a/infra/config/main.star b/infra/config/main.star
index 9d93d4b2..812eda3 100755
--- a/infra/config/main.star
+++ b/infra/config/main.star
@@ -1,4 +1,6 @@
 #!/usr/bin/env lucicfg
+# See https://chromium.googlesource.com/infra/luci/luci-go/+/HEAD/lucicfg/doc/README.md
+# for information on starlark/lucicfg
 
 # Tell lucicfg what files it is allowed to touch
 lucicfg.config(
@@ -21,11 +23,9 @@
 [lucicfg.emit(dest = f, data = io.read_file(f)) for f in (
     'commit-queue.cfg',
     'cr-buildbucket.cfg',
-    'luci-logdog.cfg',
     'luci-milo.cfg',
     'luci-notify.cfg',
     'luci-scheduler.cfg',
-    'project.cfg',
 )]
 
 # Just copy tricium-prod.cfg to the generated outputs
@@ -33,3 +33,25 @@
     dest = 'tricium-prod.cfg',
     data = io.read_file('tricium-prod.cfg'),
 )
+
+luci.project(
+    name = 'chromium',
+    logdog = 'luci-logdog.appspot.com',
+    acls = [
+        acl.entry(
+            roles = [
+                acl.LOGDOG_READER,
+                acl.PROJECT_CONFIGS_READER,
+            ],
+            groups = 'all',
+        ),
+        acl.entry(
+            roles = acl.LOGDOG_WRITER,
+            groups = 'luci-logdog-chromium-writers',
+        ),
+    ],
+)
+
+luci.logdog(
+    gs_bucket = 'chromium-luci-logdog',
+)
diff --git a/infra/config/project.cfg b/infra/config/project.cfg
deleted file mode 100644
index 531f167..0000000
--- a/infra/config/project.cfg
+++ /dev/null
@@ -1,5 +0,0 @@
-# For the schema of this file and documentation, see ProjectCfg message in
-# https://luci-config.appspot.com/schemas/projects:project.cfg
-
-name: "chromium"
-access: "group:all" # public
diff --git a/ios/chrome/browser/autofill/BUILD.gn b/ios/chrome/browser/autofill/BUILD.gn
index cd18bb0..fbb1d00 100644
--- a/ios/chrome/browser/autofill/BUILD.gn
+++ b/ios/chrome/browser/autofill/BUILD.gn
@@ -76,6 +76,9 @@
     "//ui/base",
     "//url",
   ]
+  public_deps = [
+    ":constants",
+  ]
   libs = [ "QuartzCore.framework" ]
 }
 
@@ -125,6 +128,14 @@
   libs = [ "UIKit.framework" ]
 }
 
+source_set("constants") {
+  configs += [ "//build/config/compiler:enable_arc" ]
+  sources = [
+    "form_suggestion_constants.h",
+    "form_suggestion_constants.mm",
+  ]
+}
+
 source_set("unit_tests") {
   configs += [ "//build/config/compiler:enable_arc" ]
   testonly = true
diff --git a/ios/chrome/browser/autofill/automation/automation_action.mm b/ios/chrome/browser/autofill/automation/automation_action.mm
index ea55286..37937e9 100644
--- a/ios/chrome/browser/autofill/automation/automation_action.mm
+++ b/ios/chrome/browser/autofill/automation/automation_action.mm
@@ -15,7 +15,7 @@
 #include "components/autofill/core/browser/autofill_manager.h"
 #include "components/autofill/core/browser/personal_data_manager.h"
 #include "components/autofill/ios/browser/autofill_driver_ios.h"
-#import "ios/chrome/browser/autofill/form_suggestion_label.h"
+#import "ios/chrome/browser/autofill/form_suggestion_constants.h"
 #import "ios/chrome/browser/ui/infobars/infobar_constants.h"
 #import "ios/chrome/test/app/tab_test_util.h"
 #import "ios/chrome/test/earl_grey/chrome_earl_grey.h"
diff --git a/ios/chrome/browser/autofill/form_suggestion_constants.h b/ios/chrome/browser/autofill/form_suggestion_constants.h
new file mode 100644
index 0000000..a2a8678
--- /dev/null
+++ b/ios/chrome/browser/autofill/form_suggestion_constants.h
@@ -0,0 +1,17 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_AUTOFILL_FORM_SUGGESTION_CONSTANTS_H_
+#define IOS_CHROME_BROWSER_AUTOFILL_FORM_SUGGESTION_CONSTANTS_H_
+
+#import <Foundation/Foundation.h>
+
+// Accessibility identifier for FormSuggestionLabel used to locate the autofill
+// suggestion label in automation.
+extern NSString* const kFormSuggestionLabelAccessibilityIdentifier;
+
+// Accessibility identifier for FormSuggestionView.
+extern NSString* const kFormSuggestionsViewAccessibilityIdentifier;
+
+#endif  // IOS_CHROME_BROWSER_AUTOFILL_FORM_SUGGESTION_CONSTANTS_H_
diff --git a/ios/chrome/browser/autofill/form_suggestion_constants.mm b/ios/chrome/browser/autofill/form_suggestion_constants.mm
new file mode 100644
index 0000000..86282bf
--- /dev/null
+++ b/ios/chrome/browser/autofill/form_suggestion_constants.mm
@@ -0,0 +1,15 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/autofill/form_suggestion_constants.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+NSString* const kFormSuggestionLabelAccessibilityIdentifier =
+    @"formSuggestionLabelAXID";
+
+NSString* const kFormSuggestionsViewAccessibilityIdentifier =
+    @"kFormSuggestionsViewAccessibilityIdentifier";
diff --git a/ios/chrome/browser/autofill/form_suggestion_label.h b/ios/chrome/browser/autofill/form_suggestion_label.h
index 28608bd..6b7d0e4 100644
--- a/ios/chrome/browser/autofill/form_suggestion_label.h
+++ b/ios/chrome/browser/autofill/form_suggestion_label.h
@@ -7,9 +7,6 @@
 
 #import <UIKit/UIKit.h>
 
-// a11y identifier used to locate the autofill suggestion in automation
-extern NSString* const kFormSuggestionLabelAccessibilityIdentifier;
-
 @class FormSuggestion;
 @protocol FormSuggestionClient;
 
diff --git a/ios/chrome/browser/autofill/form_suggestion_label.mm b/ios/chrome/browser/autofill/form_suggestion_label.mm
index 25f41a1..62459e8 100644
--- a/ios/chrome/browser/autofill/form_suggestion_label.mm
+++ b/ios/chrome/browser/autofill/form_suggestion_label.mm
@@ -16,6 +16,7 @@
 #include "components/autofill/core/browser/data_model/credit_card.h"
 #import "components/autofill/ios/browser/form_suggestion.h"
 #import "ios/chrome/browser/autofill/form_suggestion_client.h"
+#import "ios/chrome/browser/autofill/form_suggestion_constants.h"
 #include "ios/chrome/browser/ui/util/ui_util.h"
 #import "ios/chrome/browser/ui/util/uikit_ui_util.h"
 #import "ios/chrome/common/colors/semantic_color_names.h"
@@ -27,10 +28,6 @@
 #error "This file requires ARC support."
 #endif
 
-// a11y identifier used to locate the autofill suggestion in automation
-NSString* const kFormSuggestionLabelAccessibilityIdentifier =
-    @"formSuggestionLabelAXID";
-
 namespace {
 
 // Font size of button titles.
diff --git a/ios/chrome/browser/autofill/form_suggestion_view.h b/ios/chrome/browser/autofill/form_suggestion_view.h
index eb835b8..5d81dd5 100644
--- a/ios/chrome/browser/autofill/form_suggestion_view.h
+++ b/ios/chrome/browser/autofill/form_suggestion_view.h
@@ -7,8 +7,6 @@
 
 #import <UIKit/UIKit.h>
 
-extern NSString* const kFormSuggestionsViewAccessibilityIdentifier;
-
 @class FormSuggestion;
 @protocol FormSuggestionClient;
 @class FormSuggestionView;
diff --git a/ios/chrome/browser/autofill/form_suggestion_view.mm b/ios/chrome/browser/autofill/form_suggestion_view.mm
index b6faf2f..4a73395 100644
--- a/ios/chrome/browser/autofill/form_suggestion_view.mm
+++ b/ios/chrome/browser/autofill/form_suggestion_view.mm
@@ -9,6 +9,7 @@
 #include "components/autofill/core/browser/ui/popup_item_ids.h"
 #import "components/autofill/ios/browser/form_suggestion.h"
 #import "ios/chrome/browser/autofill/form_suggestion_client.h"
+#import "ios/chrome/browser/autofill/form_suggestion_constants.h"
 #import "ios/chrome/browser/autofill/form_suggestion_label.h"
 #include "ios/chrome/browser/ui/util/rtl_geometry.h"
 #include "ios/chrome/common/ui_util/constraints_ui_util.h"
@@ -17,9 +18,6 @@
 #error "This file requires ARC support."
 #endif
 
-NSString* const kFormSuggestionsViewAccessibilityIdentifier =
-    @"kFormSuggestionsViewAccessibilityIdentifier";
-
 namespace {
 
 // Vertical margin between suggestions and the edge of the suggestion content
diff --git a/ios/chrome/browser/ui/autofill/manual_fill/address_view_controller_egtest.mm b/ios/chrome/browser/ui/autofill/manual_fill/address_view_controller_egtest.mm
index a22cfa6c..e65ab6ee 100644
--- a/ios/chrome/browser/ui/autofill/manual_fill/address_view_controller_egtest.mm
+++ b/ios/chrome/browser/ui/autofill/manual_fill/address_view_controller_egtest.mm
@@ -14,7 +14,7 @@
 #include "components/autofill/core/common/autofill_features.h"
 #include "components/autofill/ios/browser/autofill_switches.h"
 #include "components/keyed_service/core/service_access_type.h"
-#import "ios/chrome/browser/autofill/form_suggestion_view.h"
+#import "ios/chrome/browser/autofill/form_suggestion_constants.h"
 #include "ios/chrome/browser/autofill/personal_data_manager_factory.h"
 #import "ios/chrome/browser/ui/autofill/manual_fill/address_mediator.h"
 #import "ios/chrome/browser/ui/autofill/manual_fill/address_view_controller.h"
diff --git a/ios/chrome/browser/ui/autofill/manual_fill/card_view_controller_egtest.mm b/ios/chrome/browser/ui/autofill/manual_fill/card_view_controller_egtest.mm
index 515cbb0..c3e8a639 100644
--- a/ios/chrome/browser/ui/autofill/manual_fill/card_view_controller_egtest.mm
+++ b/ios/chrome/browser/ui/autofill/manual_fill/card_view_controller_egtest.mm
@@ -15,7 +15,7 @@
 #include "components/autofill/core/browser/personal_data_manager.h"
 #include "components/autofill/core/common/autofill_features.h"
 #include "components/keyed_service/core/service_access_type.h"
-#import "ios/chrome/browser/autofill/form_suggestion_view.h"
+#import "ios/chrome/browser/autofill/form_suggestion_constants.h"
 #include "ios/chrome/browser/autofill/personal_data_manager_factory.h"
 #import "ios/chrome/browser/ui/autofill/manual_fill/card_coordinator.h"
 #import "ios/chrome/browser/ui/autofill/manual_fill/card_mediator.h"
diff --git a/ios/chrome/browser/ui/autofill/manual_fill/fallback_coordinator_egtest.mm b/ios/chrome/browser/ui/autofill/manual_fill/fallback_coordinator_egtest.mm
index db2e528..c79cab89 100644
--- a/ios/chrome/browser/ui/autofill/manual_fill/fallback_coordinator_egtest.mm
+++ b/ios/chrome/browser/ui/autofill/manual_fill/fallback_coordinator_egtest.mm
@@ -16,8 +16,7 @@
 #include "components/autofill/core/common/autofill_features.h"
 #include "components/autofill/ios/browser/autofill_switches.h"
 #include "ios/chrome/browser/application_context.h"
-#import "ios/chrome/browser/autofill/form_suggestion_label.h"
-#import "ios/chrome/browser/autofill/form_suggestion_view.h"
+#import "ios/chrome/browser/autofill/form_suggestion_constants.h"
 #include "ios/chrome/browser/autofill/personal_data_manager_factory.h"
 #import "ios/chrome/browser/ui/autofill/manual_fill/address_view_controller.h"
 #import "ios/chrome/browser/ui/autofill/manual_fill/manual_fill_accessory_view_controller.h"
diff --git a/ios/chrome/browser/ui/badges/badge_popup_menu_item.mm b/ios/chrome/browser/ui/badges/badge_popup_menu_item.mm
index 3636631e..8e3294e 100644
--- a/ios/chrome/browser/ui/badges/badge_popup_menu_item.mm
+++ b/ios/chrome/browser/ui/badges/badge_popup_menu_item.mm
@@ -23,6 +23,8 @@
 const CGFloat kMaxHeight = 100;
 // Vertical spacing between subviews.
 const CGFloat kVerticalMargin = 8;
+// Corner radius of badge background.
+const CGFloat kBadgeCornerRadius = 5.0;
 }  // namespace
 
 @interface BadgePopupMenuItem ()
@@ -143,6 +145,8 @@
     _badgeView = [[UIImageView alloc] init];
     _badgeView.translatesAutoresizingMaskIntoConstraints = NO;
     _badgeView.tintColor = [UIColor colorNamed:kBlueColor];
+    _badgeView.backgroundColor = [UIColor colorNamed:kBlueHaloColor];
+    _badgeView.layer.cornerRadius = kBadgeCornerRadius;
 
     _trailingImageView = [[UIImageView alloc]
         initWithImage:
diff --git a/ios/chrome/browser/ui/infobars/legacy_infobar_container_view_controller.mm b/ios/chrome/browser/ui/infobars/legacy_infobar_container_view_controller.mm
index 65325ec..8b637b5c 100644
--- a/ios/chrome/browser/ui/infobars/legacy_infobar_container_view_controller.mm
+++ b/ios/chrome/browser/ui/infobars/legacy_infobar_container_view_controller.mm
@@ -108,11 +108,15 @@
       CGRectGetMaxY([self.positioner parentView].frame) - height;
   containerFrame.size.height = height;
 
+  __weak __typeof(self) weakSelf = self;
   auto completion = ^(BOOL finished) {
-    if (!self.visible)
+    __typeof(self) strongSelf = weakSelf;
+    // Return if weakSelf has been niled or is not visible since there's no view
+    // to send an A11y post notification to.
+    if (!strongSelf.visible)
       return;
     UIAccessibilityPostNotification(UIAccessibilityLayoutChangedNotification,
-                                    self.view);
+                                    strongSelf.view);
   };
 
   ProceduralBlock frameUpdates = ^{
diff --git a/ios/net/cookies/cookie_store_ios_unittest.mm b/ios/net/cookies/cookie_store_ios_unittest.mm
index efd7dc172..f18d595c 100644
--- a/ios/net/cookies/cookie_store_ios_unittest.mm
+++ b/ios/net/cookies/cookie_store_ios_unittest.mm
@@ -69,6 +69,7 @@
   static const bool has_exact_change_cause = false;
   static const bool has_exact_change_ordering = false;
   static const int creation_time_granularity_in_ms = 1000;
+  static const bool supports_cookie_access_semantics = false;
 
   base::test::SingleThreadTaskEnvironment task_environment_;
 };
diff --git a/media/filters/fuchsia/fuchsia_video_decoder.cc b/media/filters/fuchsia/fuchsia_video_decoder.cc
index db34a38c..29e08cc9 100644
--- a/media/filters/fuchsia/fuchsia_video_decoder.cc
+++ b/media/filters/fuchsia/fuchsia_video_decoder.cc
@@ -245,6 +245,7 @@
   const bool enable_sw_decoding_;
 
   OutputCB output_cb_;
+  WaitingCB waiting_cb_;
 
   // Aspect ratio specified in container, or 1.0 if it's not specified. This
   // value is used only if the aspect ratio is not specified in the bitstream.
@@ -331,6 +332,8 @@
                                      InitCB init_cb,
                                      const OutputCB& output_cb,
                                      const WaitingCB& waiting_cb) {
+  DCHECK(output_cb);
+  DCHECK(waiting_cb);
   DCHECK(decode_callbacks_.empty());
 
   auto done_callback = BindToCurrentLoop(std::move(init_cb));
@@ -341,6 +344,7 @@
   DCHECK(result);
 
   output_cb_ = output_cb;
+  waiting_cb_ = waiting_cb;
   container_pixel_aspect_ratio_ = config.GetPixelAspectRatio();
 
   // If we already have |decoder_| that was initialized for the same codec then
@@ -502,7 +506,7 @@
     return false;
   }
 
-  decryptor_ = fuchsia_cdm->CreateSecureDecryptor(this);
+  decryptor_ = fuchsia_cdm->CreateVideoDecryptor(this);
 
   return true;
 }
@@ -521,8 +525,7 @@
 }
 
 void FuchsiaVideoDecoder::OnDecryptorNoKey() {
-  NOTIMPLEMENTED();
-  OnError();
+  waiting_cb_.Run(WaitingReason::kNoDecryptionKey);
 }
 
 void FuchsiaVideoDecoder::OnStreamFailed(uint64_t stream_lifetime_ordinal,
diff --git a/media/filters/fuchsia/fuchsia_video_decoder_unittest.cc b/media/filters/fuchsia/fuchsia_video_decoder_unittest.cc
index ec670c50..974783c0 100644
--- a/media/filters/fuchsia/fuchsia_video_decoder_unittest.cc
+++ b/media/filters/fuchsia/fuchsia_video_decoder_unittest.cc
@@ -202,7 +202,7 @@
             &init_cb_result, &run_loop),
         base::BindRepeating(&FuchsiaVideoDecoderTest::OnVideoFrame,
                             weak_factory_.GetWeakPtr()),
-        base::NullCallback());
+        base::DoNothing());
 
     run_loop.Run();
     return init_cb_result;
diff --git a/media/fuchsia/cdm/fuchsia_cdm.cc b/media/fuchsia/cdm/fuchsia_cdm.cc
index afa0769..2b9353a 100644
--- a/media/fuchsia/cdm/fuchsia_cdm.cc
+++ b/media/fuchsia/cdm/fuchsia_cdm.cc
@@ -10,7 +10,6 @@
 #include "fuchsia/base/mem_buffer_util.h"
 #include "media/base/callback_registry.h"
 #include "media/base/cdm_promise.h"
-#include "media/fuchsia/cdm/fuchsia_decryptor.h"
 
 #define REJECT_PROMISE_AND_RETURN_IF_BAD_CDM(promise, cdm)         \
   if (!cdm) {                                                      \
@@ -115,8 +114,9 @@
   using ResultCB =
       base::OnceCallback<void(base::Optional<CdmPromise::Exception>)>;
 
-  explicit CdmSession(const FuchsiaCdm::SessionCallbacks* callbacks)
-      : session_callbacks_(callbacks) {
+  CdmSession(const FuchsiaCdm::SessionCallbacks* callbacks,
+             base::RepeatingClosure on_new_key)
+      : session_callbacks_(callbacks), on_new_key_(on_new_key) {
     // License session events, e.g. license request message, key status change.
     // Fuchsia CDM service guarantees callback of functions (e.g.
     // GenerateLicenseRequest) are called before event callbacks. So it's safe
@@ -195,6 +195,9 @@
 
     session_callbacks_->keys_change_cb.Run(
         session_id_, has_additional_usable_key, std::move(keys_info));
+
+    if (has_additional_usable_key)
+      on_new_key_.Run();
   }
 
   void OnSessionError(zx_status_t status) {
@@ -212,13 +215,16 @@
                  : base::nullopt);
   }
 
+  const SessionCallbacks* const session_callbacks_;
+  base::RepeatingClosure on_new_key_;
+
   fuchsia::media::drm::LicenseSessionPtr session_;
   std::string session_id_;
 
   // Callback for license operation.
   ResultCB result_cb_;
 
-  const SessionCallbacks* session_callbacks_;
+  DISALLOW_COPY_AND_ASSIGN(CdmSession);
 };
 
 FuchsiaCdm::SessionCallbacks::SessionCallbacks() = default;
@@ -231,7 +237,7 @@
                        SessionCallbacks callbacks)
     : cdm_(std::move(cdm)),
       session_callbacks_(std::move(callbacks)),
-      decryptor_(new FuchsiaDecryptor(cdm_.get())) {
+      decryptor_(cdm_.get()) {
   DCHECK(cdm_);
   cdm_.set_error_handler([this](zx_status_t status) {
     ZX_LOG(ERROR, status) << "The fuchsia.media.drm.ContentDecryptionModule"
@@ -244,9 +250,28 @@
 
 FuchsiaCdm::~FuchsiaCdm() = default;
 
-std::unique_ptr<FuchsiaSecureStreamDecryptor> FuchsiaCdm::CreateSecureDecryptor(
+std::unique_ptr<FuchsiaSecureStreamDecryptor> FuchsiaCdm::CreateVideoDecryptor(
     FuchsiaSecureStreamDecryptor::Client* client) {
-  return FuchsiaSecureStreamDecryptor::Create(cdm_.get(), client);
+  fuchsia::media::drm::DecryptorParams params;
+
+  // TODO(crbug.com/997853): Enable secure mode when it's implemented in sysmem.
+  params.set_require_secure_mode(false);
+
+  params.mutable_input_details()->set_format_details_version_ordinal(0);
+  fuchsia::media::StreamProcessorPtr stream_processor;
+  cdm_->CreateDecryptor(std::move(params), stream_processor.NewRequest());
+
+  auto decryptor = std::make_unique<FuchsiaSecureStreamDecryptor>(
+      std::move(stream_processor), client);
+
+  // Save callback to use to notify the decryptor about a new key.
+  auto new_key_cb = decryptor->GetOnNewKeyClosure();
+  {
+    base::AutoLock auto_lock(new_key_cb_for_video_lock_);
+    new_key_cb_for_video_ = new_key_cb;
+  }
+
+  return decryptor;
 }
 
 void FuchsiaCdm::SetServerCertificate(
@@ -293,7 +318,9 @@
 
   uint32_t promise_id = promises_.SavePromise(std::move(promise));
 
-  auto session = std::make_unique<CdmSession>(&session_callbacks_);
+  auto session = std::make_unique<CdmSession>(
+      &session_callbacks_,
+      base::BindRepeating(&FuchsiaCdm::OnNewKey, base::Unretained(this)));
   CdmSession* session_ptr = session.get();
 
   cdm_->CreateLicenseSession(
@@ -419,8 +446,7 @@
 }
 
 Decryptor* FuchsiaCdm::GetDecryptor() {
-  DCHECK(decryptor_);
-  return decryptor_.get();
+  return &decryptor_;
 }
 
 int FuchsiaCdm::GetCdmId() const {
@@ -431,4 +457,13 @@
   return this;
 }
 
+void FuchsiaCdm::OnNewKey() {
+  decryptor_.OnNewKey();
+  {
+    base::AutoLock auto_lock(new_key_cb_for_video_lock_);
+    if (new_key_cb_for_video_)
+      new_key_cb_for_video_.Run();
+  }
+}
+
 }  // namespace media
diff --git a/media/fuchsia/cdm/fuchsia_cdm.h b/media/fuchsia/cdm/fuchsia_cdm.h
index c9ac613..574a398 100644
--- a/media/fuchsia/cdm/fuchsia_cdm.h
+++ b/media/fuchsia/cdm/fuchsia_cdm.h
@@ -14,9 +14,9 @@
 #include "media/base/cdm_promise_adapter.h"
 #include "media/base/content_decryption_module.h"
 #include "media/fuchsia/cdm/fuchsia_cdm_context.h"
+#include "media/fuchsia/cdm/fuchsia_decryptor.h"
 
 namespace media {
-class FuchsiaDecryptor;
 
 class FuchsiaCdm : public ContentDecryptionModule,
                    public CdmContext,
@@ -68,7 +68,7 @@
   FuchsiaCdmContext* GetFuchsiaCdmContext() override;
 
   // FuchsiaCdmContext implementation:
-  std::unique_ptr<FuchsiaSecureStreamDecryptor> CreateSecureDecryptor(
+  std::unique_ptr<FuchsiaSecureStreamDecryptor> CreateVideoDecryptor(
       FuchsiaSecureStreamDecryptor::Client* client) override;
 
  private:
@@ -87,7 +87,7 @@
       uint32_t promise_id,
       base::Optional<CdmPromise::Exception> exception);
 
-  void OnCdmError(zx_status_t status);
+  void OnNewKey();
 
   CdmPromiseAdapter promises_;
   base::flat_map<std::string, std::unique_ptr<CdmSession>> session_map_;
@@ -95,7 +95,11 @@
   fuchsia::media::drm::ContentDecryptionModulePtr cdm_;
   SessionCallbacks session_callbacks_;
 
-  std::unique_ptr<FuchsiaDecryptor> decryptor_;
+  FuchsiaDecryptor decryptor_;
+
+  base::Lock new_key_cb_for_video_lock_;
+  base::RepeatingClosure new_key_cb_for_video_
+      GUARDED_BY(new_key_cb_for_video_lock_);
 
   DISALLOW_COPY_AND_ASSIGN(FuchsiaCdm);
 };
diff --git a/media/fuchsia/cdm/fuchsia_cdm_context.h b/media/fuchsia/cdm/fuchsia_cdm_context.h
index dd614ff..9301cf6 100644
--- a/media/fuchsia/cdm/fuchsia_cdm_context.h
+++ b/media/fuchsia/cdm/fuchsia_cdm_context.h
@@ -15,7 +15,7 @@
   FuchsiaCdmContext() = default;
 
   // Creates FuchsiaSecureStreamDecryptor instance for the CDM context.
-  virtual std::unique_ptr<FuchsiaSecureStreamDecryptor> CreateSecureDecryptor(
+  virtual std::unique_ptr<FuchsiaSecureStreamDecryptor> CreateVideoDecryptor(
       FuchsiaSecureStreamDecryptor::Client* client) = 0;
 
  protected:
diff --git a/media/fuchsia/cdm/fuchsia_decryptor.cc b/media/fuchsia/cdm/fuchsia_decryptor.cc
index 8666cfb..9ae5406 100644
--- a/media/fuchsia/cdm/fuchsia_decryptor.cc
+++ b/media/fuchsia/cdm/fuchsia_decryptor.cc
@@ -21,8 +21,12 @@
 FuchsiaDecryptor::~FuchsiaDecryptor() = default;
 
 void FuchsiaDecryptor::RegisterNewKeyCB(StreamType stream_type,
-                                        const NewKeyCB& key_added_cb) {
-  NOTIMPLEMENTED();
+                                        const NewKeyCB& new_key_cb) {
+  if (stream_type != kAudio)
+    return;
+
+  base::AutoLock auto_lock(new_key_cb_lock_);
+  new_key_cb_ = new_key_cb;
 }
 
 void FuchsiaDecryptor::Decrypt(StreamType stream_type,
@@ -83,4 +87,10 @@
   return false;
 }
 
+void FuchsiaDecryptor::OnNewKey() {
+  base::AutoLock auto_lock(new_key_cb_lock_);
+  if (new_key_cb_)
+    new_key_cb_.Run();
+}
+
 }  // namespace media
diff --git a/media/fuchsia/cdm/fuchsia_decryptor.h b/media/fuchsia/cdm/fuchsia_decryptor.h
index 5a84135a..3af9e92 100644
--- a/media/fuchsia/cdm/fuchsia_decryptor.h
+++ b/media/fuchsia/cdm/fuchsia_decryptor.h
@@ -7,6 +7,8 @@
 
 #include <memory>
 
+#include "base/synchronization/lock.h"
+#include "base/thread_annotations.h"
 #include "media/base/decryptor.h"
 #include "media/fuchsia/cdm/fuchsia_stream_decryptor.h"
 
@@ -47,9 +49,15 @@
   void DeinitializeDecoder(StreamType stream_type) override;
   bool CanAlwaysDecrypt() override;
 
+  // Called by FuchsiaCdm to notify about the new key.
+  void OnNewKey();
+
  private:
   fuchsia::media::drm::ContentDecryptionModule* const cdm_;
 
+  base::Lock new_key_cb_lock_;
+  NewKeyCB new_key_cb_ GUARDED_BY(new_key_cb_lock_);
+
   std::unique_ptr<FuchsiaClearStreamDecryptor> audio_decryptor_;
 
   DISALLOW_COPY_AND_ASSIGN(FuchsiaDecryptor);
diff --git a/media/fuchsia/cdm/fuchsia_stream_decryptor.cc b/media/fuchsia/cdm/fuchsia_stream_decryptor.cc
index 5687b74e..c63c7c3 100644
--- a/media/fuchsia/cdm/fuchsia_stream_decryptor.cc
+++ b/media/fuchsia/cdm/fuchsia_stream_decryptor.cc
@@ -10,6 +10,7 @@
 #include "base/bind.h"
 #include "base/fuchsia/fuchsia_logging.h"
 #include "base/logging.h"
+#include "media/base/bind_to_current_loop.h"
 #include "media/base/decoder_buffer.h"
 #include "media/base/decrypt_config.h"
 #include "media/base/encryption_pattern.h"
@@ -126,6 +127,9 @@
                      base::Unretained(this)));
 }
 
+void FuchsiaStreamDecryptorBase::OnOutputFormat(
+    fuchsia::media::StreamOutputFormat format) {}
+
 void FuchsiaStreamDecryptorBase::OnInputBufferPoolCreated(
     std::unique_ptr<SysmemBufferPool> pool) {
   if (!pool) {
@@ -247,9 +251,6 @@
   NOTREACHED();
 }
 
-void FuchsiaClearStreamDecryptor::OnOutputFormat(
-    fuchsia::media::StreamOutputFormat format) {}
-
 void FuchsiaClearStreamDecryptor::OnOutputPacket(
     StreamProcessorHelper::IoPacket packet) {
   DCHECK(decrypt_cb_);
@@ -311,6 +312,10 @@
 }
 
 void FuchsiaClearStreamDecryptor::OnNoKey() {
+  // Reset the queue. The client is expected to call Decrypt() with the same
+  // buffer again when it gets kNoKey.
+  input_writer_queue_.ResetQueue();
+
   if (decrypt_cb_)
     std::move(decrypt_cb_).Run(Decryptor::kNoKey, nullptr);
 }
@@ -356,26 +361,6 @@
   output_reader_ = std::move(reader);
 }
 
-// static
-std::unique_ptr<FuchsiaSecureStreamDecryptor>
-FuchsiaSecureStreamDecryptor::Create(
-    fuchsia::media::drm::ContentDecryptionModule* cdm,
-    Client* client) {
-  DCHECK(cdm);
-
-  fuchsia::media::drm::DecryptorParams params;
-
-  // TODO(crbug.com/997853): Enable secure mode when it's implemented in sysmem.
-  params.set_require_secure_mode(false);
-
-  params.mutable_input_details()->set_format_details_version_ordinal(0);
-  fuchsia::media::StreamProcessorPtr stream_processor;
-  cdm->CreateDecryptor(std::move(params), stream_processor.NewRequest());
-
-  return std::make_unique<FuchsiaSecureStreamDecryptor>(
-      std::move(stream_processor), client);
-}
-
 FuchsiaSecureStreamDecryptor::FuchsiaSecureStreamDecryptor(
     fuchsia::media::StreamProcessorPtr processor,
     Client* client)
@@ -405,6 +390,7 @@
 
 void FuchsiaSecureStreamDecryptor::Reset() {
   ResetStream();
+  waiting_for_key_ = false;
 }
 
 void FuchsiaSecureStreamDecryptor::AllocateOutputBuffers(
@@ -420,21 +406,51 @@
   client_->OnDecryptorEndOfStreamPacket();
 }
 
-void FuchsiaSecureStreamDecryptor::OnOutputFormat(
-    fuchsia::media::StreamOutputFormat format) {}
-
 void FuchsiaSecureStreamDecryptor::OnOutputPacket(
     StreamProcessorHelper::IoPacket packet) {
   client_->OnDecryptorOutputPacket(std::move(packet));
 }
 
-void FuchsiaSecureStreamDecryptor::OnNoKey() {
-  client_->OnDecryptorNoKey();
+base::RepeatingClosure FuchsiaSecureStreamDecryptor::GetOnNewKeyClosure() {
+  return BindToCurrentLoop(base::BindRepeating(
+      &FuchsiaSecureStreamDecryptor::OnNewKey, weak_factory_.GetWeakPtr()));
 }
 
 void FuchsiaSecureStreamDecryptor::OnError() {
   ResetStream();
+
+  // No need to reset other fields since OnError() is called for non-recoverable
+  // errors.
+
   client_->OnDecryptorError();
 }
 
+void FuchsiaSecureStreamDecryptor::OnNoKey() {
+  DCHECK(!waiting_for_key_);
+
+  // Reset stream position, but keep all pending buffers. They will be
+  // resubmitted later, when we have a new key.
+  input_writer_queue_.ResetPositionAndPause();
+
+  if (retry_on_no_key_) {
+    retry_on_no_key_ = false;
+    input_writer_queue_.Unpause();
+    return;
+  }
+
+  waiting_for_key_ = true;
+  client_->OnDecryptorNoKey();
+}
+
+void FuchsiaSecureStreamDecryptor::OnNewKey() {
+  if (!waiting_for_key_) {
+    retry_on_no_key_ = true;
+    return;
+  }
+
+  DCHECK(!retry_on_no_key_);
+  waiting_for_key_ = false;
+  input_writer_queue_.Unpause();
+}
+
 }  // namespace media
diff --git a/media/fuchsia/cdm/fuchsia_stream_decryptor.h b/media/fuchsia/cdm/fuchsia_stream_decryptor.h
index 1861260..594a3ef 100644
--- a/media/fuchsia/cdm/fuchsia_stream_decryptor.h
+++ b/media/fuchsia/cdm/fuchsia_stream_decryptor.h
@@ -28,6 +28,7 @@
   // StreamProcessorHelper::Client overrides.
   void AllocateInputBuffers(
       const fuchsia::media::StreamBufferConstraints& stream_constraints) final;
+  void OnOutputFormat(fuchsia::media::StreamOutputFormat format) final;
 
   void DecryptInternal(scoped_refptr<DecoderBuffer> encrypted);
   void ResetStream();
@@ -36,6 +37,8 @@
 
   BufferAllocator allocator_;
 
+  SysmemBufferWriterQueue input_writer_queue_;
+
  private:
   void OnInputBufferPoolCreated(std::unique_ptr<SysmemBufferPool> pool);
   void OnWriterCreated(std::unique_ptr<SysmemBufferWriter> writer);
@@ -43,7 +46,6 @@
                        StreamProcessorHelper::IoPacket packet);
   void ProcessEndOfStream();
 
-  SysmemBufferWriterQueue input_writer_queue_;
   std::unique_ptr<SysmemBufferPool::Creator> input_pool_creator_;
   std::unique_ptr<SysmemBufferPool> input_pool_;
 
@@ -67,13 +69,12 @@
 
  private:
   // StreamProcessorHelper::Client overrides.
-  void AllocateOutputBuffers(const fuchsia::media::StreamBufferConstraints&
-                                 stream_constraints) override;
-  void OnProcessEos() override;
-  void OnOutputFormat(fuchsia::media::StreamOutputFormat format) override;
-  void OnOutputPacket(StreamProcessorHelper::IoPacket packet) override;
-  void OnNoKey() override;
-  void OnError() override;
+  void AllocateOutputBuffers(
+      const fuchsia::media::StreamBufferConstraints& stream_constraints) final;
+  void OnProcessEos() final;
+  void OnOutputPacket(StreamProcessorHelper::IoPacket packet) final;
+  void OnNoKey() final;
+  void OnError() final;
 
   void OnOutputBufferPoolCreated(size_t num_buffers_for_client,
                                  size_t num_buffers_for_server,
@@ -95,8 +96,8 @@
   DISALLOW_COPY_AND_ASSIGN(FuchsiaClearStreamDecryptor);
 };
 
-// Stream decryptor that decrypts data to secure sysmem buffers. Used for video
-// stream.
+// Stream decryptor that decrypts data to protected sysmem buffers. Used for
+// video stream.
 class FuchsiaSecureStreamDecryptor : public FuchsiaStreamDecryptorBase {
  public:
   class Client {
@@ -111,10 +112,6 @@
     virtual ~Client() = default;
   };
 
-  static std::unique_ptr<FuchsiaSecureStreamDecryptor> Create(
-      fuchsia::media::drm::ContentDecryptionModule* cdm,
-      Client* client);
-
   FuchsiaSecureStreamDecryptor(fuchsia::media::StreamProcessorPtr processor,
                                Client* client);
   ~FuchsiaSecureStreamDecryptor() override;
@@ -124,26 +121,55 @@
       size_t num_buffers_for_decryptor,
       size_t num_buffers_for_codec);
 
+  // Enqueues the specified buffer to the input queue. Caller is allowed to
+  // queue as many buffers as it needs without waiting for results from the
+  // previous Decrypt() calls.
   void Decrypt(scoped_refptr<DecoderBuffer> encrypted);
 
+  // Returns closure that should be called when the key changes. This class
+  // uses this notification to handle NO_KEY errors. Note that this class can
+  // queue multiple input buffers so it's also responsible for resubmitting
+  // queued buffers after a new key is received. This is different from
+  // FuchsiaClearStreamDecryptor and media::Decryptor: they report NO_KEY error
+  // to the caller and expect the caller to resubmit same buffers again after
+  // the key is updated.
+  base::RepeatingClosure GetOnNewKeyClosure();
+
   // Drops all pending decryption requests.
   void Reset();
 
  private:
   // StreamProcessorHelper::Client overrides.
-  void AllocateOutputBuffers(const fuchsia::media::StreamBufferConstraints&
-                                 stream_constraints) override;
-  void OnProcessEos() override;
-  void OnOutputFormat(fuchsia::media::StreamOutputFormat format) override;
-  void OnOutputPacket(StreamProcessorHelper::IoPacket packet) override;
-  void OnNoKey() override;
-  void OnError() override;
+  void AllocateOutputBuffers(
+      const fuchsia::media::StreamBufferConstraints& stream_constraints) final;
+  void OnProcessEos() final;
+  void OnOutputPacket(StreamProcessorHelper::IoPacket packet) final;
+  void OnNoKey() final;
+  void OnError() final;
+
+  // Callback returned by GetOnNewKeyClosure(). When waiting for a key this
+  // method unpauses the stream to decrypt any pending buffers.
+  void OnNewKey();
 
   Client* const client_;
 
   bool waiting_output_buffers_ = false;
   base::OnceClosure complete_buffer_allocation_callback_;
 
+  // Set to true if some keys have been updated recently. New key notifications
+  // are received from a LicenseSession, while DECRYPTOR_NO_KEY error is
+  // received from StreamProcessor. These are separate FIDL connections that are
+  // handled on different threads, so they are not synchronized. As result
+  // OnNewKey() may be called before we get OnNoKey(). To handle this case
+  // correctly OnNewKey() sets |retry_on_no_key_| and then OnNoKey() tries to
+  // restart the stream immediately if this flag is set.
+  bool retry_on_no_key_ = false;
+
+  // Set to true if the stream is paused while we are waiting for new keys.
+  bool waiting_for_key_ = false;
+
+  base::WeakPtrFactory<FuchsiaSecureStreamDecryptor> weak_factory_{this};
+
   DISALLOW_COPY_AND_ASSIGN(FuchsiaSecureStreamDecryptor);
 };
 
diff --git a/media/fuchsia/common/stream_processor_helper.cc b/media/fuchsia/common/stream_processor_helper.cc
index 5b39a04..8465538 100644
--- a/media/fuchsia/common/stream_processor_helper.cc
+++ b/media/fuchsia/common/stream_processor_helper.cc
@@ -143,6 +143,9 @@
   }
 
   if (error == fuchsia::media::StreamError::DECRYPTOR_NO_KEY) {
+    // Always reset the stream since the current one has failed.
+    Reset();
+
     client_->OnNoKey();
     return;
   }
diff --git a/media/fuchsia/common/sysmem_buffer_writer_queue.cc b/media/fuchsia/common/sysmem_buffer_writer_queue.cc
index d04e378..1b8d11c6 100644
--- a/media/fuchsia/common/sysmem_buffer_writer_queue.cc
+++ b/media/fuchsia/common/sysmem_buffer_writer_queue.cc
@@ -14,30 +14,32 @@
 
 namespace media {
 
-class SysmemBufferWriterQueue::PendingBuffer {
- public:
-  PendingBuffer(scoped_refptr<DecoderBuffer> buffer) : buffer_(buffer) {
-    DCHECK(buffer_);
+struct SysmemBufferWriterQueue::PendingBuffer {
+  PendingBuffer(scoped_refptr<DecoderBuffer> buffer) : buffer(buffer) {
+    DCHECK(buffer);
   }
   ~PendingBuffer() = default;
 
   PendingBuffer(PendingBuffer&& other) = default;
   PendingBuffer& operator=(PendingBuffer&& other) = default;
 
-  const DecoderBuffer* buffer() { return buffer_.get(); }
-
-  const uint8_t* data() const { return buffer_->data() + buffer_pos_; }
-  size_t bytes_left() const { return buffer_->data_size() - buffer_pos_; }
+  const uint8_t* data() const { return buffer->data() + buffer_pos; }
+  size_t bytes_left() const { return buffer->data_size() - buffer_pos; }
   void AdvanceCurrentPos(size_t bytes) {
     DCHECK_LE(bytes, bytes_left());
-    buffer_pos_ += bytes;
+    buffer_pos += bytes;
   }
 
- private:
-  scoped_refptr<DecoderBuffer> buffer_;
-  size_t buffer_pos_ = 0;
+  scoped_refptr<DecoderBuffer> buffer;
+  size_t buffer_pos = 0;
 
-  DISALLOW_COPY_AND_ASSIGN(PendingBuffer);
+  // Set to true when the consumer has finished processing the buffer and it can
+  // be released.
+  bool is_complete = false;
+
+  // Index of the last buffer in the sysmem buffer collection that was used for
+  // this input buffer. Valid only when |bytes_left()==0|.
+  size_t tail_sysmem_buffer_index = 0;
 };
 
 SysmemBufferWriterQueue::SysmemBufferWriterQueue() = default;
@@ -64,8 +66,11 @@
 void SysmemBufferWriterQueue::PumpPackets() {
   auto weak_this = weak_factory_.GetWeakPtr();
 
-  while (writer_ && !pending_buffers_.empty()) {
-    if (pending_buffers_.front().buffer()->end_of_stream()) {
+  while (writer_ && !is_paused_ &&
+         input_queue_position_ < pending_buffers_.size()) {
+    PendingBuffer* current_buffer = &pending_buffers_[input_queue_position_];
+
+    if (current_buffer->buffer->end_of_stream()) {
       pending_buffers_.pop_front();
       end_of_stream_cb_.Run();
       if (!weak_this)
@@ -80,37 +85,36 @@
       return;
     }
 
-    size_t buffer_index = index_opt.value();
+    size_t sysmem_buffer_index = index_opt.value();
 
     size_t bytes_filled = writer_->Write(
-        buffer_index, base::make_span(pending_buffers_.front().data(),
-                                      pending_buffers_.front().bytes_left()));
-    pending_buffers_.front().AdvanceCurrentPos(bytes_filled);
+        sysmem_buffer_index,
+        base::make_span(current_buffer->data(), current_buffer->bytes_left()));
+    current_buffer->AdvanceCurrentPos(bytes_filled);
 
-    bool buffer_end = pending_buffers_.front().bytes_left() == 0;
+    bool buffer_end = current_buffer->bytes_left() == 0;
 
     auto packet = StreamProcessorHelper::IoPacket::CreateInput(
-        buffer_index, bytes_filled,
-        pending_buffers_.front().buffer()->timestamp(), buffer_end,
+        sysmem_buffer_index, bytes_filled, current_buffer->buffer->timestamp(),
+        buffer_end,
         base::BindOnce(&SysmemBufferWriterQueue::ReleaseBuffer,
-                       weak_factory_.GetWeakPtr(), buffer_index));
+                       weak_factory_.GetWeakPtr(), sysmem_buffer_index));
 
-    send_packet_cb_.Run(pending_buffers_.front().buffer(), std::move(packet));
+    if (buffer_end) {
+      current_buffer->tail_sysmem_buffer_index = sysmem_buffer_index;
+      input_queue_position_ += 1;
+    }
+
+    send_packet_cb_.Run(current_buffer->buffer.get(), std::move(packet));
     if (!weak_this)
       return;
-
-    if (buffer_end)
-      pending_buffers_.pop_front();
   }
 }
 
 void SysmemBufferWriterQueue::ResetQueue() {
-  // Invalidate weak pointers to drop all ReleaseBuffer() callbacks.
-  weak_factory_.InvalidateWeakPtrs();
-
   pending_buffers_.clear();
-  if (writer_)
-    writer_->ReleaseAll();
+  input_queue_position_ = 0;
+  is_paused_ = false;
 }
 
 void SysmemBufferWriterQueue::ResetBuffers() {
@@ -119,8 +123,42 @@
   end_of_stream_cb_ = EndOfStreamCB();
 }
 
+void SysmemBufferWriterQueue::ResetPositionAndPause() {
+  for (auto& buffer : pending_buffers_) {
+    buffer.buffer_pos = 0;
+    buffer.is_complete = false;
+  }
+  input_queue_position_ = 0;
+  is_paused_ = true;
+}
+
+void SysmemBufferWriterQueue::Unpause() {
+  DCHECK(is_paused_);
+  is_paused_ = false;
+  PumpPackets();
+}
+
 void SysmemBufferWriterQueue::ReleaseBuffer(size_t buffer_index) {
   DCHECK(writer_);
+
+  // Mark the input buffer as complete.
+  for (size_t i = 0; i < input_queue_position_; ++i) {
+    if (pending_buffers_[i].tail_sysmem_buffer_index == buffer_index)
+      pending_buffers_[i].is_complete = true;
+  }
+
+  // Remove all complete buffers from the head of the queue since we no longer
+  // need them. Note that currently StreamProcessor doesn't guarantee that input
+  // buffers are released in the same order they were sent (see
+  // https://fuchsia.googlesource.com/fuchsia/+/3b12c8c5/sdk/fidl/fuchsia.media/stream_processor.fidl#1646
+  // ). This means that some complete buffers will need to stay in the queue
+  // until all preceding packets are released as well.
+  while (!pending_buffers_.empty() && pending_buffers_.front().is_complete) {
+    pending_buffers_.pop_front();
+    DCHECK_GT(input_queue_position_, 0U);
+    input_queue_position_--;
+  }
+
   writer_->Release(buffer_index);
   PumpPackets();
 }
diff --git a/media/fuchsia/common/sysmem_buffer_writer_queue.h b/media/fuchsia/common/sysmem_buffer_writer_queue.h
index 4d5f2de..61a3f86 100644
--- a/media/fuchsia/common/sysmem_buffer_writer_queue.h
+++ b/media/fuchsia/common/sysmem_buffer_writer_queue.h
@@ -53,12 +53,21 @@
   // be sent once the new collection is allocated and passed to Start().
   void ResetBuffers();
 
+  // Resets pending queue position to the start of the queue and pauses the
+  // writer. All pending buffers will be resent when Unpause() is
+  // called.
+  void ResetPositionAndPause();
+
+  // Normally this should be called after restarting a stream in a
+  // StreamProcessor.
+  void Unpause();
+
   // Number of buffers in the sysmem collection or 0 if sysmem buffers has not
   // been allocated (i.e. before Start()).
   size_t num_buffers() const;
 
  private:
-  class PendingBuffer;
+  struct PendingBuffer;
   class SysmemBuffer;
 
   // Pumps pending buffers to SendPacketCB.
@@ -68,9 +77,17 @@
   // and tries to reuse it for other buffers if any.
   void ReleaseBuffer(size_t buffer_index);
 
-  // Buffers that are waiting to be sent.
+  // Buffers that are waiting to be sent. A buffer is removed from the queue
+  // when it and all previous buffers have finished decoding.
   std::deque<PendingBuffer> pending_buffers_;
 
+  // Position of the current buffer in |pending_buffers_|.
+  size_t input_queue_position_ = 0;
+
+  // Indicates that the stream is paused and no packets should be sent until
+  // Unpause() is called.
+  bool is_paused_ = false;
+
   // Buffers for sysmem buffer collection. Not set until Start() is called.
   std::unique_ptr<SysmemBufferWriter> writer_;
 
diff --git a/media/gpu/linux/BUILD.gn b/media/gpu/linux/BUILD.gn
index a3e72784..bb677bc 100644
--- a/media/gpu/linux/BUILD.gn
+++ b/media/gpu/linux/BUILD.gn
@@ -24,6 +24,7 @@
 
   deps = [
     "//base",
+    "//gpu/ipc/common:common",
     "//media",
     "//media/gpu:command_buffer_helper",
     "//media/gpu:common",
diff --git a/media/gpu/linux/platform_video_frame_pool.cc b/media/gpu/linux/platform_video_frame_pool.cc
index 91c750d9..fbabb74 100644
--- a/media/gpu/linux/platform_video_frame_pool.cc
+++ b/media/gpu/linux/platform_video_frame_pool.cc
@@ -17,20 +17,27 @@
 namespace {
 
 // The default method to create frames.
-scoped_refptr<VideoFrame> DefaultCreateFrame(VideoPixelFormat format,
-                                             const gfx::Size& coded_size,
-                                             const gfx::Rect& visible_rect,
-                                             const gfx::Size& natural_size,
-                                             base::TimeDelta timestamp) {
-  return CreatePlatformVideoFrame(format, coded_size, visible_rect,
-                                  natural_size, timestamp,
+scoped_refptr<VideoFrame> DefaultCreateFrame(
+    gpu::GpuMemoryBufferFactory* gpu_memory_buffer_factory,
+    VideoPixelFormat format,
+    const gfx::Size& coded_size,
+    const gfx::Rect& visible_rect,
+    const gfx::Size& natural_size,
+    base::TimeDelta timestamp) {
+  return CreatePlatformVideoFrame(gpu_memory_buffer_factory, format, coded_size,
+                                  visible_rect, natural_size, timestamp,
                                   gfx::BufferUsage::SCANOUT_VDA_WRITE);
 }
 
 }  // namespace
 
-PlatformVideoFramePool::PlatformVideoFramePool()
-    : PlatformVideoFramePool(base::BindRepeating(&DefaultCreateFrame)) {}
+PlatformVideoFramePool::PlatformVideoFramePool(
+    gpu::GpuMemoryBufferFactory* gpu_memory_buffer_factory)
+    : create_frame_cb_(base::BindRepeating(&DefaultCreateFrame)),
+      gpu_memory_buffer_factory_(gpu_memory_buffer_factory) {
+  DVLOGF(4);
+  weak_this_ = weak_this_factory_.GetWeakPtr();
+}
 
 PlatformVideoFramePool::PlatformVideoFramePool(CreateFrameCB cb)
     : create_frame_cb_(std::move(cb)) {
@@ -68,9 +75,9 @@
     // VideoFrame::WrapVideoFrame() will check whether the updated visible_rect
     // is sub rect of the original visible_rect. Therefore we set visible_rect
     // as large as coded_size to guarantee this condition.
-    scoped_refptr<VideoFrame> new_frame =
-        create_frame_cb_.Run(format, coded_size, gfx::Rect(coded_size),
-                             coded_size, base::TimeDelta());
+    scoped_refptr<VideoFrame> new_frame = create_frame_cb_.Run(
+        gpu_memory_buffer_factory_, format, coded_size, gfx::Rect(coded_size),
+        coded_size, base::TimeDelta());
     if (!new_frame)
       return nullptr;
 
@@ -129,9 +136,9 @@
 
   // Create a temporary frame in order to know VideoFrameLayout that VideoFrame
   // that will be allocated in GetFrame() has.
-  auto frame =
-      create_frame_cb_.Run(layout.format(), layout.coded_size(), visible_rect,
-                           natural_size_, base::TimeDelta());
+  auto frame = create_frame_cb_.Run(gpu_memory_buffer_factory_, layout.format(),
+                                    layout.coded_size(), visible_rect,
+                                    natural_size_, base::TimeDelta());
   if (!frame) {
     VLOGF(1) << "Failed to create video frame";
     return base::nullopt;
diff --git a/media/gpu/linux/platform_video_frame_pool.h b/media/gpu/linux/platform_video_frame_pool.h
index 5cfa17a..82aa7a8 100644
--- a/media/gpu/linux/platform_video_frame_pool.h
+++ b/media/gpu/linux/platform_video_frame_pool.h
@@ -21,6 +21,10 @@
 #include "media/gpu/linux/dmabuf_video_frame_pool.h"
 #include "media/gpu/media_gpu_export.h"
 
+namespace gpu {
+class GpuMemoryBufferFactory;
+}  // namespace gpu
+
 namespace media {
 
 // Simple VideoFrame pool used to avoid unnecessarily allocating and destroying
@@ -34,7 +38,8 @@
 // old parameter values will be purged from the pool.
 class MEDIA_GPU_EXPORT PlatformVideoFramePool : public DmabufVideoFramePool {
  public:
-  PlatformVideoFramePool();
+  explicit PlatformVideoFramePool(
+      gpu::GpuMemoryBufferFactory* gpu_memory_buffer_factory);
   ~PlatformVideoFramePool() override;
 
   // VideoFramePoolBase Implementation.
@@ -56,6 +61,7 @@
   friend class PlatformVideoFramePoolTest;
 
   using CreateFrameCB = base::RepeatingCallback<scoped_refptr<VideoFrame>(
+      gpu::GpuMemoryBufferFactory* gpu_memory_buffer_factory,
       VideoPixelFormat format,
       const gfx::Size& coded_size,
       const gfx::Rect& visible_rect,
@@ -97,6 +103,11 @@
   // Every public method and OnFrameReleased() should acquire this lock.
   base::Lock lock_;
 
+  // Used to allocate the video frame GpuMemoryBuffers, passed directly to
+  // the callback that creates video frames. Indirectly owned by GpuChildThread;
+  // therefore alive as long as the GPU process is.
+  gpu::GpuMemoryBufferFactory* const gpu_memory_buffer_factory_ = nullptr;
+
   // The arguments of current frame. We allocate new frames only if a pixel
   // format or coded size in |frame_layout_| is changed. When GetFrame() is
   // called, we update |visible_rect_| and |natural_size_| of wrapped frames.
diff --git a/media/gpu/linux/platform_video_frame_pool_unittest.cc b/media/gpu/linux/platform_video_frame_pool_unittest.cc
index 9b76824..c0917386 100644
--- a/media/gpu/linux/platform_video_frame_pool_unittest.cc
+++ b/media/gpu/linux/platform_video_frame_pool_unittest.cc
@@ -28,11 +28,13 @@
   return base::ScopedFD(file.TakePlatformFile());
 }
 
-scoped_refptr<VideoFrame> CreateDmabufVideoFrame(VideoPixelFormat format,
-                                                 const gfx::Size& coded_size,
-                                                 const gfx::Rect& visible_rect,
-                                                 const gfx::Size& natural_size,
-                                                 base::TimeDelta timestamp) {
+scoped_refptr<VideoFrame> CreateDmabufVideoFrame(
+    gpu::GpuMemoryBufferFactory* factory,
+    VideoPixelFormat format,
+    const gfx::Size& coded_size,
+    const gfx::Rect& visible_rect,
+    const gfx::Size& natural_size,
+    base::TimeDelta timestamp) {
   base::Optional<VideoFrameLayout> layout =
       VideoFrameLayout::Create(format, coded_size);
   DCHECK(layout);
diff --git a/media/gpu/linux/platform_video_frame_utils.cc b/media/gpu/linux/platform_video_frame_utils.cc
index 51821579..6e7ea83 100644
--- a/media/gpu/linux/platform_video_frame_utils.cc
+++ b/media/gpu/linux/platform_video_frame_utils.cc
@@ -4,6 +4,7 @@
 
 #include "media/gpu/linux/platform_video_frame_utils.h"
 
+#include "base/atomic_sequence_num.h"
 #include "base/bind.h"
 #include "base/bind_helpers.h"
 #include "base/files/scoped_file.h"
@@ -18,55 +19,55 @@
 #include "ui/gfx/linux/native_pixmap_dmabuf.h"
 #include "ui/gfx/native_pixmap.h"
 
-#if defined(USE_OZONE)
-#include "ui/ozone/public/ozone_platform.h"
-#include "ui/ozone/public/surface_factory_ozone.h"
-#endif
+#if defined(OS_LINUX)
+#include "gpu/ipc/common/gpu_client_ids.h"
+#include "gpu/ipc/service/gpu_memory_buffer_factory.h"
+#endif  // defined(OS_LINUX)
 
 namespace media {
 
 namespace {
 
-#if defined(USE_OZONE)
-scoped_refptr<VideoFrame> CreateVideoFrameOzone(VideoPixelFormat pixel_format,
-                                                const gfx::Size& coded_size,
-                                                const gfx::Rect& visible_rect,
-                                                const gfx::Size& natural_size,
-                                                base::TimeDelta timestamp,
-                                                gfx::BufferUsage buffer_usage) {
-  ui::OzonePlatform* platform = ui::OzonePlatform::GetInstance();
-  DCHECK(platform);
-  ui::SurfaceFactoryOzone* factory = platform->GetSurfaceFactoryOzone();
-  DCHECK(factory);
+#if defined(OS_LINUX)
 
+scoped_refptr<VideoFrame> CreateVideoFrameGpu(
+    gpu::GpuMemoryBufferFactory* factory,
+    VideoPixelFormat pixel_format,
+    const gfx::Size& coded_size,
+    const gfx::Rect& visible_rect,
+    const gfx::Size& natural_size,
+    base::TimeDelta timestamp,
+    gfx::BufferUsage buffer_usage) {
+  DCHECK(factory);
   auto buffer_format = VideoPixelFormatToGfxBufferFormat(pixel_format);
   if (!buffer_format)
     return nullptr;
 
-  auto pixmap =
-      factory->CreateNativePixmap(gfx::kNullAcceleratedWidget, VK_NULL_HANDLE,
-                                  coded_size, *buffer_format, buffer_usage);
-  if (!pixmap)
+  static base::AtomicSequenceNumber buffer_id_generator;
+  auto gmb_handle = factory->CreateGpuMemoryBuffer(
+      gfx::GpuMemoryBufferId(buffer_id_generator.GetNext()), coded_size,
+      *buffer_format, buffer_usage, gpu::kPlatformVideoFramePoolClientId,
+      gfx::kNullAcceleratedWidget);
+  if (gmb_handle.is_null() || gmb_handle.type != gfx::NATIVE_PIXMAP)
     return nullptr;
 
-  const size_t num_planes = VideoFrame::NumPlanes(pixel_format);
-  std::vector<ColorPlaneLayout> planes(num_planes);
-  for (size_t i = 0; i < num_planes; ++i) {
-    planes[i].stride = pixmap->GetDmaBufPitch(i);
-    planes[i].offset = pixmap->GetDmaBufOffset(i);
-    planes[i].size = pixmap->GetDmaBufPlaneSize(i);
-  }
+  DCHECK_EQ(VideoFrame::NumPlanes(pixel_format),
+            gmb_handle.native_pixmap_handle.planes.size());
+  std::vector<ColorPlaneLayout> planes;
+  for (const auto& plane : gmb_handle.native_pixmap_handle.planes)
+    planes.emplace_back(plane.stride, plane.offset, plane.size);
+
   auto layout = VideoFrameLayout::CreateWithPlanes(
       pixel_format, coded_size, std::move(planes),
       VideoFrameLayout::kBufferAddressAlignment,
-      pixmap->GetBufferFormatModifier());
+      gmb_handle.native_pixmap_handle.modifier);
 
   if (!layout)
     return nullptr;
 
   std::vector<base::ScopedFD> dmabuf_fds;
-  for (size_t i = 0; i < num_planes; ++i) {
-    int duped_fd = HANDLE_EINTR(dup(pixmap->GetDmaBufFd(i)));
+  for (const auto& plane : gmb_handle.native_pixmap_handle.planes) {
+    int duped_fd = HANDLE_EINTR(dup(plane.fd.get()));
     if (duped_fd == -1) {
       DLOG(ERROR) << "Failed duplicating dmabuf fd";
       return nullptr;
@@ -80,40 +81,48 @@
   if (!frame)
     return nullptr;
 
-  // created |pixmap| must be owned by |frame|.
+  // Created |gmb_handle| must be owned by |frame|.
   frame->AddDestructionObserver(
-      base::BindOnce(base::DoNothing::Once<scoped_refptr<gfx::NativePixmap>>(),
-                     std::move(pixmap)));
+      base::BindOnce(base::DoNothing::Once<gfx::GpuMemoryBufferHandle>(),
+                     std::move(gmb_handle)));
+  // We also need to have the factory drop its reference to the native pixmap.
+  frame->AddDestructionObserver(
+      base::BindOnce(&gpu::GpuMemoryBufferFactory::DestroyGpuMemoryBuffer,
+                     base::Unretained(factory), gmb_handle.id,
+                     gpu::kPlatformVideoFramePoolClientId));
   return frame;
 }
-#endif  // defined(USE_OZONE)
+#endif  // defined(OS_LINUX)
 
 }  // namespace
 
 scoped_refptr<VideoFrame> CreatePlatformVideoFrame(
+    gpu::GpuMemoryBufferFactory* gpu_memory_buffer_factory,
     VideoPixelFormat pixel_format,
     const gfx::Size& coded_size,
     const gfx::Rect& visible_rect,
     const gfx::Size& natural_size,
     base::TimeDelta timestamp,
     gfx::BufferUsage buffer_usage) {
-#if defined(USE_OZONE)
-  return CreateVideoFrameOzone(pixel_format, coded_size, visible_rect,
-                               natural_size, timestamp, buffer_usage);
-#endif  // defined(USE_OZONE)
+#if defined(OS_LINUX)
+  return CreateVideoFrameGpu(gpu_memory_buffer_factory, pixel_format,
+                             coded_size, visible_rect, natural_size, timestamp,
+                             buffer_usage);
+#endif  // defined(OS_LINUX)
   NOTREACHED();
   return nullptr;
 }
 
 base::Optional<VideoFrameLayout> GetPlatformVideoFrameLayout(
+    gpu::GpuMemoryBufferFactory* gpu_memory_buffer_factory,
     VideoPixelFormat pixel_format,
     const gfx::Size& coded_size,
     gfx::BufferUsage buffer_usage) {
-  // |visible_rect| and |natural_size| are not matter here. |coded_size| is set
+  // |visible_rect| and |natural_size| do not matter here. |coded_size| is set
   // as a dummy variable.
-  auto frame =
-      CreatePlatformVideoFrame(pixel_format, coded_size, gfx::Rect(coded_size),
-                               coded_size, base::TimeDelta(), buffer_usage);
+  auto frame = CreatePlatformVideoFrame(
+      gpu_memory_buffer_factory, pixel_format, coded_size,
+      gfx::Rect(coded_size), coded_size, base::TimeDelta(), buffer_usage);
   return frame ? base::make_optional<VideoFrameLayout>(frame->layout())
                : base::nullopt;
 }
diff --git a/media/gpu/linux/platform_video_frame_utils.h b/media/gpu/linux/platform_video_frame_utils.h
index 66e24c9..ffa9b14 100644
--- a/media/gpu/linux/platform_video_frame_utils.h
+++ b/media/gpu/linux/platform_video_frame_utils.h
@@ -15,11 +15,16 @@
 struct GpuMemoryBufferHandle;
 }  // namespace gfx
 
+namespace gpu {
+class GpuMemoryBufferFactory;
+}  // namespace gpu
+
 namespace media {
 
 // Create platform dependent media::VideoFrame. |buffer_usage| is passed to
 // CreateNativePixmap(). See //media/base/video_frame.h for other parameters.
 MEDIA_GPU_EXPORT scoped_refptr<VideoFrame> CreatePlatformVideoFrame(
+    gpu::GpuMemoryBufferFactory* gpu_memory_buffer_factory,
     VideoPixelFormat pixel_format,
     const gfx::Size& coded_size,
     const gfx::Rect& visible_rect,
@@ -31,6 +36,7 @@
 // |coded_size| and |buffer_usage|. This function is not cost-free as this
 // allocates a platform dependent video frame.
 MEDIA_GPU_EXPORT base::Optional<VideoFrameLayout> GetPlatformVideoFrameLayout(
+    gpu::GpuMemoryBufferFactory* gpu_memory_buffer_factory,
     VideoPixelFormat pixel_format,
     const gfx::Size& coded_size,
     gfx::BufferUsage buffer_usage);
diff --git a/media/gpu/test/image_processor/image_processor_client.cc b/media/gpu/test/image_processor/image_processor_client.cc
index 68a4aa8..6de8fa2b 100644
--- a/media/gpu/test/image_processor/image_processor_client.cc
+++ b/media/gpu/test/image_processor/image_processor_client.cc
@@ -49,7 +49,9 @@
 
 ImageProcessorClient::ImageProcessorClient(
     std::vector<std::unique_ptr<VideoFrameProcessor>> frame_processors)
-    : frame_processors_(std::move(frame_processors)),
+    : gpu_memory_buffer_factory_(
+          gpu::GpuMemoryBufferFactory::CreateNativeType(nullptr)),
+      frame_processors_(std::move(frame_processors)),
       image_processor_client_thread_("ImageProcessorClientThread"),
       output_cv_(&output_lock_),
       num_processed_frames_(0),
@@ -114,7 +116,8 @@
   const auto& input_layout = image_processor_->input_layout();
   if (VideoFrame::IsStorageTypeMappable(
           image_processor_->input_storage_type())) {
-    return CloneVideoFrame(CreateVideoFrameFromImage(input_image).get(),
+    return CloneVideoFrame(gpu_memory_buffer_factory_.get(),
+                           CreateVideoFrameFromImage(input_image).get(),
                            input_layout, VideoFrame::STORAGE_OWNED_MEMORY);
   } else {
 #if defined(OS_CHROMEOS)
@@ -127,7 +130,8 @@
         IsYuvPlanar(input_image.PixelFormat())
             ? gfx::BufferUsage::SCANOUT_VEA_READ_CAMERA_AND_CPU_READ_WRITE
             : gfx::BufferUsage::GPU_READ_CPU_READ_WRITE;
-    return CloneVideoFrame(CreateVideoFrameFromImage(input_image).get(),
+    return CloneVideoFrame(gpu_memory_buffer_factory_.get(),
+                           CreateVideoFrameFromImage(input_image).get(),
                            input_layout, VideoFrame::STORAGE_DMABUFS,
                            dst_buffer_usage);
 #endif
@@ -152,8 +156,9 @@
     LOG_ASSERT(image_processor_->output_storage_type() ==
                VideoFrame::STORAGE_DMABUFS);
     return CreatePlatformVideoFrame(
-        output_layout.format(), output_layout.coded_size(),
-        gfx::Rect(output_image.Size()), output_image.Size(), base::TimeDelta(),
+        gpu_memory_buffer_factory_.get(), output_layout.format(),
+        output_layout.coded_size(), gfx::Rect(output_image.Size()),
+        output_image.Size(), base::TimeDelta(),
         gfx::BufferUsage::GPU_READ_CPU_READ_WRITE);
 #endif  // BUILDFLAG(USE_CHROMEOS_MEDIA_ACCELERATION)
     return nullptr;
diff --git a/media/gpu/test/image_processor/image_processor_client.h b/media/gpu/test/image_processor/image_processor_client.h
index 1148c20..2beb64b 100644
--- a/media/gpu/test/image_processor/image_processor_client.h
+++ b/media/gpu/test/image_processor/image_processor_client.h
@@ -16,6 +16,7 @@
 #include "base/threading/thread.h"
 #include "base/threading/thread_checker.h"
 #include "base/time/time.h"
+#include "gpu/ipc/service/gpu_memory_buffer_factory.h"
 #include "media/gpu/image_processor.h"
 #include "media/gpu/test/video_frame_helpers.h"
 
@@ -111,6 +112,8 @@
 
   std::unique_ptr<ImageProcessor> image_processor_;
 
+  std::unique_ptr<gpu::GpuMemoryBufferFactory> gpu_memory_buffer_factory_;
+
   // VideoFrameProcessors that will process the video frames produced by
   // |image_processor_|.
   std::vector<std::unique_ptr<VideoFrameProcessor>> frame_processors_;
diff --git a/media/gpu/test/rendering_helper.cc b/media/gpu/test/rendering_helper.cc
index dd54917..1882e3d 100644
--- a/media/gpu/test/rendering_helper.cc
+++ b/media/gpu/test/rendering_helper.cc
@@ -107,6 +107,9 @@
     done.Wait();
   }
 
+  gpu_memory_buffer_factory_ =
+      gpu::GpuMemoryBufferFactory::CreateNativeType(nullptr);
+
   render_task_.Reset(
       base::Bind(&RenderingHelper::RenderContent, base::Unretained(this)));
 
@@ -308,7 +311,8 @@
       use_gl_ ? base::BindOnce(DeleteTexture, texture_id) : base::DoNothing();
   if (pre_allocate) {
     return media::test::TextureRef::CreatePreallocated(
-        texture_id, std::move(delete_texture_cb), pixel_format, size,
+        gpu_memory_buffer_factory_.get(), texture_id,
+        std::move(delete_texture_cb), pixel_format, size,
         gfx::BufferUsage::SCANOUT_VDA_WRITE);
   }
   return media::test::TextureRef::Create(texture_id,
@@ -461,6 +465,7 @@
 }
 
 void RenderingHelper::Clear() {
+  gpu_memory_buffer_factory_.reset();
   videos_.clear();
   task_runner_ = nullptr;
   gl_context_ = NULL;
diff --git a/media/gpu/test/rendering_helper.h b/media/gpu/test/rendering_helper.h
index 5ff09db..03dfb94 100644
--- a/media/gpu/test/rendering_helper.h
+++ b/media/gpu/test/rendering_helper.h
@@ -19,6 +19,7 @@
 #include "base/single_thread_task_runner.h"
 #include "base/time/time.h"
 #include "build/build_config.h"
+#include "gpu/ipc/service/gpu_memory_buffer_factory.h"
 #include "media/base/video_types.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/gfx/geometry/size.h"
@@ -181,6 +182,8 @@
   scoped_refptr<gl::GLContext> gl_context_;
   scoped_refptr<gl::GLSurface> gl_surface_;
 
+  std::unique_ptr<gpu::GpuMemoryBufferFactory> gpu_memory_buffer_factory_;
+
   std::vector<RenderedVideo> videos_;
 
   bool render_as_thumbnails_;
diff --git a/media/gpu/test/texture_ref.cc b/media/gpu/test/texture_ref.cc
index 63b17b0..87ee915 100644
--- a/media/gpu/test/texture_ref.cc
+++ b/media/gpu/test/texture_ref.cc
@@ -9,6 +9,7 @@
 
 #include "base/logging.h"
 #include "build/build_config.h"
+#include "gpu/ipc/service/gpu_memory_buffer_factory.h"
 #include "media/gpu/test/video_frame_helpers.h"
 
 #if defined(OS_LINUX)
@@ -42,6 +43,7 @@
 
 // static
 scoped_refptr<TextureRef> TextureRef::CreatePreallocated(
+    gpu::GpuMemoryBufferFactory* gpu_memory_buffer_factory,
     uint32_t texture_id,
     base::OnceClosure no_longer_needed_cb,
     VideoPixelFormat pixel_format,
@@ -54,9 +56,9 @@
   LOG_ASSERT(texture_ref);
   // We set visible size to coded_size. The correct visible rectangle is set
   // later in ExportVideoFrame().
-  texture_ref->frame_ =
-      CreatePlatformVideoFrame(pixel_format, size, gfx::Rect(size), size,
-                               base::TimeDelta(), buffer_usage);
+  texture_ref->frame_ = CreatePlatformVideoFrame(
+      gpu_memory_buffer_factory, pixel_format, size, gfx::Rect(size), size,
+      base::TimeDelta(), buffer_usage);
 #endif  // BUILDFLAG(USE_CHROMEOS_MEDIA_ACCELERATION)
 
   return texture_ref;
diff --git a/media/gpu/test/texture_ref.h b/media/gpu/test/texture_ref.h
index 8c0f58ad..935932c2 100644
--- a/media/gpu/test/texture_ref.h
+++ b/media/gpu/test/texture_ref.h
@@ -14,6 +14,10 @@
 #include "ui/gfx/geometry/size.h"
 #include "ui/gfx/gpu_memory_buffer.h"
 
+namespace gpu {
+class GpuMemoryBufferFactory;
+}
+
 namespace media {
 namespace test {
 
@@ -26,6 +30,7 @@
       base::OnceClosure no_longer_needed_cb);
 
   static scoped_refptr<TextureRef> CreatePreallocated(
+      gpu::GpuMemoryBufferFactory* gpu_memory_buffer_factory,
       uint32_t texture_id,
       base::OnceClosure no_longer_needed_cb,
       VideoPixelFormat pixel_format,
diff --git a/media/gpu/test/video_frame_helpers.cc b/media/gpu/test/video_frame_helpers.cc
index a844e53..5602ab5 100644
--- a/media/gpu/test/video_frame_helpers.cc
+++ b/media/gpu/test/video_frame_helpers.cc
@@ -10,6 +10,7 @@
 #include "base/bind_helpers.h"
 #include "base/memory/scoped_refptr.h"
 #include "gpu/ipc/common/gpu_memory_buffer_support.h"
+#include "gpu/ipc/service/gpu_memory_buffer_factory.h"
 #include "media/base/color_plane_layout.h"
 #include "media/base/format_utils.h"
 #include "media/base/video_frame.h"
@@ -201,6 +202,7 @@
 }
 
 scoped_refptr<VideoFrame> CloneVideoFrame(
+    gpu::GpuMemoryBufferFactory* gpu_memory_buffer_factory,
     const VideoFrame* const src_frame,
     const VideoFrameLayout& dst_layout,
     VideoFrame::StorageType dst_storage_type,
@@ -222,9 +224,10 @@
         return nullptr;
       }
       dst_frame = CreatePlatformVideoFrame(
-          dst_layout.format(), dst_layout.coded_size(),
-          src_frame->visible_rect(), src_frame->visible_rect().size(),
-          src_frame->timestamp(), *dst_buffer_usage);
+          gpu_memory_buffer_factory, dst_layout.format(),
+          dst_layout.coded_size(), src_frame->visible_rect(),
+          src_frame->visible_rect().size(), src_frame->timestamp(),
+          *dst_buffer_usage);
       break;
 #endif  // BUILDFLAG(USE_CHROMEOS_MEDIA_ACCELERATION)
     case VideoFrame::STORAGE_OWNED_MEMORY:
diff --git a/media/gpu/test/video_frame_helpers.h b/media/gpu/test/video_frame_helpers.h
index 2e9f580..78c7b99 100644
--- a/media/gpu/test/video_frame_helpers.h
+++ b/media/gpu/test/video_frame_helpers.h
@@ -12,6 +12,10 @@
 #include "ui/gfx/buffer_types.h"
 #include "ui/gfx/geometry/size.h"
 
+namespace gpu {
+class GpuMemoryBufferFactory;
+}
+
 namespace media {
 namespace test {
 
@@ -53,11 +57,14 @@
 // If |dst_storage_type| is STORAGE_DMABUFS, this function creates DMABUF-backed
 // VideoFrame with |dst_layout|. If |dst_storage_type| is STORAGE_OWNED_MEMORY,
 // this function creates memory-backed VideoFrame with |dst_layout|.
-// |dst_buffer_usage| must be specified if |dst_storage_type| is STORAGE_DMABUFS
-// or STORAGE_GPU_MEMORY_BUFFER . A graphic buffer is created with this usage.
+// |dst_buffer_usage| and |gpu_memory_buffer_factory| must be specified if
+// |dst_storage_type| is STORAGE_DMABUFS or STORAGE_GPU_MEMORY_BUFFER, and in
+// that case the |gpu_memory_buffer_factory| will be used for the allocation to
+// create a graphic buffer with the requested usage.
 // The created VideoFrame's content is the same as |src_frame|. The created
 // VideoFrame owns the buffer. Returns nullptr on failure.
 scoped_refptr<VideoFrame> CloneVideoFrame(
+    gpu::GpuMemoryBufferFactory* gpu_memory_buffer_factory,
     const VideoFrame* const src_frame,
     const VideoFrameLayout& dst_layout,
     VideoFrame::StorageType dst_storage_type = VideoFrame::STORAGE_OWNED_MEMORY,
diff --git a/media/gpu/test/video_player/test_vda_video_decoder.cc b/media/gpu/test/video_player/test_vda_video_decoder.cc
index 06851df2..d45aef8 100644
--- a/media/gpu/test/video_player/test_vda_video_decoder.cc
+++ b/media/gpu/test/video_player/test_vda_video_decoder.cc
@@ -15,7 +15,6 @@
 #include "media/base/bind_to_current_loop.h"
 #include "media/base/video_frame.h"
 #include "media/base/waiting.h"
-#include "media/gpu/buildflags.h"
 #include "media/gpu/gpu_video_decode_accelerator_factory.h"
 #include "media/gpu/macros.h"
 #include "media/gpu/test/video_player/frame_renderer.h"
@@ -37,12 +36,16 @@
 TestVDAVideoDecoder::TestVDAVideoDecoder(
     AllocationMode allocation_mode,
     const gfx::ColorSpace& target_color_space,
-    FrameRenderer* const frame_renderer)
+    FrameRenderer* const frame_renderer,
+    gpu::GpuMemoryBufferFactory* gpu_memory_buffer_factory)
     : output_mode_(allocation_mode == AllocationMode::kAllocate
                        ? VideoDecodeAccelerator::Config::OutputMode::ALLOCATE
                        : VideoDecodeAccelerator::Config::OutputMode::IMPORT),
       target_color_space_(target_color_space),
       frame_renderer_(frame_renderer),
+#if BUILDFLAG(USE_CHROMEOS_MEDIA_ACCELERATION)
+      gpu_memory_buffer_factory_(gpu_memory_buffer_factory),
+#endif  // BUILDFLAG(USE_CHROMEOS_MEDIA_ACCELERATION)
       decode_start_timestamps_(kTimestampCacheSize) {
   DETACH_FROM_SEQUENCE(vda_wrapper_sequence_checker_);
 
@@ -220,8 +223,9 @@
 
 #if BUILDFLAG(USE_CHROMEOS_MEDIA_ACCELERATION)
         video_frame = CreatePlatformVideoFrame(
-            format, dimensions, visible_rect, visible_rect.size(),
-            base::TimeDelta(), gfx::BufferUsage::SCANOUT_VDA_WRITE);
+            gpu_memory_buffer_factory_, format, dimensions, visible_rect,
+            visible_rect.size(), base::TimeDelta(),
+            gfx::BufferUsage::SCANOUT_VDA_WRITE);
 #endif  // BUILDFLAG(USE_CHROMEOS_MEDIA_ACCELERATION)
 
         LOG_ASSERT(video_frame) << "Failed to create video frame";
diff --git a/media/gpu/test/video_player/test_vda_video_decoder.h b/media/gpu/test/video_player/test_vda_video_decoder.h
index bda6726c..244aed510 100644
--- a/media/gpu/test/video_player/test_vda_video_decoder.h
+++ b/media/gpu/test/video_player/test_vda_video_decoder.h
@@ -13,7 +13,9 @@
 #include "base/containers/mru_cache.h"
 #include "base/macros.h"
 #include "base/sequence_checker.h"
+#include "gpu/ipc/service/gpu_memory_buffer_factory.h"
 #include "media/base/video_decoder.h"
+#include "media/gpu/buildflags.h"
 #include "media/gpu/test/video_player/video_decoder_client.h"
 #include "media/video/video_decode_accelerator.h"
 
@@ -36,7 +38,8 @@
   // delegated to the underlying VDA.
   TestVDAVideoDecoder(AllocationMode allocation_mode,
                       const gfx::ColorSpace& target_color_space,
-                      FrameRenderer* const frame_renderer);
+                      FrameRenderer* const frame_renderer,
+                      gpu::GpuMemoryBufferFactory* gpu_memory_buffer_factory);
   ~TestVDAVideoDecoder() override;
 
   // media::VideoDecoder implementation
@@ -98,6 +101,11 @@
   // Frame renderer used to manage GL context.
   FrameRenderer* const frame_renderer_;
 
+#if BUILDFLAG(USE_CHROMEOS_MEDIA_ACCELERATION)
+  // Owned by VideoDecoderClient.
+  gpu::GpuMemoryBufferFactory* const gpu_memory_buffer_factory_;
+#endif  // BUILDFLAG(USE_CHROMEOS_MEDIA_ACCELERATION)
+
   // Map of video frames the decoder uses as output, keyed on picture buffer id.
   std::map<int32_t, scoped_refptr<VideoFrame>> video_frames_;
   // Map of video frame decoded callbacks, keyed on bitstream buffer id.
diff --git a/media/gpu/test/video_player/video_decoder_client.cc b/media/gpu/test/video_player/video_decoder_client.cc
index b290a07e..bd796ad1 100644
--- a/media/gpu/test/video_player/video_decoder_client.cc
+++ b/media/gpu/test/video_player/video_decoder_client.cc
@@ -34,6 +34,7 @@
 
 VideoDecoderClient::VideoDecoderClient(
     const VideoPlayer::EventCallback& event_cb,
+    gpu::GpuMemoryBufferFactory* gpu_memory_buffer_factory,
     std::unique_ptr<FrameRenderer> renderer,
     std::vector<std::unique_ptr<VideoFrameProcessor>> frame_processors,
     const VideoDecoderClientConfig& config)
@@ -42,7 +43,8 @@
       frame_processors_(std::move(frame_processors)),
       decoder_client_config_(config),
       decoder_client_thread_("VDAClientDecoderThread"),
-      decoder_client_state_(VideoDecoderClientState::kUninitialized) {
+      decoder_client_state_(VideoDecoderClientState::kUninitialized),
+      gpu_memory_buffer_factory_(gpu_memory_buffer_factory) {
   DETACH_FROM_SEQUENCE(decoder_client_sequence_checker_);
 
   weak_this_ = weak_this_factory_.GetWeakPtr();
@@ -67,12 +69,13 @@
 // static
 std::unique_ptr<VideoDecoderClient> VideoDecoderClient::Create(
     const VideoPlayer::EventCallback& event_cb,
+    gpu::GpuMemoryBufferFactory* gpu_memory_buffer_factory,
     std::unique_ptr<FrameRenderer> frame_renderer,
     std::vector<std::unique_ptr<VideoFrameProcessor>> frame_processors,
     const VideoDecoderClientConfig& config) {
-  auto decoder_client = base::WrapUnique(
-      new VideoDecoderClient(event_cb, std::move(frame_renderer),
-                             std::move(frame_processors), config));
+  auto decoder_client = base::WrapUnique(new VideoDecoderClient(
+      event_cb, gpu_memory_buffer_factory, std::move(frame_renderer),
+      std::move(frame_processors), config));
   if (!decoder_client->CreateDecoder()) {
     return nullptr;
   }
@@ -167,7 +170,7 @@
     if (decoder_client_config_.allocation_mode == AllocationMode::kImport) {
       decoder_ = ChromeosVideoDecoderFactory::Create(
           base::ThreadTaskRunnerHandle::Get(),
-          std::make_unique<PlatformVideoFramePool>(),
+          std::make_unique<PlatformVideoFramePool>(gpu_memory_buffer_factory_),
           std::make_unique<VideoFrameConverter>());
     } else {
       LOG(ERROR) << "VD-based video decoders only support import mode";
@@ -179,7 +182,7 @@
     // decoders.
     decoder_ = std::make_unique<TestVDAVideoDecoder>(
         decoder_client_config_.allocation_mode, gfx::ColorSpace(),
-        frame_renderer_.get());
+        frame_renderer_.get(), gpu_memory_buffer_factory_);
   }
 
   *success = (decoder_ != nullptr);
diff --git a/media/gpu/test/video_player/video_decoder_client.h b/media/gpu/test/video_player/video_decoder_client.h
index ad2003c7..162fd76 100644
--- a/media/gpu/test/video_player/video_decoder_client.h
+++ b/media/gpu/test/video_player/video_decoder_client.h
@@ -14,6 +14,7 @@
 #include "base/memory/weak_ptr.h"
 #include "base/sequence_checker.h"
 #include "base/threading/thread.h"
+#include "gpu/ipc/service/gpu_memory_buffer_factory.h"
 #include "media/base/decode_status.h"
 #include "media/base/video_decoder.h"
 #include "media/base/video_decoder_config.h"
@@ -62,14 +63,16 @@
  public:
   ~VideoDecoderClient();
 
-  // Return an instance of the VideoDecoderClient. The |video|, |frame_renderer|
-  // and |frame_processors| will not be owned by the decoder client, the caller
-  // should guarantee they outlive the decoder client. The |event_cb| will be
-  // called whenever an event occurs (e.g. frame decoded) and should be
-  // thread-safe. Initialization is performed asynchronous, upon completion a
-  // 'kInitialized' event will be thrown.
+  // Return an instance of the VideoDecoderClient. The
+  // |gpu_memory_buffer_factory|, |frame_renderer| and |frame_processors| will
+  // not be owned by the decoder client, the caller should guarantee they
+  // outlive the decoder client. The |event_cb| will be called whenever an event
+  // occurs (e.g. frame decoded) and should be thread-safe. Initialization is
+  // performed asynchronous, upon completion a 'kInitialized' event will be
+  // thrown.
   static std::unique_ptr<VideoDecoderClient> Create(
       const VideoPlayer::EventCallback& event_cb,
+      gpu::GpuMemoryBufferFactory* gpu_memory_buffer_factory,
       std::unique_ptr<FrameRenderer> frame_renderer,
       std::vector<std::unique_ptr<VideoFrameProcessor>> frame_processors,
       const VideoDecoderClientConfig& config);
@@ -109,6 +112,7 @@
 
   VideoDecoderClient(
       const VideoPlayer::EventCallback& event_cb,
+      gpu::GpuMemoryBufferFactory* gpu_memory_buffer_factory,
       std::unique_ptr<FrameRenderer> renderer,
       std::vector<std::unique_ptr<VideoFrameProcessor>> frame_processors,
       const VideoDecoderClientConfig& config);
@@ -173,6 +177,9 @@
   // The video being decoded.
   const Video* video_ = nullptr;
 
+  // Owned by VideoPlayerTestEnvironment.
+  gpu::GpuMemoryBufferFactory* const gpu_memory_buffer_factory_;
+
   SEQUENCE_CHECKER(video_player_sequence_checker_);
   SEQUENCE_CHECKER(decoder_client_sequence_checker_);
 
diff --git a/media/gpu/test/video_player/video_player.cc b/media/gpu/test/video_player/video_player.cc
index 8438f3e..2f0382f 100644
--- a/media/gpu/test/video_player/video_player.cc
+++ b/media/gpu/test/video_player/video_player.cc
@@ -32,10 +32,12 @@
 // static
 std::unique_ptr<VideoPlayer> VideoPlayer::Create(
     const VideoDecoderClientConfig& config,
+    gpu::GpuMemoryBufferFactory* gpu_memory_buffer_factory,
     std::unique_ptr<FrameRenderer> frame_renderer,
     std::vector<std::unique_ptr<VideoFrameProcessor>> frame_processors) {
   auto video_player = base::WrapUnique(new VideoPlayer());
-  if (!video_player->CreateDecoderClient(config, std::move(frame_renderer),
+  if (!video_player->CreateDecoderClient(config, gpu_memory_buffer_factory,
+                                         std::move(frame_renderer),
                                          std::move(frame_processors))) {
     return nullptr;
   }
@@ -44,6 +46,7 @@
 
 bool VideoPlayer::CreateDecoderClient(
     const VideoDecoderClientConfig& config,
+    gpu::GpuMemoryBufferFactory* gpu_memory_buffer_factory,
     std::unique_ptr<FrameRenderer> frame_renderer,
     std::vector<std::unique_ptr<VideoFrameProcessor>> frame_processors) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
@@ -55,7 +58,8 @@
       base::BindRepeating(&VideoPlayer::NotifyEvent, base::Unretained(this));
 
   decoder_client_ = VideoDecoderClient::Create(
-      event_cb, std::move(frame_renderer), std::move(frame_processors), config);
+      event_cb, gpu_memory_buffer_factory, std::move(frame_renderer),
+      std::move(frame_processors), config);
   if (!decoder_client_) {
     VLOGF(1) << "Failed to create video decoder client";
     return false;
diff --git a/media/gpu/test/video_player/video_player.h b/media/gpu/test/video_player/video_player.h
index 7fac7df..3322447 100644
--- a/media/gpu/test/video_player/video_player.h
+++ b/media/gpu/test/video_player/video_player.h
@@ -18,6 +18,10 @@
 #include "media/gpu/test/video_frame_helpers.h"
 #include "media/gpu/test/video_player/frame_renderer.h"
 
+namespace gpu {
+class GpuMemoryBufferFactory;
+}
+
 namespace media {
 namespace test {
 
@@ -57,11 +61,12 @@
 
   ~VideoPlayer();
 
-  // Create an instance of the video player. The |frame_renderer| and
-  // |frame_processors| will not be owned by the video player. The caller should
-  // guarantee they outlive the video player.
+  // Create an instance of the video player. The |gpu_memory_buffer_factory|,
+  // |frame_renderer| and |frame_processors| will not be owned by the video
+  // player. The caller should guarantee they outlive the video player.
   static std::unique_ptr<VideoPlayer> Create(
       const VideoDecoderClientConfig& config,
+      gpu::GpuMemoryBufferFactory* gpu_memory_buffer_factory,
       std::unique_ptr<FrameRenderer> frame_renderer,
       std::vector<std::unique_ptr<VideoFrameProcessor>> frame_processors = {});
 
@@ -124,6 +129,7 @@
 
   bool CreateDecoderClient(
       const VideoDecoderClientConfig& config,
+      gpu::GpuMemoryBufferFactory* gpu_memory_buffer_factory,
       std::unique_ptr<FrameRenderer> frame_renderer,
       std::vector<std::unique_ptr<VideoFrameProcessor>> frame_processors);
   void Destroy();
diff --git a/media/gpu/test/video_player/video_player_test_environment.cc b/media/gpu/test/video_player/video_player_test_environment.cc
index c77f2be3..7cb2358 100644
--- a/media/gpu/test/video_player/video_player_test_environment.cc
+++ b/media/gpu/test/video_player/video_player_test_environment.cc
@@ -48,7 +48,9 @@
       enable_validator_(enable_validator),
       use_vd_(use_vd),
       frame_output_config_(frame_output_config),
-      output_folder_(output_folder) {}
+      output_folder_(output_folder),
+      gpu_memory_buffer_factory_(
+          gpu::GpuMemoryBufferFactory::CreateNativeType(nullptr)) {}
 
 VideoPlayerTestEnvironment::~VideoPlayerTestEnvironment() = default;
 
@@ -79,6 +81,11 @@
   return video_.get();
 }
 
+gpu::GpuMemoryBufferFactory*
+VideoPlayerTestEnvironment::GetGpuMemoryBufferFactory() const {
+  return gpu_memory_buffer_factory_.get();
+}
+
 bool VideoPlayerTestEnvironment::IsValidatorEnabled() const {
   return enable_validator_;
 }
diff --git a/media/gpu/test/video_player/video_player_test_environment.h b/media/gpu/test/video_player/video_player_test_environment.h
index 9005fd7..ed99556 100644
--- a/media/gpu/test/video_player/video_player_test_environment.h
+++ b/media/gpu/test/video_player/video_player_test_environment.h
@@ -9,6 +9,7 @@
 #include <memory>
 
 #include "base/files/file_path.h"
+#include "gpu/ipc/service/gpu_memory_buffer_factory.h"
 #include "media/gpu/test/video_frame_file_writer.h"
 #include "media/gpu/test/video_test_environment.h"
 
@@ -73,6 +74,12 @@
   // Whether import mode is supported, valid after SetUp() has been called.
   bool ImportSupported() const;
 
+  // Get the GpuMemoryBufferFactory for doing buffer allocations. This needs to
+  // survive as long as the process is alive just like in production which is
+  // why it's in here as there are threads that won't immediately die when an
+  // individual test is completed.
+  gpu::GpuMemoryBufferFactory* GetGpuMemoryBufferFactory() const;
+
  private:
   VideoPlayerTestEnvironment(std::unique_ptr<media::test::Video> video,
                              bool enable_validator,
@@ -89,6 +96,8 @@
 
   // TODO(dstaessens): Remove this once all allocate-only platforms reached EOL.
   bool import_supported_ = false;
+
+  std::unique_ptr<gpu::GpuMemoryBufferFactory> gpu_memory_buffer_factory_;
 };
 }  // namespace test
 }  // namespace media
diff --git a/media/gpu/vaapi/vaapi_video_encode_accelerator.cc b/media/gpu/vaapi/vaapi_video_encode_accelerator.cc
index e9f2bce..e93d785 100644
--- a/media/gpu/vaapi/vaapi_video_encode_accelerator.cc
+++ b/media/gpu/vaapi/vaapi_video_encode_accelerator.cc
@@ -27,6 +27,7 @@
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/trace_event/trace_event.h"
 #include "build/build_config.h"
+#include "gpu/ipc/service/gpu_memory_buffer_factory.h"
 #include "media/base/bind_to_current_loop.h"
 #include "media/base/format_utils.h"
 #include "media/base/unaligned_shared_memory.h"
@@ -79,10 +80,13 @@
     // Remove this workaround once crrev.com/c/1573718 is landed.
     format = PIXEL_FORMAT_YV12;
   }
+
+  std::unique_ptr<::gpu::GpuMemoryBufferFactory> gpu_memory_buffer_factory =
+      ::gpu::GpuMemoryBufferFactory::CreateNativeType(nullptr);
   // Get a VideoFrameLayout of a graphic buffer with the same gfx::BufferUsage
   // as camera stack.
   base::Optional<VideoFrameLayout> layout = GetPlatformVideoFrameLayout(
-      format, visible_size,
+      gpu_memory_buffer_factory.get(), format, visible_size,
       gfx::BufferUsage::SCANOUT_VEA_READ_CAMERA_AND_CPU_READ_WRITE);
   if (!layout || layout->planes().empty()) {
     VLOGF(1) << "Failed to allocate VideoFrameLayout";
diff --git a/media/gpu/video_decode_accelerator_perf_tests.cc b/media/gpu/video_decode_accelerator_perf_tests.cc
index 8f9441d4..37c38ee1 100644
--- a/media/gpu/video_decode_accelerator_perf_tests.cc
+++ b/media/gpu/video_decode_accelerator_perf_tests.cc
@@ -322,8 +322,9 @@
     if (!g_env->ImportSupported())
       config.allocation_mode = AllocationMode::kAllocate;
 
-    auto video_player = VideoPlayer::Create(config, std::move(frame_renderer),
-                                            std::move(frame_processors));
+    auto video_player = VideoPlayer::Create(
+        config, g_env->GetGpuMemoryBufferFactory(), std::move(frame_renderer),
+        std::move(frame_processors));
     LOG_ASSERT(video_player);
     LOG_ASSERT(video_player->Initialize(video));
 
diff --git a/media/gpu/video_decode_accelerator_tests.cc b/media/gpu/video_decode_accelerator_tests.cc
index 28f7412..f45e9a1 100644
--- a/media/gpu/video_decode_accelerator_tests.cc
+++ b/media/gpu/video_decode_accelerator_tests.cc
@@ -107,8 +107,9 @@
     // Use the new VD-based video decoders if requested.
     config.use_vd = g_env->UseVD();
 
-    auto video_player = VideoPlayer::Create(config, std::move(frame_renderer),
-                                            std::move(frame_processors));
+    auto video_player = VideoPlayer::Create(
+        config, g_env->GetGpuMemoryBufferFactory(), std::move(frame_renderer),
+        std::move(frame_processors));
     LOG_ASSERT(video_player);
     LOG_ASSERT(video_player->Initialize(video));
 
@@ -381,7 +382,8 @@
 TEST_F(VideoDecoderTest, DestroyBeforeInitialize) {
   VideoDecoderClientConfig config = VideoDecoderClientConfig();
   config.use_vd = g_env->UseVD();
-  auto tvp = VideoPlayer::Create(config, FrameRendererDummy::Create());
+  auto tvp = VideoPlayer::Create(config, g_env->GetGpuMemoryBufferFactory(),
+                                 FrameRendererDummy::Create());
   EXPECT_NE(tvp, nullptr);
 }
 
diff --git a/media/gpu/video_encode_accelerator_unittest.cc b/media/gpu/video_encode_accelerator_unittest.cc
index df09d3b6..94b17373c 100644
--- a/media/gpu/video_encode_accelerator_unittest.cc
+++ b/media/gpu/video_encode_accelerator_unittest.cc
@@ -44,6 +44,7 @@
 #include "base/time/time.h"
 #include "base/timer/timer.h"
 #include "build/build_config.h"
+#include "gpu/ipc/service/gpu_memory_buffer_factory.h"
 #include "media/base/bind_to_current_loop.h"
 #include "media/base/bitstream_buffer.h"
 #include "media/base/cdm_context.h"
@@ -1736,6 +1737,9 @@
 
   // The last timestamp popped from |frame_timestamps_|.
   base::TimeDelta previous_timestamp_;
+
+  // Buffer factory for use with CloneVideoFrame.
+  std::unique_ptr<gpu::GpuMemoryBufferFactory> gpu_memory_buffer_factory_;
 };
 
 VEAClient::VEAClient(TestStream* test_stream,
@@ -1811,6 +1815,9 @@
     EXPECT_EQ(0, base::WriteFile(out_filename, NULL, 0));
   }
 
+  gpu_memory_buffer_factory_ =
+      gpu::GpuMemoryBufferFactory::CreateNativeType(nullptr);
+
   // Initialize the parameters of the test streams.
   UpdateTestStreamData(mid_stream_bitrate_switch, mid_stream_framerate_switch);
 
@@ -2147,8 +2154,8 @@
   if (video_frame && g_native_input) {
 #if defined(OS_LINUX)
     video_frame = test::CloneVideoFrame(
-        video_frame.get(), video_frame->layout(),
-        VideoFrame::STORAGE_GPU_MEMORY_BUFFER,
+        gpu_memory_buffer_factory_.get(), video_frame.get(),
+        video_frame->layout(), VideoFrame::STORAGE_GPU_MEMORY_BUFFER,
         gfx::BufferUsage::SCANOUT_VEA_READ_CAMERA_AND_CPU_READ_WRITE);
 #else
     video_frame = nullptr;
diff --git a/media/mojo/services/gpu_mojo_media_client.cc b/media/mojo/services/gpu_mojo_media_client.cc
index 31cd9d0..94b7b8d 100644
--- a/media/mojo/services/gpu_mojo_media_client.cc
+++ b/media/mojo/services/gpu_mojo_media_client.cc
@@ -15,7 +15,6 @@
 #include "media/base/fallback_video_decoder.h"
 #include "media/base/media_switches.h"
 #include "media/base/video_decoder.h"
-#include "media/gpu/buildflags.h"
 #include "media/gpu/gpu_video_accelerator_util.h"
 #include "media/gpu/gpu_video_decode_accelerator_factory.h"
 #include "media/gpu/gpu_video_decode_accelerator_helpers.h"
@@ -111,6 +110,7 @@
     const gpu::GpuFeatureInfo& gpu_feature_info,
     scoped_refptr<base::SingleThreadTaskRunner> gpu_task_runner,
     base::WeakPtr<MediaGpuChannelManager> media_gpu_channel_manager,
+    gpu::GpuMemoryBufferFactory* gpu_memory_buffer_factory,
     AndroidOverlayMojoFactoryCB android_overlay_factory_cb,
     CdmProxyFactoryCB cdm_proxy_factory_cb)
     : gpu_preferences_(gpu_preferences),
@@ -119,7 +119,11 @@
       gpu_task_runner_(std::move(gpu_task_runner)),
       media_gpu_channel_manager_(std::move(media_gpu_channel_manager)),
       android_overlay_factory_cb_(std::move(android_overlay_factory_cb)),
-      cdm_proxy_factory_cb_(std::move(cdm_proxy_factory_cb)) {}
+#if BUILDFLAG(USE_CHROMEOS_MEDIA_ACCELERATION)
+      gpu_memory_buffer_factory_(gpu_memory_buffer_factory),
+#endif  // BUILDFLAG(USE_CHROMEOS_MEDIA_ACCELERATION)
+      cdm_proxy_factory_cb_(std::move(cdm_proxy_factory_cb)) {
+}
 
 GpuMojoMediaClient::~GpuMojoMediaClient() = default;
 
@@ -237,8 +241,9 @@
 
 #elif defined(OS_CHROMEOS)
       if (base::FeatureList::IsEnabled(kChromeosVideoDecoder)) {
-#if BUILDFLAG(USE_V4L2_CODEC) || BUILDFLAG(USE_VAAPI)
-        auto frame_pool = std::make_unique<PlatformVideoFramePool>();
+#if BUILDFLAG(USE_CHROMEOS_MEDIA_ACCELERATION)
+        auto frame_pool = std::make_unique<PlatformVideoFramePool>(
+            gpu_memory_buffer_factory_);
         auto frame_converter = MailboxVideoFrameConverter::Create(
             base::BindRepeating(&PlatformVideoFramePool::UnwrapFrame,
                                 base::Unretained(frame_pool.get())),
@@ -249,7 +254,7 @@
                                 command_buffer_id->route_id));
         video_decoder = ChromeosVideoDecoderFactory::Create(
             task_runner, std::move(frame_pool), std::move(frame_converter));
-#endif  // BUILDFLAG(USE_V4L2_CODEC) || BUILDFLAG(USE_VAAPI)
+#endif  // BUILDFLAG(USE_CHROMEOS_MEDIA_ACCELERATION)
       } else {
         video_decoder = VdaVideoDecoder::Create(
             task_runner, gpu_task_runner_, media_log->Clone(),
diff --git a/media/mojo/services/gpu_mojo_media_client.h b/media/mojo/services/gpu_mojo_media_client.h
index a8adab4..76f6e60 100644
--- a/media/mojo/services/gpu_mojo_media_client.h
+++ b/media/mojo/services/gpu_mojo_media_client.h
@@ -18,9 +18,14 @@
 #include "gpu/config/gpu_preferences.h"
 #include "media/base/android_overlay_mojo_factory.h"
 #include "media/cdm/cdm_proxy.h"
+#include "media/gpu/buildflags.h"
 #include "media/mojo/services/mojo_media_client.h"
 #include "media/video/supported_video_decoder_config.h"
 
+namespace gpu {
+class GpuMemoryBufferFactory;
+}  // namespace gpu
+
 namespace media {
 
 class MediaGpuChannelManager;
@@ -37,6 +42,7 @@
       const gpu::GpuFeatureInfo& gpu_feature_info,
       scoped_refptr<base::SingleThreadTaskRunner> gpu_task_runner,
       base::WeakPtr<MediaGpuChannelManager> media_gpu_channel_manager,
+      gpu::GpuMemoryBufferFactory* gpu_memory_buffer_factory,
       AndroidOverlayMojoFactoryCB android_overlay_factory_cb,
       CdmProxyFactoryCB cdm_proxy_factory_cb);
   ~GpuMojoMediaClient() final;
@@ -66,6 +72,10 @@
   scoped_refptr<base::SingleThreadTaskRunner> gpu_task_runner_;
   base::WeakPtr<MediaGpuChannelManager> media_gpu_channel_manager_;
   AndroidOverlayMojoFactoryCB android_overlay_factory_cb_;
+#if BUILDFLAG(USE_CHROMEOS_MEDIA_ACCELERATION)
+  // Indirectly owned by GpuChildThread.
+  gpu::GpuMemoryBufferFactory* const gpu_memory_buffer_factory_;
+#endif  // BUILDFLAG(USE_CHROMEOS_MEDIA_ACCELERATION)
   CdmProxyFactoryCB cdm_proxy_factory_cb_;
 #if defined(OS_WIN)
   base::Optional<SupportedVideoDecoderConfigs> d3d11_supported_configs_;
diff --git a/media/mojo/services/media_service_factory.cc b/media/mojo/services/media_service_factory.cc
index 1b5e95b..ce12bc38 100644
--- a/media/mojo/services/media_service_factory.cc
+++ b/media/mojo/services/media_service_factory.cc
@@ -37,12 +37,14 @@
     const gpu::GpuFeatureInfo& gpu_feature_info,
     scoped_refptr<base::SingleThreadTaskRunner> task_runner,
     base::WeakPtr<MediaGpuChannelManager> media_gpu_channel_manager,
+    gpu::GpuMemoryBufferFactory* gpu_memory_buffer_factory,
     AndroidOverlayMojoFactoryCB android_overlay_factory_cb,
     CdmProxyFactoryCB cdm_proxy_factory_cb) {
   return std::make_unique<MediaService>(
       std::make_unique<GpuMojoMediaClient>(
           gpu_preferences, gpu_workarounds, gpu_feature_info, task_runner,
-          media_gpu_channel_manager, std::move(android_overlay_factory_cb),
+          media_gpu_channel_manager, gpu_memory_buffer_factory,
+          std::move(android_overlay_factory_cb),
           std::move(cdm_proxy_factory_cb)),
       std::move(request));
 }
diff --git a/media/mojo/services/media_service_factory.h b/media/mojo/services/media_service_factory.h
index abe15af8..d019249 100644
--- a/media/mojo/services/media_service_factory.h
+++ b/media/mojo/services/media_service_factory.h
@@ -19,6 +19,10 @@
 #include "services/service_manager/public/cpp/service.h"
 #include "services/service_manager/public/mojom/service.mojom.h"
 
+namespace gpu {
+class GpuMemoryBufferFactory;
+}  // namespace gpu
+
 namespace media {
 
 class MediaGpuChannelManager;
@@ -42,6 +46,7 @@
     const gpu::GpuFeatureInfo& gpu_feature_info,
     scoped_refptr<base::SingleThreadTaskRunner> task_runner,
     base::WeakPtr<MediaGpuChannelManager> media_gpu_channel_manager,
+    gpu::GpuMemoryBufferFactory* gpu_memory_buffer_factory,
     AndroidOverlayMojoFactoryCB android_overlay_factory_cb,
     CdmProxyFactoryCB cdm_proxy_factory_cb);
 
diff --git a/net/cookies/cookie_constants.h b/net/cookies/cookie_constants.h
index 8257114..6a58470 100644
--- a/net/cookies/cookie_constants.h
+++ b/net/cookies/cookie_constants.h
@@ -70,13 +70,10 @@
   kMaxValue = kExtended
 };
 
-// What rules to apply when determining when whether access to a particular
-// cookie is allowed.
-// TODO(crbug.com/978172): Machinery to read the content setting and set the
-// appropriate CookieAccessSemantics on the cookie (will be added as a new
-// metadata field of CanonicalCookie).
+// What rules to apply when determining whether access to a particular cookie is
+// allowed.
 enum class CookieAccessSemantics {
-  // Has not been checked yet.
+  // Has not been checked yet or there is no way to check.
   UNKNOWN = -1,
   // Has been checked and the cookie should *not* be subject to legacy access
   // rules.
diff --git a/net/cookies/cookie_monster.cc b/net/cookies/cookie_monster.cc
index 6c2abf8..ce67127b 100644
--- a/net/cookies/cookie_monster.cc
+++ b/net/cookies/cookie_monster.cc
@@ -387,7 +387,7 @@
 void CookieMonster::SetAllCookiesAsync(const CookieList& list,
                                        SetCookiesCallback callback) {
   DoCookieCallback(base::BindOnce(
-      // base::Unretained is safe as DoCookieCallbackForURL stores
+      // base::Unretained is safe as DoCookieCallback stores
       // the callback on |*this|, so the callback will not outlive
       // the object.
       &CookieMonster::SetAllCookies, base::Unretained(this), list,
@@ -404,7 +404,7 @@
   std::string domain = cookie->Domain();
   DoCookieCallbackForHostOrDomain(
       base::BindOnce(
-          // base::Unretained is safe as DoCookieCallbackForURL stores
+          // base::Unretained is safe as DoCookieCallbackForHostOrDomain stores
           // the callback on |*this|, so the callback will not outlive
           // the object.
           &CookieMonster::SetCanonicalCookie, base::Unretained(this),
@@ -429,17 +429,28 @@
 
 void CookieMonster::GetAllCookiesAsync(GetAllCookiesCallback callback) {
   DoCookieCallback(base::BindOnce(
-      // base::Unretained is safe as DoCookieCallbackForURL stores
+      // base::Unretained is safe as DoCookieCallback stores
       // the callback on |*this|, so the callback will not outlive
       // the object.
       &CookieMonster::GetAllCookies, base::Unretained(this),
       std::move(callback)));
 }
 
+void CookieMonster::GetAllCookiesWithAccessSemanticsAsync(
+    GetAllCookiesWithAccessSemanticsCallback callback) {
+  DoCookieCallback(base::BindOnce(
+      // base::Unretained is safe as DoCookieCallback stores
+      // the callback on |*this|, so the callback will not outlive
+      // the object.
+      &CookieMonster::GetAllCookies, base::Unretained(this),
+      base::BindOnce(&CookieMonster::AttachAccessSemanticsListForCookieList,
+                     base::Unretained(this), std::move(callback))));
+}
+
 void CookieMonster::DeleteCanonicalCookieAsync(const CanonicalCookie& cookie,
                                                DeleteCallback callback) {
   DoCookieCallback(base::BindOnce(
-      // base::Unretained is safe as DoCookieCallbackForURL stores
+      // base::Unretained is safe as DoCookieCallback stores
       // the callback on |*this|, so the callback will not outlive
       // the object.
       &CookieMonster::DeleteCanonicalCookie, base::Unretained(this), cookie,
@@ -450,7 +461,7 @@
     const TimeRange& creation_range,
     DeleteCallback callback) {
   DoCookieCallback(base::BindOnce(
-      // base::Unretained is safe as DoCookieCallbackForURL stores
+      // base::Unretained is safe as DoCookieCallback stores
       // the callback on |*this|, so the callback will not outlive
       // the object.
       &CookieMonster::DeleteAllCreatedInTimeRange, base::Unretained(this),
@@ -460,7 +471,7 @@
 void CookieMonster::DeleteAllMatchingInfoAsync(CookieDeletionInfo delete_info,
                                                DeleteCallback callback) {
   DoCookieCallback(base::BindOnce(
-      // base::Unretained is safe as DoCookieCallbackForURL stores
+      // base::Unretained is safe as DoCookieCallback stores
       // the callback on |*this|, so the callback will not outlive
       // the object.
       &CookieMonster::DeleteAllMatchingInfo, base::Unretained(this),
@@ -470,7 +481,7 @@
 void CookieMonster::DeleteSessionCookiesAsync(
     CookieStore::DeleteCallback callback) {
   DoCookieCallback(base::BindOnce(
-      // base::Unretained is safe as DoCookieCallbackForURL stores
+      // base::Unretained is safe as DoCookieCallback stores
       // the callback on |*this|, so the callback will not outlive
       // the object.
       &CookieMonster::DeleteSessionCookies, base::Unretained(this),
@@ -593,6 +604,17 @@
   MaybeRunCookieCallback(std::move(callback), cookie_list);
 }
 
+void CookieMonster::AttachAccessSemanticsListForCookieList(
+    GetAllCookiesWithAccessSemanticsCallback callback,
+    const CookieList& cookie_list) {
+  std::vector<CookieAccessSemantics> access_semantics_list;
+  for (const CanonicalCookie& cookie : cookie_list) {
+    access_semantics_list.push_back(GetAccessSemanticsForCookie(cookie));
+  }
+  MaybeRunCookieCallback(std::move(callback), cookie_list,
+                         access_semantics_list);
+}
+
 void CookieMonster::GetCookieListWithOptions(const GURL& url,
                                              const CookieOptions& options,
                                              GetCookieListCallback callback) {
diff --git a/net/cookies/cookie_monster.h b/net/cookies/cookie_monster.h
index e739587..05226c7c 100644
--- a/net/cookies/cookie_monster.h
+++ b/net/cookies/cookie_monster.h
@@ -165,6 +165,8 @@
                                      const CookieOptions& options,
                                      GetCookieListCallback callback) override;
   void GetAllCookiesAsync(GetAllCookiesCallback callback) override;
+  void GetAllCookiesWithAccessSemanticsAsync(
+      GetAllCookiesWithAccessSemanticsCallback callback) override;
   void DeleteCanonicalCookieAsync(const CanonicalCookie& cookie,
                                   DeleteCallback callback) override;
   void DeleteAllCreatedInTimeRangeAsync(
@@ -358,6 +360,10 @@
 
   void GetAllCookies(GetAllCookiesCallback callback);
 
+  void AttachAccessSemanticsListForCookieList(
+      GetAllCookiesWithAccessSemanticsCallback callback,
+      const CookieList& cookie_list);
+
   void GetCookieListWithOptions(const GURL& url,
                                 const CookieOptions& options,
                                 GetCookieListCallback callback);
diff --git a/net/cookies/cookie_monster_unittest.cc b/net/cookies/cookie_monster_unittest.cc
index 6908bbd9438..db86678 100644
--- a/net/cookies/cookie_monster_unittest.cc
+++ b/net/cookies/cookie_monster_unittest.cc
@@ -92,6 +92,7 @@
   static const bool has_exact_change_cause = true;
   static const bool has_exact_change_ordering = true;
   static const int creation_time_granularity_in_ms = 0;
+  static const bool supports_cookie_access_semantics = true;
 };
 
 INSTANTIATE_TYPED_TEST_SUITE_P(CookieMonster,
diff --git a/net/cookies/cookie_store.cc b/net/cookies/cookie_store.cc
index 6c31a75..b0dfe658 100644
--- a/net/cookies/cookie_store.cc
+++ b/net/cookies/cookie_store.cc
@@ -11,6 +11,24 @@
 
 CookieStore::~CookieStore() = default;
 
+// Default implementation which returns a default vector of UNKNOWN
+// CookieAccessSemantics.
+void CookieStore::GetAllCookiesWithAccessSemanticsAsync(
+    GetAllCookiesWithAccessSemanticsCallback callback) {
+  GetAllCookiesCallback adapted_callback = base::BindOnce(
+      [](CookieStore::GetAllCookiesWithAccessSemanticsCallback
+             original_callback,
+         const CookieList& cookies) {
+        std::vector<CookieAccessSemantics> default_access_semantics_list;
+        default_access_semantics_list.assign(cookies.size(),
+                                             CookieAccessSemantics::UNKNOWN);
+        std::move(original_callback)
+            .Run(cookies, default_access_semantics_list);
+      },
+      std::move(callback));
+  GetAllCookiesAsync(std::move(adapted_callback));
+}
+
 void CookieStore::DeleteAllAsync(DeleteCallback callback) {
   DeleteAllCreatedInTimeRangeAsync(CookieDeletionInfo::TimeRange(),
                                    std::move(callback));
diff --git a/net/cookies/cookie_store.h b/net/cookies/cookie_store.h
index ccc3471..923e4c5 100644
--- a/net/cookies/cookie_store.h
+++ b/net/cookies/cookie_store.h
@@ -49,6 +49,10 @@
                               const CookieStatusList& excluded_list)>;
   using GetAllCookiesCallback =
       base::OnceCallback<void(const CookieList& cookies)>;
+  // |access_semantics_list| is guaranteed to the same length as |cookies|.
+  using GetAllCookiesWithAccessSemanticsCallback = base::OnceCallback<void(
+      const CookieList& cookies,
+      const std::vector<CookieAccessSemantics>& access_semantics_list)>;
   using SetCookiesCallback =
       base::OnceCallback<void(CanonicalCookie::CookieInclusionStatus status)>;
   using DeleteCallback = base::OnceCallback<void(uint32_t num_deleted)>;
@@ -83,6 +87,18 @@
   // longest path, then by earliest creation date.
   virtual void GetAllCookiesAsync(GetAllCookiesCallback callback) = 0;
 
+  // Returns all the cookies, for use in management UI, etc. This does not mark
+  // the cookies as having been accessed. The returned cookies are ordered by
+  // longest path, then by earliest creation date.
+  // Additionally returns a vector of CookieAccessSemantics values for the
+  // returned cookies, which will be the same length as the vector of returned
+  // cookies. This vector will either contain all CookieAccessSemantics::UNKNOWN
+  // (if the default implementation is used), or each entry in the
+  // vector of CookieAccessSemantics will indicate the access semantics
+  // applicable to the cookie at the same index in the returned CookieList.
+  virtual void GetAllCookiesWithAccessSemanticsAsync(
+      GetAllCookiesWithAccessSemanticsCallback callback);
+
   // Deletes one specific cookie. |cookie| must have been returned by a previous
   // query on this CookieStore. Invokes |callback| with 1 if a cookie was
   // deleted, 0 otherwise.
diff --git a/net/cookies/cookie_store_test_callbacks.cc b/net/cookies/cookie_store_test_callbacks.cc
index dff2abe..87ab3aee 100644
--- a/net/cookies/cookie_store_test_callbacks.cc
+++ b/net/cookies/cookie_store_test_callbacks.cc
@@ -80,4 +80,21 @@
   CallbackEpilogue();
 }
 
+GetAllCookiesWithAccessSemanticsCallback::
+    GetAllCookiesWithAccessSemanticsCallback() = default;
+GetAllCookiesWithAccessSemanticsCallback::
+    GetAllCookiesWithAccessSemanticsCallback(base::Thread* run_in_thread)
+    : CookieCallback(run_in_thread) {}
+
+GetAllCookiesWithAccessSemanticsCallback::
+    ~GetAllCookiesWithAccessSemanticsCallback() = default;
+
+void GetAllCookiesWithAccessSemanticsCallback::Run(
+    const CookieList& cookies,
+    const std::vector<CookieAccessSemantics>& access_semantics_list) {
+  cookies_ = cookies;
+  access_semantics_list_ = access_semantics_list;
+  CallbackEpilogue();
+}
+
 }  // namespace net
diff --git a/net/cookies/cookie_store_test_callbacks.h b/net/cookies/cookie_store_test_callbacks.h
index af7f6f29..54b00b5 100644
--- a/net/cookies/cookie_store_test_callbacks.h
+++ b/net/cookies/cookie_store_test_callbacks.h
@@ -13,6 +13,7 @@
 #include "base/run_loop.h"
 #include "base/single_thread_task_runner.h"
 #include "net/cookies/canonical_cookie.h"
+#include "net/cookies/cookie_constants.h"
 #include "net/cookies/cookie_store.h"
 
 namespace base {
@@ -151,6 +152,36 @@
   CookieList cookies_;
 };
 
+class GetAllCookiesWithAccessSemanticsCallback : public CookieCallback {
+ public:
+  GetAllCookiesWithAccessSemanticsCallback();
+  explicit GetAllCookiesWithAccessSemanticsCallback(
+      base::Thread* run_in_thread);
+
+  ~GetAllCookiesWithAccessSemanticsCallback();
+
+  void Run(const CookieList& cookies,
+           const std::vector<CookieAccessSemantics>& access_semantics_list);
+
+  // Makes a callback that will invoke Run. Assumes that |this| will be kept
+  // alive till the time the callback is used.
+  base::OnceCallback<void(const CookieList&,
+                          const std::vector<CookieAccessSemantics>&)>
+  MakeCallback() {
+    return base::BindOnce(&GetAllCookiesWithAccessSemanticsCallback::Run,
+                          base::Unretained(this));
+  }
+
+  const CookieList& cookies() { return cookies_; }
+  const std::vector<CookieAccessSemantics>& access_semantics_list() {
+    return access_semantics_list_;
+  }
+
+ private:
+  CookieList cookies_;
+  std::vector<CookieAccessSemantics> access_semantics_list_;
+};
+
 }  // namespace net
 
 #endif  // NET_COOKIES_COOKIE_STORE_TEST_CALLBACKS_H_
diff --git a/net/cookies/cookie_store_unittest.h b/net/cookies/cookie_store_unittest.h
index 8fe683929..d91026b 100644
--- a/net/cookies/cookie_store_unittest.h
+++ b/net/cookies/cookie_store_unittest.h
@@ -24,6 +24,7 @@
 #include "net/cookies/cookie_store.h"
 #include "net/cookies/cookie_store_test_callbacks.h"
 #include "net/cookies/cookie_store_test_helpers.h"
+#include "net/cookies/test_cookie_access_delegate.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/gurl.h"
 
@@ -101,6 +102,11 @@
 //   // Time to wait between two cookie insertions to ensure that cookies have
 //   // different creation times.
 //   static const int creation_time_granularity_in_ms;
+//
+//   // The cookie store supports setting a CookieAccessDelegate and using it to
+//   // get the access semantics for each cookie via
+//   // CookieStore::GetAllCookiesWithAccessSemanticsAsync().
+//   static const bool supports_cookie_access_semantics;
 // };
 
 template <class CookieStoreTestTraits>
@@ -1661,6 +1667,58 @@
   ASSERT_TRUE(++it == cookies.end());
 }
 
+TYPED_TEST_P(CookieStoreTest, GetAllCookiesWithAccessSemanticsAsync) {
+  CookieStore* cs = this->GetCookieStore();
+  auto access_delegate = std::make_unique<TestCookieAccessDelegate>();
+  TestCookieAccessDelegate* test_delegate = access_delegate.get();
+  // if !supports_cookie_access_semantics, setting a delegate here will do
+  // nothing.
+  cs->SetCookieAccessDelegate(std::move(access_delegate));
+
+  test_delegate->SetExpectationForCookieDomain("domain1.test",
+                                               CookieAccessSemantics::LEGACY);
+  test_delegate->SetExpectationForCookieDomain(
+      "domain2.test", CookieAccessSemantics::NONLEGACY);
+  test_delegate->SetExpectationForCookieDomain("domain3.test",
+                                               CookieAccessSemantics::UNKNOWN);
+
+  this->CreateAndSetCookie(cs, GURL("http://domain1.test"), "cookie=1",
+                           CookieOptions::MakeAllInclusive());
+  this->CreateAndSetCookie(cs, GURL("http://domain2.test"), "cookie=1",
+                           CookieOptions::MakeAllInclusive());
+  this->CreateAndSetCookie(cs, GURL("http://domain3.test"), "cookie=1",
+                           CookieOptions::MakeAllInclusive());
+  this->CreateAndSetCookie(cs, GURL("http://domain4.test"), "cookie=1",
+                           CookieOptions::MakeAllInclusive());
+
+  GetAllCookiesWithAccessSemanticsCallback callback;
+  cs->GetAllCookiesWithAccessSemanticsAsync(callback.MakeCallback());
+  callback.WaitUntilDone();
+  EXPECT_TRUE(callback.was_run());
+
+  EXPECT_EQ(callback.cookies().size(), callback.access_semantics_list().size());
+  EXPECT_EQ(4u, callback.access_semantics_list().size());
+  EXPECT_EQ("domain1.test", callback.cookies()[0].Domain());
+  EXPECT_EQ("domain2.test", callback.cookies()[1].Domain());
+  EXPECT_EQ("domain3.test", callback.cookies()[2].Domain());
+  EXPECT_EQ("domain4.test", callback.cookies()[3].Domain());
+
+  if (!TypeParam::supports_cookie_access_semantics) {
+    for (CookieAccessSemantics semantics : callback.access_semantics_list()) {
+      EXPECT_EQ(CookieAccessSemantics::UNKNOWN, semantics);
+    }
+  } else {
+    EXPECT_EQ(CookieAccessSemantics::LEGACY,
+              callback.access_semantics_list()[0]);
+    EXPECT_EQ(CookieAccessSemantics::NONLEGACY,
+              callback.access_semantics_list()[1]);
+    EXPECT_EQ(CookieAccessSemantics::UNKNOWN,
+              callback.access_semantics_list()[2]);
+    EXPECT_EQ(CookieAccessSemantics::UNKNOWN,
+              callback.access_semantics_list()[3]);
+  }
+}
+
 TYPED_TEST_P(CookieStoreTest, DeleteCanonicalCookieAsync) {
   CookieStore* cs = this->GetCookieStore();
 
@@ -1756,6 +1814,7 @@
                             EmptyName,
                             CookieOrdering,
                             GetAllCookiesAsync,
+                            GetAllCookiesWithAccessSemanticsAsync,
                             DeleteCanonicalCookieAsync,
                             DeleteSessionCookie);
 
diff --git a/net/http/http_util.cc b/net/http/http_util.cc
index e6331a7..e3a1e809 100644
--- a/net/http/http_util.cc
+++ b/net/http/http_util.cc
@@ -387,29 +387,31 @@
 
 // A header string containing any of the following fields will cause
 // an error. The list comes from the XMLHttpRequest standard.
-// http://www.w3.org/TR/XMLHttpRequest/#the-setrequestheader-method
+// https://fetch.spec.whatwg.org/#forbidden-header-name
 const char* const kForbiddenHeaderFields[] = {
-  "accept-charset",
-  "accept-encoding",
-  "access-control-request-headers",
-  "access-control-request-method",
-  "connection",
-  "content-length",
-  "cookie",
-  "cookie2",
-  "content-transfer-encoding",
-  "date",
-  "expect",
-  "host",
-  "keep-alive",
-  "origin",
-  "referer",
-  "te",
-  "trailer",
-  "transfer-encoding",
-  "upgrade",
-  "user-agent",
-  "via",
+    "accept-charset",
+    "accept-encoding",
+    "access-control-request-headers",
+    "access-control-request-method",
+    "connection",
+    "content-length",
+    "cookie",
+    "cookie2",
+    "date",
+    "dnt",
+    "expect",
+    "host",
+    "keep-alive",
+    "origin",
+    "referer",
+    "te",
+    "trailer",
+    "transfer-encoding",
+    "upgrade",
+    // TODO(mmenke): This is no longer banned, but still here due to issues
+    // mentioned in https://crbug.com/571722.
+    "user-agent",
+    "via",
 };
 
 }  // namespace
diff --git a/net/http/http_util_unittest.cc b/net/http/http_util_unittest.cc
index d8d316ca..305565e 100644
--- a/net/http/http_util_unittest.cc
+++ b/net/http/http_util_unittest.cc
@@ -14,35 +14,35 @@
 
 TEST(HttpUtilTest, IsSafeHeader) {
   static const char* const unsafe_headers[] = {
-    "sec-",
-    "sEc-",
-    "sec-foo",
-    "sEc-FoO",
-    "proxy-",
-    "pRoXy-",
-    "proxy-foo",
-    "pRoXy-FoO",
-    "accept-charset",
-    "accept-encoding",
-    "access-control-request-headers",
-    "access-control-request-method",
-    "connection",
-    "content-length",
-    "cookie",
-    "cookie2",
-    "content-transfer-encoding",
-    "date",
-    "expect",
-    "host",
-    "keep-alive",
-    "origin",
-    "referer",
-    "te",
-    "trailer",
-    "transfer-encoding",
-    "upgrade",
-    "user-agent",
-    "via",
+      "sec-",
+      "sEc-",
+      "sec-foo",
+      "sEc-FoO",
+      "proxy-",
+      "pRoXy-",
+      "proxy-foo",
+      "pRoXy-FoO",
+      "accept-charset",
+      "accept-encoding",
+      "access-control-request-headers",
+      "access-control-request-method",
+      "connection",
+      "content-length",
+      "cookie",
+      "cookie2",
+      "date",
+      "dnt",
+      "expect",
+      "host",
+      "keep-alive",
+      "origin",
+      "referer",
+      "te",
+      "trailer",
+      "transfer-encoding",
+      "upgrade",
+      "user-agent",
+      "via",
   };
   for (size_t i = 0; i < base::size(unsafe_headers); ++i) {
     EXPECT_FALSE(HttpUtil::IsSafeHeader(unsafe_headers[i]))
@@ -51,44 +51,45 @@
         << unsafe_headers[i];
   }
   static const char* const safe_headers[] = {
-    "foo",
-    "x-",
-    "x-foo",
-    "content-disposition",
-    "update",
-    "accept-charseta",
-    "accept_charset",
-    "accept-encodinga",
-    "accept_encoding",
-    "access-control-request-headersa",
-    "access-control-request-header",
-    "access_control_request_header",
-    "access-control-request-methoda",
-    "access_control_request_method",
-    "connectiona",
-    "content-lengtha",
-    "content_length",
-    "cookiea",
-    "cookie2a",
-    "cookie3",
-    "content-transfer-encodinga",
-    "content_transfer_encoding",
-    "datea",
-    "expecta",
-    "hosta",
-    "keep-alivea",
-    "keep_alive",
-    "origina",
-    "referera",
-    "referrer",
-    "tea",
-    "trailera",
-    "transfer-encodinga",
-    "transfer_encoding",
-    "upgradea",
-    "user-agenta",
-    "user_agent",
-    "viaa",
+      "foo",
+      "x-",
+      "x-foo",
+      "content-disposition",
+      "update",
+      "accept-charseta",
+      "accept_charset",
+      "accept-encodinga",
+      "accept_encoding",
+      "access-control-request-headersa",
+      "access-control-request-header",
+      "access_control_request_header",
+      "access-control-request-methoda",
+      "access_control_request_method",
+      "connectiona",
+      "content-lengtha",
+      "content_length",
+      "content-transfer-encoding",
+      "cookiea",
+      "cookie2a",
+      "cookie3",
+      "content-transfer-encodinga",
+      "content_transfer_encoding",
+      "datea",
+      "expecta",
+      "hosta",
+      "keep-alivea",
+      "keep_alive",
+      "origina",
+      "referera",
+      "referrer",
+      "tea",
+      "trailera",
+      "transfer-encodinga",
+      "transfer_encoding",
+      "upgradea",
+      "user-agenta",
+      "user_agent",
+      "viaa",
   };
   for (size_t i = 0; i < base::size(safe_headers); ++i) {
     EXPECT_TRUE(HttpUtil::IsSafeHeader(safe_headers[i])) << safe_headers[i];
diff --git a/net/socket/socket_test_util.h b/net/socket/socket_test_util.h
index f503319..26690f97 100644
--- a/net/socket/socket_test_util.h
+++ b/net/socket/socket_test_util.h
@@ -629,10 +629,9 @@
 class MockProxyClientSocket;
 
 // ClientSocketFactory which contains arrays of sockets of each type.
-// You should first fill the arrays using AddMock{SSL,}Socket. When the factory
-// is asked to create a socket, it takes next entry from appropriate array.
-// You can use ResetNextMockIndexes to reset that next entry index for all mock
-// socket types.
+// You should first fill the arrays using Add{SSL,ProxyClient,}SocketDataProvider(). When the
+// factory is asked to create a socket, it takes next entry from appropriate array. You can use
+// ResetNextMockIndexes to reset that next entry index for all mock socket types.
 class MockClientSocketFactory : public ClientSocketFactory {
  public:
   MockClientSocketFactory();
diff --git a/ppapi/tests/test_url_loader.cc b/ppapi/tests/test_url_loader.cc
index 0d28ab6..eeb409d4 100644
--- a/ppapi/tests/test_url_loader.cc
+++ b/ppapi/tests/test_url_loader.cc
@@ -693,17 +693,13 @@
     ASSERT_EQ(PP_OK, OpenTrusted("GET", "Accept-Encoding:\n"));
     ASSERT_EQ(PP_OK, OpenTrusted("GET", "Connection:\n"));
     ASSERT_EQ(PP_OK, OpenTrusted("GET", "Cookie:\n"));
-    ASSERT_EQ(PP_OK, OpenTrusted("GET", "Cookie2:\n"));
-    ASSERT_EQ(PP_OK, OpenTrusted("GET", "Content-Transfer-Encoding:\n"));
     ASSERT_EQ(PP_OK, OpenTrusted("GET", "Date:\n"));
+    ASSERT_EQ(PP_OK, OpenTrusted("GET", "DNT:\n"));
     ASSERT_EQ(PP_OK, OpenTrusted("GET", "Expect:\n"));
 
     // Host header is still forbidden because it can conflict with specific URL.
 
-    ASSERT_EQ(PP_OK, OpenTrusted("GET", "Keep-Alive:\n"));
     ASSERT_EQ(PP_OK, OpenTrusted("GET", "Referer:\n"));
-    ASSERT_EQ(PP_OK, OpenTrusted("GET", "TE:\n"));
-    ASSERT_EQ(PP_OK, OpenTrusted("GET", "Transfer-Encoding:\n"));
     ASSERT_EQ(PP_OK, OpenTrusted("GET", "User-Agent:\n"));
     ASSERT_EQ(PP_OK, OpenTrusted("GET", "Via:\n"));
     ASSERT_EQ(PP_OK, OpenTrusted("GET", "Sec-foo:\n"));
diff --git a/remoting/android/BUILD.gn b/remoting/android/BUILD.gn
index d120999..fcad9b5 100644
--- a/remoting/android/BUILD.gn
+++ b/remoting/android/BUILD.gn
@@ -17,6 +17,7 @@
     "java/src/org/chromium/chromoting/jni/GlDisplay.java",
     "java/src/org/chromium/chromoting/jni/JniInterface.java",
     "java/src/org/chromium/chromoting/jni/JniOAuthTokenGetter.java",
+    "java/src/org/chromium/chromoting/jni/NotificationPresenter.java",
     "java/src/org/chromium/chromoting/jni/TouchEventData.java",
   ]
 }
diff --git a/remoting/android/client_java_tmpl.gni b/remoting/android/client_java_tmpl.gni
index 7c56ed8..d8c2e9d 100644
--- a/remoting/android/client_java_tmpl.gni
+++ b/remoting/android/client_java_tmpl.gni
@@ -75,6 +75,7 @@
       "jni/DirectoryService.java",
       "jni/JniInterface.java",
       "jni/JniOAuthTokenGetter.java",
+      "jni/NotificationPresenter.java",
       "jni/TouchEventData.java",
     ]
 
@@ -97,7 +98,10 @@
 
     annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ]
 
-    srcjar_deps = [ "//remoting/client/jni:jni_enums" ]
+    srcjar_deps = [
+      "//remoting/client/jni:jni_enums",
+      "//remoting/client/notification:notification_enums",
+    ]
 
     if (defined(invoker.play_services_package)) {
       deps += [
diff --git a/remoting/android/java/src/org/chromium/chromoting/Chromoting.java b/remoting/android/java/src/org/chromium/chromoting/Chromoting.java
index 76d31da7..ce26f43e 100644
--- a/remoting/android/java/src/org/chromium/chromoting/Chromoting.java
+++ b/remoting/android/java/src/org/chromium/chromoting/Chromoting.java
@@ -43,6 +43,7 @@
 import org.chromium.chromoting.jni.DirectoryService;
 import org.chromium.chromoting.jni.DirectoryServiceRequestError;
 import org.chromium.chromoting.jni.JniOAuthTokenGetter;
+import org.chromium.chromoting.jni.NotificationPresenter;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -97,6 +98,9 @@
     /** Dialog for reporting connection progress. */
     private ProgressDialog mProgressIndicator;
 
+    /** Helper for fetching notification and presenting it. */
+    private NotificationPresenter mNotificationPresenter;
+
     /**
      * Helper used by SessionConnection for session authentication. Receives onNewIntent()
      * notifications to handle third-party authentication.
@@ -236,6 +240,7 @@
         setSupportActionBar(toolbar);
 
         mDirectoryService = new DirectoryService();
+        mNotificationPresenter = new NotificationPresenter(this);
 
         // Get ahold of our view widgets.
         mHostListView = (ListView) findViewById(R.id.hostList_chooser);
@@ -626,6 +631,7 @@
         }
         mAccount = accountName;
         JniOAuthTokenGetter.setAccount(accountName);
+        mNotificationPresenter.presentIfNecessary(accountName);
 
         // The current host list is no longer valid for the new account, so clear the list.
         mHosts = new HostInfo[0];
diff --git a/remoting/android/java/src/org/chromium/chromoting/jni/NotificationPresenter.java b/remoting/android/java/src/org/chromium/chromoting/jni/NotificationPresenter.java
new file mode 100644
index 0000000..965e69d
--- /dev/null
+++ b/remoting/android/java/src/org/chromium/chromoting/jni/NotificationPresenter.java
@@ -0,0 +1,102 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chromoting.jni;
+
+import android.app.Activity;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.net.Uri;
+import android.support.v7.app.AlertDialog;
+
+import androidx.annotation.IntDef;
+
+import org.chromium.base.annotations.CalledByNative;
+import org.chromium.base.annotations.JNINamespace;
+import org.chromium.base.annotations.NativeMethods;
+import org.chromium.chromoting.NotificationAppearance;
+import org.chromium.chromoting.Preconditions;
+import org.chromium.chromoting.R;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/** Helper for fetching and presenting a notification */
+@JNINamespace("remoting")
+public final class NotificationPresenter {
+    @IntDef({State.NOT_FETCHED, State.FETCHING, State.FETCHED})
+    @Retention(RetentionPolicy.SOURCE)
+    private @interface State {
+        int NOT_FETCHED = 0;
+        int FETCHING = 1;
+        int FETCHED = 2;
+    }
+
+    private final long mNativeJniNotificationPresenter;
+    private final Activity mActivity;
+
+    private @State int mState;
+
+    /**
+     * @param activity The activity on which the notification will be shown.
+     */
+    public NotificationPresenter(Activity activity) {
+        mNativeJniNotificationPresenter = NotificationPresenterJni.get().init(this);
+        mActivity = activity;
+        mState = State.NOT_FETCHED;
+    }
+
+    @Override
+    public void finalize() {
+        NotificationPresenterJni.get().destroy(mNativeJniNotificationPresenter);
+    }
+
+    /**
+     * Presents notification for the given |username| if no previous notification has been shown,
+     * and the user is selected for the notification.
+     * @param username String that can uniquely identify a user
+     */
+    public void presentIfNecessary(String username) {
+        if (mState != State.NOT_FETCHED) {
+            return;
+        }
+        mState = State.FETCHING;
+        NotificationPresenterJni.get().fetchNotification(mNativeJniNotificationPresenter, username);
+    }
+
+    @CalledByNative
+    void onNotificationFetched(@NotificationAppearance int appearance, String messageText,
+            String linkText, String linkUrl) {
+        Preconditions.isTrue(mState == State.FETCHING);
+        mState = State.FETCHED;
+
+        // TODO(yuweih): Add Don't Show Again support.
+        final AlertDialog.Builder builder = new AlertDialog.Builder(mActivity);
+        builder.setMessage(messageText);
+        builder.setPositiveButton(linkText, (DialogInterface dialog, int id) -> {
+            Intent openLink = new Intent(Intent.ACTION_VIEW, Uri.parse(linkUrl));
+            mActivity.startActivity(openLink);
+        });
+        builder.setNegativeButton(R.string.dismiss,
+                (DialogInterface dialog, int id)
+                        -> {
+                                // Do nothing
+                        });
+        final AlertDialog dialog = builder.create();
+        dialog.show();
+    }
+
+    @CalledByNative
+    void onNoNotification() {
+        Preconditions.isTrue(mState == State.FETCHING);
+        mState = State.NOT_FETCHED;
+    }
+
+    @NativeMethods
+    interface Natives {
+        long init(NotificationPresenter javaPresenter);
+        void fetchNotification(long nativeJniNotificationPresenter, String username);
+        void destroy(long nativeJniNotificationPresenter);
+    }
+}
diff --git a/remoting/client/jni/BUILD.gn b/remoting/client/jni/BUILD.gn
index 30ff03e..a39da40b 100644
--- a/remoting/client/jni/BUILD.gn
+++ b/remoting/client/jni/BUILD.gn
@@ -22,6 +22,7 @@
     "//remoting/base",
     "//remoting/client",
     "//remoting/client/display",
+    "//remoting/client/notification",
     "//remoting/proto/remoting/v1:directory_grpc_library",
     "//remoting/protocol",
     "//ui/gfx",
@@ -35,6 +36,8 @@
     "jni_directory_service.h",
     "jni_gl_display_handler.cc",
     "jni_gl_display_handler.h",
+    "jni_notification_presenter.cc",
+    "jni_notification_presenter.h",
     "jni_oauth_token_getter.cc",
     "jni_oauth_token_getter.h",
     "jni_runtime_delegate.cc",
diff --git a/remoting/client/jni/jni_notification_presenter.cc b/remoting/client/jni/jni_notification_presenter.cc
new file mode 100644
index 0000000..d4b540c
--- /dev/null
+++ b/remoting/client/jni/jni_notification_presenter.cc
@@ -0,0 +1,77 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "remoting/client/jni/jni_notification_presenter.h"
+
+#include "base/android/jni_android.h"
+#include "base/android/jni_string.h"
+#include "base/bind.h"
+#include "base/threading/sequenced_task_runner_handle.h"
+#include "remoting/android/jni_headers/NotificationPresenter_jni.h"
+#include "remoting/client/chromoting_client_runtime.h"
+#include "remoting/client/notification/notification_message.h"
+
+using base::android::ConvertJavaStringToUTF8;
+using base::android::ConvertUTF8ToJavaString;
+
+namespace remoting {
+
+JniNotificationPresenter::JniNotificationPresenter(
+    const JavaObjectWeakGlobalRef& java_presenter)
+    : java_presenter_(java_presenter),
+      notification_client_(
+          ChromotingClientRuntime::GetInstance()->network_task_runner()),
+      sequence_(base::SequencedTaskRunnerHandle::Get()) {}
+
+JniNotificationPresenter::~JniNotificationPresenter() {
+  DCHECK(sequence_->RunsTasksInCurrentSequence());
+}
+
+void JniNotificationPresenter::FetchNotification(
+    JNIEnv* env,
+    const base::android::JavaParamRef<jstring>& username) {
+  DCHECK(sequence_->RunsTasksInCurrentSequence());
+  std::string username_str = ConvertJavaStringToUTF8(env, username);
+  // Safe to use unretained: callback is dropped once client is deleted.
+  notification_client_.GetNotification(
+      username_str,
+      base::BindOnce(&JniNotificationPresenter::OnNotificationFetched,
+                     base::Unretained(this)));
+}
+
+void JniNotificationPresenter::Destroy(JNIEnv* env) {
+  if (sequence_->RunsTasksInCurrentSequence()) {
+    delete this;
+  } else {
+    sequence_->DeleteSoon(FROM_HERE, this);
+  }
+}
+
+void JniNotificationPresenter::OnNotificationFetched(
+    base::Optional<NotificationMessage> notification) {
+  DCHECK(sequence_->RunsTasksInCurrentSequence());
+  JNIEnv* env = base::android::AttachCurrentThread();
+  auto java_presenter = java_presenter_.get(env);
+  if (!notification) {
+    Java_NotificationPresenter_onNoNotification(env, java_presenter);
+    return;
+  }
+  jint j_appearance = static_cast<jint>(notification->appearance);
+  auto j_message_text =
+      ConvertUTF8ToJavaString(env, notification->message_text);
+  auto j_link_text = ConvertUTF8ToJavaString(env, notification->link_text);
+  auto j_link_url = ConvertUTF8ToJavaString(env, notification->link_url);
+  Java_NotificationPresenter_onNotificationFetched(env, java_presenter,
+                                                   j_appearance, j_message_text,
+                                                   j_link_text, j_link_url);
+}
+
+static jlong JNI_NotificationPresenter_Init(
+    JNIEnv* env,
+    const base::android::JavaParamRef<jobject>& java_presenter) {
+  return reinterpret_cast<intptr_t>(new JniNotificationPresenter(
+      JavaObjectWeakGlobalRef(env, java_presenter)));
+}
+
+}  // namespace remoting
diff --git a/remoting/client/jni/jni_notification_presenter.h b/remoting/client/jni/jni_notification_presenter.h
new file mode 100644
index 0000000..eca8903
--- /dev/null
+++ b/remoting/client/jni/jni_notification_presenter.h
@@ -0,0 +1,41 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef REMOTING_CLIENT_JNI_JNI_NOTIFICATION_PRESENTER_H_
+#define REMOTING_CLIENT_JNI_JNI_NOTIFICATION_PRESENTER_H_
+
+#include <jni.h>
+
+#include "base/android/jni_weak_ref.h"
+#include "base/macros.h"
+#include "base/memory/scoped_refptr.h"
+#include "base/sequenced_task_runner.h"
+#include "remoting/client/notification/notification_client.h"
+
+namespace remoting {
+
+// C++ counterpart for org.chromium.chromoting.jni.NotificationPresenter.
+class JniNotificationPresenter final {
+ public:
+  explicit JniNotificationPresenter(
+      const JavaObjectWeakGlobalRef& java_presenter);
+  ~JniNotificationPresenter();
+
+  void FetchNotification(JNIEnv* env,
+                         const base::android::JavaParamRef<jstring>& username);
+  void Destroy(JNIEnv* env);
+
+ private:
+  void OnNotificationFetched(base::Optional<NotificationMessage> notification);
+
+  JavaObjectWeakGlobalRef java_presenter_;
+  NotificationClient notification_client_;
+  scoped_refptr<base::SequencedTaskRunner> sequence_;
+
+  DISALLOW_COPY_AND_ASSIGN(JniNotificationPresenter);
+};
+
+}  // namespace remoting
+
+#endif  // REMOTING_CLIENT_JNI_JNI_NOTIFICATION_PRESENTER_H_
\ No newline at end of file
diff --git a/remoting/client/notification/BUILD.gn b/remoting/client/notification/BUILD.gn
index e8cfae11..ffc58a41 100644
--- a/remoting/client/notification/BUILD.gn
+++ b/remoting/client/notification/BUILD.gn
@@ -2,6 +2,11 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+if (is_android) {
+  import("//build/config/android/config.gni")
+  import("//build/config/android/rules.gni")
+}
+
 source_set("notification") {
   sources = [
     "gstatic_json_fetcher.cc",
@@ -25,6 +30,14 @@
   ]
 }
 
+if (is_android) {
+  java_cpp_enum("notification_enums") {
+    sources = [
+      "notification_message.h",
+    ]
+  }
+}
+
 source_set("unit_tests") {
   testonly = true
 
diff --git a/remoting/client/notification/notification_message.h b/remoting/client/notification/notification_message.h
index 45c90b77..215dc96 100644
--- a/remoting/client/notification/notification_message.h
+++ b/remoting/client/notification/notification_message.h
@@ -10,6 +10,8 @@
 namespace remoting {
 
 struct NotificationMessage final {
+  // GENERATED_JAVA_ENUM_PACKAGE: org.chromium.chromoting
+  // GENERATED_JAVA_CLASS_NAME_OVERRIDE: NotificationAppearance
   enum class Appearance {
     TOAST,
     DIALOG,
diff --git a/remoting/resources/remoting_strings.grd b/remoting/resources/remoting_strings.grd
index cc7be0d..52f55b1 100644
--- a/remoting/resources/remoting_strings.grd
+++ b/remoting/resources/remoting_strings.grd
@@ -659,7 +659,7 @@
         <message name="IDS_PRIVACY_POLICY" desc="The label to access the privacy policy, displayed in navigation menu." formatter_data="android_java">
           Privacy Policy
         </message>
-        <message name="IDS_DISMISS" desc="Label for button to dismiss a dialog">
+        <message name="IDS_DISMISS" desc="Label for button to dismiss a dialog" formatter_data="android_java">
           Dismiss
         </message>
         <message name="IDS_DONT_SHOW_AGAIN" desc="Label for checkbox to make a dialog not showing again.">
diff --git a/sandbox/win/src/handle_closer_agent.cc b/sandbox/win/src/handle_closer_agent.cc
index 65410b19..a0f12a5 100644
--- a/sandbox/win/src/handle_closer_agent.cc
+++ b/sandbox/win/src/handle_closer_agent.cc
@@ -8,6 +8,7 @@
 #include <stddef.h>
 
 #include "base/logging.h"
+#include "base/win/static_constants.h"
 #include "sandbox/win/src/nt_internals.h"
 #include "sandbox/win/src/win_utils.h"
 
@@ -173,7 +174,7 @@
 
   // Skip closing these handles when Application Verifier is in use in order to
   // avoid invalid-handle exceptions.
-  if (GetModuleHandleW(L"vrfcore.dll"))
+  if (GetModuleHandleA(base::win::kApplicationVerifierDllName))
     return true;
 
   // Set up buffers for the type info and the name.
diff --git a/sandbox/win/src/target_interceptions.cc b/sandbox/win/src/target_interceptions.cc
index 2dc2fd1..1b46781 100644
--- a/sandbox/win/src/target_interceptions.cc
+++ b/sandbox/win/src/target_interceptions.cc
@@ -4,6 +4,7 @@
 
 #include "sandbox/win/src/target_interceptions.h"
 
+#include "base/win/static_constants.h"
 #include "sandbox/win/src/interception_agent.h"
 #include "sandbox/win/src/sandbox_factory.h"
 #include "sandbox/win/src/sandbox_nt_util.h"
@@ -12,7 +13,6 @@
 
 SANDBOX_INTERCEPT NtExports g_nt;
 
-const char VERIFIER_DLL_NAME[] = "verifier.dll";
 const char KERNEL32_DLL_NAME[] = "kernel32.dll";
 
 enum SectionLoadState {
@@ -60,8 +60,9 @@
         // indicates Application Verifier is enabled and we should wait until
         // the next module is loaded.
         if (ansi_module_name &&
-            (g_nt._strnicmp(ansi_module_name, VERIFIER_DLL_NAME,
-                            sizeof(VERIFIER_DLL_NAME)) == 0))
+            (g_nt._strnicmp(
+                 ansi_module_name, base::win::kApplicationVerifierDllName,
+                 g_nt.strlen(base::win::kApplicationVerifierDllName) + 1) == 0))
           break;
 
         if (ansi_module_name &&
diff --git a/services/network/cookie_manager.cc b/services/network/cookie_manager.cc
index c0f105a..5a08722 100644
--- a/services/network/cookie_manager.cc
+++ b/services/network/cookie_manager.cc
@@ -73,6 +73,11 @@
   cookie_store_->GetAllCookiesAsync(std::move(callback));
 }
 
+void CookieManager::GetAllCookiesWithAccessSemantics(
+    GetAllCookiesWithAccessSemanticsCallback callback) {
+  cookie_store_->GetAllCookiesWithAccessSemanticsAsync(std::move(callback));
+}
+
 void CookieManager::GetCookieList(const GURL& url,
                                   const net::CookieOptions& cookie_options,
                                   GetCookieListCallback callback) {
diff --git a/services/network/cookie_manager.h b/services/network/cookie_manager.h
index 47eadf97..9a76752 100644
--- a/services/network/cookie_manager.h
+++ b/services/network/cookie_manager.h
@@ -56,6 +56,8 @@
 
   // mojom::CookieManager
   void GetAllCookies(GetAllCookiesCallback callback) override;
+  void GetAllCookiesWithAccessSemantics(
+      GetAllCookiesWithAccessSemanticsCallback callback) override;
   void GetCookieList(const GURL& url,
                      const net::CookieOptions& cookie_options,
                      GetCookieListCallback callback) override;
diff --git a/services/network/cookie_manager_unittest.cc b/services/network/cookie_manager_unittest.cc
index 6794f77..39dfb0e 100644
--- a/services/network/cookie_manager_unittest.cc
+++ b/services/network/cookie_manager_unittest.cc
@@ -23,6 +23,7 @@
 #include "net/cookies/cookie_store_test_callbacks.h"
 #include "net/cookies/cookie_store_test_helpers.h"
 #include "net/cookies/cookie_util.h"
+#include "net/cookies/test_cookie_access_delegate.h"
 #include "services/network/public/mojom/cookie_manager.mojom.h"
 #include "services/network/session_cleanup_cookie_store.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -82,6 +83,24 @@
     return cookies_out;
   }
 
+  std::vector<net::CanonicalCookie> GetAllCookiesWithAccessSemantics(
+      std::vector<net::CookieAccessSemantics>* access_semantics_list_out) {
+    base::RunLoop run_loop;
+    std::vector<net::CanonicalCookie> cookies_out;
+    cookie_service_->GetAllCookiesWithAccessSemantics(
+        base::BindLambdaForTesting(
+            [&run_loop, &cookies_out, access_semantics_list_out](
+                const std::vector<net::CanonicalCookie>& cookies,
+                const std::vector<net::CookieAccessSemantics>&
+                    access_semantics_list) {
+              cookies_out = cookies;
+              *access_semantics_list_out = access_semantics_list;
+              run_loop.Quit();
+            }));
+    run_loop.Run();
+    return cookies_out;
+  }
+
   std::vector<net::CanonicalCookie> GetCookieList(const GURL& url,
                                                   net::CookieOptions options) {
     base::RunLoop run_loop;
@@ -439,6 +458,75 @@
   EXPECT_EQ(net::COOKIE_PRIORITY_MEDIUM, cookies[3].Priority());
 }
 
+TEST_F(CookieManagerTest, GetAllCookiesWithAccessSemantics) {
+  auto delegate = std::make_unique<net::TestCookieAccessDelegate>();
+  delegate->SetExpectationForCookieDomain("domain1.test",
+                                          net::CookieAccessSemantics::UNKNOWN);
+  delegate->SetExpectationForCookieDomain("domain2.test",
+                                          net::CookieAccessSemantics::LEGACY);
+  delegate->SetExpectationForCookieDomain(
+      ".domainwithdot.test", net::CookieAccessSemantics::NONLEGACY);
+  cookie_store()->SetCookieAccessDelegate(std::move(delegate));
+
+  // Set some cookies for the test to play with.
+  // TODO(chlily): Because the order of the cookies with respect to the access
+  // semantics entries should match up, for the purposes of this test we need
+  // the cookies to sort in a predictable order. Since the longest path is
+  // first, we can manipulate the path attribute of each cookie set below to get
+  // them in the right order. This will have to change if CookieSorter ever
+  // starts sorting the cookies differently.
+
+  // UNKNOWN
+  EXPECT_TRUE(SetCanonicalCookie(
+      net::CanonicalCookie("A", "B", "domain1.test",
+                           "/this/path/is/the/longest/for/sorting/purposes",
+                           base::Time(), base::Time(), base::Time(),
+                           /*secure=*/false, /*httponly=*/false,
+                           net::CookieSameSite::NO_RESTRICTION,
+                           net::COOKIE_PRIORITY_MEDIUM),
+      "https", true));
+  // LEGACY
+  EXPECT_TRUE(SetCanonicalCookie(
+      net::CanonicalCookie(
+          "C", "D", "domain2.test", "/with/longer/path", base::Time(),
+          base::Time(), base::Time(), /*secure=*/false, /*httponly=*/false,
+          net::CookieSameSite::NO_RESTRICTION, net::COOKIE_PRIORITY_MEDIUM),
+      "https", true));
+  // not set (UNKNOWN)
+  EXPECT_TRUE(SetCanonicalCookie(
+      net::CanonicalCookie(
+          "HttpOnly", "F", "domain3.test", "/with/path", base::Time(),
+          base::Time(), base::Time(), /*secure=*/false,
+          /*httponly=*/true, net::CookieSameSite::NO_RESTRICTION,
+          net::COOKIE_PRIORITY_MEDIUM),
+      "https", true));
+  // NONLEGACY
+  EXPECT_TRUE(SetCanonicalCookie(
+      net::CanonicalCookie(
+          "Secure", "E", ".domainwithdot.test", "/", base::Time(), base::Time(),
+          base::Time(), /*secure=*/true,
+          /*httponly=*/false, net::CookieSameSite::NO_RESTRICTION,
+          net::COOKIE_PRIORITY_MEDIUM),
+      "https", true));
+
+  std::vector<net::CookieAccessSemantics> access_semantics_list;
+  std::vector<net::CanonicalCookie> cookies =
+      service_wrapper()->GetAllCookiesWithAccessSemantics(
+          &access_semantics_list);
+
+  ASSERT_EQ(4u, cookies.size());
+  EXPECT_EQ(cookies.size(), access_semantics_list.size());
+  EXPECT_EQ("domain1.test", cookies[0].Domain());
+  EXPECT_EQ("domain2.test", cookies[1].Domain());
+  EXPECT_EQ("domain3.test", cookies[2].Domain());
+  EXPECT_EQ(".domainwithdot.test", cookies[3].Domain());
+
+  EXPECT_EQ(net::CookieAccessSemantics::UNKNOWN, access_semantics_list[0]);
+  EXPECT_EQ(net::CookieAccessSemantics::LEGACY, access_semantics_list[1]);
+  EXPECT_EQ(net::CookieAccessSemantics::UNKNOWN, access_semantics_list[2]);
+  EXPECT_EQ(net::CookieAccessSemantics::NONLEGACY, access_semantics_list[3]);
+}
+
 TEST_F(CookieManagerTest, GetCookieList) {
   // Set some cookies for the test to play with.
   EXPECT_TRUE(SetCanonicalCookie(
diff --git a/services/network/loader_util.cc b/services/network/loader_util.cc
index 3b1cb19..6720b8c 100644
--- a/services/network/loader_util.cc
+++ b/services/network/loader_util.cc
@@ -38,17 +38,11 @@
 } kConcerningHeaders[] = {
     {net::HttpRequestHeaders::kConnection, ConcerningHeaderId::kConnection},
     {net::HttpRequestHeaders::kCookie, ConcerningHeaderId::kCookie},
-    {"Cookie2", ConcerningHeaderId::kCookie2},
-    {"Content-transfer-encoding", ConcerningHeaderId::kContentTransferEncoding},
     {"Date", ConcerningHeaderId::kDate},
     {"Expect", ConcerningHeaderId::kExpect},
-    {"Keep-alive", ConcerningHeaderId::kKeepAlive},
     // The referer is passed in from the caller on a per-request basis, but
     // there's a separate field for it that should be used instead.
     {net::HttpRequestHeaders::kReferer, ConcerningHeaderId::kReferer},
-    {"Te", ConcerningHeaderId::kTe},
-    {net::HttpRequestHeaders::kTransferEncoding,
-     ConcerningHeaderId::kTransferEncoding},
     {"Via", ConcerningHeaderId::kVia},
 };
 
diff --git a/services/network/network_context_unittest.cc b/services/network/network_context_unittest.cc
index de29309..22e29237 100644
--- a/services/network/network_context_unittest.cc
+++ b/services/network/network_context_unittest.cc
@@ -213,6 +213,9 @@
   auto params = mojom::URLLoaderFactoryParams::New();
   params->process_id = process_id;
   params->is_corb_enabled = false;
+  params->network_isolation_key =
+      net::NetworkIsolationKey(url::Origin::Create(GURL("https://abc.com")),
+                               url::Origin::Create(GURL("https://xyz.com")));
   network_context->CreateURLLoaderFactory(mojo::MakeRequest(&loader_factory),
                                           std::move(params));
 
@@ -1365,10 +1368,14 @@
     network_context->NotifyExternalCacheHit(test_url, test_url.scheme(), key);
     EXPECT_EQ(i + 1, mock_cache.disk_cache()->GetExternalCacheHits().size());
 
-    // Potentially a brittle check as the value sent to disk_cache is a "key."
-    // This key just happens to be the same as the GURL from the test input.
-    // So if this breaks check HttpCache::GenerateCacheKey() for changes.
-    EXPECT_EQ(test_url, mock_cache.disk_cache()->GetExternalCacheHits().back());
+    // Note: if this breaks check HttpCache::GenerateCacheKey() for changes.
+    std::string expected_key_prefix =
+        base::FeatureList::IsEnabled(
+            net::features::kSplitCacheByNetworkIsolationKey)
+            ? base::StrCat({"_dk_", key.ToString(), " "})
+            : "";
+    EXPECT_EQ(expected_key_prefix + test_url.spec(),
+              mock_cache.disk_cache()->GetExternalCacheHits().back());
   }
 }
 
diff --git a/services/network/public/cpp/cookie_manager.typemap b/services/network/public/cpp/cookie_manager.typemap
index de9384bd..a365737 100644
--- a/services/network/public/cpp/cookie_manager.typemap
+++ b/services/network/public/cpp/cookie_manager.typemap
@@ -17,6 +17,7 @@
   "network.mojom.CookiePriority=::net::CookiePriority",
   "network.mojom.CookieSameSite=::net::CookieSameSite",
   "network.mojom.CookieSameSiteContext=::net::CookieOptions::SameSiteCookieContext",
+  "network.mojom.CookieAccessSemantics=::net::CookieAccessSemantics",
   "network.mojom.CookieOptions=::net::CookieOptions",
   "network.mojom.CanonicalCookie=::net::CanonicalCookie",
   "network.mojom.CookieInclusionStatusWarningReason=::net::CanonicalCookie::CookieInclusionStatus::WarningReason",
diff --git a/services/network/public/cpp/cookie_manager_mojom_traits.cc b/services/network/public/cpp/cookie_manager_mojom_traits.cc
index 6f3461ff..067058d8 100644
--- a/services/network/public/cpp/cookie_manager_mojom_traits.cc
+++ b/services/network/public/cpp/cookie_manager_mojom_traits.cc
@@ -86,6 +86,43 @@
   return false;
 }
 
+network::mojom::CookieAccessSemantics EnumTraits<
+    network::mojom::CookieAccessSemantics,
+    net::CookieAccessSemantics>::ToMojom(net::CookieAccessSemantics input) {
+  switch (input) {
+    case net::CookieAccessSemantics::UNKNOWN:
+      return network::mojom::CookieAccessSemantics::UNKNOWN;
+    case net::CookieAccessSemantics::NONLEGACY:
+      return network::mojom::CookieAccessSemantics::NONLEGACY;
+    case net::CookieAccessSemantics::LEGACY:
+      return network::mojom::CookieAccessSemantics::LEGACY;
+    default:
+      break;
+  }
+  NOTREACHED();
+  return static_cast<network::mojom::CookieAccessSemantics>(input);
+}
+
+bool EnumTraits<network::mojom::CookieAccessSemantics,
+                net::CookieAccessSemantics>::
+    FromMojom(network::mojom::CookieAccessSemantics input,
+              net::CookieAccessSemantics* output) {
+  switch (input) {
+    case network::mojom::CookieAccessSemantics::UNKNOWN:
+      *output = net::CookieAccessSemantics::UNKNOWN;
+      return true;
+    case network::mojom::CookieAccessSemantics::NONLEGACY:
+      *output = net::CookieAccessSemantics::NONLEGACY;
+      return true;
+    case network::mojom::CookieAccessSemantics::LEGACY:
+      *output = net::CookieAccessSemantics::LEGACY;
+      return true;
+    default:
+      break;
+  }
+  return false;
+}
+
 network::mojom::CookieInclusionStatusWarningReason
 EnumTraits<network::mojom::CookieInclusionStatusWarningReason,
            net::CanonicalCookie::CookieInclusionStatus::WarningReason>::
diff --git a/services/network/public/cpp/cookie_manager_mojom_traits.h b/services/network/public/cpp/cookie_manager_mojom_traits.h
index e5a345b..8730de54 100644
--- a/services/network/public/cpp/cookie_manager_mojom_traits.h
+++ b/services/network/public/cpp/cookie_manager_mojom_traits.h
@@ -29,6 +29,15 @@
 };
 
 template <>
+struct EnumTraits<network::mojom::CookieAccessSemantics,
+                  net::CookieAccessSemantics> {
+  static network::mojom::CookieAccessSemantics ToMojom(
+      net::CookieAccessSemantics input);
+  static bool FromMojom(network::mojom::CookieAccessSemantics input,
+                        net::CookieAccessSemantics* output);
+};
+
+template <>
 struct EnumTraits<network::mojom::CookieInclusionStatusWarningReason,
                   net::CanonicalCookie::CookieInclusionStatus::WarningReason> {
   static network::mojom::CookieInclusionStatusWarningReason ToMojom(
diff --git a/services/network/public/cpp/cookie_manager_mojom_traits_unittest.cc b/services/network/public/cpp/cookie_manager_mojom_traits_unittest.cc
index 78dcf9b..b87238cd 100644
--- a/services/network/public/cpp/cookie_manager_mojom_traits_unittest.cc
+++ b/services/network/public/cpp/cookie_manager_mojom_traits_unittest.cc
@@ -117,7 +117,8 @@
 TEST(CookieManagerTraitsTest, Roundtrips_CookieSameSite) {
   for (net::CookieSameSite cookie_state :
        {net::CookieSameSite::NO_RESTRICTION, net::CookieSameSite::LAX_MODE,
-        net::CookieSameSite::STRICT_MODE}) {
+        net::CookieSameSite::STRICT_MODE, net::CookieSameSite::EXTENDED_MODE,
+        net::CookieSameSite::UNSPECIFIED}) {
     net::CookieSameSite roundtrip;
     ASSERT_TRUE(SerializeAndDeserializeEnum<mojom::CookieSameSite>(cookie_state,
                                                                    &roundtrip));
@@ -125,6 +126,18 @@
   }
 }
 
+TEST(CookieManagerTraitsTest, Roundtrips_CookieAccessSemantics) {
+  for (net::CookieAccessSemantics access_semantics :
+       {net::CookieAccessSemantics::UNKNOWN,
+        net::CookieAccessSemantics::NONLEGACY,
+        net::CookieAccessSemantics::LEGACY}) {
+    net::CookieAccessSemantics roundtrip;
+    ASSERT_TRUE(SerializeAndDeserializeEnum<mojom::CookieAccessSemantics>(
+        access_semantics, &roundtrip));
+    EXPECT_EQ(access_semantics, roundtrip);
+  }
+}
+
 TEST(CookieManagerTraitsTest, Roundtrips_CookieSameSiteContext) {
   for (net::CookieOptions::SameSiteCookieContext context_state :
        {net::CookieOptions::SameSiteCookieContext::CROSS_SITE,
diff --git a/services/network/public/cpp/header_util.cc b/services/network/public/cpp/header_util.cc
index d9057f8..9f94189 100644
--- a/services/network/public/cpp/header_util.cc
+++ b/services/network/public/cpp/header_util.cc
@@ -13,7 +13,12 @@
 
 // Headers that consumers are not trusted to set. All "Proxy-" prefixed messages
 // are blocked inline. The"Authorization" auth header is deliberately not
-// included, since OAuth requires websites be able to set it directly.
+// included, since OAuth requires websites be able to set it directly. These are
+// a subset of headers forbidden by the fetch spec.
+//
+// This list has some values in common with
+// https://fetch.spec.whatwg.org/#forbidden-header-name, but excludes some
+// values that are still set by the caller in Chrome.
 const char* kUnsafeHeaders[] = {
     // This is determined by the upload body and set by net/. A consumer
     // overriding that could allow for Bad Things.
@@ -24,13 +29,22 @@
     net::HttpRequestHeaders::kHost,
 
     // Trailers are not supported.
-    "Trailer",
+    "Trailer", "Te",
 
     // Websockets use a different API.
     "Upgrade",
 
-    // TODO(mmenke): Gather stats on the following headers before adding them:
-    // Cookie, Cookie2, Referer, TE, Keep-Alive, Via.
+    // Obsolete header, and network stack manages headers itself.
+    "Cookie2",
+
+    // Not supported by net/.
+    "Keep-Alive",
+
+    // Forbidden by the fetch spec.
+    net::HttpRequestHeaders::kTransferEncoding,
+
+    // TODO(mmenke): Figure out what to do about the remaining headers:
+    // Connection, Cookie, Date, Expect, Referer, Via.
 };
 
 // Headers that consumers are currently allowed to set, with the exception of
@@ -43,10 +57,6 @@
 } kUnsafeHeaderValues[] = {
     // Websockets use a different API.
     {net::HttpRequestHeaders::kConnection, "Upgrade"},
-
-    // Telling a server a non-chunked upload is chunked has similar implications
-    // as sending the wrong Content-Length.
-    {net::HttpRequestHeaders::kTransferEncoding, "Chunked"},
 };
 
 }  // namespace
diff --git a/services/network/public/cpp/header_util_unittest.cc b/services/network/public/cpp/header_util_unittest.cc
index c34155e..57e6595 100644
--- a/services/network/public/cpp/header_util_unittest.cc
+++ b/services/network/public/cpp/header_util_unittest.cc
@@ -29,17 +29,22 @@
       {net::HttpRequestHeaders::kConnection, "Upgrade", false},
       {net::HttpRequestHeaders::kConnection, "Close", true},
       {net::HttpRequestHeaders::kTransferEncoding, "Chunked", false},
-      {net::HttpRequestHeaders::kTransferEncoding, "Chunky", true},
+      {net::HttpRequestHeaders::kTransferEncoding, "Chunky", false},
       {"cOnNeCtIoN", "uPgRaDe", false},
 
       {net::HttpRequestHeaders::kProxyAuthorization,
        "Basic Zm9vOmJhcg==", false},
       {"Proxy-Foo", "bar", false},
       {"PrOxY-FoO", "bar", false},
+
+      {"dnt", "1", true},
   };
 
-  for (const auto& header : kHeaders)
+  for (const auto& header : kHeaders) {
+    SCOPED_TRACE(header.key);
+    SCOPED_TRACE(header.value);
     EXPECT_EQ(header.is_safe, IsRequestHeaderSafe(header.key, header.value));
+  }
 }
 
 TEST(HeaderUtilTest, AreRequestHeadersSafe) {
@@ -52,21 +57,27 @@
 
       {net::HttpRequestHeaders::kContentLength, "42", false},
       {net::HttpRequestHeaders::kHost, "foo.test", false},
+      {"hOsT", "foo.test", false},
       {"Trailer", "header-names", false},
+      {"Te", "deflate", false},
       {"Upgrade", "websocket", false},
       {"Upgrade", "webbedsocket", false},
-      {"hOsT", "foo.test", false},
+      {"Cookie2", "tastiness=5", false},
+      {"Keep-Alive", "timeout=5, max=1000", false},
+      {net::HttpRequestHeaders::kTransferEncoding, "gzip", false},
 
       {net::HttpRequestHeaders::kConnection, "Upgrade", false},
       {net::HttpRequestHeaders::kConnection, "Close", true},
       {net::HttpRequestHeaders::kTransferEncoding, "Chunked", false},
-      {net::HttpRequestHeaders::kTransferEncoding, "Chunky", true},
+      {net::HttpRequestHeaders::kTransferEncoding, "Chunky", false},
       {"cOnNeCtIoN", "uPgRaDe", false},
 
       {net::HttpRequestHeaders::kProxyAuthorization,
        "Basic Zm9vOmJhcg==", false},
       {"Proxy-Foo", "bar", false},
       {"PrOxY-FoO", "bar", false},
+
+      {"dnt", "1", true},
   };
 
   // Check each header in isolation, and all combinations of two header
@@ -79,6 +90,10 @@
     for (const auto& header2 : kHeaders) {
       if (base::EqualsCaseInsensitiveASCII(header1.key, header2.key))
         continue;
+      SCOPED_TRACE(header1.key);
+      SCOPED_TRACE(header1.value);
+      SCOPED_TRACE(header2.key);
+      SCOPED_TRACE(header2.value);
 
       net::HttpRequestHeaders request_headers2;
       request_headers2.SetHeader(header1.key, header1.value);
diff --git a/services/network/public/mojom/cookie_manager.mojom b/services/network/public/mojom/cookie_manager.mojom
index 69903ebd..c69b208 100644
--- a/services/network/public/mojom/cookie_manager.mojom
+++ b/services/network/public/mojom/cookie_manager.mojom
@@ -52,7 +52,6 @@
   LAX_MODE = 1,
   STRICT_MODE = 2,
   EXTENDED_MODE = 3,
-  // 4 is reserved: LAX_MODE_ALLOW_UNSAFE is not used here.
 };
 
 enum CookieSameSiteContext {
@@ -62,6 +61,15 @@
   SAME_SITE_STRICT
 };
 
+// What rules to apply when determining whether access to a particular cookie is
+// allowed.
+// Keep in sync with net/cookies/cookie_constants.h.
+enum CookieAccessSemantics {
+  UNKNOWN = -1,
+  NONLEGACY = 0,
+  LEGACY,
+};
+
 // Keep defaults here in sync with net/cookies/cookie_options.cc.
 struct CookieOptions {
   bool exclude_httponly = true;
@@ -215,6 +223,21 @@
   // on origin.  Should the returned cookies also be sorted by origin?
   GetAllCookies() => (array<CanonicalCookie> cookies);
 
+  // Get all the cookies known to the service.
+  // Returned cookie list is sorted first by path length (longest first)
+  // and second by creation time.
+  // Additionally get a list of the CookieAccessSemantics that applies to each,
+  // if known. The |access_semantics_list| is guaranteed to be the same length
+  // as |cookies|, with each element in |cookies| having the access semantics
+  // which is given by the same index in |access_semantics_list|. If this method
+  // is not implemented in the underlying CookieStore, the returned
+  // |access_semantics_list| will just contain all UNKNOWNs. If it is
+  // supported, the access semantics values will have been determined by
+  // querying the CookieStore's CookieAccessDelegate.
+  GetAllCookiesWithAccessSemantics()
+      => (array<CanonicalCookie> cookies,
+          array<CookieAccessSemantics> access_semantics_list);
+
   // Get all cookies for the specified URL and cookie options.
   // Returned cookie list is sorted first by path length (longest first)
   // and second by creation time. If the |return_excluded_cookies| option is set
diff --git a/services/network/test/test_cookie_manager.h b/services/network/test/test_cookie_manager.h
index e9ab7f7d..fc197a1 100644
--- a/services/network/test/test_cookie_manager.h
+++ b/services/network/test/test_cookie_manager.h
@@ -27,6 +27,8 @@
                           const net::CookieOptions& cookie_options,
                           SetCanonicalCookieCallback callback) override;
   void GetAllCookies(GetAllCookiesCallback callback) override {}
+  void GetAllCookiesWithAccessSemantics(
+      GetAllCookiesWithAccessSemanticsCallback callback) override {}
   void GetCookieList(const GURL& url,
                      const net::CookieOptions& cookie_options,
                      GetCookieListCallback callback) override {}
diff --git a/services/network/url_loader_unittest.cc b/services/network/url_loader_unittest.cc
index d22957b..4760f1ed 100644
--- a/services/network/url_loader_unittest.cc
+++ b/services/network/url_loader_unittest.cc
@@ -1974,7 +1974,7 @@
   net::HttpRequestHeaders redirect_headers;
   redirect_headers.SetHeader(net::HttpRequestHeaders::kReferer,
                              "https://somewhere.test/");
-  redirect_headers.SetHeader(net::HttpRequestHeaders::kTransferEncoding, "Hat");
+  redirect_headers.SetHeader("Via", "Albuquerque");
   loader->FollowRedirect({}, redirect_headers, base::nullopt);
 
   client.RunUntilComplete();
@@ -1989,7 +1989,7 @@
       "NetworkService.ConcerningRequestHeader.AddedOnRedirect", false, 0);
   for (int i = 0; i < static_cast<int>(ConcerningHeaderId::kMaxValue); ++i) {
     if (i == static_cast<int>(ConcerningHeaderId::kReferer) ||
-        i == static_cast<int>(ConcerningHeaderId::kTransferEncoding)) {
+        i == static_cast<int>(ConcerningHeaderId::kVia)) {
       histograms.ExpectBucketCount(
           "NetworkService.ConcerningRequestHeader.HeaderAddedOnRedirect", i, 1);
     } else {
diff --git a/services/tracing/public/cpp/trace_event_agent.cc b/services/tracing/public/cpp/trace_event_agent.cc
index 41f5fe4e..ba0219a05 100644
--- a/services/tracing/public/cpp/trace_event_agent.cc
+++ b/services/tracing/public/cpp/trace_event_agent.cc
@@ -114,8 +114,8 @@
       recorder_->AddMetadata(std::move(*metadata));
   }
 
-  base::trace_event::TraceLog::GetInstance()->Flush(
-      base::Bind(&TraceEventAgent::OnTraceLogFlush, base::Unretained(this)));
+  base::trace_event::TraceLog::GetInstance()->Flush(base::BindRepeating(
+      &TraceEventAgent::OnTraceLogFlush, base::Unretained(this)));
 }
 
 void TraceEventAgent::RequestBufferStatus(
diff --git a/services/tracing/public/cpp/trace_event_agent_unittest.cc b/services/tracing/public/cpp/trace_event_agent_unittest.cc
index b04ec2b..8a75e0b2 100644
--- a/services/tracing/public/cpp/trace_event_agent_unittest.cc
+++ b/services/tracing/public/cpp/trace_event_agent_unittest.cc
@@ -38,8 +38,8 @@
 
   std::string events() const { return events_; }
   std::string metadata() const { return metadata_; }
-  void set_quit_closure(base::Closure quit_closure) {
-    quit_closure_ = quit_closure;
+  void set_quit_closure(base::OnceClosure quit_closure) {
+    quit_closure_ = std::move(quit_closure);
   }
 
  private:
@@ -72,13 +72,13 @@
 
   void OnConnectionError() {
     if (quit_closure_)
-      quit_closure_.Run();
+      std::move(quit_closure_).Run();
   }
 
   mojo::Receiver<mojom::Recorder> receiver_;
   std::string events_;
   std::string metadata_;
-  base::Closure quit_closure_;
+  base::OnceClosure quit_closure_;
 };
 
 class TraceEventAgentTest : public testing::Test {
@@ -97,11 +97,11 @@
         base::BindRepeating([](bool success) { EXPECT_TRUE(success); }));
   }
 
-  void StopAndFlush(base::Closure quit_closure) {
+  void StopAndFlush(base::OnceClosure quit_closure) {
     mojo::PendingRemote<mojom::Recorder> recorder;
     recorder_.reset(
         new MockRecorder(recorder.InitWithNewPipeAndPassReceiver()));
-    recorder_->set_quit_closure(quit_closure);
+    recorder_->set_quit_closure(std::move(quit_closure));
     TraceEventAgent::GetInstance()->StopAndFlush(std::move(recorder));
   }
 
diff --git a/services/tracing/recorder_unittest.cc b/services/tracing/recorder_unittest.cc
index 2059673..b1710c8 100644
--- a/services/tracing/recorder_unittest.cc
+++ b/services/tracing/recorder_unittest.cc
@@ -27,12 +27,12 @@
 
   void CreateRecorder(mojo::PendingReceiver<mojom::Recorder> receiver,
                       mojom::TraceDataType data_type,
-                      const base::Closure& callback) {
+                      const base::RepeatingClosure& callback) {
     recorder_.reset(new Recorder(std::move(receiver), data_type, callback));
   }
 
   void CreateRecorder(mojom::TraceDataType data_type,
-                      const base::Closure& callback) {
+                      const base::RepeatingClosure& callback) {
     CreateRecorder(mojo::NullReceiver(), data_type, callback);
   }
 
@@ -118,13 +118,14 @@
   {
     mojo::PendingRemote<mojom::Recorder> remote;
     auto receiver = remote.InitWithNewPipeAndPassReceiver();
-    CreateRecorder(std::move(receiver), mojom::TraceDataType::STRING,
-                   base::BindRepeating(
-                       [](size_t* num_calls, base::Closure quit_closure) {
-                         (*num_calls)++;
-                         quit_closure.Run();
-                       },
-                       base::Unretained(&num_calls), run_loop.QuitClosure()));
+    CreateRecorder(
+        std::move(receiver), mojom::TraceDataType::STRING,
+        base::BindRepeating(
+            [](size_t* num_calls, base::RepeatingClosure quit_closure) {
+              (*num_calls)++;
+              quit_closure.Run();
+            },
+            base::Unretained(&num_calls), run_loop.QuitClosure()));
   }
   // |remote| is deleted at this point and so the recorder should notify us that
   // the client is not going to send any more data by running the callback.
diff --git a/testing/buildbot/chromium.perf.json b/testing/buildbot/chromium.perf.json
index 7c71dffe..e99859c 100644
--- a/testing/buildbot/chromium.perf.json
+++ b/testing/buildbot/chromium.perf.json
@@ -301,43 +301,6 @@
       {
         "args": [
           "--gtest-benchmark-name",
-          "dawn_perf_tests"
-        ],
-        "isolate_name": "dawn_perf_tests",
-        "merge": {
-          "script": "//tools/perf/process_perf_results.py"
-        },
-        "name": "dawn_perf_tests",
-        "override_compile_targets": [
-          "dawn_perf_tests"
-        ],
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "10de:1cb3",
-              "os": "Windows-2008ServerR2-SP1",
-              "pool": "chrome.tests.perf"
-            }
-          ],
-          "expiration": 7200,
-          "hard_timeout": 43200,
-          "ignore_task_failure": false,
-          "io_timeout": 21600,
-          "shards": 1
-        },
-        "trigger_script": {
-          "args": [
-            "--multiple-dimension-script-verbose",
-            "True"
-          ],
-          "requires_simultaneous_shard_dispatch": true,
-          "script": "//testing/trigger_scripts/perf_device_trigger.py"
-        }
-      },
-      {
-        "args": [
-          "--gtest-benchmark-name",
           "load_library_perf_tests"
         ],
         "isolate_name": "load_library_perf_tests",
diff --git a/testing/buildbot/filters/gpu.skiarenderer_vulkan_content_browsertests.filter b/testing/buildbot/filters/gpu.skiarenderer_vulkan_content_browsertests.filter
index 90afe57..016590b 100644
--- a/testing/buildbot/filters/gpu.skiarenderer_vulkan_content_browsertests.filter
+++ b/testing/buildbot/filters/gpu.skiarenderer_vulkan_content_browsertests.filter
@@ -1,127 +1,31 @@
-# crbug.com/963266: Android SkiaRenderer Vulkan content_browsertests failures
--AccessibilityHitTestingBrowserTest.HitTestingWithPinchZoom
--BrowserSideFlingBrowserTest.InertialGSEGetsBubbledFromOOPIF
--BrowserSideFlingBrowserTest.InertialGSUBubblingStopsWhenParentCannotScroll
--*CompositorImplLowEndBrowserTest.CompositorImplDropsResourcesOnBackground*
+# MediaBrowserTest error during playback. https://crbug.com/963266
 -DevToolsVideoConsumerTest.SetMinAndMaxFramesChangesDimensions
--DumpAccessibilityTreeTest.*
 -File/MediaTest.VideoBearMp4/0
 -File/MediaTest.VideoTulipWebm/0
+-File/MediaTest.VideoBearSilentMp4/0
 -Http/MediaTest.VideoBearMp4/0
 -Http/MediaTest.VideoBearSilentMp4/0
 -Http/MediaTest.VideoTulipWebm/0
--MediaColorTest.Yuv420pTheora
--MediaColorTest.Yuv420pVp8
--MediaSessionImplBrowserTest.MetadataWhenFileUrlScheme
--MediaSourceTest.Playback_Video_MP4_Audio_WEBM
--MediaTest.VideoBearRotated0
--MediaTest.VideoBearRotated90
--MediaTest.VideoBearRotated180
--MediaTest.VideoBearRotated270
--MSE_ClearKey/EncryptedMediaTest.Playback_Encryption_CBCS/0
--MSE_ClearKey/EncryptedMediaTest.Playback_Encryption_CBCS_Video_CENC_Audio/0
--MSE_ClearKey/EncryptedMediaTest.Playback_Encryption_CENC/0
--MSE_ClearKey/EncryptedMediaTest.Playback_Encryption_CENC_Video_CBCS_Audio/0
--MSE_ExternalClearKey/EncryptedMediaTest.*
--NavigationBrowserTest.*
--NavigationControllerBrowserTest.DontIgnoreBackAfterNavEntryLimit
--NavigationControllerBrowserTest.ReloadWithUrlAnchor
--NavigationControllerBrowserTest.ReloadWithUrlAnchorAndScroll
--NavigationDownloadBrowserTest.StopLoadingAfterDroppedNavigation/0
--OOPBrowserTest.Basic
+-MSE_*ClearKey/EncryptedMediaTest.ConfigChangeVideo_ClearToClear/0
+-MSE_*ClearKey/EncryptedMediaTest.ConfigChangeVideo_ClearToEncrypted/0
+-MSE_*ClearKey/EncryptedMediaTest.ConfigChangeVideo_EncryptedToEncrypted/0
+-MSE_*ClearKey/EncryptedMediaTest.FrameSizeChangeVideo/0
+-MSE_*ClearKey/EncryptedMediaTest.Playback_AudioClearVideo_WebM/0
+-MSE_*ClearKey/EncryptedMediaTest.Playback_Encryption_CBCS/0
+-MSE_*ClearKey/EncryptedMediaTest.Playback_Encryption_CBCS_Video_CENC_Audio/0
+-MSE_*ClearKey/EncryptedMediaTest.Playback_Encryption_CENC/0
+-MSE_*ClearKey/EncryptedMediaTest.Playback_Encryption_CENC_Video_CBCS_Audio/0
 -P/CompositorImplBrowserTestRefreshRate.VideoPreference/0
--Pixel_WebGL2_BlitFramebuffer_Result_Displayed
--SitePerProcessBrowserTest.ChildFrameCrashMetrics_ScrolledIntoViewAfterTabIsShown
--SitePerProcessBrowserTest.HiddenOOPIFWillNotGenerateCompositorFrames
--SitePerProcessBrowserTest.HiddenOOPIFWillNotGenerateCompositorFramesAfterNavigation
--SitePerProcessBrowserTest.PageScaleFactorPropagatesToOOPIFs
--SitePerProcessBrowserTest.ReloadHiddenTabWithCrashedSubframe
--SitePerProcessBrowserTest.ScaledIframeRasterSize
--SitePerProcessBrowserTest.ScrollOopifInPinchZoomedPage
--SitePerProcessBrowserTest.SubframeReusesExistingProcess
--SitePerProcessBrowserTest.SubframeVisibleAfterRenderViewBecomesSwappedOut
--SitePerProcessBrowserTest.UnloadNestedPendingDeletion
--SitePerProcessEmulatedTouchBrowserTest.EmulatedGestureScrollBubbles/0
--SitePerProcessEmulatedTouchBrowserTest.EmulatedTouchScrollBubbles/0
--SitePerProcessEmulatedTouchBrowserTest.EmulatedTouchShowPressHasTouchID/0
--SitePerProcessHighDPIHitTestBrowserTest.OverlapSurfaceHitTestTest/0
--SitePerProcessHitTestBrowserTest.BubbledScrollEventsTransformedCorrectly/0
--SitePerProcessHitTestBrowserTest.CrossProcessMouseCapture/0
--SitePerProcessHitTestBrowserTest.CrossProcessMouseCapture/1
--SitePerProcessHitTestBrowserTest.CrossProcessMouseEnterAndLeaveTest/0
--SitePerProcessHitTestBrowserTest.CrossProcessMousePointerCapture/0
--SitePerProcessHitTestBrowserTest.CrossProcessMousePointerCapture/1
--SitePerProcessHitTestBrowserTest.CrossProcessTooltipTestAndroid/0
--SitePerProcessHitTestBrowserTest.HitTestStaleDataDeletedView/0
--SitePerProcessHitTestBrowserTest.MouseCaptureOnDragSelection/0
--SitePerProcessHitTestBrowserTest.OverlapSurfaceHitTestTest/0
--SitePerProcessHitTestBrowserTest.TouchAndGestureEventPositionChange/0
--SitePerProcessHitTestBrowserTest.TouchAndGestureEventPositionChange/1
--SitePerProcessHitTestBrowserTest.TouchAndGestureEventPositionChange/2
--SitePerProcessHitTestBrowserTest.TouchpadPinchWhenMissingHitTestDataDoesNotCrash/0
--SitePerProcessHitTestBrowserTest.TouchpadPinchWhenMissingHitTestDataDoesNotCrash/1
--SnapshotBrowserTest.SyncMultiWindowTest
--SRC_ClearKey/EncryptedMediaTest.FrameSizeChangeVideo/0
--SRC_ExternalClearKey/EncryptedMediaTest.*
--TouchActionBrowserTest.DefaultAuto/1
--TouchActionBrowserTest.PanXMainThreadJanky/1
--TouchActionBrowserTest.PanXYMainThreadJanky/1
--TouchActionBrowserTest.PanYMainThreadJanky/1
--TouchSelectionControllerClientAndroidSiteIsolationTest.BasicSelectionIsolatedIframe
--TracingControllerTest.ProcessesPresentInTrace
--WebContentsImplBrowserTest.PopupWindowBrowserNavResumeLoad
--WebContentsImplBrowserTest.SetPageFrozen
--WebRtcBrowserTest.*
--WebRtcCaptureFromElementBrowserTest.CaptureFromCanvas2DHandlesContextLoss
--WebRtcCaptureFromElementBrowserTest.VerifyCanvas2DCaptureColor
--WebRtcCaptureFromElementBrowserTest.VerifyCanvasCaptureOffscreenCanvasFrames
--WebRtcCaptureFromElementBrowserTest.VerifyCanvasCaptureWebGLFrames
--WebRtcCaptureFromElementBrowserTest.VerifyCanvasCapture2DFrames
--WebRtcCaptureFromElementBrowserTest.VerifyCanvasWebGLCaptureColor
--WebRtcGetUserMediaBrowserTest.*
--WithOutOfBlinkCors/CrossSiteDocumentBlockingTest.BlockImages/0
--WithoutOutOfBlinkCors/CrossSiteDocumentBlockingTest.BlockImages/0
+-SRC_*ClearKey/EncryptedMediaTest.FrameSizeChangeVideo/0
 
-# crbug.com/985387: vulkan_content_browsertests fail on Android FYI SkiaRenderer
-# Vulkan (Pixel 2). These tests broke in a consistent way and need investigation
--MSE_ClearKey/EncryptedMediaTest.FrameSizeChangeVideo/0
--MSE_ExternalClearKey/EncryptedMediaTest.FrameSizeChangeVideo/0
--File/MediaTest.VideoBearSilentMp4/0
+# GPU Crashes consistently until timeout. https://crbug.com/1012279
+-P/CompositorImplBrowserTestRefreshRate.VideoPreference/0
+-MediaSourceTest.Playback_Video_MP4_Audio_WEBM
 
-# crbug.com/990116: vulkan_content_browsertests experience flaky failures for
-# these tests.
--MediaSourceTest.Playback_VideoAudio_WebM
--WebRtcDataBrowserTest.CallWithSctpDataAndMedia
--WebRtcDataBrowserTest.CallWithDataAndMedia
--Http/MediaTest.VideoBearMp4Vp9/0
--File/MediaTest.VideoBearMp4Vp9/0
--MediaSourceTest.Playback_VideoOnly_WebM
--MSE_ClearKey/EncryptedMediaTest.ConfigChangeVideo_ClearToClear/0
--MSE_ClearKey/EncryptedMediaTest.ConfigChangeVideo_ClearToEncrypted/0
--MediaSourceTest.ConfigChangeVideo
--SitePerProcessHitTestBrowserTest.RenderWidgetUserActivationStateTest/0
--SitePerProcessHitTestBrowserTest.RenderWidgetUserActivationStateTest/1
--SRC_ClearKey/EncryptedMediaTest.Playback_AudioClearVideo_WebM/0
--MediaSessionImplBrowserTest.PositionStateRouteWithOnePlayer
--MSE_ClearKey/EncryptedMediaTest.Playback_AudioClearVideo_WebM/0
--WebRtcDataBrowserTest.CanSetupLegacyCall
--DownloadContentTest.ResumeWithoutStrongValidators
--PeriodicBackgroundSyncBrowserTest.RegisterNoMinInterval
--UsingRealWebcam/WebRtcImageCaptureSucceedsBrowserTest.GrabFrame/0
--SRC_ClearKey/EncryptedMediaTest.Playback_VideoOnly_WebM/0
--NavigationBaseBrowserTest.ReplacingDocumentLoaderFiresLoadEvent/1
--MSE_ClearKey/EncryptedMediaTest.ConfigChangeVideo_EncryptedToEncrypted/0
--NativeFileSystemFileWriterBrowserTest.EachWriterHasUniqueSwapFileRacy
--NavigationBaseBrowserTest.FetchResponseAfterNavigationURLLoaderDeleted/1
--PeriodicBackgroundSyncBrowserTest.FindATag
--TextFragmentAnchorBrowserTest.DisabledOnScriptHistoryNavigation/0
--SRC_ClearKey/EncryptedMediaTest.Playback_VideoClearAudio_WebM_Opus/0
--SitePerProcessHitTestBrowserTest.PopupMenuTest/1
--SyntheticMouseEventTest.MouseEventAck
--AudioContextManagerTest.AudioContextPlaybackTimeUkm
--NavigationBaseBrowserTest.AddRequestHeaderRemoveOnRedirect/1
--NavigationDisableWebSecurityTest.ValidateBaseUrlForDataUrl/0
--NetworkIsolationNavigationBrowserTest.RenderNavigationNetworkIsolationKey/0
--NetworkIsolationNavigationBrowserTest.RenderNavigationNetworkIsolationKey/1
--PeriodicBackgroundSyncBrowserTest.RegisterFromServiceWorker
--TextFragmentAnchorBrowserTest.EnabledOnUserGestureScriptNavigation/1
+# SkiaOutputSurfaceImplOnGpu::MakeCurrent Fails. https://crbug.com/1012282
+-*CompositorImplLowEndBrowserTest.CompositorImplDropsResourcesOnBackground*
+
+# FeatureList Flakes. https://crbug.com/1012372
+-NavigationBrowserTest.HistoryBackCancelPendingNavigationUserGesture/1
+-SitePerProcessBrowserTest.*
+-TextFragmentAnchorBrowserTest.DisabledOnScriptNavigation/0
diff --git a/testing/libfuzzer/reproducing.md b/testing/libfuzzer/reproducing.md
index 19ef5efc..23a6f40 100644
--- a/testing/libfuzzer/reproducing.md
+++ b/testing/libfuzzer/reproducing.md
@@ -1,10 +1,5 @@
 # Reproducing libFuzzer and AFL crashes
 
-libFuzzer and AFL crashes can be reproduced easily with the
-[ClusterFuzz Reproduce Tool]. However, if you are unable to use the tool (e.g.
-unsupported platform, some other tool issue), you can still reproduce the crash
-manually using the steps below:
-
 *** note
 **Requirements:** For Windows, you must convert the forward slashes (/) to
 backslashes (\\) in the commands below and use `set` command instead of `export`
@@ -133,7 +128,6 @@
 (e.g. overnight) for better results.
 
 
-[ClusterFuzz Reproduce Tool]: https://github.com/google/clusterfuzz-tools
 [File a bug]: https://bugs.chromium.org/p/chromium/issues/entry?component=Tools%3EStability%3ElibFuzzer&comment=What%20problem%20are%20you%20seeing
 [here]: getting_started.md#symbolize-stacktrace
 [these tips]: https://github.com/google/sanitizers/wiki/AddressSanitizerWindowsPort#debugging
diff --git a/third_party/blink/public/common/features.h b/third_party/blink/public/common/features.h
index 281ba198..cfd172c 100644
--- a/third_party/blink/public/common/features.h
+++ b/third_party/blink/public/common/features.h
@@ -126,6 +126,8 @@
 BLINK_COMMON_EXPORT extern const base::Feature
     kLowerJavaScriptPriorityWhenForceDeferred;
 
+BLINK_COMMON_EXPORT extern const base::Feature kARIAAnnotationRoles;
+
 }  // namespace features
 }  // namespace blink
 
diff --git a/third_party/blink/public/mojom/web_feature/web_feature.mojom b/third_party/blink/public/mojom/web_feature/web_feature.mojom
index 2a7f261..4306ad43 100644
--- a/third_party/blink/public/mojom/web_feature/web_feature.mojom
+++ b/third_party/blink/public/mojom/web_feature/web_feature.mojom
@@ -2430,7 +2430,8 @@
   kPluginElementLoadedDocument = 3046,
   kPluginElementLoadedImage = 3047,
   kPluginElementLoadedExternal = 3048,
-
+  kRenderSubtreeAttribute = 3049,
+  kARIAAnnotationRoles = 3050,
 
   // Add new features immediately above this line. Don't change assigned
   // numbers of any item, and don't reuse removed slots.
diff --git a/third_party/blink/public/platform/web_runtime_features.h b/third_party/blink/public/platform/web_runtime_features.h
index ead4a63..1d330d1 100644
--- a/third_party/blink/public/platform/web_runtime_features.h
+++ b/third_party/blink/public/platform/web_runtime_features.h
@@ -74,6 +74,8 @@
   BLINK_PLATFORM_EXPORT static void EnableCompositorTouchAction(bool);
 
   BLINK_PLATFORM_EXPORT static void EnableAccelerated2dCanvas(bool);
+  BLINK_PLATFORM_EXPORT static void EnableAccessibilityExposeARIAAnnotations(
+      bool);
   BLINK_PLATFORM_EXPORT static void EnableAccessibilityExposeDisplayNone(bool);
   BLINK_PLATFORM_EXPORT static void EnableAccessibilityObjectModel(bool);
   BLINK_PLATFORM_EXPORT static void EnableAdTagging(bool);
diff --git a/third_party/blink/renderer/core/animation/css_transform_interpolation_type.cc b/third_party/blink/renderer/core/animation/css_transform_interpolation_type.cc
index 4922c1ae..986db9d9 100644
--- a/third_party/blink/renderer/core/animation/css_transform_interpolation_type.cc
+++ b/third_party/blink/renderer/core/animation/css_transform_interpolation_type.cc
@@ -93,8 +93,10 @@
       }
       for (const CSSValue* argument : transform_function) {
         const auto& primitive_value = To<CSSPrimitiveValue>(*argument);
-        if (!primitive_value.IsLength())
+        if (!primitive_value.IsLength() &&
+            !primitive_value.IsCalculatedPercentageWithLength()) {
           continue;
+        }
         primitive_value.AccumulateLengthUnitTypes(types);
       }
     }
diff --git a/third_party/blink/renderer/core/css/resolver/element_style_resources.cc b/third_party/blink/renderer/core/css/resolver/element_style_resources.cc
index eeaca59e..8325036a 100644
--- a/third_party/blink/renderer/core/css/resolver/element_style_resources.cc
+++ b/third_party/blink/renderer/core/css/resolver/element_style_resources.cc
@@ -314,7 +314,8 @@
               mask_layer->GetImage()->IsPendingImage()) {
             mask_layer->SetImage(LoadPendingImage(
                 style, To<StylePendingImage>(mask_layer->GetImage()),
-                FetchParameters::kAllowPlaceholder));
+                FetchParameters::kAllowPlaceholder,
+                kCrossOriginAttributeAnonymous));
           }
         }
         break;
diff --git a/third_party/blink/renderer/core/dom/element.cc b/third_party/blink/renderer/core/dom/element.cc
index f8ddaaf8..178dd9c 100644
--- a/third_party/blink/renderer/core/dom/element.cc
+++ b/third_party/blink/renderer/core/dom/element.cc
@@ -2276,6 +2276,8 @@
     } else if (RuntimeEnabledFeatures::DisplayLockingEnabled() &&
                name == html_names::kRendersubtreeAttr &&
                params.old_value != params.new_value) {
+      UseCounter::Count(GetDocument(), WebFeature::kRenderSubtreeAttribute);
+
       // This is needed to ensure that proper containment is put in place.
       SetNeedsStyleRecalc(kLocalStyleChange,
                           StyleChangeReasonForTracing::FromAttribute(name));
diff --git a/third_party/blink/renderer/core/inspector/devtools_session.cc b/third_party/blink/renderer/core/inspector/devtools_session.cc
index 96b4cb3..8cf28b8 100644
--- a/third_party/blink/renderer/core/inspector/devtools_session.cc
+++ b/third_party/blink/renderer/core/inspector/devtools_session.cc
@@ -56,20 +56,6 @@
   return unwrap_message;
 }
 
-mojom::blink::DevToolsMessagePtr WrapMessage(
-    protocol::ProtocolMessage message) {
-  auto result = mojom::blink::DevToolsMessage::New();
-
-  if (message.json.IsEmpty()) {
-    result->data = message.binary.ReleaseVector();
-  } else {
-    WTF::StringUTF8Adaptor adaptor(message.json);
-    result->data =
-        mojo_base::BigBuffer(base::as_bytes(base::make_span(adaptor)));
-  }
-  return result;
-}
-
 protocol::ProtocolMessage ToProtocolMessage(
     std::unique_ptr<v8_inspector::StringBuffer> buffer) {
   protocol::ProtocolMessage message;
@@ -331,15 +317,7 @@
   if (WebTestSupport::IsRunningWebTest())
     agent_->FlushProtocolNotifications();
 
-  mojom::blink::DevToolsMessagePtr serialized = WrapMessage(message);
-  if (!client_expects_binary_responses_) {
-    std::vector<uint8_t> json;
-    IPEStatus status = ConvertCBORToJSON(
-        span<uint8_t>(serialized->data.data(), serialized->data.size()), &json);
-    CHECK(status.ok()) << status.ToASCIIString();
-    serialized->data = mojo_base::BigBuffer(json);
-  }
-  host_remote_->DispatchProtocolResponse(std::move(serialized), call_id,
+  host_remote_->DispatchProtocolResponse(FinalizeMessage(message), call_id,
                                          session_state_.TakeUpdates());
 }
 
@@ -364,16 +342,16 @@
       std::unique_ptr<v8_inspector::StringBuffer> notification)
       : v8_notification_(std::move(notification)) {}
 
-  mojom::blink::DevToolsMessagePtr Serialize() {
-    protocol::ProtocolMessage serialized;
+  protocol::ProtocolMessage Serialize() {
+    protocol::ProtocolMessage result;
     if (blink_notification_) {
-      serialized = blink_notification_->serialize(/*binary=*/true);
+      result = blink_notification_->serialize(/*binary=*/true);
       blink_notification_.reset();
     } else if (v8_notification_) {
-      serialized = ToProtocolMessage(std::move(v8_notification_));
+      result = ToProtocolMessage(std::move(v8_notification_));
       v8_notification_.reset();
     }
-    return WrapMessage(std::move(serialized));
+    return result;
   }
 
  private:
@@ -407,18 +385,9 @@
   if (v8_session_)
     v8_session_state_cbor_.Set(v8_session_->state());
   for (wtf_size_t i = 0; i < notification_queue_.size(); ++i) {
-    mojom::blink::DevToolsMessagePtr serialized =
-        notification_queue_[i]->Serialize();
-    if (!client_expects_binary_responses_) {
-      std::vector<uint8_t> json;
-      IPEStatus status = ConvertCBORToJSON(
-          span<uint8_t>(serialized->data.data(), serialized->data.size()),
-          &json);
-      CHECK(status.ok()) << status.ToASCIIString();
-      serialized->data = mojo_base::BigBuffer(json);
-    }
-    host_remote_->DispatchProtocolNotification(std::move(serialized),
-                                               session_state_.TakeUpdates());
+    host_remote_->DispatchProtocolNotification(
+        FinalizeMessage(notification_queue_[i]->Serialize()),
+        session_state_.TakeUpdates());
   }
   notification_queue_.clear();
 }
@@ -428,4 +397,18 @@
   visitor->Trace(agents_);
 }
 
+blink::mojom::blink::DevToolsMessagePtr DevToolsSession::FinalizeMessage(
+    protocol::ProtocolMessage message) {
+  std::vector<uint8_t> message_to_send = message.binary.ReleaseVector();
+  if (!client_expects_binary_responses_) {
+    std::vector<uint8_t> json;
+    IPEStatus status = ConvertCBORToJSON(SpanFrom(message_to_send), &json);
+    CHECK(status.ok()) << status.ToASCIIString();
+    message_to_send = std::move(json);
+  }
+  auto mojo_msg = mojom::blink::DevToolsMessage::New();
+  mojo_msg->data = std::move(message_to_send);
+  return mojo_msg;
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/inspector/devtools_session.h b/third_party/blink/renderer/core/inspector/devtools_session.h
index 6450ea3..4cb447ca 100644
--- a/third_party/blink/renderer/core/inspector/devtools_session.h
+++ b/third_party/blink/renderer/core/inspector/devtools_session.h
@@ -93,6 +93,10 @@
   void SendProtocolResponse(int call_id,
                             const protocol::ProtocolMessage& message);
 
+  // Converts to JSON if requested by the client.
+  blink::mojom::blink::DevToolsMessagePtr FinalizeMessage(
+      protocol::ProtocolMessage message);
+
   Member<DevToolsAgent> agent_;
   mojo::AssociatedReceiver<mojom::blink::DevToolsSession> receiver_;
   mojo::AssociatedRemote<mojom::blink::DevToolsSessionHost> host_remote_;
diff --git a/third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.cc b/third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.cc
index 92b82f0..d361d785a 100644
--- a/third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.cc
+++ b/third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.cc
@@ -245,9 +245,7 @@
                           owning_layer_.GetSquashingDisallowedReasons());
 
   graphics_layer_->SetHitTestable(true);
-  UpdateOpacity(GetLayoutObject().StyleRef());
   UpdateTransform(GetLayoutObject().StyleRef());
-  UpdateLayerBlendMode(GetLayoutObject().StyleRef());
 }
 
 void CompositedLayerMapping::DestroyGraphicsLayers() {
@@ -262,10 +260,6 @@
   scrolling_contents_layer_ = nullptr;
 }
 
-void CompositedLayerMapping::UpdateOpacity(const ComputedStyle& style) {
-  graphics_layer_->SetOpacity(CompositingOpacity(style.Opacity()));
-}
-
 void CompositedLayerMapping::UpdateTransform(const ComputedStyle& style) {
   // FIXME: This could use m_owningLayer.transform(), but that currently has
   // transform-origin baked into it, and we don't want that.
@@ -282,10 +276,6 @@
   graphics_layer_->SetTransform(t);
 }
 
-void CompositedLayerMapping::UpdateLayerBlendMode(const ComputedStyle& style) {
-  SetBlendMode(style.GetBlendMode());
-}
-
 void CompositedLayerMapping::UpdateBackgroundPaintsOntoScrollingContentsLayer(
     bool& invalidate_graphics_layer,
     bool& invalidate_scrolling_contents_layer) {
@@ -826,10 +816,6 @@
   if (!GetLayoutObject().StyleRef().IsRunningTransformAnimationOnCompositor())
     UpdateTransform(GetLayoutObject().StyleRef());
 
-  // Set opacity, if it is not animating.
-  if (!GetLayoutObject().StyleRef().IsRunningOpacityAnimationOnCompositor())
-    UpdateOpacity(GetLayoutObject().StyleRef());
-
   IntRect local_compositing_bounds;
   IntRect relative_compositing_bounds;
   PhysicalOffset offset_from_composited_ancestor;
@@ -870,7 +856,6 @@
       owning_layer_.GetScrollableArea()->ScrollsOverflow())
     owning_layer_.GetScrollableArea()->PositionOverflowControls();
 
-  UpdateLayerBlendMode(GetLayoutObject().StyleRef());
   UpdateContentsRect();
   UpdateBackgroundColor();
 
@@ -1713,34 +1698,6 @@
   return static_cast<GraphicsLayerPaintingPhase>(phase);
 }
 
-float CompositedLayerMapping::CompositingOpacity(
-    float layout_object_opacity) const {
-  float final_opacity = layout_object_opacity;
-
-  for (PaintLayer* curr = owning_layer_.Parent(); curr; curr = curr->Parent()) {
-    // We only care about parents that are stacking contexts.
-    // Recall that opacity creates stacking context.
-    if (!curr->GetLayoutObject().StyleRef().IsStackingContext())
-      continue;
-
-    // If we found a composited layer, regardless of whether it actually
-    // paints into it, we want to compute opacity relative to it. So we can
-    // break here.
-    //
-    // FIXME: with grouped backings, a composited descendant will have to
-    // continue past the grouped (squashed) layers that its parents may
-    // contribute to. This whole confusion can be avoided by specifying
-    // explicitly the composited ancestor where we would stop accumulating
-    // opacity.
-    if (curr->GetCompositingState() == kPaintsIntoOwnBacking)
-      break;
-
-    final_opacity *= curr->GetLayoutObject().StyleRef().Opacity();
-  }
-
-  return final_opacity;
-}
-
 Color CompositedLayerMapping::LayoutObjectBackgroundColor() const {
   const auto& object = GetLayoutObject();
   auto background_color = object.ResolveColor(GetCSSPropertyBackgroundColor());
@@ -2010,10 +1967,6 @@
   return graphics_layer_.get();
 }
 
-void CompositedLayerMapping::SetBlendMode(BlendMode blend_mode) {
-  graphics_layer_->SetBlendMode(blend_mode);
-}
-
 GraphicsLayerUpdater::UpdateType CompositedLayerMapping::UpdateTypeForChildren(
     GraphicsLayerUpdater::UpdateType update_type) const {
   if (pending_update_scope_ >= kGraphicsLayerUpdateSubtree)
diff --git a/third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.h b/third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.h
index 18ab08e..d3be8cd 100644
--- a/third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.h
+++ b/third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.h
@@ -377,11 +377,7 @@
   // Result is transform origin in pixels.
   FloatPoint3D ComputeTransformOrigin(const IntRect& border_box) const;
 
-  void UpdateOpacity(const ComputedStyle&);
   void UpdateTransform(const ComputedStyle&);
-  void UpdateLayerBlendMode(const ComputedStyle&);
-  // Return the opacity value that this layer should use for compositing.
-  float CompositingOpacity(float layout_object_opacity) const;
 
   bool PaintsChildren() const;
 
diff --git a/third_party/blink/renderer/core/paint/compositing/graphics_layer_tree_as_text.cc b/third_party/blink/renderer/core/paint/compositing/graphics_layer_tree_as_text.cc
index d6b213f..41ee0bd 100644
--- a/third_party/blink/renderer/core/paint/compositing/graphics_layer_tree_as_text.cc
+++ b/third_party/blink/renderer/core/paint/compositing/graphics_layer_tree_as_text.cc
@@ -72,14 +72,6 @@
   if (layer->Size() != gfx::Size())
     json->SetArray("bounds", SizeAsJSONArray(IntSize(layer->Size())));
 
-  if (layer->Opacity() != 1)
-    json->SetDouble("opacity", layer->Opacity());
-
-  if (layer->GetBlendMode() != BlendMode::kNormal) {
-    json->SetString("blendMode", CompositeOperatorName(kCompositeSourceOver,
-                                                       layer->GetBlendMode()));
-  }
-
   if (layer->ContentsOpaque())
     json->SetBoolean("contentsOpaque", true);
 
diff --git a/third_party/blink/renderer/core/timing/performance.cc b/third_party/blink/renderer/core/timing/performance.cc
index 0dad3091..83c0564d 100644
--- a/third_party/blink/renderer/core/timing/performance.cc
+++ b/third_party/blink/renderer/core/timing/performance.cc
@@ -658,9 +658,7 @@
     GetUserTiming().AddMarkToPerformanceTimeline(*performance_mark);
     NotifyObserversOfEntry(*performance_mark);
   }
-  if (RuntimeEnabledFeatures::CustomUserTimingEnabled())
-    return performance_mark;
-  return nullptr;
+  return performance_mark;
 }
 
 void Performance::clearMarks(const AtomicString& mark_name) {
@@ -721,78 +719,57 @@
     base::Optional<String> end_mark,
     ExceptionState& exception_state) {
   DCHECK(!start_or_options.IsNull());
-  if (RuntimeEnabledFeatures::CustomUserTimingEnabled()) {
-    // An empty option is treated with no difference as null, undefined.
-    if (start_or_options.IsPerformanceMeasureOptions() &&
-        !IsMeasureOptionsEmpty(
-            *start_or_options.GetAsPerformanceMeasureOptions())) {
-      // measure("name", { start, end }, *)
-      if (end_mark) {
-        exception_state.ThrowTypeError(
-            "If a non-empty PerformanceMeasureOptions object was passed, "
-            "|end_mark| must not be passed.");
-        return nullptr;
-      }
-      const PerformanceMeasureOptions* options =
-          start_or_options.GetAsPerformanceMeasureOptions();
-      if (!options->hasStart() && !options->hasEnd()) {
-        exception_state.ThrowTypeError(
-            "If a non-empty PerformanceMeasureOptions object was passed, at "
-            "least one of its 'start' or 'end' properties must be present.");
-        return nullptr;
-      }
-
-      if (options->hasStart() && options->hasDuration() && options->hasEnd()) {
-        exception_state.ThrowTypeError(
-            "If a non-empty PerformanceMeasureOptions object was passed, it "
-            "must not have all of its 'start', 'duration', and 'end' "
-            "properties defined");
-        return nullptr;
-      }
-
-      base::Optional<double> duration = base::nullopt;
-      if (options->hasDuration()) {
-        duration = options->duration();
-      }
-
-      return MeasureWithDetail(script_state, measure_name, options->start(),
-                               std::move(duration), options->end(),
-                               options->detail(), exception_state);
+  // An empty option is treated with no difference as null, undefined.
+  if (start_or_options.IsPerformanceMeasureOptions() &&
+      !IsMeasureOptionsEmpty(
+          *start_or_options.GetAsPerformanceMeasureOptions())) {
+    // measure("name", { start, end }, *)
+    if (end_mark) {
+      exception_state.ThrowTypeError(
+          "If a non-empty PerformanceMeasureOptions object was passed, "
+          "|end_mark| must not be passed.");
+      return nullptr;
     }
-    // measure("name", "mark1", *)
-    StringOrDouble converted_start;
-    if (start_or_options.IsString()) {
-      converted_start =
-          StringOrDouble::FromString(start_or_options.GetAsString());
+    const PerformanceMeasureOptions* options =
+        start_or_options.GetAsPerformanceMeasureOptions();
+    if (!options->hasStart() && !options->hasEnd()) {
+      exception_state.ThrowTypeError(
+          "If a non-empty PerformanceMeasureOptions object was passed, at "
+          "least one of its 'start' or 'end' properties must be present.");
+      return nullptr;
     }
-    // We let |end_mark| behave the same whether it's empty, undefined or null
-    // in JS, as long as |end_mark| is null in C++.
-    return MeasureWithDetail(
-        script_state, measure_name, converted_start,
-        /* duration = */ base::nullopt,
-        end_mark ? StringOrDouble::FromString(*end_mark)
-                 : NativeValueTraits<StringOrDouble>::NullValue(),
-        ScriptValue::CreateNull(script_state->GetIsolate()), exception_state);
+
+    if (options->hasStart() && options->hasDuration() && options->hasEnd()) {
+      exception_state.ThrowTypeError(
+          "If a non-empty PerformanceMeasureOptions object was passed, it "
+          "must not have all of its 'start', 'duration', and 'end' "
+          "properties defined");
+      return nullptr;
+    }
+
+    base::Optional<double> duration = base::nullopt;
+    if (options->hasDuration()) {
+      duration = options->duration();
+    }
+
+    return MeasureWithDetail(script_state, measure_name, options->start(),
+                             std::move(duration), options->end(),
+                             options->detail(), exception_state);
   }
-  // For consistency with UserTimingL2: the L2 API took |start| as a string,
-  // so any object passed in became a string '[object, object]', null became
-  // string 'null'.
+  // measure("name", "mark1", *)
   StringOrDouble converted_start;
-  if (!start_or_options.IsPerformanceMeasureOptions()) {
-    // |start_or_options| is not nullable.
-    DCHECK(start_or_options.IsString());
+  if (start_or_options.IsString()) {
     converted_start =
         StringOrDouble::FromString(start_or_options.GetAsString());
   }
-
-  MeasureWithDetail(script_state, measure_name, converted_start,
-                    /* duration = */ base::nullopt,
-                    end_mark ? StringOrDouble::FromString(*end_mark)
-                             : NativeValueTraits<StringOrDouble>::NullValue(),
-                    ScriptValue::CreateNull(script_state->GetIsolate()),
-                    exception_state);
-  // Return nullptr to distinguish from L3.
-  return nullptr;
+  // We let |end_mark| behave the same whether it's empty, undefined or null
+  // in JS, as long as |end_mark| is null in C++.
+  return MeasureWithDetail(
+      script_state, measure_name, converted_start,
+      /* duration = */ base::nullopt,
+      end_mark ? StringOrDouble::FromString(*end_mark)
+               : NativeValueTraits<StringOrDouble>::NullValue(),
+      ScriptValue::CreateNull(script_state->GetIsolate()), exception_state);
 }
 
 PerformanceMeasure* Performance::MeasureWithDetail(
diff --git a/third_party/blink/renderer/core/timing/performance.idl b/third_party/blink/renderer/core/timing/performance.idl
index 239221b5..a093c44a4 100644
--- a/third_party/blink/renderer/core/timing/performance.idl
+++ b/third_party/blink/renderer/core/timing/performance.idl
@@ -58,22 +58,13 @@
 
     // User Timing
     // https://w3c.github.io/user-timing/#extensions-performance-interface
-    // We use the returned value for feature detection:
-    // * L2 API returns null but this is a bug: crbug.com/914441.
-    // * L3 API returns the created entry.
-    [MeasureAs=UserTiming, CallWith=ScriptState, RaisesException] PerformanceMark? mark(DOMString markName);
-    // TODO(maxlg): PerformanceMarkOptions will be changed to optional after L3 is default to be enabled.
-    [MeasureAs=UserTiming, CallWith=ScriptState, RuntimeEnabled=CustomUserTiming, RaisesException] PerformanceMark? mark(DOMString markName, PerformanceMarkOptions markOptions);
+    // TODO(https://crbug.com/996275): Once our IDL parser supports it, we need
+    // to supply a default-value of '{}' to markOptions.
+    [MeasureAs=UserTiming, CallWith=ScriptState, RaisesException] PerformanceMark mark(DOMString markName, optional PerformanceMarkOptions markOptions);
     [MeasureAs=UserTiming] void clearMarks(optional DOMString markName);
-
-    // Doing either of the following requires enabling CustomUserTiming:
-    // * passing PerformanceMeasureOptions to |startOrOptions|
-    // * passing timestamps to |startOrOptions| or |end|
-    // We use the returned value for feature detection:
-    // * L2 API returns null but this is a bug: crbug.com/914441.
-    // * L3 API returns the created entry.
-    // https://w3c.github.io/user-timing/#extensions-performance-interface
-    [MeasureAs=UserTiming, CallWith=ScriptState, RaisesException] PerformanceMeasure? measure(DOMString measureName, optional (DOMString or PerformanceMeasureOptions) startOrOptions, optional DOMString end);
+    // TODO(https://crbug.com/996275): Once our IDL parser supports it, we need
+    // to supply a default-value of '{}' to startOrOptions.
+    [MeasureAs=UserTiming, CallWith=ScriptState, RaisesException] PerformanceMeasure measure(DOMString measureName, optional (DOMString or PerformanceMeasureOptions) startOrOptions, optional DOMString end);
     [MeasureAs=UserTiming] void clearMeasures(optional DOMString measureName);
 
     // TODO(foolip): There is no spec for the Memory Info API, see blink-dev:
diff --git a/third_party/blink/renderer/core/timing/performance_mark.idl b/third_party/blink/renderer/core/timing/performance_mark.idl
index b5cd0ae3..b13216c 100644
--- a/third_party/blink/renderer/core/timing/performance_mark.idl
+++ b/third_party/blink/renderer/core/timing/performance_mark.idl
@@ -30,5 +30,5 @@
  ConstructorCallWith=ScriptState,
  RaisesException=Constructor]
 interface PerformanceMark : PerformanceEntry {
-    [CallWith=ScriptState, RuntimeEnabled=CustomUserTiming] readonly attribute any detail;
+    [CallWith=ScriptState] readonly attribute any detail;
 };
diff --git a/third_party/blink/renderer/core/timing/performance_measure.idl b/third_party/blink/renderer/core/timing/performance_measure.idl
index 2ee528c..51c94f8 100644
--- a/third_party/blink/renderer/core/timing/performance_measure.idl
+++ b/third_party/blink/renderer/core/timing/performance_measure.idl
@@ -27,5 +27,5 @@
 
 [Exposed=(Window,Worker)]
 interface PerformanceMeasure : PerformanceEntry {
-  [CallWith=ScriptState, RuntimeEnabled=CustomUserTiming] readonly attribute any detail;
+  [CallWith=ScriptState] readonly attribute any detail;
 };
diff --git a/third_party/blink/renderer/core/timing/performance_user_timing.cc b/third_party/blink/renderer/core/timing/performance_user_timing.cc
index c8fb44c..597cd10 100644
--- a/third_party/blink/renderer/core/timing/performance_user_timing.cc
+++ b/third_party/blink/renderer/core/timing/performance_user_timing.cc
@@ -116,7 +116,7 @@
   }
 
   ScriptValue detail = ScriptValue::CreateNull(script_state->GetIsolate());
-  if (RuntimeEnabledFeatures::CustomUserTimingEnabled() && mark_options)
+  if (mark_options)
     detail = mark_options->detail();
 
   bool is_worker_global_scope =
diff --git a/third_party/blink/renderer/devtools/BUILD.gn b/third_party/blink/renderer/devtools/BUILD.gn
index eb2516d..2003e7a 100644
--- a/third_party/blink/renderer/devtools/BUILD.gn
+++ b/third_party/blink/renderer/devtools/BUILD.gn
@@ -373,6 +373,16 @@
   "front_end/mobile_throttling/ThrottlingPresets.js",
   "front_end/mobile_throttling/throttlingSettingsTab.css",
   "front_end/mobile_throttling/ThrottlingSettingsTab.js",
+  "front_end/media/eventDisplayTable.css",
+  "front_end/media/EventDisplayTable.js",
+  "front_end/media/MainView.js",
+  "front_end/media/MediaTable.js",
+  "front_end/media/PlayerDetailView.js",
+  "front_end/media/playerListView.css",
+  "front_end/media/PlayerListView.js",
+  "front_end/media/module.json",
+  "front_end/media/MediaModel.js",
+  "front_end/media/mediaView.css",
   "front_end/ndb_app.json",
   "front_end/network/binaryResourceView.css",
   "front_end/network/blockedURLsPane.css",
@@ -1416,6 +1426,7 @@
   "$resources_out_dir/js_profiler/js_profiler_module.js",
   "$resources_out_dir/layer_viewer/layer_viewer_module.js",
   "$resources_out_dir/layers/layers_module.js",
+  "$resources_out_dir/media/media_module.js",
   "$resources_out_dir/network/network_module.js",
   "$resources_out_dir/node_debugger/node_debugger_module.js",
   "$resources_out_dir/object_ui/object_ui_module.js",
diff --git a/third_party/blink/renderer/devtools/front_end/Images/smallIcons.svg b/third_party/blink/renderer/devtools/front_end/Images/smallIcons.svg
index 1ac8139..45cb0a90 100644
--- a/third_party/blink/renderer/devtools/front_end/Images/smallIcons.svg
+++ b/third_party/blink/renderer/devtools/front_end/Images/smallIcons.svg
@@ -1 +1 @@
-<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="110" height="110"><defs><linearGradient id="y" x1="113" x2="127" y1="104" y2="104" gradientTransform="translate(-.714 -69.286) scale(.71429)" gradientUnits="userSpaceOnUse" xlink:href="#a"/><linearGradient id="a"><stop stop-color="#606eda" offset="0"/><stop stop-color="#021db2" offset="1"/></linearGradient><linearGradient id="A" x2="24" gradientTransform="matrix(0 -.41667 -.41667 0 25 10)" gradientUnits="userSpaceOnUse" xlink:href="#b"/><linearGradient id="b"><stop stop-color="#d7687d" offset="0"/><stop stop-color="#b21402" offset="1"/></linearGradient></defs><path d="M0 20.995c0-.55.456-.995.995-.995h8.01c.55 0 .995.455.995.995v8.01c0 .549-.456.995-.995.995H.995C.445 30 0 29.544 0 29.004zm5.123 4.744C7.691 25.312 8.75 24.546 8.75 22h-1.5c0 1.62-.44 1.939-2.373 2.26-2.568.429-3.627 1.194-3.627 3.74h1.5c0-1.62.44-1.937 2.373-2.26zM23.65 27.21l-1.44-2.03L21 26.39 23.55 30 30 22.33 28.88 21z"/><path d="M6.5 46c0 .55.45 1 1 1s1-.45 1-1-.45-1-1-1-1 .45-1 1" fill="#bababa"/><path d="M5.75 42.75L2.25 46l3.5 3.25" fill="none" stroke="#bababa" stroke-width="1.5"/><path d="M27.5 42.43l-.93-.93L24 44.07l-2.57-2.57-.93.93L23.07 45l-2.57 2.57.93.93L24 45.93l2.57 2.57.93-.93L24.93 45l2.57-2.57z" fill-opacity=".24"/><path d="M27.5 41.93l-.93-.93L24 43.57 21.43 41l-.93.93 2.57 2.57-2.57 2.57.93.93L24 45.43 26.57 48l.93-.93-2.57-2.57 2.57-2.57z" fill="#676767"/><path d="M43.25 20c-.7 0-1.25.5-1.25 1.25v7.5c0 .7.5 1.25 1.25 1.25h3.5c.7 0 1.25-.5 1.25-1.25v-7.5c0-.7-.5-1.25-1.25-1.25zM43 21h4v7h-4zm2 7.25c.4 0 .75.3.75.75 0 .4-.3.75-.75.75-.4 0-.75-.3-.75-.75 0-.4.3-.75.75-.75z"/><g transform="translate(40 40)"><path transform="translate(-20)" d="M25 0c-2.76 0-5 2.24-5 5s2.24 5 5 5 5-2.24 5-5-2.24-5-5-5" fill="url(#c)"/><path d="M.36 5C.36 7.56 2.44 9.64 5 9.64c2.56 0 4.64-2.08 4.64-4.64C9.64 2.44 7.56.36 5 .36 2.44.36.36 2.44.36 5" fill="#eb3941"/><path d="M3 3l4 4M7 3L3 7" stroke="#fff"/><defs><linearGradient id="d"><stop stop-color="#d7687d" offset="0"/><stop stop-color="#b21402" offset="1"/></linearGradient><linearGradient id="c" x2="24" gradientTransform="matrix(0 -.41667 -.41667 0 25 10)" gradientUnits="userSpaceOnUse" xlink:href="#d"/></defs></g><path d="M4.5 61.3l4 3.7-4 3.7v-1.2H1.425l.075-5h3z" fill="#adf2ad" stroke="#007200"/><g transform="translate(20 60)"><path transform="translate(-140)" d="M144.95 10A5.002 5.002 0 0 1 140 4.95 5.002 5.002 0 0 1 145.05 0c2.76.03 4.98 2.29 4.95 5.05a5.002 5.002 0 0 1-5.05 4.95z" fill="url(#e)"/><path d="M9.5 5.05A4.494 4.494 0 0 1 4.95 9.5 4.494 4.494 0 0 1 .5 4.95 4.494 4.494 0 0 1 5.05.5C7.54.53 9.53 2.56 9.5 5.05z" fill="#00be00"/><path transform="translate(-140)" d="M145.08.53c1.97.02 3.55 1.06 3.54 2.32-.01 1.26-1.62 2.26-3.59 2.24-1.97-.02-3.55-1.06-3.54-2.32.01-1.26 1.62-2.26 3.59-2.24z" fill="url(#f)"/><path transform="translate(-140)" d="M144.98 9.41c1.66.02 3.01-.68 3.02-1.56.01-.88-1.33-1.61-2.98-1.63-1.66-.02-3.01.68-3.02 1.56-.01.88 1.33 1.61 2.98 1.63z" fill="url(#g)"/><defs><linearGradient id="h"><stop stop-color="#00d600" stop-opacity="0" offset="0"/><stop stop-color="#d8fc7b" stop-opacity=".81" offset="1"/></linearGradient><linearGradient id="i"><stop stop-color="#00ba00" offset="0"/><stop stop-color="#fff" stop-opacity=".91" offset="1"/></linearGradient><linearGradient id="j"><stop stop-color="#00a104" offset="0"/><stop stop-color="#00c605" offset="1"/></linearGradient><linearGradient id="g" x1="227.88" x2="235.12" y1="103.16" y2="103.16" gradientTransform="matrix(-.0048 .4396 .78038 .00853 65.608 -94.834)" gradientUnits="userSpaceOnUse" xlink:href="#h"/><linearGradient id="f" x1="227.88" x2="235.12" y1="103.16" y2="103.16" gradientTransform="matrix(.00687 -.62923 .9267 .01012 47.871 147.44)" gradientUnits="userSpaceOnUse" xlink:href="#i"/><linearGradient id="e" x1="227.88" x2="235.12" y1="103.16" y2="103.16" gradientTransform="matrix(-.01507 1.3791 -1.3006 -.0142 282.66 -312.8)" gradientUnits="userSpaceOnUse" xlink:href="#j"/></defs></g><g transform="translate(40 60)"><path transform="translate(-80)" d="M85 0c-2.76 0-5 2.24-5 5s2.24 5 5 5 5-2.24 5-5-2.24-5-5-5" fill="url(#k)"/><path d="M.36 5C.36 7.56 2.44 9.64 5 9.64c2.56 0 4.64-2.08 4.64-4.64C9.64 2.44 7.56.36 5 .36 2.44.36.36 2.44.36 5" fill="#2a53cd"/><path d="M3.93 2.14c-.03-.53.55-.97 1.06-.83.5.12.79.73.56 1.18-.2.44-.79.61-1.2.36a.812.812 0 0 1-.42-.71zm1.7 5.46h.67v.53H3.41V7.6h.66V3.99h-.66v-.53h2.22V7.6z" fill="#fff"/><defs><linearGradient id="l"><stop stop-color="#606eda" offset="0"/><stop stop-color="#021db2" offset="1"/></linearGradient><linearGradient id="k" x1="113" x2="127" y1="104" y2="104" gradientTransform="translate(-.714 -69.286) scale(.71429)" gradientUnits="userSpaceOnUse" xlink:href="#l"/></defs></g><path d="M60.45 20.467v9.079h5.373l3.582-4.54-3.582-4.539z" fill="#698cfe" stroke="#4073f4" stroke-width=".908"/><path d="M60.45 40.467v9.08h5.372l3.581-4.54-3.581-4.54z" fill="#ef9d0d" stroke="#a36c01" stroke-width=".908"/><g transform="translate(60 60)"><path d="M5 10c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5z" fill="#e5a600"/><path d="M9.5 5c0 2.49-2.01 4.5-4.5 4.5S.5 7.49.5 5 2.51.5 5 .5 9.5 2.51 9.5 5z" fill="#ffbd00"/><path transform="translate(-160)" d="M165.03.53c1.97 0 3.56 1.02 3.56 2.28 0 1.26-1.59 2.28-3.56 2.28s-3.56-1.02-3.56-2.28c0-1.26 1.59-2.28 3.56-2.28z" fill="url(#m)"/><path transform="translate(-160)" d="M164.99 9.42c1.66 0 3-.71 3-1.59 0-.88-1.34-1.59-3-1.59s-3 .71-3 1.59c0 .88 1.34 1.59 3 1.59z" fill="url(#n)"/><defs><linearGradient id="o"><stop stop-color="#ffa801" stop-opacity="0" offset="0"/><stop stop-color="#f0fb3d" offset="1"/></linearGradient><linearGradient id="p"><stop stop-color="#ffbd00" stop-opacity=".65" offset="0"/><stop stop-color="#fff" stop-opacity=".91" offset="1"/></linearGradient><linearGradient id="n" x1="227.88" x2="235.12" y1="103.16" y2="103.16" gradientTransform="matrix(0 .43966 .78049 0 84.444 -93.924)" gradientUnits="userSpaceOnUse" xlink:href="#o"/><linearGradient id="m" x1="227.88" x2="235.12" y1="103.16" y2="103.16" gradientTransform="matrix(0 -.62931 .92683 0 69.47 148.53)" gradientUnits="userSpaceOnUse" xlink:href="#p"/></defs></g><g transform="translate(0 80)"><path transform="translate(-120)" d="M125 10c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5z" fill="url(#q)"/><path d="M9.5 5c0 2.49-2.01 4.5-4.5 4.5S.5 7.49.5 5 2.51.5 5 .5 9.5 2.51 9.5 5z" fill="#d00"/><path transform="translate(-120)" d="M125.03.53c1.97 0 3.56 1.02 3.56 2.28 0 1.26-1.59 2.28-3.56 2.28s-3.56-1.02-3.56-2.28c0-1.26 1.59-2.28 3.56-2.28z" fill="url(#r)"/><path transform="translate(-120)" d="M125.03 9.47c1.66 0 3-.71 3-1.59 0-.88-1.34-1.59-3-1.59s-3 .71-3 1.59c0 .88 1.34 1.59 3 1.59z" fill="url(#s)"/><defs><linearGradient id="t"><stop stop-color="red" stop-opacity="0" offset="0"/><stop stop-color="#f0cb68" stop-opacity=".71" offset="1"/></linearGradient><linearGradient id="u"><stop stop-color="#e60000" stop-opacity=".65" offset="0"/><stop stop-color="#fff" stop-opacity=".91" offset="1"/></linearGradient><linearGradient id="v"><stop stop-color="#a10000" offset="0"/><stop stop-color="#c60000" offset="1"/></linearGradient><linearGradient id="s" x1="227.88" x2="235.12" y1="103.16" y2="103.16" gradientTransform="matrix(0 .43966 .78049 0 44.488 -93.88)" gradientUnits="userSpaceOnUse" xlink:href="#t"/><linearGradient id="r" x1="227.88" x2="235.12" y1="103.16" y2="103.16" gradientTransform="matrix(0 -.62931 .92683 0 29.47 148.53)" gradientUnits="userSpaceOnUse" xlink:href="#u"/><linearGradient id="q" x1="227.88" x2="235.12" y1="103.16" y2="103.16" gradientTransform="matrix(0 1.3793 -1.3008 0 259.08 -314.35)" gradientUnits="userSpaceOnUse" xlink:href="#v"/></defs></g><path d="M20 80h10v10H20z" fill="none"/><path d="M27.5 82.5V80H20v7.5h2.5V90H30v-7.5zM21 81h5.5v5.5H21zm2.5 6.5h4v-4H29V89h-5.5z"/><path d="M23.5 87.5h4v-4H29V89h-5.5z" fill-opacity=".25"/><g fill="#acf2ae" stroke="#007200" stroke-width="2.577"><path transform="matrix(.29356 0 0 .2909 2.65 86.864)" d="M144.95 10A5.002 5.002 0 0 1 140 4.95 5.002 5.002 0 0 1 145.05 0c2.76.03 4.98 2.29 4.95 5.05a5.002 5.002 0 0 1-5.05 4.95z"/><path transform="matrix(.29356 0 0 .2909 2.65 86.864)" d="M149.5 5.05a4.494 4.494 0 0 1-4.55 4.45 4.494 4.494 0 0 1-4.45-4.55A4.494 4.494 0 0 1 145.05.5c2.49.03 4.48 2.06 4.45 4.55z"/></g><path d="M48.313 82.5L45.21 86l-3.102-3.5h1.24v-1.92h3.723v1.92z" fill="#adf2ad" stroke="#007200"/><path d="M66.537 88.514a1.314 1.314 0 0 1-1.335 1.295 1.313 1.313 0 0 1-1.306-1.324 1.314 1.314 0 0 1 1.335-1.294c.731.009 1.315.6 1.307 1.323z" fill="#acf2ae" stroke="#007200" stroke-width=".753"/><path d="M62.108 83.5l3.102-2.982 3.102 2.982h-1.24v2h-3.723v-2z" fill="#adf2ad" stroke="#007200"/><path d="M83.25 21.75l3.5 3.25-3.5 3.25" fill="none" stroke="#367cf1" stroke-width="1.5"/><path d="M86 40l-5 5 5 5v-2h3v-6h-3zM84 60l5 5-5 5.022v-2.045L81 68v-6h3z" fill="#4688f1"/><path d="M84.5 88l3.5-6h-7M7 104.5L2 101v7M24 100l4 7h-8"/><path d="M43.25 102.75l3.5 3.25-3.5 3.25" fill="none" stroke="#939393" stroke-width="1.5"/><path d="M61 109l4-8 4 8z" stroke="#c19600" stroke-width="2" stroke-linejoin="round"/><path d="M61 109l4-8 4 8z" fill="#f4bd00" stroke="#f5bd00" stroke-width="1.5" stroke-linejoin="round"/><path d="M63.75 102.75h2.5v2.5l-.5 1.75h-1.5l-.5-1.75v-2.5m0 5.25h2.5v1.25h-2.5" fill="#ad8601"/><path d="M64 103h2v2.25l-.5 1.75h-1l-.5-1.75V103m0 5h2v1h-2" fill="#fff"/><text style="line-height:0%" x="3.051" y="118.387" font-weight="400" font-family="sans-serif" letter-spacing="0" word-spacing="0" fill="#ccc"><tspan x="3.051" y="118.387" style="line-height:1.25" font-size="8">a</tspan></text><text style="line-height:0%" x="23.051" y="118.387" font-weight="400" font-family="sans-serif" letter-spacing="0" word-spacing="0" fill="#ccc"><tspan x="23.051" y="118.387" style="line-height:1.25" font-size="8">b</tspan></text><text style="line-height:0%" x="43.051" y="118.387" font-weight="400" font-family="sans-serif" letter-spacing="0" word-spacing="0" fill="#ccc"><tspan x="43.051" y="118.387" style="line-height:1.25" font-size="8">c</tspan></text><text style="line-height:0%" x="63.051" y="118.387" font-weight="400" font-family="sans-serif" letter-spacing="0" word-spacing="0" fill="#ccc"><tspan x="63.051" y="118.387" style="line-height:1.25" font-size="8">d</tspan></text><text style="line-height:0%" x="83.051" y="118.387" font-weight="400" font-family="sans-serif" letter-spacing="0" word-spacing="0" fill="#ccc"><tspan x="83.051" y="118.387" style="line-height:1.25" font-size="8">e</tspan></text><text style="line-height:0%" x="-7.026" y="107.818" font-weight="400" font-family="sans-serif" letter-spacing="0" word-spacing="0" fill="#ccc"><tspan x="-7.026" y="107.818" style="line-height:1.25" font-size="8">1</tspan></text><text style="line-height:0%" x="-6.819" y="87.88" font-weight="400" font-family="sans-serif" letter-spacing="0" word-spacing="0" fill="#ccc"><tspan x="-6.819" y="87.88" style="line-height:1.25" font-size="8">2</tspan></text><text style="line-height:0%" x="-6.756" y="67.992" font-weight="400" font-family="sans-serif" letter-spacing="0" word-spacing="0" fill="#ccc"><tspan x="-6.756" y="67.992" style="line-height:1.25" font-size="8">3</tspan></text><text style="line-height:0%" x="-6.917" y="47.994" font-weight="400" font-family="sans-serif" letter-spacing="0" word-spacing="0" fill="#ccc"><tspan x="-6.917" y="47.994" style="line-height:1.25" font-size="8">4</tspan></text><text style="line-height:0%" x="-7.108" y="28.056" font-weight="400" font-family="sans-serif" letter-spacing="0" word-spacing="0" fill="#ccc"><tspan x="-7.108" y="28.056" style="line-height:1.25" font-size="8">5</tspan></text><circle cx="3" cy="3" r="2.5" transform="matrix(1.13208 0 0 1.13208 81.604 102.604)" fill="#00bcd4" stroke="#000" stroke-width=".3"/><path d="M5 0a5 5 0 1 0 0 10A5 5 0 0 0 5 0zm.048.721c1.083 0 2.179.4 3.005 1.226a4.234 4.234 0 0 1 0 5.986L5.048 4.952l-2.98-3.005A4.18 4.18 0 0 1 5.047.721z"/><text style="line-height:0%" x="-6.68" y="8" font-weight="400" font-family="sans-serif" letter-spacing="0" word-spacing="0" fill="#ccc"><tspan x="-6.68" y="8" style="line-height:1.25" font-size="8">6</tspan></text><path d="M17.601-2.913h16.232v16.232H17.601z" fill="none"/><path d="M21.11 5.556L20 6.667 23.334 10 30 3.334l-1.11-1.111-5.556 5.555z" fill-rule="evenodd"/><path d="M44.954 8.42a3.467 3.467 0 0 1-3.462-3.46c0-.678.2-1.309.531-1.84l4.77 4.77c-.531.338-1.162.53-1.84.53m3.462-3.46c0 .676-.2 1.307-.53 1.838l-4.77-4.77a3.405 3.405 0 0 1 1.839-.53 3.467 3.467 0 0 1 3.461 3.461M45 .005a5 5 0 1 0 0 10 5 5 0 0 0 0-10"/><path d="M68.875 2.219L63.344 7.78l-.407-.406L61.844 8.5l1.5 1.5L70 3.344l-1.125-1.125z" fill-rule="evenodd"/><text style="line-height:0%" x="103.739" y="118.482" font-weight="400" font-family="sans-serif" letter-spacing="0" word-spacing="0" fill="#ccc"><tspan x="103.739" y="118.482" style="line-height:1.25" font-size="8">f</tspan></text><g transform="translate(100 100)" mask="url(#w)"><path d="M1 9l4-8 4 8z" stroke="#c19600" stroke-width="2" stroke-linejoin="round"/><path d="M1 9l4-8 4 8z" fill="#f4bd00" stroke="#f5bd00" stroke-width="1.5" stroke-linejoin="round"/><path d="M3.75 2.75h2.5v2.5L5.75 7h-1.5l-.5-1.75v-2.5m0 5.25h2.5v1.25h-2.5" fill="#ad8601"/><mask id="w"><path fill="#fff" d="M0 0h10v10H0z"/><path transform="translate(-60)" d="M64 3h2v2.25L65.5 7h-1L64 5.25V3m0 5h2v1h-2"/></mask></g><g transform="translate(100 80)" mask="url(#x)"><path transform="translate(-80)" d="M85 0c-2.76 0-5 2.24-5 5s2.24 5 5 5 5-2.24 5-5-2.24-5-5-5" fill="url(#y)"/><path d="M.36 5C.36 7.56 2.44 9.64 5 9.64c2.56 0 4.64-2.08 4.64-4.64C9.64 2.44 7.56.36 5 .36 2.44.36.36 2.44.36 5" fill="#2a53cd"/><mask id="x"><path fill="#fff" d="M0 0h10v10H0z"/><path transform="translate(-80)" d="M83.93 2.14c-.03-.53.55-.97 1.06-.83.5.12.79.73.56 1.18-.2.44-.79.61-1.2.36a.812.812 0 0 1-.42-.71zm1.7 5.46h.67v.53h-2.89V7.6h.66V3.99h-.66v-.53h2.22V7.6z"/></mask></g><g transform="translate(100 60)" mask="url(#z)"><path transform="translate(-20)" d="M25 0c-2.76 0-5 2.24-5 5s2.24 5 5 5 5-2.24 5-5-2.24-5-5-5" fill="url(#A)"/><path d="M.36 5C.36 7.56 2.44 9.64 5 9.64c2.56 0 4.64-2.08 4.64-4.64C9.64 2.44 7.56.36 5 .36 2.44.36.36 2.44.36 5" fill="#eb3941"/><mask id="z"><path fill="#fff" d="M0 0h10v10H0z"/><path transform="translate(-20)" d="M23 3l4 4M27 3l-4 4" stroke="#000"/></mask></g><g><path d="M105.1 40.267a4.835 4.835 0 0 0-4.833 4.833 4.835 4.835 0 0 0 4.833 4.833 4.835 4.835 0 0 0 4.833-4.833 4.835 4.835 0 0 0-4.833-4.833zm0 1.45c.802 0 1.45.647 1.45 1.45 0 .802-.648 1.45-1.45 1.45-.802 0-1.45-.648-1.45-1.45 0-.803.648-1.45 1.45-1.45zm0 6.863a3.48 3.48 0 0 1-2.9-1.556c.014-.962 1.933-1.489 2.9-1.489.962 0 2.885.527 2.9 1.489a3.48 3.48 0 0 1-2.9 1.556z"/><path d="M99.3 39.3h11.6v11.6H99.3z" fill="none"/></g><path d="M105 22.528l-4 4.43.94 1.042 3.06-3.382L108.06 28l.94-1.041z"/><path d="M76.448 14.99h17.528v19.416H76.448z" fill="none"/><path d="M88.06 3.06L85 6.113 81.94 3.06 81 4l4 4 4-4z"/></svg>
\ No newline at end of file
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="130" height="110"><defs><linearGradient id="y" x1="113" x2="127" y1="104" y2="104" gradientTransform="translate(-.714 -69.286) scale(.71429)" gradientUnits="userSpaceOnUse" xlink:href="#a"/><linearGradient id="a"><stop stop-color="#606eda" offset="0"/><stop stop-color="#021db2" offset="1"/></linearGradient><linearGradient id="A" x2="24" gradientTransform="matrix(0 -.41667 -.41667 0 25 10)" gradientUnits="userSpaceOnUse" xlink:href="#b"/><linearGradient id="b"><stop stop-color="#d7687d" offset="0"/><stop stop-color="#b21402" offset="1"/></linearGradient></defs><path d="M0 20.995c0-.55.456-.995.995-.995h8.01c.55 0 .995.455.995.995v8.01c0 .549-.456.995-.995.995H.995C.445 30 0 29.544 0 29.004zm5.123 4.744C7.691 25.312 8.75 24.546 8.75 22h-1.5c0 1.62-.44 1.939-2.373 2.26-2.568.429-3.627 1.194-3.627 3.74h1.5c0-1.62.44-1.937 2.373-2.26zM23.65 27.21l-1.44-2.03L21 26.39 23.55 30 30 22.33 28.88 21z"/><path d="M6.5 46c0 .55.45 1 1 1s1-.45 1-1-.45-1-1-1-1 .45-1 1" fill="#bababa"/><path d="M5.75 42.75L2.25 46l3.5 3.25" fill="none" stroke="#bababa" stroke-width="1.5"/><path d="M27.5 42.43l-.93-.93L24 44.07l-2.57-2.57-.93.93L23.07 45l-2.57 2.57.93.93L24 45.93l2.57 2.57.93-.93L24.93 45l2.57-2.57z" fill-opacity=".24"/><path d="M27.5 41.93l-.93-.93L24 43.57 21.43 41l-.93.93 2.57 2.57-2.57 2.57.93.93L24 45.43 26.57 48l.93-.93-2.57-2.57 2.57-2.57z" fill="#676767"/><path d="M43.25 20c-.7 0-1.25.5-1.25 1.25v7.5c0 .7.5 1.25 1.25 1.25h3.5c.7 0 1.25-.5 1.25-1.25v-7.5c0-.7-.5-1.25-1.25-1.25zM43 21h4v7h-4zm2 7.25c.4 0 .75.3.75.75 0 .4-.3.75-.75.75-.4 0-.75-.3-.75-.75 0-.4.3-.75.75-.75z"/><g transform="translate(40 40)"><path transform="translate(-20)" d="M25 0c-2.76 0-5 2.24-5 5s2.24 5 5 5 5-2.24 5-5-2.24-5-5-5" fill="url(#c)"/><path d="M.36 5C.36 7.56 2.44 9.64 5 9.64c2.56 0 4.64-2.08 4.64-4.64C9.64 2.44 7.56.36 5 .36 2.44.36.36 2.44.36 5" fill="#eb3941"/><path d="M3 3l4 4M7 3L3 7" stroke="#fff"/><defs><linearGradient id="d"><stop stop-color="#d7687d" offset="0"/><stop stop-color="#b21402" offset="1"/></linearGradient><linearGradient id="c" x2="24" gradientTransform="matrix(0 -.41667 -.41667 0 25 10)" gradientUnits="userSpaceOnUse" xlink:href="#d"/></defs></g><path d="M4.5 61.3l4 3.7-4 3.7v-1.2H1.425l.075-5h3z" fill="#adf2ad" stroke="#007200"/><g transform="translate(20 60)"><path transform="translate(-140)" d="M144.95 10A5.002 5.002 0 01140 4.95 5.002 5.002 0 01145.05 0c2.76.03 4.98 2.29 4.95 5.05a5.002 5.002 0 01-5.05 4.95z" fill="url(#e)"/><path d="M9.5 5.05A4.494 4.494 0 014.95 9.5 4.494 4.494 0 01.5 4.95 4.494 4.494 0 015.05.5C7.54.53 9.53 2.56 9.5 5.05z" fill="#00be00"/><path transform="translate(-140)" d="M145.08.53c1.97.02 3.55 1.06 3.54 2.32-.01 1.26-1.62 2.26-3.59 2.24-1.97-.02-3.55-1.06-3.54-2.32.01-1.26 1.62-2.26 3.59-2.24z" fill="url(#f)"/><path transform="translate(-140)" d="M144.98 9.41c1.66.02 3.01-.68 3.02-1.56.01-.88-1.33-1.61-2.98-1.63-1.66-.02-3.01.68-3.02 1.56-.01.88 1.33 1.61 2.98 1.63z" fill="url(#g)"/><defs><linearGradient id="h"><stop stop-color="#00d600" stop-opacity="0" offset="0"/><stop stop-color="#d8fc7b" stop-opacity=".81" offset="1"/></linearGradient><linearGradient id="i"><stop stop-color="#00ba00" offset="0"/><stop stop-color="#fff" stop-opacity=".91" offset="1"/></linearGradient><linearGradient id="j"><stop stop-color="#00a104" offset="0"/><stop stop-color="#00c605" offset="1"/></linearGradient><linearGradient id="g" x1="227.88" x2="235.12" y1="103.16" y2="103.16" gradientTransform="matrix(-.0048 .4396 .78038 .00853 65.608 -94.834)" gradientUnits="userSpaceOnUse" xlink:href="#h"/><linearGradient id="f" x1="227.88" x2="235.12" y1="103.16" y2="103.16" gradientTransform="matrix(.00687 -.62923 .9267 .01012 47.871 147.44)" gradientUnits="userSpaceOnUse" xlink:href="#i"/><linearGradient id="e" x1="227.88" x2="235.12" y1="103.16" y2="103.16" gradientTransform="matrix(-.01507 1.3791 -1.3006 -.0142 282.66 -312.8)" gradientUnits="userSpaceOnUse" xlink:href="#j"/></defs></g><g transform="translate(40 60)"><path transform="translate(-80)" d="M85 0c-2.76 0-5 2.24-5 5s2.24 5 5 5 5-2.24 5-5-2.24-5-5-5" fill="url(#k)"/><path d="M.36 5C.36 7.56 2.44 9.64 5 9.64c2.56 0 4.64-2.08 4.64-4.64C9.64 2.44 7.56.36 5 .36 2.44.36.36 2.44.36 5" fill="#2a53cd"/><path d="M3.93 2.14c-.03-.53.55-.97 1.06-.83.5.12.79.73.56 1.18-.2.44-.79.61-1.2.36a.812.812 0 01-.42-.71zm1.7 5.46h.67v.53H3.41V7.6h.66V3.99h-.66v-.53h2.22V7.6z" fill="#fff"/><defs><linearGradient id="l"><stop stop-color="#606eda" offset="0"/><stop stop-color="#021db2" offset="1"/></linearGradient><linearGradient id="k" x1="113" x2="127" y1="104" y2="104" gradientTransform="translate(-.714 -69.286) scale(.71429)" gradientUnits="userSpaceOnUse" xlink:href="#l"/></defs></g><path d="M60.45 20.467v9.079h5.373l3.582-4.54-3.582-4.539z" fill="#698cfe" stroke="#4073f4" stroke-width=".908"/><path d="M60.45 40.467v9.08h5.372l3.581-4.54-3.581-4.54z" fill="#ef9d0d" stroke="#a36c01" stroke-width=".908"/><g transform="translate(60 60)"><path d="M5 10c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5z" fill="#e5a600"/><path d="M9.5 5c0 2.49-2.01 4.5-4.5 4.5S.5 7.49.5 5 2.51.5 5 .5 9.5 2.51 9.5 5z" fill="#ffbd00"/><path transform="translate(-160)" d="M165.03.53c1.97 0 3.56 1.02 3.56 2.28 0 1.26-1.59 2.28-3.56 2.28s-3.56-1.02-3.56-2.28c0-1.26 1.59-2.28 3.56-2.28z" fill="url(#m)"/><path transform="translate(-160)" d="M164.99 9.42c1.66 0 3-.71 3-1.59 0-.88-1.34-1.59-3-1.59s-3 .71-3 1.59c0 .88 1.34 1.59 3 1.59z" fill="url(#n)"/><defs><linearGradient id="o"><stop stop-color="#ffa801" stop-opacity="0" offset="0"/><stop stop-color="#f0fb3d" offset="1"/></linearGradient><linearGradient id="p"><stop stop-color="#ffbd00" stop-opacity=".65" offset="0"/><stop stop-color="#fff" stop-opacity=".91" offset="1"/></linearGradient><linearGradient id="n" x1="227.88" x2="235.12" y1="103.16" y2="103.16" gradientTransform="matrix(0 .43966 .78049 0 84.444 -93.924)" gradientUnits="userSpaceOnUse" xlink:href="#o"/><linearGradient id="m" x1="227.88" x2="235.12" y1="103.16" y2="103.16" gradientTransform="matrix(0 -.62931 .92683 0 69.47 148.53)" gradientUnits="userSpaceOnUse" xlink:href="#p"/></defs></g><g transform="translate(0 80)"><path transform="translate(-120)" d="M125 10c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5z" fill="url(#q)"/><path d="M9.5 5c0 2.49-2.01 4.5-4.5 4.5S.5 7.49.5 5 2.51.5 5 .5 9.5 2.51 9.5 5z" fill="#d00"/><path transform="translate(-120)" d="M125.03.53c1.97 0 3.56 1.02 3.56 2.28 0 1.26-1.59 2.28-3.56 2.28s-3.56-1.02-3.56-2.28c0-1.26 1.59-2.28 3.56-2.28z" fill="url(#r)"/><path transform="translate(-120)" d="M125.03 9.47c1.66 0 3-.71 3-1.59 0-.88-1.34-1.59-3-1.59s-3 .71-3 1.59c0 .88 1.34 1.59 3 1.59z" fill="url(#s)"/><defs><linearGradient id="t"><stop stop-color="red" stop-opacity="0" offset="0"/><stop stop-color="#f0cb68" stop-opacity=".71" offset="1"/></linearGradient><linearGradient id="u"><stop stop-color="#e60000" stop-opacity=".65" offset="0"/><stop stop-color="#fff" stop-opacity=".91" offset="1"/></linearGradient><linearGradient id="v"><stop stop-color="#a10000" offset="0"/><stop stop-color="#c60000" offset="1"/></linearGradient><linearGradient id="s" x1="227.88" x2="235.12" y1="103.16" y2="103.16" gradientTransform="matrix(0 .43966 .78049 0 44.488 -93.88)" gradientUnits="userSpaceOnUse" xlink:href="#t"/><linearGradient id="r" x1="227.88" x2="235.12" y1="103.16" y2="103.16" gradientTransform="matrix(0 -.62931 .92683 0 29.47 148.53)" gradientUnits="userSpaceOnUse" xlink:href="#u"/><linearGradient id="q" x1="227.88" x2="235.12" y1="103.16" y2="103.16" gradientTransform="matrix(0 1.3793 -1.3008 0 259.08 -314.35)" gradientUnits="userSpaceOnUse" xlink:href="#v"/></defs></g><path d="M20 80h10v10H20z" fill="none"/><path d="M27.5 82.5V80H20v7.5h2.5V90H30v-7.5zM21 81h5.5v5.5H21zm2.5 6.5h4v-4H29V89h-5.5z"/><path d="M23.5 87.5h4v-4H29V89h-5.5z" fill-opacity=".25"/><g fill="#acf2ae" stroke="#007200" stroke-width="2.577"><path transform="matrix(.29356 0 0 .2909 2.65 86.864)" d="M144.95 10A5.002 5.002 0 01140 4.95 5.002 5.002 0 01145.05 0c2.76.03 4.98 2.29 4.95 5.05a5.002 5.002 0 01-5.05 4.95z"/><path transform="matrix(.29356 0 0 .2909 2.65 86.864)" d="M149.5 5.05a4.494 4.494 0 01-4.55 4.45 4.494 4.494 0 01-4.45-4.55A4.494 4.494 0 01145.05.5c2.49.03 4.48 2.06 4.45 4.55z"/></g><path d="M48.313 82.5L45.21 86l-3.102-3.5h1.24v-1.92h3.723v1.92z" fill="#adf2ad" stroke="#007200"/><path d="M66.537 88.514a1.314 1.314 0 01-1.335 1.295 1.313 1.313 0 01-1.306-1.324 1.314 1.314 0 011.335-1.294c.731.009 1.315.6 1.307 1.323z" fill="#acf2ae" stroke="#007200" stroke-width=".753"/><path d="M62.108 83.5l3.102-2.982 3.102 2.982h-1.24v2h-3.723v-2z" fill="#adf2ad" stroke="#007200"/><path d="M83.25 21.75l3.5 3.25-3.5 3.25" fill="none" stroke="#367cf1" stroke-width="1.5"/><path d="M86 40l-5 5 5 5v-2h3v-6h-3zM84 60l5 5-5 5.022v-2.045L81 68v-6h3z" fill="#4688f1"/><path d="M84.5 88l3.5-6h-7M7 104.5L2 101v7M24 100l4 7h-8"/><path d="M43.25 102.75l3.5 3.25-3.5 3.25" fill="none" stroke="#939393" stroke-width="1.5"/><path d="M61 109l4-8 4 8z" stroke="#c19600" stroke-width="2" stroke-linejoin="round"/><path d="M61 109l4-8 4 8z" fill="#f4bd00" stroke="#f5bd00" stroke-width="1.5" stroke-linejoin="round"/><path d="M63.75 102.75h2.5v2.5l-.5 1.75h-1.5l-.5-1.75v-2.5m0 5.25h2.5v1.25h-2.5" fill="#ad8601"/><path d="M64 103h2v2.25l-.5 1.75h-1l-.5-1.75V103m0 5h2v1h-2" fill="#fff"/><text style="line-height:0%" x="3.051" y="118.387" font-weight="400" font-family="sans-serif" letter-spacing="0" word-spacing="0" fill="#ccc"><tspan x="3.051" y="118.387" style="line-height:1.25" font-size="8">a</tspan></text><text style="line-height:0%" x="23.051" y="118.387" font-weight="400" font-family="sans-serif" letter-spacing="0" word-spacing="0" fill="#ccc"><tspan x="23.051" y="118.387" style="line-height:1.25" font-size="8">b</tspan></text><text style="line-height:0%" x="43.051" y="118.387" font-weight="400" font-family="sans-serif" letter-spacing="0" word-spacing="0" fill="#ccc"><tspan x="43.051" y="118.387" style="line-height:1.25" font-size="8">c</tspan></text><text style="line-height:0%" x="63.051" y="118.387" font-weight="400" font-family="sans-serif" letter-spacing="0" word-spacing="0" fill="#ccc"><tspan x="63.051" y="118.387" style="line-height:1.25" font-size="8">d</tspan></text><text style="line-height:0%" x="83.051" y="118.387" font-weight="400" font-family="sans-serif" letter-spacing="0" word-spacing="0" fill="#ccc"><tspan x="83.051" y="118.387" style="line-height:1.25" font-size="8">e</tspan></text><text style="line-height:0%" x="-7.026" y="107.818" font-weight="400" font-family="sans-serif" letter-spacing="0" word-spacing="0" fill="#ccc"><tspan x="-7.026" y="107.818" style="line-height:1.25" font-size="8">1</tspan></text><text style="line-height:0%" x="-6.819" y="87.88" font-weight="400" font-family="sans-serif" letter-spacing="0" word-spacing="0" fill="#ccc"><tspan x="-6.819" y="87.88" style="line-height:1.25" font-size="8">2</tspan></text><text style="line-height:0%" x="-6.756" y="67.992" font-weight="400" font-family="sans-serif" letter-spacing="0" word-spacing="0" fill="#ccc"><tspan x="-6.756" y="67.992" style="line-height:1.25" font-size="8">3</tspan></text><text style="line-height:0%" x="-6.917" y="47.994" font-weight="400" font-family="sans-serif" letter-spacing="0" word-spacing="0" fill="#ccc"><tspan x="-6.917" y="47.994" style="line-height:1.25" font-size="8">4</tspan></text><text style="line-height:0%" x="-7.108" y="28.056" font-weight="400" font-family="sans-serif" letter-spacing="0" word-spacing="0" fill="#ccc"><tspan x="-7.108" y="28.056" style="line-height:1.25" font-size="8">5</tspan></text><circle cx="3" cy="3" r="2.5" transform="matrix(1.13208 0 0 1.13208 81.604 102.604)" fill="#00bcd4" stroke="#000" stroke-width=".3"/><path d="M5 0a5 5 0 100 10A5 5 0 005 0zm.048.721c1.083 0 2.179.4 3.005 1.226a4.234 4.234 0 010 5.986L5.048 4.952l-2.98-3.005A4.18 4.18 0 015.047.721z"/><text style="line-height:0%" x="-6.68" y="8" font-weight="400" font-family="sans-serif" letter-spacing="0" word-spacing="0" fill="#ccc"><tspan x="-6.68" y="8" style="line-height:1.25" font-size="8">6</tspan></text><path d="M17.601-2.913h16.232v16.232H17.601z" fill="none"/><path d="M21.11 5.556L20 6.667 23.334 10 30 3.334l-1.11-1.111-5.556 5.555z" fill-rule="evenodd"/><path d="M44.954 8.42a3.467 3.467 0 01-3.462-3.46c0-.678.2-1.309.531-1.84l4.77 4.77c-.531.338-1.162.53-1.84.53m3.462-3.46c0 .676-.2 1.307-.53 1.838l-4.77-4.77a3.405 3.405 0 011.839-.53 3.467 3.467 0 013.461 3.461M45 .005a5 5 0 100 10 5 5 0 000-10"/><path d="M68.875 2.219L63.344 7.78l-.407-.406L61.844 8.5l1.5 1.5L70 3.344l-1.125-1.125z" fill-rule="evenodd"/><text style="line-height:0%" x="103.739" y="118.482" font-weight="400" font-family="sans-serif" letter-spacing="0" word-spacing="0" fill="#ccc"><tspan x="103.739" y="118.482" style="line-height:1.25" font-size="8">f</tspan></text><g transform="translate(100 100)" mask="url(#w)"><path d="M1 9l4-8 4 8z" stroke="#c19600" stroke-width="2" stroke-linejoin="round"/><path d="M1 9l4-8 4 8z" fill="#f4bd00" stroke="#f5bd00" stroke-width="1.5" stroke-linejoin="round"/><path d="M3.75 2.75h2.5v2.5L5.75 7h-1.5l-.5-1.75v-2.5m0 5.25h2.5v1.25h-2.5" fill="#ad8601"/><mask id="w"><path fill="#fff" d="M0 0h10v10H0z"/><path transform="translate(-60)" d="M64 3h2v2.25L65.5 7h-1L64 5.25V3m0 5h2v1h-2"/></mask></g><g transform="translate(100 80)" mask="url(#x)"><path transform="translate(-80)" d="M85 0c-2.76 0-5 2.24-5 5s2.24 5 5 5 5-2.24 5-5-2.24-5-5-5" fill="url(#y)"/><path d="M.36 5C.36 7.56 2.44 9.64 5 9.64c2.56 0 4.64-2.08 4.64-4.64C9.64 2.44 7.56.36 5 .36 2.44.36.36 2.44.36 5" fill="#2a53cd"/><mask id="x"><path fill="#fff" d="M0 0h10v10H0z"/><path transform="translate(-80)" d="M83.93 2.14c-.03-.53.55-.97 1.06-.83.5.12.79.73.56 1.18-.2.44-.79.61-1.2.36a.812.812 0 01-.42-.71zm1.7 5.46h.67v.53h-2.89V7.6h.66V3.99h-.66v-.53h2.22V7.6z"/></mask></g><g transform="translate(100 60)" mask="url(#z)"><path transform="translate(-20)" d="M25 0c-2.76 0-5 2.24-5 5s2.24 5 5 5 5-2.24 5-5-2.24-5-5-5" fill="url(#A)"/><path d="M.36 5C.36 7.56 2.44 9.64 5 9.64c2.56 0 4.64-2.08 4.64-4.64C9.64 2.44 7.56.36 5 .36 2.44.36.36 2.44.36 5" fill="#eb3941"/><mask id="z"><path fill="#fff" d="M0 0h10v10H0z"/><path transform="translate(-20)" d="M23 3l4 4M27 3l-4 4" stroke="#000"/></mask></g><g><path d="M105.1 40.267a4.835 4.835 0 00-4.833 4.833 4.835 4.835 0 004.833 4.833 4.835 4.835 0 004.833-4.833 4.835 4.835 0 00-4.833-4.833zm0 1.45c.802 0 1.45.647 1.45 1.45 0 .802-.648 1.45-1.45 1.45-.802 0-1.45-.648-1.45-1.45 0-.803.648-1.45 1.45-1.45zm0 6.863a3.48 3.48 0 01-2.9-1.556c.014-.962 1.933-1.489 2.9-1.489.962 0 2.885.527 2.9 1.489a3.48 3.48 0 01-2.9 1.556z"/><path d="M99.3 39.3h11.6v11.6H99.3z" fill="none"/></g><path d="M105 22.528l-4 4.43.94 1.042 3.06-3.382L108.06 28l.94-1.041z"/><path d="M76.448 14.99h17.528v19.416H76.448z" fill="none"/><path d="M88.06 3.06L85 6.113 81.94 3.06 81 4l4 4 4-4zM102 2h2v6h-2z"/><a><path d="M106 2h2v6h-2z"/></a><text style="line-height:0%" x="122.66" y="117.219" font-weight="400" font-family="sans-serif" letter-spacing="0" word-spacing="0" fill="#ccc"><tspan x="122.66" y="117.219" style="line-height:1.25" font-size="8">g</tspan></text><path d="M128 5l-6 3V2zM128 26.5l-1.5 1.5-1.5-1.5-1.5 1.5-1.5-1.5 1.5-1.5-1.5-1.5 1.5-1.5 1.5 1.5 1.5-1.5 1.5 1.5-1.5 1.5z"/></svg>
\ No newline at end of file
diff --git a/third_party/blink/renderer/devtools/front_end/Images/src/optimize_svg.hashes b/third_party/blink/renderer/devtools/front_end/Images/src/optimize_svg.hashes
index 8504ee28..2fc263c 100644
--- a/third_party/blink/renderer/devtools/front_end/Images/src/optimize_svg.hashes
+++ b/third_party/blink/renderer/devtools/front_end/Images/src/optimize_svg.hashes
@@ -1,13 +1,13 @@
 {
     "securityIcons.svg": "27676f7c1f1542659c7c49a8052259dc",
+    "accelerometer-back.svg": "342973eb940ef43b409b28c2c6b0d520",
     "largeIcons.svg": "faf26930e93e7525a3cbcc595527662c",
-    "breakpoint.svg": "69cd92d807259c022791112809b97799",
     "breakpointConditional.svg": "4cf90210b2af2ed84db2f60b07bcde28",
     "checkboxCheckmark.svg": "f039bf85cee42ad5c30ca3bfdce7912a",
     "errorWave.svg": "e183fa242a22ed4784a92f6becbc2c45",
-    "smallIcons.svg": "19940dda6f171380bfd7d04d0061b44c",
+    "smallIcons.svg": "ed10eae550f101ce8d1cc9e26dd8a33d",
     "mediumIcons.svg": "9cb32f670ba43a7ab424eab281043e6b",
-    "accelerometer-back.svg": "342973eb940ef43b409b28c2c6b0d520",
+    "breakpoint.svg": "69cd92d807259c022791112809b97799",
     "treeoutlineTriangles.svg": "2d26ab85d919f83d5021f2f385dffd0b",
     "chevrons.svg": "79b4b527771e30b6388ce664077b3409",
     "audits_logo.svg": "3a4893bd2ef5bb233e924f15e51af69a",
diff --git a/third_party/blink/renderer/devtools/front_end/Images/src/smallIcons.svg b/third_party/blink/renderer/devtools/front_end/Images/src/smallIcons.svg
index ab48f666..3a8a9ef7 100644
--- a/third_party/blink/renderer/devtools/front_end/Images/src/smallIcons.svg
+++ b/third_party/blink/renderer/devtools/front_end/Images/src/smallIcons.svg
@@ -8,11 +8,11 @@
    xmlns:xlink="http://www.w3.org/1999/xlink"
    xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
    xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
-   width="110"
+   width="130"
    height="110"
    id="svg4185"
    version="1.1"
-   inkscape:version="0.92.2pre0 (973e216, 2017-07-25)"
+   inkscape:version="0.92.4 (5da689c313, 2019-01-14)"
    sodipodi:docname="smallIcons.svg"
    inkscape:export-filename="/Users/pfeldman/code/chromium/src/third_party/WebKit/Source/devtools/front_end/Images/smallIcons_2x.png"
    inkscape:export-xdpi="180"
@@ -78,16 +78,16 @@
      guidetolerance="10"
      inkscape:pageopacity="0"
      inkscape:pageshadow="2"
-     inkscape:window-width="1272"
-     inkscape:window-height="1006"
+     inkscape:window-width="1432"
+     inkscape:window-height="1419"
      id="namedview4455"
      showgrid="true"
-     inkscape:zoom="7.4167646"
-     inkscape:cx="72.154899"
-     inkscape:cy="32.871453"
-     inkscape:window-x="768"
-     inkscape:window-y="0"
-     inkscape:window-maximized="0"
+     inkscape:zoom="5.2444445"
+     inkscape:cx="94.607116"
+     inkscape:cy="82.226632"
+     inkscape:window-x="4002"
+     inkscape:window-y="1063"
+     inkscape:window-maximized="1"
      inkscape:current-layer="svg4185">
     <inkscape:grid
        type="xygrid"
@@ -1046,4 +1046,61 @@
      inkscape:connector-curvature="0"
      d="M 88.06,3.059999 85,6.113332 81.94,3.059999 l -0.94,0.94 4,4 4,-4 z"
      id="path3974" />
+  <path
+     style="stroke-width:0.02383474"
+     d=""
+     id="path5923"
+     inkscape:connector-curvature="0" />
+  <path
+     style="stroke-width:0.02383474"
+     d=""
+     id="path5925"
+     inkscape:connector-curvature="0" />
+  <path
+     style="stroke-width:0.02383474"
+     d=""
+     id="path5927"
+     inkscape:connector-curvature="0" />
+  <rect
+     id="rect5943"
+     width="2"
+     height="6"
+     x="102"
+     y="1.9999998"
+     style="stroke-width:1.13202608" />
+  <a
+     id="a6015">
+    <rect
+       style="stroke-width:1.13202608"
+       y="2"
+       x="106"
+       height="6"
+       width="2"
+       id="rect5943-5" />
+  </a>
+  <text
+     xml:space="preserve"
+     style="font-style:normal;font-weight:normal;line-height:0%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#cccccc;fill-opacity:1;stroke:none"
+     x="122.66023"
+     y="117.2188"
+     id="text5191-77-3-3"><tspan
+       sodipodi:role="line"
+       id="tspan5193-6-6-5"
+       x="122.66023"
+       y="117.2188"
+       style="font-size:8px;line-height:1.25;font-family:sans-serif">g</tspan></text>
+  <path
+     inkscape:transform-center-x="0.029808665"
+     inkscape:transform-center-y="-0.33844643"
+     d="m 128,5 -6,3 V 2 Z"
+     id="path6489"
+     inkscape:connector-curvature="0"
+     sodipodi:nodetypes="cccc" />
+  <path
+     inkscape:transform-center-x="0.34573841"
+     inkscape:transform-center-y="-0.78316773"
+     d="M 128,26.5 126.5,28 125,26.5 123.5,28 122,26.5 123.5,25 122,23.5 l 1.5,-1.5 1.5,1.5 1.5,-1.5 1.5,1.5 -1.5,1.5 z"
+     id="path6495"
+     inkscape:connector-curvature="0"
+     sodipodi:nodetypes="ccccccccccccc" />
 </svg>
diff --git a/third_party/blink/renderer/devtools/front_end/color_picker/Spectrum.js b/third_party/blink/renderer/devtools/front_end/color_picker/Spectrum.js
index 7bdd505..18f0729 100644
--- a/third_party/blink/renderer/devtools/front_end/color_picker/Spectrum.js
+++ b/third_party/blink/renderer/devtools/front_end/color_picker/Spectrum.js
@@ -76,7 +76,11 @@
 
     const displaySwitcher = toolsContainer.createChild('div', 'spectrum-display-switcher spectrum-switcher');
     appendSwitcherIcon(displaySwitcher);
-    displaySwitcher.addEventListener('click', this._formatViewSwitch.bind(this));
+    displaySwitcher.tabIndex = 0;
+    self.onInvokeElement(displaySwitcher, event => {
+      this._formatViewSwitch();
+      event.consume(true);
+    });
 
     // RGBA/HSLA display.
     this._displayContainer = toolsContainer.createChild('div', 'spectrum-text source-code');
@@ -1125,13 +1129,16 @@
     this._swatchInnerElement = swatchElement.createChild('span', 'swatch-inner');
 
     this._swatchOverlayElement = swatchElement.createChild('span', 'swatch-overlay');
+    UI.ARIAUtils.markAsButton(this._swatchOverlayElement);
+    UI.ARIAUtils.setPressed(this._swatchOverlayElement, false);
     this._swatchOverlayElement.tabIndex = 0;
     self.onInvokeElement(this._swatchOverlayElement, this._onCopyText.bind(this));
     this._swatchOverlayElement.addEventListener('mouseout', this._onCopyIconMouseout.bind(this));
     this._swatchOverlayElement.addEventListener('blur', this._onCopyIconMouseout.bind(this));
     this._swatchCopyIcon = UI.Icon.create('largeicon-copy', 'copy-color-icon');
-    this._swatchCopyIcon.title = Common.UIString('Copy color to clipboard');
+    this._swatchCopyIcon.title = ls`Copy color to clipboard`;
     this._swatchOverlayElement.appendChild(this._swatchCopyIcon);
+    UI.ARIAUtils.setAccessibleName(this._swatchOverlayElement, this._swatchCopyIcon.title);
   }
 
   /**
@@ -1157,10 +1164,12 @@
   _onCopyText(event) {
     this._swatchCopyIcon.setIconType('largeicon-checkmark');
     Host.InspectorFrontendHost.copyText(this._colorString);
+    UI.ARIAUtils.setPressed(this._swatchOverlayElement, true);
     event.consume();
   }
 
   _onCopyIconMouseout() {
     this._swatchCopyIcon.setIconType('largeicon-copy');
+    UI.ARIAUtils.setPressed(this._swatchOverlayElement, false);
   }
 };
diff --git a/third_party/blink/renderer/devtools/front_end/devtools_app.json b/third_party/blink/renderer/devtools/front_end/devtools_app.json
index 54a1b6f..a4039e3 100644
--- a/third_party/blink/renderer/devtools/front_end/devtools_app.json
+++ b/third_party/blink/renderer/devtools/front_end/devtools_app.json
@@ -25,7 +25,8 @@
     { "name": "security" },
     { "name": "timeline" },
     { "name": "timeline_model" },
-    { "name": "web_audio" }
+    { "name": "web_audio" },
+    { "name": "media" }
   ],
   "extends": "shell",
   "has_html": true
diff --git a/third_party/blink/renderer/devtools/front_end/elements/elements_strings.grdp b/third_party/blink/renderer/devtools/front_end/elements/elements_strings.grdp
index 8360b3a6..7ea3753 100644
--- a/third_party/blink/renderer/devtools/front_end/elements/elements_strings.grdp
+++ b/third_party/blink/renderer/devtools/front_end/elements/elements_strings.grdp
@@ -189,9 +189,6 @@
   <message name="IDS_DEVTOOLS_9a0364b9e99bb480dd25e1f0284c8555" desc="Text in Metrics Sidebar Pane of the Elements panel">
     content
   </message>
-  <message name="IDS_DEVTOOLS_9fc2d28c05ed9eb1d75ba4465abf15a9" desc="Title of the 'Properties' tool in the sidebar of the elements tool">
-    Properties
-  </message>
   <message name="IDS_DEVTOOLS_a05fb8660778069f6d4a5c5b40a6dbc9" desc="Title of a setting under the Elements category in Settings">
     Show user agent shadow DOM
   </message>
diff --git a/third_party/blink/renderer/devtools/front_end/langpacks/devtools_ui_strings.grd b/third_party/blink/renderer/devtools/front_end/langpacks/devtools_ui_strings.grd
index 175477a..e6cb21bc 100644
--- a/third_party/blink/renderer/devtools/front_end/langpacks/devtools_ui_strings.grd
+++ b/third_party/blink/renderer/devtools/front_end/langpacks/devtools_ui_strings.grd
@@ -41,6 +41,7 @@
       <part file="../layer_viewer/layer_viewer_strings.grdp" />
       <part file="../layers/layers_strings.grdp" />
       <part file="../main/main_strings.grdp" />
+      <part file="../media/media_strings.grdp" />
       <part file="../mobile_throttling/mobile_throttling_strings.grdp" />
       <part file="../network/network_strings.grdp" />
       <part file="../node_debugger/node_debugger_strings.grdp" />
diff --git a/third_party/blink/renderer/devtools/front_end/langpacks/shared_strings.grdp b/third_party/blink/renderer/devtools/front_end/langpacks/shared_strings.grdp
index 10f74b3..aaca2e29 100644
--- a/third_party/blink/renderer/devtools/front_end/langpacks/shared_strings.grdp
+++ b/third_party/blink/renderer/devtools/front_end/langpacks/shared_strings.grdp
@@ -382,6 +382,9 @@
   <message name="IDS_DEVTOOLS_9f29da220ed82809ec5dd70af4e52904" desc="Text for the total time of something">
     Total Time
   </message>
+  <message name="IDS_DEVTOOLS_9fc2d28c05ed9eb1d75ba4465abf15a9" desc="Title of the 'Properties' tool in the sidebar of the elements tool">
+    Properties
+  </message>
   <message name="IDS_DEVTOOLS_a02c83a7dbd96295beaefb72c2bee2de" desc="Text that refers to the main target">
     Main
   </message>
diff --git a/third_party/blink/renderer/devtools/front_end/main/Main.js b/third_party/blink/renderer/devtools/front_end/main/Main.js
index a047e50..e7a5ec1b 100644
--- a/third_party/blink/renderer/devtools/front_end/main/Main.js
+++ b/third_party/blink/renderer/devtools/front_end/main/Main.js
@@ -129,6 +129,7 @@
     Root.Runtime.experiments.register('emptySourceMapAutoStepping', 'Empty sourcemap auto-stepping');
     Root.Runtime.experiments.register('inputEventsOnTimelineOverview', 'Input events on Timeline overview', true);
     Root.Runtime.experiments.register('liveHeapProfile', 'Live heap profile', true);
+    Root.Runtime.experiments.register('mediaInspector', 'Media Element Inspection');
     Root.Runtime.experiments.register('nativeHeapProfiler', 'Native memory sampling heap profiler', true);
     Root.Runtime.experiments.register('protocolMonitor', 'Protocol Monitor');
     Root.Runtime.experiments.register(
diff --git a/third_party/blink/renderer/devtools/front_end/media/EventDisplayTable.js b/third_party/blink/renderer/devtools/front_end/media/EventDisplayTable.js
new file mode 100644
index 0000000..746e919
--- /dev/null
+++ b/third_party/blink/renderer/devtools/front_end/media/EventDisplayTable.js
@@ -0,0 +1,154 @@
+// 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.
+
+/**
+ * @typedef {{
+ *     id: string,
+ *     title: string,
+ *     sortable: boolean,
+ *     weight: (number|undefined),
+ *     sortingFunction: (!function(!Media.EventNode, !Media.EventNode):number|undefined),
+ * }}
+ */
+Media.EventDisplayColumnConfig;
+
+/**
+ * @typedef {{
+ *     name: string,
+ *     value: string,
+ *     timestamp: (number|string|undefined)
+ * }}
+ */
+Media.Event;
+
+/**
+ * @unrestricted
+ */
+Media.EventNode = class extends DataGrid.SortableDataGridNode {
+  /**
+   * @param {!Media.Event} event
+   */
+  constructor(event) {
+    super(event, false);
+  }
+
+  /**
+   * @override
+   * @param {string} columnId
+   * @return {!Element}
+   */
+  createCell(columnId) {
+    const cell = this.createTD(columnId);
+    const cellData = /** @type string */ (this.data[columnId]);
+    cell.createTextChild(cellData);
+    return cell;
+  }
+
+  /**
+   * @override
+   * @return {number}
+   */
+  nodeSelfHeight() {
+    return 20;
+  }
+};
+
+/**
+ * @unrestricted
+ */
+Media.EventDisplayTable = class extends UI.VBox {
+  /**
+   * @param {!Array.<!Media.EventDisplayColumnConfig>} headerDescriptors
+   * @param {?string=} uniqueColumn
+   * @param {?string=} defaultSortingColumnId
+   */
+  constructor(headerDescriptors, uniqueColumn, defaultSortingColumnId) {
+    super();
+
+    // Set up element styles.
+    this.registerRequiredCSS('media/eventDisplayTable.css');
+    this.contentElement.classList.add('event-display-table-contents-table-container');
+
+    this._uniqueColumnEntryKey = uniqueColumn;
+    this._uniqueColumnMap = new Map();
+
+    this._dataGrid = this._createDataGrid(headerDescriptors, defaultSortingColumnId);
+    this._dataGrid.setStriped(true);
+    this._dataGrid.asWidget().show(this.contentElement);
+  }
+
+  /**
+   * @param {!Array.<!Media.EventDisplayColumnConfig>} headers
+   * @param {?string|undefined} default_sort
+   * @return !DataGrid.SortableDataGrid
+   */
+  _createDataGrid(headers, default_sort) {
+    const gridColumnDescs = [];
+    const sortFunctionMap = new Map();
+    for (const headerDesc of headers) {
+      gridColumnDescs.push(Media.EventDisplayTable._convertToGridDescriptor(headerDesc));
+      if (headerDesc.sortable) {
+        sortFunctionMap.set(headerDesc.id, headerDesc.sortingFunction);
+        if (!default_sort) {
+          default_sort = headerDesc.id;
+        }
+      }
+    }
+
+    const datagrid = new DataGrid.SortableDataGrid(gridColumnDescs);
+    if (default_sort) {
+      datagrid.sortNodes(sortFunctionMap.get(default_sort), !datagrid.isSortOrderAscending());
+
+      function sortGrid() {
+        const comparator = sortFunctionMap.get(datagrid.sortColumnId());
+        datagrid.sortNodes(comparator, !datagrid.isSortOrderAscending());
+      }
+
+      datagrid.addEventListener(DataGrid.DataGrid.Events.SortingChanged, sortGrid);
+    }
+    datagrid.asWidget().contentElement.classList.add('no-border-top-datagrid');
+    return datagrid;
+  }
+
+  /**
+   * @param {!Array.<!Media.Event>} events
+   */
+  addEvents(events) {
+    for (const event of events) {
+      this.addEvent(event);
+    }
+  }
+
+  /**
+   * @param {!Media.Event} event
+   */
+  addEvent(event) {
+    if (this._uniqueColumnEntryKey) {
+      const eventValue = event[this._uniqueColumnEntryKey];
+      if (this._uniqueColumnMap.has(eventValue)) {
+        this._uniqueColumnMap.get(eventValue).data = event;
+        return;
+      }
+    }
+    const node = new Media.EventNode(event);
+    this._dataGrid.rootNode().insertChildOrdered(node);
+    if (this._uniqueColumnEntryKey) {
+      this._uniqueColumnMap.set(event[this._uniqueColumnEntryKey], node);
+    }
+  }
+
+  /**
+   * @param {!Media.EventDisplayColumnConfig} columnConfig
+   * @return {!DataGrid.DataGrid.ColumnDescriptor}
+   */
+  static _convertToGridDescriptor(columnConfig) {
+    return /** @type {!DataGrid.DataGrid.ColumnDescriptor} */ ({
+      id: columnConfig.id,
+      title: columnConfig.title,
+      sortable: columnConfig.sortable,
+      weight: columnConfig.weight || 0,
+      sort: DataGrid.DataGrid.Order.Ascending
+    });
+  }
+};
\ No newline at end of file
diff --git a/third_party/blink/renderer/devtools/front_end/media/MainView.js b/third_party/blink/renderer/devtools/front_end/media/MainView.js
new file mode 100644
index 0000000..2ae8ef7
--- /dev/null
+++ b/third_party/blink/renderer/devtools/front_end/media/MainView.js
@@ -0,0 +1,141 @@
+// 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.
+
+/**
+ * @implements {SDK.SDKModelObserver<!Media.MediaModel>}
+ */
+Media.MainView = class extends UI.PanelWithSidebar {
+  constructor() {
+    super('Media');
+    this.registerRequiredCSS('media/mediaView.css');
+
+    // Map<Media.PlayerDetailView>
+    this._detailPanels = new Map();
+
+    // Map<string>
+    this._deletedPlayers = new Set();
+
+    this._sidebar = new Media.PlayerListView(this);
+    this._sidebar.show(this.panelSidebarElement());
+
+    SDK.targetManager.observeModels(Media.MediaModel, this);
+  }
+
+  /**
+   * @param {string} playerID
+   * @param {!Array.<!Media.Event>} changes
+   * @param {!Media.MediaModel.MediaChangeTypeKeys} changeType
+   */
+  renderChanges(playerID, changes, changeType) {
+    if (this._deletedPlayers.has(playerID)) {
+      return;
+    }
+
+    if (!this._detailPanels.has(playerID)) {
+      return;
+    }
+
+    this._sidebar.renderChanges(playerID, changes, changeType);
+    this._detailPanels.get(playerID).renderChanges(playerID, changes, changeType);
+  }
+
+  /**
+   * @param {string} playerID
+   */
+  renderMainPanel(playerID) {
+    if (!this._detailPanels.has(playerID)) {
+      return;
+    }
+    this.splitWidget().mainWidget().detachChildWidgets();
+    this._detailPanels.get(playerID).show(this.mainElement());
+  }
+
+  /**
+   * @param {string} playerID
+   */
+  _onPlayerCreated(playerID) {
+    this._sidebar.addMediaElementItem(playerID);
+    this._detailPanels.set(playerID, new Media.PlayerDetailView());
+  }
+
+  /**
+   * @override
+   */
+  wasShown() {
+    super.wasShown();
+    for (const model of SDK.targetManager.models(Media.MediaModel)) {
+      this._addEventListeners(model);
+    }
+  }
+
+  /**
+   * @override
+   */
+  willHide() {
+    for (const model of SDK.targetManager.models(Media.MediaModel)) {
+      this._removeEventListeners(model);
+    }
+  }
+
+  /**
+   * @override
+   * @param {!Media.MediaModel} mediaModel
+   */
+  modelAdded(mediaModel) {
+    if (this.isShowing()) {
+      this._addEventListeners(mediaModel);
+    }
+  }
+
+  /**
+   * @override
+   * @param {!Media.MediaModel} mediaModel
+   */
+  modelRemoved(mediaModel) {
+    this._removeEventListeners(mediaModel);
+  }
+
+  /**
+   * @param {!Media.MediaModel} mediaModel
+   */
+  _addEventListeners(mediaModel) {
+    mediaModel.ensureEnabled();
+    mediaModel.addEventListener(Media.MediaModel.Events.PlayerPropertiesChanged, this._propertiesChanged, this);
+    mediaModel.addEventListener(Media.MediaModel.Events.PlayerEventsAdded, this._eventsAdded, this);
+    mediaModel.addEventListener(Media.MediaModel.Events.PlayersCreated, this._playersCreated, this);
+  }
+
+  /**
+   * @param {!Media.MediaModel} mediaModel
+   */
+  _removeEventListeners(mediaModel) {
+    mediaModel.removeEventListener(Media.MediaModel.Events.PlayerPropertiesChanged, this._propertiesChanged, this);
+    mediaModel.removeEventListener(Media.MediaModel.Events.PlayerEventsAdded, this._eventsAdded, this);
+    mediaModel.removeEventListener(Media.MediaModel.Events.PlayersCreated, this._playersCreated, this);
+  }
+
+  /**
+   * @param {!Common.Event} event
+   */
+  _propertiesChanged(event) {
+    this.renderChanges(event.data.playerId, event.data.properties, Media.MediaModel.MediaChangeTypeKeys.Property);
+  }
+
+  /**
+   * @param {!Common.Event} event
+   */
+  _eventsAdded(event) {
+    this.renderChanges(event.data.playerId, event.data.events, Media.MediaModel.MediaChangeTypeKeys.Event);
+  }
+
+  /**
+   * @param {!Common.Event} event
+   */
+  _playersCreated(event) {
+    const playerlist = /** @type {!Iterable.<string>} */ (event.data);
+    for (const playerID of playerlist) {
+      this._onPlayerCreated(playerID);
+    }
+  }
+};
diff --git a/third_party/blink/renderer/devtools/front_end/media/MediaModel.js b/third_party/blink/renderer/devtools/front_end/media/MediaModel.js
new file mode 100644
index 0000000..8e31201
--- /dev/null
+++ b/third_party/blink/renderer/devtools/front_end/media/MediaModel.js
@@ -0,0 +1,78 @@
+// 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.
+
+/**
+ * @implements {Protocol.MediaDispatcher}
+ */
+Media.MediaModel = class extends SDK.SDKModel {
+  /**
+   * @param {!SDK.Target} target
+   */
+  constructor(target) {
+    super(target);
+
+    this._enabled = false;
+    this._agent = target.mediaAgent();
+
+    target.registerMediaDispatcher(this);
+  }
+
+  /**
+   * @override
+   * @return {!Promise}
+   */
+  resumeModel() {
+    if (!this._enabled) {
+      return Promise.resolve();
+    }
+    return this._agent.enable();
+  }
+
+  ensureEnabled() {
+    this._agent.enable();
+    this._enabled = true;
+  }
+
+  /**
+   * @param {!Protocol.Media.PlayerId} playerId
+   * @param {!Array.<!Protocol.Media.PlayerProperty>} properties
+   * @override
+   */
+  playerPropertiesChanged(playerId, properties) {
+    this.dispatchEventToListeners(
+        Media.MediaModel.Events.PlayerPropertiesChanged, {playerId: playerId, properties: properties});
+  }
+
+  /**
+   * @param {!Protocol.Media.PlayerId} playerId
+   * @param {!Array.<!Protocol.Media.PlayerEvent>} events
+   * @override
+   */
+  playerEventsAdded(playerId, events) {
+    this.dispatchEventToListeners(Media.MediaModel.Events.PlayerEventsAdded, {playerId: playerId, events: events});
+  }
+
+  /**
+   * @param {!Array.<!Protocol.Media.PlayerId>} playerIds
+   * @override
+   */
+  playersCreated(playerIds) {
+    this.dispatchEventToListeners(Media.MediaModel.Events.PlayersCreated, playerIds);
+  }
+};
+
+SDK.SDKModel.register(Media.MediaModel, SDK.Target.Capability.DOM, false);
+
+/** @enum {symbol} */
+Media.MediaModel.Events = {
+  PlayerPropertiesChanged: Symbol('PlayerPropertiesChanged'),
+  PlayerEventsAdded: Symbol('PlayerEventsAdded'),
+  PlayersCreated: Symbol('PlayersCreated')
+};
+
+/** @enum {string} */
+Media.MediaModel.MediaChangeTypeKeys = {
+  Event: 'Events',
+  Property: 'Properties'
+};
diff --git a/third_party/blink/renderer/devtools/front_end/media/MediaTable.js b/third_party/blink/renderer/devtools/front_end/media/MediaTable.js
new file mode 100644
index 0000000..d972506
--- /dev/null
+++ b/third_party/blink/renderer/devtools/front_end/media/MediaTable.js
@@ -0,0 +1,75 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @unrestricted
+ */
+Media.MediaPlayerPropertiesRenderer = class extends Media.EventDisplayTable {
+  constructor() {
+    super(
+        [
+          {
+            id: 'name',
+            title: 'Property Name',
+            sortable: true,
+            weight: 2,
+            sortingFunction: DataGrid.SortableDataGrid.StringComparator.bind(null, 'name')
+          },
+          {id: 'value', title: 'Value', sortable: false, weight: 7}
+        ],
+        'name');
+  }
+
+  /**
+   * @param {string} playerID
+   * @param {!Array.<!Media.Event>} changes
+   * @param {!Media.MediaModel.MediaChangeTypeKeys} change_type
+   */
+  renderChanges(playerID, changes, change_type) {
+    this.addEvents(changes);
+  }
+};
+
+/**
+ * @unrestricted
+ */
+Media.MediaPlayerEventTableRenderer = class extends Media.EventDisplayTable {
+  constructor() {
+    super([
+      {
+        id: 'timestamp',
+        title: 'Timestamp',
+        weight: 1,
+        sortable: true,
+        sortingFunction: DataGrid.SortableDataGrid.NumericComparator.bind(null, 'timestamp')
+      },
+      {id: 'name', title: 'Event Name', weight: 2, sortable: false},
+      {id: 'value', title: 'Value', weight: 7, sortable: false}
+    ]);
+
+    this._firstEventTime = 0;
+  }
+
+  /**
+   * @param {string} playerID
+   * @param {!Array.<!Media.Event>} changes
+   * @param {!Media.MediaModel.MediaChangeTypeKeys} change_type
+   */
+  renderChanges(playerID, changes, change_type) {
+    if (this._firstEventTime === 0 && changes.length > 0) {
+      this._firstEventTime = changes[0].timestamp;
+    }
+
+    this.addEvents(changes.map(this._subtractFirstEventTime.bind(this, this._firstEventTime)));
+  }
+
+  /**
+   * @param {number|string|undefined} first_event_time
+   * @param {!Media.Event} event
+   */
+  _subtractFirstEventTime(first_event_time, event) {
+    event.timestamp = (event.timestamp - first_event_time).toFixed(3);
+    return event;
+  }
+};
\ No newline at end of file
diff --git a/third_party/blink/renderer/devtools/front_end/media/PlayerDetailView.js b/third_party/blink/renderer/devtools/front_end/media/PlayerDetailView.js
new file mode 100644
index 0000000..78af535
--- /dev/null
+++ b/third_party/blink/renderer/devtools/front_end/media/PlayerDetailView.js
@@ -0,0 +1,47 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @unrestricted
+ */
+Media.PlayerDetailView = class extends UI.TabbedPane {
+  constructor() {
+    super();
+
+    const propertyTable = new Media.MediaPlayerPropertiesRenderer();
+    const eventTable = new Media.MediaPlayerEventTableRenderer();
+
+    // maps handler type to a list of panels that support rendering changes.
+    this._panels = new Map([
+      [Media.MediaModel.MediaChangeTypeKeys.Property, [propertyTable]],
+      [Media.MediaModel.MediaChangeTypeKeys.Event, [eventTable]]
+    ]);
+
+    this.appendTab(
+        Media.PlayerDetailView.Tabs.Properties, Common.UIString('Properties'), propertyTable,
+        Common.UIString('Player properties'));
+
+    this.appendTab(
+        Media.PlayerDetailView.Tabs.Events, Common.UIString('Events'), eventTable, Common.UIString('Player events'));
+  }
+
+  /**
+   * @param {string} playerID
+   * @param {!Array.<!Media.Event>} changes
+   * @param {!Media.MediaModel.MediaChangeTypeKeys} changeType
+   */
+  renderChanges(playerID, changes, changeType) {
+    for (const panel of this._panels.get(changeType)) {
+      panel.renderChanges(playerID, changes, changeType);
+    }
+  }
+};
+
+/**
+ * @enum {string}
+ */
+Media.PlayerDetailView.Tabs = {
+  Events: 'events',
+  Properties: 'properties',
+};
\ No newline at end of file
diff --git a/third_party/blink/renderer/devtools/front_end/media/PlayerListView.js b/third_party/blink/renderer/devtools/front_end/media/PlayerListView.js
new file mode 100644
index 0000000..e131a30
--- /dev/null
+++ b/third_party/blink/renderer/devtools/front_end/media/PlayerListView.js
@@ -0,0 +1,155 @@
+// 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.
+
+/**
+ * @typedef {{playerTitle: string, playerID: string, exists: boolean, playing: boolean, titleEdited: boolean}}
+ */
+Media.PlayerStatus;
+
+/**
+ * @typedef {{playerStatus: !Media.PlayerStatus, playerTitleElement: ?HTMLElement}}
+ */
+Media.PlayerStatusMapElement;
+
+
+Media.PlayerEntryTreeElement = class extends UI.TreeElement {
+  /**
+   * @param {!Media.PlayerStatus} playerStatus
+   * @param {!Media.MainView} displayContainer
+   */
+  constructor(playerStatus, displayContainer) {
+    super(playerStatus.playerTitle, false);
+    this.titleFromUrl = true;
+    this._playerStatus = playerStatus;
+    this._displayContainer = displayContainer;
+    this.setLeadingIcons([UI.Icon.create('smallicon-videoplayer-playing', 'media-player')]);
+  }
+
+  /**
+   * @override
+   * @return {boolean}
+   */
+  onselect(selectedByUser) {
+    this._displayContainer.renderMainPanel(this._playerStatus.playerID);
+    return true;
+  }
+};
+
+
+Media.PlayerListView = class extends UI.VBox {
+  /**
+   * @param {!Media.MainView} mainContainer
+   */
+  constructor(mainContainer) {
+    super(true);
+
+    this._playerStatuses = new Map();
+
+    // Container where new panels can be added based on clicks.
+    this._mainContainer = mainContainer;
+
+    // The parent tree for storing sections
+    this._sidebarTree = new UI.TreeOutlineInShadow();
+    this.contentElement.appendChild(this._sidebarTree.element);
+    this._sidebarTree.registerRequiredCSS('media/playerListView.css');
+
+    // Audio capture / output devices.
+    this._audioDevices = this._addListSection(Common.UIString('Audio I/O'));
+
+    // Video capture devices.
+    this._videoDevices = this._addListSection(Common.UIString('Video Capture Devices'));
+
+    // Players active in this tab.
+    this._playerList = this._addListSection(Common.UIString('Players'));
+  }
+
+  /**
+   * @param {string} title
+   * @return {!UI.TreeElement}
+   */
+  _addListSection(title) {
+    const treeElement = new UI.TreeElement(title, true);
+    treeElement.listItemElement.classList.add('storage-group-list-item');
+    treeElement.setCollapsible(false);
+    treeElement.selectable = false;
+    this._sidebarTree.appendChild(treeElement);
+    return treeElement;
+  }
+
+  /**
+   * @param {string} playerID
+   */
+  addMediaElementItem(playerID) {
+    const playerStatus = {playerTitle: playerID, playerID: playerID, exists: true, playing: false, titleEdited: false};
+    const playerElement = new Media.PlayerEntryTreeElement(playerStatus, this._mainContainer);
+    this._playerStatuses.set(playerID, playerElement);
+    this._playerList.appendChild(playerElement);
+  }
+
+  /**
+   * @param {string} playerID
+   * @param {string} newTitle
+   * @param {boolean} isTitleExtractedFromUrl
+   */
+  setMediaElementPlayerTitle(playerID, newTitle, isTitleExtractedFromUrl) {
+    if (this._playerStatuses.has(playerID)) {
+      const sidebarEntry = this._playerStatuses.get(playerID);
+      if (!isTitleExtractedFromUrl || sidebarEntry.titleFromUrl) {
+        sidebarEntry.title = newTitle;
+        sidebarEntry.titleFromUrl = isTitleExtractedFromUrl;
+      }
+    }
+  }
+
+  /**
+   * @param {string} playerID
+   * @param {string} iconName
+   */
+  setMediaElementPlayerIcon(playerID, iconName) {
+    if (this._playerStatuses.has(playerID)) {
+      const sidebarEntry = this._playerStatuses.get(playerID);
+      sidebarEntry.setLeadingIcons([UI.Icon.create('smallicon-videoplayer-' + iconName, 'media-player')]);
+    }
+  }
+
+  /**
+   * @param {string} playerID
+   * @param {!Array.<!Media.Event>} changes
+   * @param {string} changeType
+   */
+  renderChanges(playerID, changes, changeType) {
+    // We only want to try setting the title from the 'frame_title' and 'frame_url' properties.
+    if (changeType === Media.MediaModel.MediaChangeTypeKeys.Property) {
+      for (const change of changes) {
+        // Sometimes frame_title can be an empty string.
+        if (change.name === 'frame_title' && change.value) {
+          this.setMediaElementPlayerTitle(playerID, change.value, false);
+        }
+
+        if (change.name === 'frame_url') {
+          const url_path_component = change.value.substring(change.value.lastIndexOf('/') + 1);
+          this.setMediaElementPlayerTitle(playerID, url_path_component, true);
+        }
+      }
+    }
+
+    if (changeType === Media.MediaModel.MediaChangeTypeKeys.Event) {
+      let change_to = null;
+      for (const change of changes) {
+        if (change.name === 'Event') {
+          if (change.value === 'PLAY') {
+            change_to = 'playing';
+          } else if (change.value === 'PAUSE') {
+            change_to = 'paused';
+          } else if (change.value === 'WEBMEDIAPLAYER_DESTROYED') {
+            change_to = 'destroyed';
+          }
+        }
+      }
+      if (change_to) {
+        this.setMediaElementPlayerIcon(playerID, change_to);
+      }
+    }
+  }
+};
\ No newline at end of file
diff --git a/third_party/blink/renderer/devtools/front_end/media/eventDisplayTable.css b/third_party/blink/renderer/devtools/front_end/media/eventDisplayTable.css
new file mode 100644
index 0000000..60c190e8
--- /dev/null
+++ b/third_party/blink/renderer/devtools/front_end/media/eventDisplayTable.css
@@ -0,0 +1,14 @@
+/*
+ * 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.
+ */
+
+.no-border-top-datagrid>.data-grid {
+  /* make sure there is no top border, it ruins the menu view */
+  border-top: 0px;
+}
+
+.event-display-table-contents-table-container>.widget>.data-grid {
+  height: 100%;
+}
\ No newline at end of file
diff --git a/third_party/blink/renderer/devtools/front_end/media/mediaView.css b/third_party/blink/renderer/devtools/front_end/media/mediaView.css
new file mode 100644
index 0000000..2b08b3e9
--- /dev/null
+++ b/third_party/blink/renderer/devtools/front_end/media/mediaView.css
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2019 The Chromium Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+.playerlist-sidebar {
+  display: flex;
+  flex-direction: column;
+  align-items: stretch;
+}
+
+.playerlist-sidebar-header {
+  font-size: 22px;
+  padding: 8px 20px;
+  border-bottom:1px solid var(--divider-color);
+}
+
+.playerlist-entry-title>pre {
+  margin: 0px;
+}
+
+.playerlist-entry-title {
+  float: left;
+}
\ No newline at end of file
diff --git a/third_party/blink/renderer/devtools/front_end/media/media_strings.grdp b/third_party/blink/renderer/devtools/front_end/media/media_strings.grdp
new file mode 100644
index 0000000..2c62341
--- /dev/null
+++ b/third_party/blink/renderer/devtools/front_end/media/media_strings.grdp
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<grit-part>
+  <message name="IDS_DEVTOOLS_421b47ffd946ca083b65cd668c6b17e6" desc="Video player">
+    video
+  </message>
+  <message name="IDS_DEVTOOLS_62933a2951ef01f4eafd9bdf4d3cd2f0" desc="Media player">
+    media
+  </message>
+  <message name="IDS_DEVTOOLS_83341e272df7a4f2982e7b96b7f26a4a" desc="Button text for viewing properties.">
+    Player properties
+  </message>
+  <message name="IDS_DEVTOOLS_87f9f735a1d36793ceaecd4e47124b63" desc="Button text for viewing events.">
+    Events
+  </message>
+  <message name="IDS_DEVTOOLS_cf6523ecab64a91c7a9b3d83b58f9e61" desc="Hover text for the Events button.">
+    Player events
+  </message>
+  <message name="IDS_DEVTOOLS_93ec972c68d5caca43370a9746a08cea" desc="Side-panel entry title text for the players section.">
+    Players
+  </message>
+  <message name="IDS_DEVTOOLS_a20467c4e87b5469cbb1a6c31775393c" desc="Side-panel entry title text for the audio devices section.">
+    Audio I/O
+  </message>
+  <message name="IDS_DEVTOOLS_d9f6db9c0f0579391eccedbac66aef9b" desc="Side-panel entry title text for the video capture devices section.">
+    Video Capture Devices
+  </message>
+</grit-part>
\ No newline at end of file
diff --git a/third_party/blink/renderer/devtools/front_end/media/module.json b/third_party/blink/renderer/devtools/front_end/media/module.json
new file mode 100644
index 0000000..c215352
--- /dev/null
+++ b/third_party/blink/renderer/devtools/front_end/media/module.json
@@ -0,0 +1,34 @@
+{
+  "extensions": [
+      {
+          "type": "view",
+          "location": "panel",
+          "id": "media",
+          "title": "Media",
+          "persistence": "closeable",
+          "order": 100,
+          "className": "Media.MainView",
+          "tags": "media, video",
+          "experiment": "mediaInspector"
+      }
+  ],
+  "dependencies": [
+      "components",
+      "sdk",
+      "ui",
+      "data_grid"
+  ],
+  "scripts": [
+      "EventDisplayTable.js",
+      "MainView.js",
+      "MediaTable.js",
+      "PlayerDetailView.js",
+      "PlayerListView.js",
+      "MediaModel.js"
+  ],
+  "resources": [
+    "eventDisplayTable.css",
+    "mediaView.css",
+    "playerListView.css"
+  ]
+}
\ No newline at end of file
diff --git a/third_party/blink/renderer/devtools/front_end/media/playerListView.css b/third_party/blink/renderer/devtools/front_end/media/playerListView.css
new file mode 100644
index 0000000..2282d43
--- /dev/null
+++ b/third_party/blink/renderer/devtools/front_end/media/playerListView.css
@@ -0,0 +1,22 @@
+/*
+ * 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.
+ */
+
+.tree-outline {
+    padding-left: 0;
+    color: rgb(90, 90, 90);
+}
+
+li.storage-group-list-item {
+    padding: 10px 8px 6px 8px;
+}
+
+li.storage-group-list-item:not(:first-child) {
+    border-top: 1px solid rgb(230, 230, 230);
+}
+
+li.storage-group-list-item::before {
+    display: none;
+}
\ No newline at end of file
diff --git a/third_party/blink/renderer/devtools/front_end/profiler/HeapSnapshotDataGrids.js b/third_party/blink/renderer/devtools/front_end/profiler/HeapSnapshotDataGrids.js
index 519d9ea..ee5783d 100644
--- a/third_party/blink/renderer/devtools/front_end/profiler/HeapSnapshotDataGrids.js
+++ b/third_party/blink/renderer/devtools/front_end/profiler/HeapSnapshotDataGrids.js
@@ -146,6 +146,9 @@
     contextMenu.revealSection().appendItem(ls`Reveal in Summary view`, () => {
       this._dataDisplayDelegate.showObject(node.snapshotNodeId, ls`Summary`);
     });
+    if (gridNode.linkElement && !contextMenu.containsTarget(gridNode.linkElement)) {
+      contextMenu.appendApplicableItems(gridNode.linkElement);
+    }
   }
 
   resetSortingCache() {
diff --git a/third_party/blink/renderer/devtools/front_end/profiler/HeapSnapshotGridNodes.js b/third_party/blink/renderer/devtools/front_end/profiler/HeapSnapshotGridNodes.js
index c1d46a1e..f6269f22 100644
--- a/third_party/blink/renderer/devtools/front_end/profiler/HeapSnapshotGridNodes.js
+++ b/third_party/blink/renderer/devtools/front_end/profiler/HeapSnapshotGridNodes.js
@@ -616,6 +616,7 @@
     const link = await this._dataGrid.dataDisplayDelegate().linkifyObject(this.snapshotNodeIndex);
     if (link) {
       linkContainer.appendChild(link);
+      this.linkElement = link;
     } else {
       linkContainer.remove();
     }
diff --git a/third_party/blink/renderer/devtools/front_end/ui/Icon.js b/third_party/blink/renderer/devtools/front_end/ui/Icon.js
index 9b4c2df..b1c75a43 100644
--- a/third_party/blink/renderer/devtools/front_end/ui/Icon.js
+++ b/third_party/blink/renderer/devtools/front_end/ui/Icon.js
@@ -139,6 +139,9 @@
   'smallicon-clear-info': {position: 'f2', spritesheet: 'smallicons'},
   'smallicon-clear-error': {position: 'f3', spritesheet: 'smallicons'},
   'smallicon-account-circle': {position: 'f4', spritesheet: 'smallicons'},
+  'smallicon-videoplayer-paused': {position: 'f6', spritesheet: 'smallicons', isMask: true},
+  'smallicon-videoplayer-playing': {position: 'g6', spritesheet: 'smallicons', isMask: true},
+  'smallicon-videoplayer-destroyed': {position: 'g5', spritesheet: 'smallicons', isMask: true},
 
   'mediumicon-clear-storage': {position: 'a4', spritesheet: 'mediumicons', isMask: true},
   'mediumicon-cookie': {position: 'b4', spritesheet: 'mediumicons', isMask: true},
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object.cc b/third_party/blink/renderer/modules/accessibility/ax_object.cc
index 2e9a5a5..1d846232 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_object.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_object.cc
@@ -28,6 +28,7 @@
 
 #include "third_party/blink/renderer/modules/accessibility/ax_object.h"
 
+#include "third_party/blink/public/common/features.h"
 #include "third_party/blink/public/platform/web_scroll_into_view_params.h"
 #include "third_party/blink/renderer/core/aom/accessible_node.h"
 #include "third_party/blink/renderer/core/aom/accessible_node_list.h"
@@ -55,6 +56,7 @@
 #include "third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.h"
 #include "third_party/blink/renderer/modules/accessibility/ax_range.h"
 #include "third_party/blink/renderer/modules/accessibility/ax_sparse_attribute_setter.h"
+#include "third_party/blink/renderer/platform/instrumentation/use_counter.h"
 #include "third_party/blink/renderer/platform/language.h"
 #include "third_party/blink/renderer/platform/runtime_enabled_features.h"
 #include "third_party/blink/renderer/platform/text/platform_locale.h"
@@ -2072,6 +2074,22 @@
 
   ax::mojom::Role role = AriaRoleToWebCoreRole(aria_role);
 
+  switch (role) {
+    case ax::mojom::Role::kAnnotationAttribution:
+    case ax::mojom::Role::kAnnotationCommentary:
+    case ax::mojom::Role::kAnnotationPresence:
+    case ax::mojom::Role::kAnnotationRevision:
+    case ax::mojom::Role::kAnnotationSuggestion:
+      UseCounter::Count(GetDocument(), WebFeature::kARIAAnnotationRoles);
+      if (!RuntimeEnabledFeatures::
+              AccessibilityExposeARIAAnnotationsEnabled()) {
+        role = ax::mojom::Role::kGenericContainer;
+      }
+      break;
+    default:
+      break;
+  }
+
   // ARIA states if an item can get focus, it should not be presentational.
   // It also states user agents should ignore the presentational role if
   // the element has global ARIA states and properties.
diff --git a/third_party/blink/renderer/modules/serial/serial_port.cc b/third_party/blink/renderer/modules/serial/serial_port.cc
index 52eb78e..9fddc35d 100644
--- a/third_party/blink/renderer/modules/serial/serial_port.cc
+++ b/third_party/blink/renderer/modules/serial/serial_port.cc
@@ -430,8 +430,12 @@
   DCHECK(!readable_);
   underlying_source_ = MakeGarbageCollected<SerialPortUnderlyingSource>(
       script_state, this, std::move(readable_pipe));
+  // Ideally the stream would report the number of bytes that can be read from
+  // the underlying Mojo data pipe. As an approximation the high water mark is
+  // set to 0 so that data remains in the pipe rather than being queued in the
+  // stream and thus adding an extra layer of buffering.
   readable_ = ReadableStream::CreateWithCountQueueingStrategy(
-      script_state, underlying_source_, 0);
+      script_state, underlying_source_, /*high_water_mark=*/0);
 }
 
 void SerialPort::InitializeWritableStream(
@@ -441,8 +445,13 @@
   DCHECK(!writable_);
   underlying_sink_ = MakeGarbageCollected<SerialPortUnderlyingSink>(
       this, std::move(writable_pipe));
+  // Ideally the stream would report the number of bytes that could be written
+  // to the underlying Mojo data pipe. As an approximation the high water mark
+  // is set to 1 so that the stream appears ready but producers observing
+  // backpressure won't queue additional chunks in the stream and thus add an
+  // extra layer of buffering.
   writable_ = WritableStream::CreateWithCountQueueingStrategy(
-      script_state, underlying_sink_, 0);
+      script_state, underlying_sink_, /*high_water_mark=*/1);
 }
 
 void SerialPort::OnGetSignals(
diff --git a/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.cc b/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.cc
index 87d82645..5e1a13c 100644
--- a/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.cc
+++ b/third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.cc
@@ -445,6 +445,7 @@
       break;
 
     case kMiddleOfLine:
+    case kInPreprocessorDirective:
       if (c == '/' && Peek(temp)) {
         if (temp == '/') {
           parse_state_ = kInSingleLineComment;
@@ -468,14 +469,6 @@
       Emit(c);
       break;
 
-    case kInPreprocessorDirective:
-      // No matter what the character is, just pass it
-      // through. Do not parse comments in this state. This
-      // might not be the right thing to do long term, but it
-      // should handle the #error preprocessor directive.
-      Emit(c);
-      break;
-
     case kInSingleLineComment:
       // Line-continuation characters are processed before comment processing.
       // Advance string if a new line character is immediately behind
diff --git a/third_party/blink/renderer/platform/BUILD.gn b/third_party/blink/renderer/platform/BUILD.gn
index 158e3c8..9be4862 100644
--- a/third_party/blink/renderer/platform/BUILD.gn
+++ b/third_party/blink/renderer/platform/BUILD.gn
@@ -1468,7 +1468,6 @@
   deps = [
     ":platform_export",
     "//base/allocator:buildflags",
-    "//components/paint_preview/common",
     "//components/viz/client",
     "//components/viz/common",
     "//crypto",
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 cfd7ae6f..9a64c19 100644
--- a/third_party/blink/renderer/platform/exported/web_runtime_features.cc
+++ b/third_party/blink/renderer/platform/exported/web_runtime_features.cc
@@ -96,6 +96,10 @@
   RuntimeEnabledFeatures::SetAccelerated2dCanvasEnabled(enable);
 }
 
+void WebRuntimeFeatures::EnableAccessibilityExposeARIAAnnotations(bool enable) {
+  RuntimeEnabledFeatures::SetAccessibilityExposeARIAAnnotationsEnabled(enable);
+}
+
 void WebRuntimeFeatures::EnableAccessibilityExposeDisplayNone(bool enable) {
   RuntimeEnabledFeatures::SetAccessibilityExposeDisplayNoneEnabled(enable);
 }
diff --git a/third_party/blink/renderer/platform/graphics/DEPS b/third_party/blink/renderer/platform/graphics/DEPS
index 37eff60..5647769 100644
--- a/third_party/blink/renderer/platform/graphics/DEPS
+++ b/third_party/blink/renderer/platform/graphics/DEPS
@@ -11,7 +11,6 @@
     "+base/barrier_closure.h",
     "+base/callback_helpers.h",
     "+cc",
-    "+components/paint_preview/common",
     "+components/viz/client",
     "+components/viz/common",
     "+gpu/config",
diff --git a/third_party/blink/renderer/platform/graphics/canvas_resource_provider.cc b/third_party/blink/renderer/platform/graphics/canvas_resource_provider.cc
index 4d69ed7..b9768c3 100644
--- a/third_party/blink/renderer/platform/graphics/canvas_resource_provider.cc
+++ b/third_party/blink/renderer/platform/graphics/canvas_resource_provider.cc
@@ -695,6 +695,15 @@
     base::WeakPtr<CanvasResourceDispatcher> resource_dispatcher,
     bool is_origin_top_left) {
   std::unique_ptr<CanvasResourceProvider> provider;
+
+  if (context_provider_wrapper) {
+    const int max_texture_size = context_provider_wrapper->ContextProvider()
+                                     ->GetCapabilities()
+                                     .max_texture_size;
+    if (size.Width() > max_texture_size || size.Height() > max_texture_size)
+      usage = ResourceUsage::kSoftwareResourceUsage;
+  }
+
   const Vector<CanvasResourceType>& fallback_list =
       GetResourceTypeFallbackList(usage);
 
@@ -784,13 +793,9 @@
                 ->GetCapabilities()
                 .texture_storage_image;
 
-        const int max_texture_size = context_provider_wrapper->ContextProvider()
-                                         ->GetCapabilities()
-                                         .max_texture_size;
         const bool can_use_gmbs =
             is_gpu_memory_buffer_image_allowed &&
-            Platform::Current()->GetGpuMemoryBufferManager() &&
-            size.Width() < max_texture_size && size.Height() < max_texture_size;
+            Platform::Current()->GetGpuMemoryBufferManager();
 
         const bool is_overlay_candidate =
             usage_wants_overlays && can_use_overlays;
diff --git a/third_party/blink/renderer/platform/graphics/canvas_resource_provider_test.cc b/third_party/blink/renderer/platform/graphics/canvas_resource_provider_test.cc
index 235a0718..1f0eb8f 100644
--- a/third_party/blink/renderer/platform/graphics/canvas_resource_provider_test.cc
+++ b/third_party/blink/renderer/platform/graphics/canvas_resource_provider_test.cc
@@ -16,6 +16,7 @@
 #include "third_party/blink/renderer/platform/graphics/test/fake_gles2_interface.h"
 #include "third_party/blink/renderer/platform/graphics/test/fake_web_graphics_context_3d_provider.h"
 #include "third_party/blink/renderer/platform/graphics/test/gpu_memory_buffer_test_platform.h"
+#include "third_party/blink/renderer/platform/graphics/test/gpu_test_utils.h"
 #include "third_party/blink/renderer/platform/wtf/functional.h"
 
 using testing::_;
@@ -35,93 +36,14 @@
   MOCK_METHOD0(BeginFrame, bool());
 };
 
-class MockWebGraphisContext3DProviderWrapper
-    : public WebGraphicsContext3DProvider {
- public:
-  MockWebGraphisContext3DProviderWrapper(cc::ImageDecodeCache* cache = nullptr)
-      : image_decode_cache_(cache ? cache : &stub_image_decode_cache_) {
-    // enable all gpu features.
-    for (unsigned feature = 0; feature < gpu::NUMBER_OF_GPU_FEATURE_TYPES;
-         ++feature) {
-      gpu_feature_info_.status_values[feature] = gpu::kGpuFeatureStatusEnabled;
-    }
-  }
-  ~MockWebGraphisContext3DProviderWrapper() override = default;
-
-  GrContext* GetGrContext() override {
-    return GetTestContextProvider()->GrContext();
-  }
-
-  const gpu::Capabilities& GetCapabilities() const override {
-    return capabilities_;
-  }
-  void SetCapabilities(const gpu::Capabilities& c) { capabilities_ = c; }
-
-  const gpu::GpuFeatureInfo& GetGpuFeatureInfo() const override {
-    return gpu_feature_info_;
-  }
-
-  const WebglPreferences& GetWebglPreferences() const override {
-    return webgl_preferences_;
-  }
-
-  viz::GLHelper* GetGLHelper() override { return nullptr; }
-
-  gpu::gles2::GLES2Interface* ContextGL() override {
-    return GetTestContextProvider()->ContextGL();
-  }
-
-  gpu::webgpu::WebGPUInterface* WebGPUInterface() override { return nullptr; }
-
-  scoped_refptr<viz::TestContextProvider> GetTestContextProvider() {
-    if (!test_context_provider_) {
-      test_context_provider_ = viz::TestContextProvider::Create();
-      // Needed for CanvasResourceProviderDirect2DGpuMemoryBuffer.
-      test_context_provider_->UnboundTestContextGL()
-          ->set_support_texture_format_bgra8888(true);
-      test_context_provider_->BindToCurrentThread();
-    }
-    return test_context_provider_;
-  }
-
-  bool BindToCurrentThread() override { return false; }
-  void SetLostContextCallback(base::RepeatingClosure) override {}
-  void SetErrorMessageCallback(
-      base::RepeatingCallback<void(const char*, int32_t id)>) override {}
-  cc::ImageDecodeCache* ImageDecodeCache(SkColorType color_type) override {
-    return image_decode_cache_;
-  }
-  viz::TestSharedImageInterface* SharedImageInterface() override {
-    return GetTestContextProvider()->SharedImageInterface();
-  }
-  void CopyVideoFrame(media::PaintCanvasVideoRenderer* video_render,
-                      media::VideoFrame* video_frame,
-                      cc::PaintCanvas* canvas) override {}
-
- private:
-  cc::StubDecodeCache stub_image_decode_cache_;
-
-  scoped_refptr<viz::TestContextProvider> test_context_provider_;
-  gpu::Capabilities capabilities_;
-  gpu::GpuFeatureInfo gpu_feature_info_;
-  WebglPreferences webgl_preferences_;
-  cc::ImageDecodeCache* image_decode_cache_;
-};
-
 }  // anonymous namespace
 
 class CanvasResourceProviderTest : public Test {
  public:
   void SetUp() override {
-    // Install our mock GL context so that it gets served by SharedGpuContext.
-    auto factory = [](bool* gpu_compositing_disabled)
-        -> std::unique_ptr<WebGraphicsContext3DProvider> {
-      *gpu_compositing_disabled = false;
-      // Unretained is safe since TearDown() cleans up the SharedGpuContext.
-      return std::make_unique<MockWebGraphisContext3DProviderWrapper>();
-    };
-    SharedGpuContext::SetContextProviderFactoryForTesting(
-        WTF::BindRepeating(factory));
+    test_context_provider_ = viz::TestContextProvider::Create();
+    InitializeSharedGpuContext(test_context_provider_.get(),
+                               &image_decode_cache_);
     context_provider_wrapper_ = SharedGpuContext::ContextProviderWrapper();
   }
 
@@ -138,7 +60,7 @@
     auto capabilities = context_provider->GetCapabilities();
     capabilities.gpu_memory_buffer_formats.Add(buffer_format);
 
-    static_cast<MockWebGraphisContext3DProviderWrapper*>(context_provider)
+    static_cast<FakeWebGraphicsContext3DProvider*>(context_provider)
         ->SetCapabilities(capabilities);
   }
 
@@ -147,7 +69,7 @@
     auto capabilities = context_provider->GetCapabilities();
     capabilities.texture_storage_image = true;
     capabilities.max_texture_size = 1024;
-    static_cast<MockWebGraphisContext3DProviderWrapper*>(context_provider)
+    static_cast<FakeWebGraphicsContext3DProvider*>(context_provider)
         ->SetCapabilities(capabilities);
   }
 
@@ -159,6 +81,8 @@
   }
 
  protected:
+  cc::StubDecodeCache image_decode_cache_;
+  scoped_refptr<viz::TestContextProvider> test_context_provider_;
   base::WeakPtr<WebGraphicsContext3DProviderWrapper> context_provider_wrapper_;
   ScopedTestingPlatformSupport<GpuMemoryBufferTestPlatform> platform_;
 };
@@ -341,11 +265,11 @@
 
 TEST_F(CanvasResourceProviderTest,
        CanvasResourceProviderSharedImageCopyOnWriteDisabled) {
-  auto* mock_context = static_cast<MockWebGraphisContext3DProviderWrapper*>(
+  auto* fake_context = static_cast<FakeWebGraphicsContext3DProvider*>(
       context_provider_wrapper_->ContextProvider());
-  auto caps = mock_context->GetCapabilities();
+  auto caps = fake_context->GetCapabilities();
   caps.disable_2d_canvas_copy_on_write = true;
-  mock_context->SetCapabilities(caps);
+  fake_context->SetCapabilities(caps);
 
   const IntSize kSize(10, 10);
   const CanvasColorParams kColorParams(kSRGBCanvasColorSpace,
@@ -542,4 +466,72 @@
   callback->Run(gpu::SyncToken(), true /* is_lost */);
 }
 
+TEST_F(CanvasResourceProviderTest, DimensionsExceedMaxTextureSize) {
+  const CanvasColorParams kColorParams(kSRGBCanvasColorSpace,
+                                       kRGBA8CanvasPixelFormat, kNonOpaque);
+  const int max_texture_size = context_provider_wrapper_->ContextProvider()
+                                   ->GetCapabilities()
+                                   .max_texture_size;
+  EnsureBufferFormatIsSupported(kColorParams.GetBufferFormat());
+  EnsureOverlaysSupported();
+
+  for (int i = 0;
+       i < static_cast<int>(CanvasResourceProvider::ResourceUsage::kMaxValue);
+       ++i) {
+    SCOPED_TRACE(i);
+    auto usage = static_cast<CanvasResourceProvider::ResourceUsage>(i);
+    bool should_support_compositing = false;
+    switch (usage) {
+      case CanvasResourceProvider::ResourceUsage::kSoftwareResourceUsage:
+        should_support_compositing = false;
+        break;
+      case CanvasResourceProvider::ResourceUsage::
+          kSoftwareCompositedResourceUsage:
+        FALLTHROUGH;
+      case CanvasResourceProvider::ResourceUsage::
+          kSoftwareCompositedDirect2DResourceUsage:
+        should_support_compositing = PlatformSupportsGMBs();
+        break;
+      case CanvasResourceProvider::ResourceUsage::kAcceleratedResourceUsage:
+        FALLTHROUGH;
+      case CanvasResourceProvider::ResourceUsage::
+          kAcceleratedCompositedResourceUsage:
+        FALLTHROUGH;
+      case CanvasResourceProvider::ResourceUsage::
+          kAcceleratedDirect2DResourceUsage:
+        FALLTHROUGH;
+      case CanvasResourceProvider::ResourceUsage::
+          kAcceleratedDirect3DResourceUsage:
+        should_support_compositing = true;
+        break;
+    }
+
+    auto provider = CanvasResourceProvider::Create(
+        IntSize(max_texture_size - 1, max_texture_size), usage,
+        context_provider_wrapper_, 0 /* msaa_sample_count */,
+        kLow_SkFilterQuality, kColorParams,
+        CanvasResourceProvider::kAllowImageChromiumPresentationMode,
+        nullptr /* resource_dispatcher */, true /* is_origin_top_left */);
+    EXPECT_EQ(provider->SupportsDirectCompositing(),
+              should_support_compositing);
+
+    provider = CanvasResourceProvider::Create(
+        IntSize(max_texture_size, max_texture_size), usage,
+        context_provider_wrapper_, 0 /* msaa_sample_count */,
+        kLow_SkFilterQuality, kColorParams,
+        CanvasResourceProvider::kAllowImageChromiumPresentationMode,
+        nullptr /* resource_dispatcher */, true /* is_origin_top_left */);
+    EXPECT_EQ(provider->SupportsDirectCompositing(),
+              should_support_compositing);
+
+    provider = CanvasResourceProvider::Create(
+        IntSize(max_texture_size + 1, max_texture_size), usage,
+        context_provider_wrapper_, 0 /* msaa_sample_count */,
+        kLow_SkFilterQuality, kColorParams,
+        CanvasResourceProvider::kAllowImageChromiumPresentationMode,
+        nullptr /* resource_dispatcher */, true /* is_origin_top_left */);
+    EXPECT_FALSE(provider->SupportsDirectCompositing());
+  }
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/graphics/graphics_context.cc b/third_party/blink/renderer/platform/graphics/graphics_context.cc
index 2976ccd..a822cd3 100644
--- a/third_party/blink/renderer/platform/graphics/graphics_context.cc
+++ b/third_party/blink/renderer/platform/graphics/graphics_context.cc
@@ -30,7 +30,6 @@
 
 #include "base/optional.h"
 #include "build/build_config.h"
-#include "components/paint_preview/common/paint_preview_tracker.h"
 #include "skia/ext/platform_canvas.h"
 #include "third_party/blink/renderer/platform/fonts/text_run_paint_info.h"
 #include "third_party/blink/renderer/platform/geometry/float_rect.h"
@@ -90,14 +89,12 @@
 
 GraphicsContext::GraphicsContext(PaintController& paint_controller,
                                  DisabledMode disable_context_or_painting,
-                                 printing::MetafileSkia* metafile,
-                                 paint_preview::PaintPreviewTracker* tracker)
+                                 printing::MetafileSkia* metafile)
     : canvas_(nullptr),
       paint_controller_(paint_controller),
       paint_state_stack_(),
       paint_state_index_(0),
       metafile_(metafile),
-      tracker_(tracker),
 #if DCHECK_IS_ON()
       layer_count_(0),
       disable_destruction_checks_(false),
@@ -1371,12 +1368,6 @@
     return;
   DCHECK(canvas_);
 
-  // Intercept URL rects when painting previews.
-  if (IsPaintingPreview() && tracker_) {
-    tracker_->AnnotateLink(link.GetString().Utf8(), dest_rect);
-    return;
-  }
-
   sk_sp<SkData> url(SkData::MakeWithCString(link.GetString().Utf8().c_str()));
   canvas_->Annotate(cc::PaintCanvas::AnnotationType::URL, dest_rect,
                     std::move(url));
@@ -1388,12 +1379,6 @@
     return;
   DCHECK(canvas_);
 
-  // Intercept URL rects when painting previews.
-  if (IsPaintingPreview() && tracker_) {
-    tracker_->AnnotateLink(dest_name.Utf8(), rect);
-    return;
-  }
-
   sk_sp<SkData> sk_dest_name(SkData::MakeWithCString(dest_name.Utf8().c_str()));
   canvas_->Annotate(cc::PaintCanvas::AnnotationType::LINK_TO_DESTINATION, rect,
                     std::move(sk_dest_name));
diff --git a/third_party/blink/renderer/platform/graphics/graphics_context.h b/third_party/blink/renderer/platform/graphics/graphics_context.h
index 5a42e810..950e1f4 100644
--- a/third_party/blink/renderer/platform/graphics/graphics_context.h
+++ b/third_party/blink/renderer/platform/graphics/graphics_context.h
@@ -53,10 +53,6 @@
 class SkRRect;
 struct SkRect;
 
-namespace paint_preview {
-class PaintPreviewTracker;
-}  // namespace paint_preview
-
 namespace blink {
 
 class FloatRect;
@@ -78,8 +74,7 @@
 
   explicit GraphicsContext(PaintController&,
                            DisabledMode = kNothingDisabled,
-                           printing::MetafileSkia* = nullptr,
-                           paint_preview::PaintPreviewTracker* = nullptr);
+                           printing::MetafileSkia* = nullptr);
 
   ~GraphicsContext();
 
@@ -166,19 +161,6 @@
   bool Printing() const { return printing_; }
   void SetPrinting(bool printing) { printing_ = printing; }
 
-  // Returns if the context is saving a paint preview instead of displaying.
-  // In such cases, clipping should not occur.
-  bool IsPaintingPreview() const { return is_painting_preview_; }
-  void SetIsPaintingPreview(bool is_painting_preview) {
-    is_painting_preview_ = is_painting_preview;
-  }
-
-  // Returns if the context is printing or painting a preview. Many of the
-  // behaviors required for printing and paint previews are shared.
-  bool IsPrintingOrPaintingPreview() const {
-    return Printing() || IsPaintingPreview();
-  }
-
   SkColorFilter* GetColorFilter() const;
   void SetColorFilter(ColorFilter);
   // ---------- End state management methods -----------------
@@ -516,7 +498,6 @@
   PaintRecorder paint_recorder_;
 
   printing::MetafileSkia* metafile_;
-  paint_preview::PaintPreviewTracker* tracker_;
 
 #if DCHECK_IS_ON()
   int layer_count_;
@@ -531,7 +512,6 @@
   DarkModeFilter dark_mode_filter_;
 
   unsigned printing_ : 1;
-  unsigned is_painting_preview_ : 1;
   unsigned in_drawing_recorder_ : 1;
 
   DISALLOW_COPY_AND_ASSIGN(GraphicsContext);
diff --git a/third_party/blink/renderer/platform/graphics/graphics_layer.cc b/third_party/blink/renderer/platform/graphics/graphics_layer.cc
index 9a3febb..b37753b 100644
--- a/third_party/blink/renderer/platform/graphics/graphics_layer.cc
+++ b/third_party/blink/renderer/platform/graphics/graphics_layer.cc
@@ -665,22 +665,6 @@
   return CcLayer()->double_sided();
 }
 
-void GraphicsLayer::SetOpacity(float opacity) {
-  CcLayer()->SetOpacity(opacity);
-}
-
-float GraphicsLayer::Opacity() const {
-  return CcLayer()->opacity();
-}
-
-void GraphicsLayer::SetBlendMode(BlendMode blend_mode) {
-  CcLayer()->SetBlendMode(WebCoreBlendModeToSkBlendMode(blend_mode));
-}
-
-BlendMode GraphicsLayer::GetBlendMode() const {
-  return BlendModeFromSkBlendMode(CcLayer()->blend_mode());
-}
-
 void GraphicsLayer::SetHitTestable(bool should_hit_test) {
   if (hit_testable_ == should_hit_test)
     return;
diff --git a/third_party/blink/renderer/platform/graphics/graphics_layer.h b/third_party/blink/renderer/platform/graphics/graphics_layer.h
index 4b8e7ab..91e8f42 100644
--- a/third_party/blink/renderer/platform/graphics/graphics_layer.h
+++ b/third_party/blink/renderer/platform/graphics/graphics_layer.h
@@ -167,12 +167,6 @@
 
   bool BackfaceVisibility() const;
 
-  float Opacity() const;
-  void SetOpacity(float);
-
-  BlendMode GetBlendMode() const;
-  void SetBlendMode(BlendMode);
-
   void SetHitTestable(bool);
   bool GetHitTestable() const { return hit_testable_; }
 
diff --git a/third_party/blink/renderer/platform/graphics/paint/paint_record_builder.cc b/third_party/blink/renderer/platform/graphics/paint/paint_record_builder.cc
index b7b3241..6b690463 100644
--- a/third_party/blink/renderer/platform/graphics/paint/paint_record_builder.cc
+++ b/third_party/blink/renderer/platform/graphics/paint/paint_record_builder.cc
@@ -10,11 +10,9 @@
 
 namespace blink {
 
-PaintRecordBuilder::PaintRecordBuilder(
-    printing::MetafileSkia* metafile,
-    GraphicsContext* containing_context,
-    PaintController* paint_controller,
-    paint_preview::PaintPreviewTracker* tracker)
+PaintRecordBuilder::PaintRecordBuilder(printing::MetafileSkia* metafile,
+                                       GraphicsContext* containing_context,
+                                       PaintController* paint_controller)
     : paint_controller_(nullptr) {
   GraphicsContext::DisabledMode disabled_mode =
       GraphicsContext::kNothingDisabled;
@@ -32,13 +30,12 @@
   paint_controller_->UpdateCurrentPaintChunkProperties(
       base::nullopt, PropertyTreeState::Root());
 
-  context_ = std::make_unique<GraphicsContext>(
-      *paint_controller_, disabled_mode, metafile, tracker);
+  context_ = std::make_unique<GraphicsContext>(*paint_controller_,
+                                               disabled_mode, metafile);
   if (containing_context) {
     context_->SetDarkMode(containing_context->dark_mode_settings());
     context_->SetDeviceScaleFactor(containing_context->DeviceScaleFactor());
     context_->SetPrinting(containing_context->Printing());
-    context_->SetIsPaintingPreview(containing_context->IsPaintingPreview());
   }
 }
 
diff --git a/third_party/blink/renderer/platform/graphics/paint/paint_record_builder.h b/third_party/blink/renderer/platform/graphics/paint/paint_record_builder.h
index 42631cf..521dd79c 100644
--- a/third_party/blink/renderer/platform/graphics/paint/paint_record_builder.h
+++ b/third_party/blink/renderer/platform/graphics/paint/paint_record_builder.h
@@ -19,15 +19,12 @@
 class PaintCanvas;
 }
 
-namespace paint_preview {
-class PaintPreviewTracker;
-}
-
 namespace blink {
 class GraphicsContext;
 class PaintController;
 
 class PLATFORM_EXPORT PaintRecordBuilder final : public DisplayItemClient {
+
  public:
   // Constructs a new builder for the resulting paint record. If |metadata|
   // is specified, that metadata is propagated to the builder's internal canvas.
@@ -42,8 +39,7 @@
   // CompositeAfterPaint.
   PaintRecordBuilder(printing::MetafileSkia* metafile = nullptr,
                      GraphicsContext* containing_context = nullptr,
-                     PaintController* = nullptr,
-                     paint_preview::PaintPreviewTracker* tracker = nullptr);
+                     PaintController* = nullptr);
   ~PaintRecordBuilder() override;
 
   GraphicsContext& Context() { return *context_; }
diff --git a/third_party/blink/renderer/platform/graphics/test/gpu_test_utils.cc b/third_party/blink/renderer/platform/graphics/test/gpu_test_utils.cc
index 296aa8a..e674662 100644
--- a/third_party/blink/renderer/platform/graphics/test/gpu_test_utils.cc
+++ b/third_party/blink/renderer/platform/graphics/test/gpu_test_utils.cc
@@ -17,8 +17,12 @@
                     cc::ImageDecodeCache* cache, bool* gpu_compositing_disabled)
       -> std::unique_ptr<WebGraphicsContext3DProvider> {
     *gpu_compositing_disabled = false;
-    return std::make_unique<FakeWebGraphicsContext3DProvider>(gl, cache,
-                                                              context);
+    auto fake_context =
+        std::make_unique<FakeWebGraphicsContext3DProvider>(gl, cache, context);
+    auto caps = fake_context->GetCapabilities();
+    caps.max_texture_size = 1024;
+    fake_context->SetCapabilities(caps);
+    return fake_context;
   };
   context_provider->BindToCurrentThread();
   gpu::gles2::GLES2Interface* gl = context_provider->ContextGL();
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index e012391..18155ce 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -99,6 +99,10 @@
       status: "stable",
     },
     {
+      name: "AccessibilityExposeARIAAnnotations",
+      status: "experimental",
+    },
+    {
       name: "AccessibilityExposeDisplayNone",
       status: "test",
     },
@@ -505,10 +509,6 @@
       status: "stable",
     },
     {
-      name: "CustomUserTiming",
-      status: "stable",
-    },
-    {
       name: "Database",
       status: "stable",
     },
diff --git a/third_party/blink/web_tests/animations/responsive/interpolation/transform-responsive.html b/third_party/blink/web_tests/animations/responsive/interpolation/transform-responsive.html
index 1463267..112e516 100644
--- a/third_party/blink/web_tests/animations/responsive/interpolation/transform-responsive.html
+++ b/third_party/blink/web_tests/animations/responsive/interpolation/transform-responsive.html
@@ -57,4 +57,24 @@
     ],
   }],
 });
+
+// https://crbug.com/1004096
+assertCSSResponsive({
+  property: 'transform',
+  from: 'translateX(0px)',
+  to: 'translateX(calc(10em + 0%))',
+  configurations: [{
+    state: {'font-size': '16px'},
+    expect: [
+      {at: 0.25, is: 'translateX(40px)'},
+      {at: 0.75, is: 'translateX(120px)'},
+    ],
+  }, {
+    state: {'font-size': '20px'},
+    expect: [
+      {at: 0.25, is: 'translateX(50px)'},
+      {at: 0.75, is: 'translateX(150px)'},
+    ],
+  }],
+});
 </script>
diff --git a/third_party/blink/web_tests/compositing/contents-opaque/layer-opacity-expected.txt b/third_party/blink/web_tests/compositing/contents-opaque/layer-opacity-expected.txt
index 01bfe5fc..dd59699 100644
--- a/third_party/blink/web_tests/compositing/contents-opaque/layer-opacity-expected.txt
+++ b/third_party/blink/web_tests/compositing/contents-opaque/layer-opacity-expected.txt
@@ -9,7 +9,6 @@
     {
       "name": "LayoutNGBlockFlow DIV class='box opaque-background translucent composited'",
       "bounds": [100, 100],
-      "opacity": 0.5,
       "contentsOpaque": true,
       "backgroundColor": "#008000",
       "transform": 1
diff --git a/third_party/blink/web_tests/compositing/overflow/rotate-then-clip-effect-interleave-expected.txt b/third_party/blink/web_tests/compositing/overflow/rotate-then-clip-effect-interleave-expected.txt
index 6b51c0c..cb50b2b 100644
--- a/third_party/blink/web_tests/compositing/overflow/rotate-then-clip-effect-interleave-expected.txt
+++ b/third_party/blink/web_tests/compositing/overflow/rotate-then-clip-effect-interleave-expected.txt
@@ -9,7 +9,6 @@
     {
       "name": "LayoutNGBlockFlow DIV",
       "bounds": [240, 240],
-      "opacity": 0.899999976158142,
       "transform": 2
     },
     {
diff --git a/third_party/blink/web_tests/compositing/squashing/opacity-squashed-owner-expected.txt b/third_party/blink/web_tests/compositing/squashing/opacity-squashed-owner-expected.txt
index 0825200..9f788cad 100644
--- a/third_party/blink/web_tests/compositing/squashing/opacity-squashed-owner-expected.txt
+++ b/third_party/blink/web_tests/compositing/squashing/opacity-squashed-owner-expected.txt
@@ -9,7 +9,6 @@
     {
       "name": "LayoutNGBlockFlow DIV id='target' class='composited box opaque'",
       "bounds": [100, 100],
-      "opacity": 0.5,
       "contentsOpaque": true,
       "backgroundColor": "#ADD8E6",
       "transform": 1
diff --git a/third_party/blink/web_tests/compositing/visibility/layer-visible-content-expected.txt b/third_party/blink/web_tests/compositing/visibility/layer-visible-content-expected.txt
index aa91a576..ac508fd1 100644
--- a/third_party/blink/web_tests/compositing/visibility/layer-visible-content-expected.txt
+++ b/third_party/blink/web_tests/compositing/visibility/layer-visible-content-expected.txt
@@ -12,8 +12,7 @@
     },
     {
       "name": "LayoutNGBlockFlow PRE id='layer-tree'",
-      "bounds": [800, 16],
-      "opacity": 0
+      "bounds": [800, 16]
     }
   ]
 }
diff --git a/third_party/blink/web_tests/css3/blending/mix-blend-mode-composited-reason-children-expected.txt b/third_party/blink/web_tests/css3/blending/mix-blend-mode-composited-reason-children-expected.txt
index ef50783..32dac8e 100644
--- a/third_party/blink/web_tests/css3/blending/mix-blend-mode-composited-reason-children-expected.txt
+++ b/third_party/blink/web_tests/css3/blending/mix-blend-mode-composited-reason-children-expected.txt
@@ -14,7 +14,6 @@
     {
       "name": "LayoutNGBlockFlow DIV class='blended'",
       "bounds": [160, 90],
-      "blendMode": "multiply",
       "backgroundColor": "#0000FF",
       "transform": 1
     },
diff --git a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_6.json b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_6.json
index e7153d3..b036f87 100644
--- a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_6.json
+++ b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_6.json
@@ -11953,6 +11953,18 @@
      {}
     ]
    ],
+   "css/CSS2/linebox/line-breaking-font-size-zero-001.html": [
+    [
+     "css/CSS2/linebox/line-breaking-font-size-zero-001.html",
+     [
+      [
+       "/css/CSS2/linebox/line-breaking-font-size-zero-001-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
    "css/CSS2/linebox/line-height-002.xht": [
     [
      "css/CSS2/linebox/line-height-002.xht",
@@ -124906,9 +124918,6 @@
    "common/canvas-tests.js.headers": [
     []
    ],
-   "common/css-paint-tests.js.headers": [
-    []
-   ],
    "common/css-red.txt": [
     []
    ],
@@ -125119,6 +125128,9 @@
    "common/worklet-reftest.js": [
     []
    ],
+   "common/worklet-reftest.js.headers": [
+    []
+   ],
    "compat/META.yml": [
     []
    ],
@@ -128476,6 +128488,9 @@
    "css/CSS2/linebox/line-box-height-002-ref.xht": [
     []
    ],
+   "css/CSS2/linebox/line-breaking-font-size-zero-001-ref.html": [
+    []
+   ],
    "css/CSS2/linebox/line-height-002-ref.xht": [
     []
    ],
@@ -208806,6 +208821,12 @@
      {}
     ]
    ],
+   "css/css-logical/animations/float-interpolation.html": [
+    [
+     "css/css-logical/animations/float-interpolation.html",
+     {}
+    ]
+   ],
    "css/css-logical/inheritance.html": [
     [
      "css/css-logical/inheritance.html",
@@ -338366,10 +338387,6 @@
    "6805c323df5a975231648b830e33ce183c3cbbd3",
    "support"
   ],
-  "common/css-paint-tests.js.headers": [
-   "6805c323df5a975231648b830e33ce183c3cbbd3",
-   "support"
-  ],
   "common/css-red.txt": [
    "9ef04cbd12daf19dd8810cae8844bd22f3264926",
    "support"
@@ -338650,6 +338667,10 @@
    "d92c76cabf3c4a5bd5fa56306382e09a6d13b43f",
    "support"
   ],
+  "common/worklet-reftest.js.headers": [
+   "6805c323df5a975231648b830e33ce183c3cbbd3",
+   "support"
+  ],
   "compat/META.yml": [
    "f2aec22ba3293202686b7dc8832911f701bb23c0",
    "support"
@@ -347654,6 +347675,14 @@
    "9c23382644a2c2df557d080034874eec9d50ed80",
    "reftest"
   ],
+  "css/CSS2/linebox/line-breaking-font-size-zero-001-ref.html": [
+   "c9ad49695f1a4936d444adf50b8d890b1d0e5815",
+   "support"
+  ],
+  "css/CSS2/linebox/line-breaking-font-size-zero-001.html": [
+   "6378af39fa1758b8114d2b608cedfd62d84dd275",
+   "reftest"
+  ],
   "css/CSS2/linebox/line-height-001.xht": [
    "093c528849be17c21490b0d66118531d46e3b24d",
    "visual"
@@ -383730,6 +383759,10 @@
    "c1bed638722d0fc10bfb6605b0bdbbdd96284bbb",
    "testharness"
   ],
+  "css/css-logical/animations/float-interpolation.html": [
+   "c067f2f03ceb568154bee543f5f9b7a1989b940a",
+   "testharness"
+  ],
   "css/css-logical/cascading-001-ref.html": [
    "79a432c4557bbda081a9b1c8d0dd9602c0eb85e5",
    "support"
@@ -499731,7 +499764,7 @@
    "support"
   ],
   "tools/manifest/XMLParser.py": [
-   "cc0a069495c1c15425b37a56b63553748be23089",
+   "6f5ff4d35dab679599b1155c6bbb334b2f07f7b1",
    "support"
   ],
   "tools/manifest/__init__.py": [
@@ -499751,7 +499784,7 @@
    "support"
   ],
   "tools/manifest/item.py": [
-   "9619813fc9beadb9f6584df2300c9d8e56b162b5",
+   "d7165787ca4d6f8e398ecb4400673da4aeaf2d49",
    "support"
   ],
   "tools/manifest/log.py": [
@@ -500051,7 +500084,7 @@
    "support"
   ],
   "tools/requirements_mypy.txt": [
-   "c1bf4bc088e7003d7ea2b362a28048e8952d2031",
+   "606d801b5fdaef6e4d93167416a2bf82994ecdaf",
    "support"
   ],
   "tools/runner/css/bootstrap-theme.min.css": [
@@ -504011,7 +504044,7 @@
    "support"
   ],
   "tools/wptrunner/requirements.txt": [
-   "9eeb836230c39def1291c210a9690cbb5b24c117",
+   "dbed37ccdcdd132993ccda93c235125920096984",
    "support"
   ],
   "tools/wptrunner/requirements_android_webview.txt": [
diff --git a/third_party/blink/web_tests/external/wpt/common/css-paint-tests.js.headers b/third_party/blink/web_tests/external/wpt/common/worklet-reftest.js.headers
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/common/css-paint-tests.js.headers
rename to third_party/blink/web_tests/external/wpt/common/worklet-reftest.js.headers
diff --git a/third_party/blink/web_tests/external/wpt/css/CSS2/floats-clear/remove-block-before-self-collapsing-sibling-with-clearance.html b/third_party/blink/web_tests/external/wpt/css/CSS2/floats-clear/remove-block-before-self-collapsing-sibling-with-clearance.html
new file mode 100644
index 0000000..c7a4006
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/CSS2/floats-clear/remove-block-before-self-collapsing-sibling-with-clearance.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+<link rel="help" href="https://www.w3.org/TR/CSS22/visuren.html#clearance">
+<link rel="help" href="https://www.w3.org/TR/CSS22/box.html#collapsing-margins">
+<link rel="help" href="https://www.w3.org/TR/CSS22/visuren.html#phantom-line-box">
+<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=1003810">
+<link rel="match" href="../reference/ref-filled-green-100px-square.xht">
+<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
+<div style="overflow:hidden;">
+  <div id="first" style="height:100px;"></div>
+  <div id="victim" style="width:100px; padding-bottom:1px; background:red;">
+    <span></span>
+    <div style="float:left; width:100%; height:100px; background:green;"></div>
+    <div style="clear:both;"></div>
+  </div>
+</div>
+<script>
+  document.body.offsetTop;
+  victim.style.paddingBottom = "0";
+  first.style.display = "none";
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/pointerevents/compat/pointerevent_touch-action_two-finger_interaction-manual.html b/third_party/blink/web_tests/external/wpt/pointerevents/compat/pointerevent_touch-action_two-finger_interaction.html
similarity index 61%
rename from third_party/blink/web_tests/external/wpt/pointerevents/compat/pointerevent_touch-action_two-finger_interaction-manual.html
rename to third_party/blink/web_tests/external/wpt/pointerevents/compat/pointerevent_touch-action_two-finger_interaction.html
index 3537e0e..c33aa20 100644
--- a/third_party/blink/web_tests/external/wpt/pointerevents/compat/pointerevent_touch-action_two-finger_interaction-manual.html
+++ b/third_party/blink/web_tests/external/wpt/pointerevents/compat/pointerevent_touch-action_two-finger_interaction.html
@@ -9,16 +9,38 @@
     <link rel="stylesheet" type="text/css" href="../pointerevent_styles.css">
     <script src="/resources/testharness.js"></script>
     <script src="/resources/testharnessreport.js"></script>
+    <script src="/resources/testdriver.js"></script>
+    <script src="/resources/testdriver-actions.js"></script>
+    <script src="/resources/testdriver-vendor.js"></script>
     <script type="text/javascript" src="../pointerevent_support.js"></script>
     <script type="text/javascript">
       var event_log = [];
       var active_pointers = 0;
+      var actions_promise;
 
       function resetTestState() {
         event_log = [];
         active_pointers = 0;
       }
 
+      function twoFingerDrag(target) {
+        return new test_driver.Actions()
+                      .addPointer("touchPointer1", "touch")
+                      .addPointer("touchPointer2", "touch")
+                      .pointerMove(0, 0, {origin: target, sourceName: "touchPointer1"})
+                      .pointerMove(10, 0, {origin: target, sourceName: "touchPointer2"})
+                      .pointerDown({sourceName: "touchPointer1"})
+                      .pointerDown({sourceName: "touchPointer2"})
+                      .pointerMove(0, 10, {origin: target, sourceName: "touchPointer1"})
+                      .pointerMove(10, 10, {origin: target, sourceName: "touchPointer2"})
+                      .pointerMove(0, 20, {origin: target, sourceName: "touchPointer1"})
+                      .pointerMove(10, 20, {origin: target, sourceName: "touchPointer2"})
+                      .pause(100)
+                      .pointerUp({sourceName: "touchPointer1"})
+                      .pointerUp({sourceName: "touchPointer2"})
+                      .send();
+      }
+
       function run() {
         var test_pointer_events = [
           setup_pointerevent_test("two-finger pan on 'touch-action: pan-x pan-y'", ["touch"]),
@@ -29,18 +51,29 @@
           "pointerdown@grey, pointerdown@grey, pointercancel@grey, pointercancel@grey"
         ];
         var current_test_index = 0;
+        var black = document.getElementById("black");
+        var grey = document.getElementById("grey");
+        var done = document.getElementById("done");
 
-        on_event(document.getElementById("done"), "click", function() {
+        on_event(done, "click", function() {
           test_pointer_events[current_test_index].step(function () {
             assert_equals(active_pointers, 0);
             assert_equals(event_log.join(", "), expected_events[current_test_index]);
           });
           event_log = [];
 
-          test_pointer_events[current_test_index++].done();
+          // Make sure the test finishes after all the input actions are completed.
+          actions_promise.then( () => {
+            test_pointer_events[current_test_index++].done();
+            if (current_test_index < 2) {
+              actions_promise = twoFingerDrag(grey).then(function() {
+                return clickInTarget("touch", done);
+              });
+            }
+          });
         });
 
-        var targets = [document.getElementById("black"), document.getElementById("grey")];
+        var targets = [black, grey];
 
         ["pointerdown", "pointerup", "pointercancel"].forEach(function(eventName) {
           targets.forEach(function(target){
@@ -49,13 +82,17 @@
 
               if (event.type == "pointerdown") {
                 active_pointers++;
-
               } else {
                 active_pointers--;
               }
             });
           });
         });
+
+        // Inject touch inputs.
+        actions_promise = twoFingerDrag(black).then(function() {
+          return clickInTarget("touch", done);
+        });
       }
     </script>
     <style>
diff --git a/third_party/blink/web_tests/external/wpt/tools/manifest/XMLParser.py b/third_party/blink/web_tests/external/wpt/tools/manifest/XMLParser.py
index cc0a069..6f5ff4d3 100644
--- a/third_party/blink/web_tests/external/wpt/tools/manifest/XMLParser.py
+++ b/third_party/blink/web_tests/external/wpt/tools/manifest/XMLParser.py
@@ -20,8 +20,8 @@
 def _wrap_error(e):
     # type: (expat.error) -> etree.ParseError
     err = etree.ParseError(e)
-    err.code = e.code  # type: ignore
-    err.position = e.lineno, e.offset  # type: ignore
+    err.code = e.code
+    err.position = e.lineno, e.offset
     raise err
 
 _names = {}  # type: Dict[str, str]
diff --git a/third_party/blink/web_tests/external/wpt/tools/manifest/item.py b/third_party/blink/web_tests/external/wpt/tools/manifest/item.py
index 9619813f..d716578 100644
--- a/third_party/blink/web_tests/external/wpt/tools/manifest/item.py
+++ b/third_party/blink/web_tests/external/wpt/tools/manifest/item.py
@@ -31,14 +31,14 @@
     attribute, and otherwise behaves like an ABCMeta."""
 
     def __new__(cls, name, bases, attrs):
-        # type: (Type[ManifestItemMeta], str, Tuple[ManifestItemMeta, ...], Dict[str, Any]) -> type
-        rv = ABCMeta.__new__(cls, name, bases, attrs)
+        # type: (Type[ManifestItemMeta], str, Tuple[ManifestItemMeta, ...], Dict[str, Any]) -> ManifestItemMeta
+        rv = super(ManifestItemMeta, cls).__new__(cls, name, bases, attrs)
         if not isabstract(rv):
             assert issubclass(rv, ManifestItem)
             assert isinstance(rv.item_type, str)
             item_types[rv.item_type] = rv
 
-        return rv
+        return rv  # type: ignore
 
 
 class ManifestItem(with_metaclass(ManifestItemMeta)):
diff --git a/third_party/blink/web_tests/external/wpt/tools/requirements_mypy.txt b/third_party/blink/web_tests/external/wpt/tools/requirements_mypy.txt
index c1bf4bc0..606d801 100644
--- a/third_party/blink/web_tests/external/wpt/tools/requirements_mypy.txt
+++ b/third_party/blink/web_tests/external/wpt/tools/requirements_mypy.txt
@@ -1,3 +1,3 @@
-mypy==0.720
+mypy==0.730
 mypy-extensions==0.4.1
 typed-ast==1.4.0
diff --git a/third_party/blink/web_tests/external/wpt/tools/wptrunner/requirements.txt b/third_party/blink/web_tests/external/wpt/tools/wptrunner/requirements.txt
index 9eeb8362..dbed37cc 100644
--- a/third_party/blink/web_tests/external/wpt/tools/wptrunner/requirements.txt
+++ b/third_party/blink/web_tests/external/wpt/tools/wptrunner/requirements.txt
@@ -2,7 +2,7 @@
 mozinfo==1.1.0
 mozlog==4.2.0
 mozdebug==0.1.1
-pillow==6.1.0
+pillow==6.2.0
 urllib3[secure]==1.25.6
 requests==2.22.0
 six==1.12.0
diff --git a/third_party/blink/web_tests/external/wpt_automation/pointerevents/compat/pointerevent_touch-action_two-finger_interaction-manual-automation.js b/third_party/blink/web_tests/external/wpt_automation/pointerevents/compat/pointerevent_touch-action_two-finger_interaction-manual-automation.js
deleted file mode 100644
index 4c994216..0000000
--- a/third_party/blink/web_tests/external/wpt_automation/pointerevents/compat/pointerevent_touch-action_two-finger_interaction-manual-automation.js
+++ /dev/null
@@ -1,11 +0,0 @@
-importAutomationScript('/pointerevents/pointerevent_common_input.js');
-
-function inject_input() {
-  return twoPointerDragInTarget('touch', '#black', 'down').then(function() {
-    return touchTapInTarget('#done');
-  }).then(function() {
-    return twoPointerDragInTarget('touch', '#grey', 'down');
-  }).then(function() {
-    return touchTapInTarget('#done');
-  });
-}
diff --git a/third_party/blink/web_tests/external/wpt_automation/pointerevents/pointerevent_common_input.js b/third_party/blink/web_tests/external/wpt_automation/pointerevents/pointerevent_common_input.js
index 3bc0bec2..ba53a78 100644
--- a/third_party/blink/web_tests/external/wpt_automation/pointerevents/pointerevent_common_input.js
+++ b/third_party/blink/web_tests/external/wpt_automation/pointerevents/pointerevent_common_input.js
@@ -279,57 +279,6 @@
   });
 }
 
-function twoPointerDragInTarget(pointerType, targetSelector, direction) {
-  return new Promise(function(resolve, reject) {
-    if (window.chrome && chrome.gpuBenchmarking) {
-      scrollPageIfNeeded(targetSelector, document);
-      var target = document.querySelector(targetSelector);
-      var targetRect = target.getBoundingClientRect();
-      var xPosition1 = targetRect.left + boundaryOffset + scrollOffset;
-      var yPosition1 = targetRect.top + boundaryOffset + scrollOffset;
-      var xPosition2 = xPosition1;
-      var yPosition2 = yPosition1;
-      var xPosition3 = xPosition1;
-      var yPosition3 = yPosition1;
-      if (direction == "down") {
-        yPosition1 -= scrollOffset;
-        yPosition3 += scrollOffset;
-      } else if (direction == "up") {
-        yPosition1 += scrollOffset;
-        yPosition3 -= scrollOffset;
-      } else if (direction == "right") {
-        xPosition1 -= scrollOffset;
-        xPosition3 += scrollOffset;
-      } else if (direction == "left") {
-        xPosition1 += scrollOffset;
-        xPosition3 -= scrollOffset;
-      } else {
-        throw("drag direction '" + direction + "' is not expected, direction should be 'down', 'up', 'left' or 'right'");
-      }
-
-      chrome.gpuBenchmarking.pointerActionSequence( [
-        {source: pointerType,
-         actions: [
-            { name: 'pointerDown', x: xPosition1, y: yPosition1 },
-            { name: 'pointerMove', x: xPosition2, y: yPosition2 },
-            { name: 'pointerMove', x: xPosition3, y: yPosition3 },
-            { name: 'pause', duration: 100 },
-            { name: 'pointerUp' }
-        ]},
-        {source: pointerType,
-         actions: [
-            { name: 'pointerDown', x: xPosition1 + boundaryOffset, y: yPosition1 },
-            { name: 'pointerMove', x: xPosition2 + boundaryOffset, y: yPosition2 },
-            { name: 'pointerMove', x: xPosition3 + boundaryOffset, y: yPosition3 },
-            { name: 'pause', duration: 100 },
-            { name: 'pointerUp' }
-        ]}], resolve);
-    } else {
-      reject();
-    }
-  });
-}
-
 function pointerDragInTarget(pointerType, targetSelector, direction) {
   return new Promise(function(resolve, reject) {
     if (window.chrome && chrome.gpuBenchmarking) {
diff --git a/third_party/blink/web_tests/flag-specific/disable-blink-features=LayoutNG/compositing/contents-opaque/layer-opacity-expected.txt b/third_party/blink/web_tests/flag-specific/disable-blink-features=LayoutNG/compositing/contents-opaque/layer-opacity-expected.txt
index cf30c4e..875836b 100644
--- a/third_party/blink/web_tests/flag-specific/disable-blink-features=LayoutNG/compositing/contents-opaque/layer-opacity-expected.txt
+++ b/third_party/blink/web_tests/flag-specific/disable-blink-features=LayoutNG/compositing/contents-opaque/layer-opacity-expected.txt
@@ -9,7 +9,6 @@
     {
       "name": "LayoutBlockFlow DIV class='box opaque-background translucent composited'",
       "bounds": [100, 100],
-      "opacity": 0.5,
       "contentsOpaque": true,
       "backgroundColor": "#008000",
       "transform": 1
diff --git a/third_party/blink/web_tests/flag-specific/disable-blink-features=LayoutNG/compositing/geometry/preserve-3d-switching-expected.txt b/third_party/blink/web_tests/flag-specific/disable-blink-features=LayoutNG/compositing/geometry/preserve-3d-switching-expected.txt
index cd50d87..6bea2705 100644
--- a/third_party/blink/web_tests/flag-specific/disable-blink-features=LayoutNG/compositing/geometry/preserve-3d-switching-expected.txt
+++ b/third_party/blink/web_tests/flag-specific/disable-blink-features=LayoutNG/compositing/geometry/preserve-3d-switching-expected.txt
@@ -24,7 +24,6 @@
     {
       "name": "LayoutBlockFlow (positioned) DIV",
       "bounds": [200, 200],
-      "opacity": 0.699999988079071,
       "contentsOpaque": true,
       "backgroundColor": "#008000",
       "transform": 9
diff --git a/third_party/blink/web_tests/flag-specific/disable-blink-features=LayoutNG/compositing/squashing/opacity-squashed-owner-expected.txt b/third_party/blink/web_tests/flag-specific/disable-blink-features=LayoutNG/compositing/squashing/opacity-squashed-owner-expected.txt
index 6c5ea2b..3e467c7 100644
--- a/third_party/blink/web_tests/flag-specific/disable-blink-features=LayoutNG/compositing/squashing/opacity-squashed-owner-expected.txt
+++ b/third_party/blink/web_tests/flag-specific/disable-blink-features=LayoutNG/compositing/squashing/opacity-squashed-owner-expected.txt
@@ -9,7 +9,6 @@
     {
       "name": "LayoutBlockFlow DIV id='target' class='composited box opaque'",
       "bounds": [100, 100],
-      "opacity": 0.5,
       "contentsOpaque": true,
       "backgroundColor": "#ADD8E6",
       "transform": 1
diff --git a/third_party/blink/web_tests/flag-specific/disable-blink-features=LayoutNG/compositing/visibility/layer-visible-content-expected.txt b/third_party/blink/web_tests/flag-specific/disable-blink-features=LayoutNG/compositing/visibility/layer-visible-content-expected.txt
index 7cf52ec8..fe0c652 100644
--- a/third_party/blink/web_tests/flag-specific/disable-blink-features=LayoutNG/compositing/visibility/layer-visible-content-expected.txt
+++ b/third_party/blink/web_tests/flag-specific/disable-blink-features=LayoutNG/compositing/visibility/layer-visible-content-expected.txt
@@ -12,8 +12,7 @@
     },
     {
       "name": "LayoutBlockFlow PRE id='layer-tree'",
-      "bounds": [800, 16],
-      "opacity": 0
+      "bounds": [800, 16]
     }
   ]
 }
diff --git a/third_party/blink/web_tests/flag-specific/disable-blink-features=LayoutNG/css3/blending/mix-blend-mode-composited-layers-expected.txt b/third_party/blink/web_tests/flag-specific/disable-blink-features=LayoutNG/css3/blending/mix-blend-mode-composited-layers-expected.txt
index 9b8bc579..770d1ff 100644
--- a/third_party/blink/web_tests/flag-specific/disable-blink-features=LayoutNG/css3/blending/mix-blend-mode-composited-layers-expected.txt
+++ b/third_party/blink/web_tests/flag-specific/disable-blink-features=LayoutNG/css3/blending/mix-blend-mode-composited-layers-expected.txt
@@ -7,7 +7,6 @@
     {
       "name": "LayoutBlockFlow DIV",
       "bounds": [10, 10],
-      "blendMode": "multiply",
       "backgroundColor": "#0000FF",
       "transform": 1
     },
diff --git a/third_party/blink/web_tests/flag-specific/disable-blink-features=LayoutNG/css3/blending/mix-blend-mode-composited-reason-children-expected.txt b/third_party/blink/web_tests/flag-specific/disable-blink-features=LayoutNG/css3/blending/mix-blend-mode-composited-reason-children-expected.txt
index c2a33245..3b46e57 100644
--- a/third_party/blink/web_tests/flag-specific/disable-blink-features=LayoutNG/css3/blending/mix-blend-mode-composited-reason-children-expected.txt
+++ b/third_party/blink/web_tests/flag-specific/disable-blink-features=LayoutNG/css3/blending/mix-blend-mode-composited-reason-children-expected.txt
@@ -14,7 +14,6 @@
     {
       "name": "LayoutBlockFlow DIV class='blended'",
       "bounds": [160, 90],
-      "blendMode": "multiply",
       "backgroundColor": "#0000FF",
       "transform": 1
     },
diff --git a/third_party/blink/web_tests/flag-specific/disable-blink-features=LayoutNG/paint/invalidation/animation/opacity-animation-expected.txt b/third_party/blink/web_tests/flag-specific/disable-blink-features=LayoutNG/paint/invalidation/animation/opacity-animation-expected.txt
index 7676a15a..99ecce64 100644
--- a/third_party/blink/web_tests/flag-specific/disable-blink-features=LayoutNG/paint/invalidation/animation/opacity-animation-expected.txt
+++ b/third_party/blink/web_tests/flag-specific/disable-blink-features=LayoutNG/paint/invalidation/animation/opacity-animation-expected.txt
@@ -9,7 +9,6 @@
     {
       "name": "LayoutBlockFlow DIV id='target'",
       "bounds": [100, 100],
-      "opacity": 0.5,
       "contentsOpaque": true,
       "backgroundColor": "#808080",
       "transform": 1
diff --git a/third_party/blink/web_tests/flag-specific/disable-blink-features=LayoutNG/paint/invalidation/compositing/fixed-pos-inside-composited-intermediate-layer-expected.txt b/third_party/blink/web_tests/flag-specific/disable-blink-features=LayoutNG/paint/invalidation/compositing/fixed-pos-inside-composited-intermediate-layer-expected.txt
index d8c9e82..329dc4ba 100644
--- a/third_party/blink/web_tests/flag-specific/disable-blink-features=LayoutNG/paint/invalidation/compositing/fixed-pos-inside-composited-intermediate-layer-expected.txt
+++ b/third_party/blink/web_tests/flag-specific/disable-blink-features=LayoutNG/paint/invalidation/compositing/fixed-pos-inside-composited-intermediate-layer-expected.txt
@@ -24,7 +24,6 @@
     {
       "name": "LayoutBlockFlow (positioned) DIV class='fixed'",
       "bounds": [50, 50],
-      "opacity": 0.990000009536743,
       "contentsOpaque": true,
       "backgroundColor": "#00FF00",
       "transform": 6
diff --git a/third_party/blink/web_tests/flag-specific/disable-blink-features=LayoutNG/paint/invalidation/compositing/fixed-pos-with-abs-pos-child-scroll-expected.txt b/third_party/blink/web_tests/flag-specific/disable-blink-features=LayoutNG/paint/invalidation/compositing/fixed-pos-with-abs-pos-child-scroll-expected.txt
index c149434..a3f16770 100644
--- a/third_party/blink/web_tests/flag-specific/disable-blink-features=LayoutNG/paint/invalidation/compositing/fixed-pos-with-abs-pos-child-scroll-expected.txt
+++ b/third_party/blink/web_tests/flag-specific/disable-blink-features=LayoutNG/paint/invalidation/compositing/fixed-pos-with-abs-pos-child-scroll-expected.txt
@@ -11,7 +11,6 @@
       "name": "LayoutBlockFlow (positioned) DIV id='fixed'",
       "position": [-360, -90],
       "bounds": [360, 90],
-      "opacity": 0.990000009536743,
       "contentsOpaque": true,
       "transform": 1
     }
diff --git a/third_party/blink/web_tests/flag-specific/disable-blink-features=LayoutNG/paint/invalidation/compositing/fixed-scroll-in-empty-root-layer-expected.txt b/third_party/blink/web_tests/flag-specific/disable-blink-features=LayoutNG/paint/invalidation/compositing/fixed-scroll-in-empty-root-layer-expected.txt
index a471a538..bbf39de4 100644
--- a/third_party/blink/web_tests/flag-specific/disable-blink-features=LayoutNG/paint/invalidation/compositing/fixed-scroll-in-empty-root-layer-expected.txt
+++ b/third_party/blink/web_tests/flag-specific/disable-blink-features=LayoutNG/paint/invalidation/compositing/fixed-scroll-in-empty-root-layer-expected.txt
@@ -21,7 +21,6 @@
     {
       "name": "LayoutBlockFlow (positioned) DIV",
       "bounds": [100, 100],
-      "opacity": 0.990000009536743,
       "contentsOpaque": true,
       "backgroundColor": "#008000",
       "transform": 4
diff --git a/third_party/blink/web_tests/flag-specific/disable-blink-features=LayoutNG/paint/invalidation/compositing/new-stacking-context-expected.txt b/third_party/blink/web_tests/flag-specific/disable-blink-features=LayoutNG/paint/invalidation/compositing/new-stacking-context-expected.txt
index a3de227..02659d8 100644
--- a/third_party/blink/web_tests/flag-specific/disable-blink-features=LayoutNG/paint/invalidation/compositing/new-stacking-context-expected.txt
+++ b/third_party/blink/web_tests/flag-specific/disable-blink-features=LayoutNG/paint/invalidation/compositing/new-stacking-context-expected.txt
@@ -38,7 +38,6 @@
     {
       "name": "LayoutBlockFlow DIV id='target'",
       "bounds": [200, 200],
-      "opacity": 0.5,
       "contentsOpaque": true,
       "backgroundColor": "#008000",
       "paintInvalidations": [
diff --git a/third_party/blink/web_tests/flag-specific/disable-blink-features=LayoutNG/paint/invalidation/compositing/opacity-from-zero-to-non-zero-composited-expected.txt b/third_party/blink/web_tests/flag-specific/disable-blink-features=LayoutNG/paint/invalidation/compositing/opacity-from-zero-to-non-zero-composited-expected.txt
index a17bc471..7f4b5f8 100644
--- a/third_party/blink/web_tests/flag-specific/disable-blink-features=LayoutNG/paint/invalidation/compositing/opacity-from-zero-to-non-zero-composited-expected.txt
+++ b/third_party/blink/web_tests/flag-specific/disable-blink-features=LayoutNG/paint/invalidation/compositing/opacity-from-zero-to-non-zero-composited-expected.txt
@@ -9,7 +9,6 @@
     {
       "name": "LayoutBlockFlow DIV id='target'",
       "bounds": [100, 100],
-      "opacity": 0.5,
       "contentsOpaque": true,
       "backgroundColor": "#008000",
       "transform": 1
diff --git a/third_party/blink/web_tests/flag-specific/disable-blink-features=LayoutNG/paint/invalidation/compositing/should-not-repaint-composited-opacity-expected.txt b/third_party/blink/web_tests/flag-specific/disable-blink-features=LayoutNG/paint/invalidation/compositing/should-not-repaint-composited-opacity-expected.txt
index 3f9ff3b..2f53b999 100644
--- a/third_party/blink/web_tests/flag-specific/disable-blink-features=LayoutNG/paint/invalidation/compositing/should-not-repaint-composited-opacity-expected.txt
+++ b/third_party/blink/web_tests/flag-specific/disable-blink-features=LayoutNG/paint/invalidation/compositing/should-not-repaint-composited-opacity-expected.txt
@@ -9,7 +9,6 @@
     {
       "name": "LayoutBlockFlow DIV id='composited-box'",
       "bounds": [100, 100],
-      "opacity": 0.5,
       "contentsOpaque": true,
       "backfaceVisibility": "hidden",
       "backgroundColor": "#008000",
diff --git a/third_party/blink/web_tests/flag-specific/disable-blink-features=LayoutNG/paint/invalidation/filters/filter-on-html-element-with-fixed-position-child-expected.txt b/third_party/blink/web_tests/flag-specific/disable-blink-features=LayoutNG/paint/invalidation/filters/filter-on-html-element-with-fixed-position-child-expected.txt
index 0717cc7c..cf2a932 100644
--- a/third_party/blink/web_tests/flag-specific/disable-blink-features=LayoutNG/paint/invalidation/filters/filter-on-html-element-with-fixed-position-child-expected.txt
+++ b/third_party/blink/web_tests/flag-specific/disable-blink-features=LayoutNG/paint/invalidation/filters/filter-on-html-element-with-fixed-position-child-expected.txt
@@ -8,7 +8,6 @@
     {
       "name": "LayoutBlockFlow (positioned) DIV",
       "bounds": [100, 100],
-      "opacity": 0.990000009536743,
       "contentsOpaque": true,
       "backgroundColor": "#D3D3D3",
       "transform": 2
@@ -16,7 +15,6 @@
     {
       "name": "LayoutBlockFlow (positioned) DIV",
       "bounds": [100, 100],
-      "opacity": 0.990000009536743,
       "contentsOpaque": true,
       "backgroundColor": "#D3D3D3",
       "transform": 3
diff --git a/third_party/blink/web_tests/flag-specific/disable-blink-features=LayoutNG/paint/invalidation/position/absolute-position-changed-expected.txt b/third_party/blink/web_tests/flag-specific/disable-blink-features=LayoutNG/paint/invalidation/position/absolute-position-changed-expected.txt
index 08ee69ee..eb20064 100644
--- a/third_party/blink/web_tests/flag-specific/disable-blink-features=LayoutNG/paint/invalidation/position/absolute-position-changed-expected.txt
+++ b/third_party/blink/web_tests/flag-specific/disable-blink-features=LayoutNG/paint/invalidation/position/absolute-position-changed-expected.txt
@@ -18,7 +18,6 @@
     {
       "name": "LayoutBlockFlow (positioned) DIV class='fixed red'",
       "bounds": [100, 100],
-      "opacity": 0.990000009536743,
       "contentsOpaque": true,
       "backgroundColor": "#FF0000",
       "transform": 2
diff --git a/third_party/blink/web_tests/flag-specific/disable-blink-features=LayoutNG/paint/invalidation/position/fixed-expected.txt b/third_party/blink/web_tests/flag-specific/disable-blink-features=LayoutNG/paint/invalidation/position/fixed-expected.txt
index 9e49a19..187cf27 100644
--- a/third_party/blink/web_tests/flag-specific/disable-blink-features=LayoutNG/paint/invalidation/position/fixed-expected.txt
+++ b/third_party/blink/web_tests/flag-specific/disable-blink-features=LayoutNG/paint/invalidation/position/fixed-expected.txt
@@ -11,7 +11,6 @@
     {
       "name": "LayoutBlockFlow (positioned) DIV id='t'",
       "bounds": [100, 100],
-      "opacity": 0.990000009536743,
       "contentsOpaque": true,
       "backgroundColor": "#008000",
       "paintInvalidations": [
diff --git a/third_party/blink/web_tests/flag-specific/disable-blink-features=LayoutNG/paint/invalidation/scroll/fixed-child-move-after-scroll-expected.txt b/third_party/blink/web_tests/flag-specific/disable-blink-features=LayoutNG/paint/invalidation/scroll/fixed-child-move-after-scroll-expected.txt
index 167c24c..3fdfe84 100644
--- a/third_party/blink/web_tests/flag-specific/disable-blink-features=LayoutNG/paint/invalidation/scroll/fixed-child-move-after-scroll-expected.txt
+++ b/third_party/blink/web_tests/flag-specific/disable-blink-features=LayoutNG/paint/invalidation/scroll/fixed-child-move-after-scroll-expected.txt
@@ -11,7 +11,6 @@
     {
       "name": "LayoutBlockFlow (positioned) DIV id='toMove'",
       "bounds": [100, 100],
-      "opacity": 0.990000009536743,
       "contentsOpaque": true,
       "backgroundColor": "#008000",
       "paintInvalidations": [
diff --git a/third_party/blink/web_tests/flag-specific/disable-blink-features=LayoutNG/paint/invalidation/scroll/fixed-child-of-fixed-move-after-scroll-expected.txt b/third_party/blink/web_tests/flag-specific/disable-blink-features=LayoutNG/paint/invalidation/scroll/fixed-child-of-fixed-move-after-scroll-expected.txt
index 167c24c..3fdfe84 100644
--- a/third_party/blink/web_tests/flag-specific/disable-blink-features=LayoutNG/paint/invalidation/scroll/fixed-child-of-fixed-move-after-scroll-expected.txt
+++ b/third_party/blink/web_tests/flag-specific/disable-blink-features=LayoutNG/paint/invalidation/scroll/fixed-child-of-fixed-move-after-scroll-expected.txt
@@ -11,7 +11,6 @@
     {
       "name": "LayoutBlockFlow (positioned) DIV id='toMove'",
       "bounds": [100, 100],
-      "opacity": 0.990000009536743,
       "contentsOpaque": true,
       "backgroundColor": "#008000",
       "paintInvalidations": [
diff --git a/third_party/blink/web_tests/flag-specific/disable-blink-features=LayoutNG/paint/invalidation/scroll/fixed-move-after-scroll-expected.txt b/third_party/blink/web_tests/flag-specific/disable-blink-features=LayoutNG/paint/invalidation/scroll/fixed-move-after-scroll-expected.txt
index 167c24c..3fdfe84 100644
--- a/third_party/blink/web_tests/flag-specific/disable-blink-features=LayoutNG/paint/invalidation/scroll/fixed-move-after-scroll-expected.txt
+++ b/third_party/blink/web_tests/flag-specific/disable-blink-features=LayoutNG/paint/invalidation/scroll/fixed-move-after-scroll-expected.txt
@@ -11,7 +11,6 @@
     {
       "name": "LayoutBlockFlow (positioned) DIV id='toMove'",
       "bounds": [100, 100],
-      "opacity": 0.990000009536743,
       "contentsOpaque": true,
       "backgroundColor": "#008000",
       "paintInvalidations": [
diff --git a/third_party/blink/web_tests/flag-specific/disable-blink-features=LayoutNG/paint/invalidation/scroll/fixed-under-composited-absolute-scrolled-expected.txt b/third_party/blink/web_tests/flag-specific/disable-blink-features=LayoutNG/paint/invalidation/scroll/fixed-under-composited-absolute-scrolled-expected.txt
index f1c67a2..ef10b2c2 100644
--- a/third_party/blink/web_tests/flag-specific/disable-blink-features=LayoutNG/paint/invalidation/scroll/fixed-under-composited-absolute-scrolled-expected.txt
+++ b/third_party/blink/web_tests/flag-specific/disable-blink-features=LayoutNG/paint/invalidation/scroll/fixed-under-composited-absolute-scrolled-expected.txt
@@ -19,7 +19,6 @@
     {
       "name": "LayoutBlockFlow (positioned) DIV id='fixed'",
       "bounds": [100, 200],
-      "opacity": 0.990000009536743,
       "contentsOpaque": true,
       "backgroundColor": "#008000",
       "paintInvalidations": [
diff --git a/third_party/blink/web_tests/flag-specific/disable-blink-features=LayoutNG/paint/invalidation/scroll/fixed-with-border-under-composited-absolute-scrolled-expected.txt b/third_party/blink/web_tests/flag-specific/disable-blink-features=LayoutNG/paint/invalidation/scroll/fixed-with-border-under-composited-absolute-scrolled-expected.txt
index 6d7af97..eb1f9d77 100644
--- a/third_party/blink/web_tests/flag-specific/disable-blink-features=LayoutNG/paint/invalidation/scroll/fixed-with-border-under-composited-absolute-scrolled-expected.txt
+++ b/third_party/blink/web_tests/flag-specific/disable-blink-features=LayoutNG/paint/invalidation/scroll/fixed-with-border-under-composited-absolute-scrolled-expected.txt
@@ -19,7 +19,6 @@
     {
       "name": "LayoutBlockFlow (positioned) DIV id='fixed'",
       "bounds": [120, 220],
-      "opacity": 0.990000009536743,
       "contentsOpaque": true,
       "backgroundColor": "#008000",
       "paintInvalidations": [
diff --git a/third_party/blink/web_tests/flag-specific/disable-blink-features=LayoutNG/virtual/threaded/compositing/visibility/layer-visible-content-expected.txt b/third_party/blink/web_tests/flag-specific/disable-blink-features=LayoutNG/virtual/threaded/compositing/visibility/layer-visible-content-expected.txt
deleted file mode 100644
index 7cf52ec8..0000000
--- a/third_party/blink/web_tests/flag-specific/disable-blink-features=LayoutNG/virtual/threaded/compositing/visibility/layer-visible-content-expected.txt
+++ /dev/null
@@ -1,20 +0,0 @@
-{
-  "layers": [
-    {
-      "name": "Scrolling Contents Layer",
-      "bounds": [800, 600],
-      "contentsOpaque": true,
-      "backgroundColor": "#FFFFFF"
-    },
-    {
-      "name": "LayoutBlockFlow (positioned) DIV class='container'",
-      "bounds": [200, 200]
-    },
-    {
-      "name": "LayoutBlockFlow PRE id='layer-tree'",
-      "bounds": [800, 16],
-      "opacity": 0
-    }
-  ]
-}
-
diff --git a/third_party/blink/web_tests/http/tests/misc/destroy-middle-click-locked-target-crash.html b/third_party/blink/web_tests/http/tests/misc/destroy-middle-click-locked-target-crash.html
new file mode 100644
index 0000000..2b89ba9
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/misc/destroy-middle-click-locked-target-crash.html
@@ -0,0 +1,81 @@
+<!DOCTYPE html>
+<title>Clear middle click target if target is destroyed when autoscroll is in progress</title>
+<style>
+#target-iframe1 {
+  height: 100px;
+  width: 100px;
+  overflow-y: scroll;
+  position: absolute;
+  left: 100px;
+  top: 100px;
+}
+</style>
+<script src='/resources/testharness.js'></script>
+<script src='/resources/testharnessreport.js'></script>
+<script src='/js-test-resources/gesture-util.js'></script>
+<script>
+  var container;
+  var iframeScrolled = false;
+  var iframeLoadCount = 0;
+
+  async_test(t => {
+    // On document load, perform middle click on the iframe to start the
+    // autoscroll. When middle click autoscroll is in progress on iframe,
+    // navigate iframe to another cross-domain url and perform mouse move
+    // on root frame. If the bug (crbug.com/1007983) repros, it will crash
+    // the process.
+     window.addEventListener('load', async () => {
+      container = document.getElementById('container');
+      var subFrame = document.getElementsByTagName('iframe')[0];
+      var startx = subFrame.offsetLeft + (subFrame.offsetWidth / 2);
+      var starty = subFrame.offsetTop;
+
+      // move the mouse inside the iframe.
+      await mouseMoveTo(startx + 10 , starty + 10);
+      // middle click in iframe to start autoscroll.
+      await mouseClickOn(startx + 10, starty + 10, 1);
+      await mouseMoveTo(startx + 10, starty + 30);
+
+      //wait for the iframe to scroll.
+      await waitFor(() => {
+          return (iframeScrolled === true);
+      });
+
+      t.step(() => { assert_equals(iframeLoadCount, 1)});
+
+      // Change the source of iframe to naviagate to a cross-domain url,
+      // so it will destory the view.
+      subFrame.src = 'about:blank';
+
+      // Wait for the iframe to load.
+       await waitFor(() => {
+         return (iframeLoadCount === 2);
+       });
+
+      // Peform mouse event on root frame. If the bug repros, renderer will
+      // crash.
+      await mouseMoveTo(10, 10);
+      t.done();
+    });
+  });
+
+  window.addEventListener("message", handleMessage);
+
+  function handleMessage(event){
+    if(event.data.scrollTop > 0) {
+      iframeScrolled = true;
+    }
+  }
+
+  function onIframeLoad(){
+    // Increment the count each time iframe is loaded.
+    iframeLoadCount = iframeLoadCount+1;
+  }
+
+</script>
+<div id="container" style="height: 400px; width: 400px; overflow: auto">
+    <div id="filler" style="height:4000px; width: 4000px"></div>
+<iframe id="target-iframe1" onload="onIframeLoad()"
+  src= "http://localhost:8000/misc/resources/cross-origin-subframe-for-scrolling.html">
+</iframe>
+</div>
diff --git a/third_party/blink/web_tests/http/tests/misc/resources/cross-origin-subframe-for-scrolling.html b/third_party/blink/web_tests/http/tests/misc/resources/cross-origin-subframe-for-scrolling.html
index a20e150..d1030b6 100644
--- a/third_party/blink/web_tests/http/tests/misc/resources/cross-origin-subframe-for-scrolling.html
+++ b/third_party/blink/web_tests/http/tests/misc/resources/cross-origin-subframe-for-scrolling.html
@@ -33,6 +33,10 @@
 window.onscroll = (() => {
   if (port) {
     port.postMessage({scrolled: frame_id, scrollTop: document.documentElement.scrollTop}, "*");
+  } else{
+    // If parent is performing middle click autoscroll on iframe, send the
+    // scrollTop data back to parent.
+    parent.postMessage({scrolled: frame_id, scrollTop: document.documentElement.scrollTop}, "*");
   }
 });
 
diff --git a/third_party/blink/web_tests/http/tests/security/mask-image-cors-allow-origin-expected.html b/third_party/blink/web_tests/http/tests/security/mask-image-cors-allow-origin-expected.html
new file mode 100644
index 0000000..83364b7
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/security/mask-image-cors-allow-origin-expected.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<style>
+body { padding: 50px; }
+
+#expected {
+  width: 100px;
+  height: 100px;
+  background-color: green;
+}
+</style>
+<body>
+<div id="expected"></div>
+</body>
diff --git a/third_party/blink/web_tests/http/tests/security/mask-image-cors-allow-origin.html b/third_party/blink/web_tests/http/tests/security/mask-image-cors-allow-origin.html
new file mode 100644
index 0000000..25e6d1b
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/security/mask-image-cors-allow-origin.html
@@ -0,0 +1,13 @@
+<!doctype html>
+<style>
+#target {
+  width: 200px;
+  height: 200px;
+  background-color: green;
+  /* Cross-origin request is OK because the "Access-Control-Allow-Origin: *" is returned. */
+  -webkit-mask-image: url("http://localhost:8080/security/resources/image-access-control.php?file=../../resources/square100.png&allow=true");
+  -webkit-mask-repeat: no-repeat;
+  -webkit-mask-position: center;
+}
+</style>
+<div id="target"></div>
diff --git a/third_party/blink/web_tests/http/tests/security/mask-image-cors-data-url-expected.html b/third_party/blink/web_tests/http/tests/security/mask-image-cors-data-url-expected.html
new file mode 100644
index 0000000..83364b7
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/security/mask-image-cors-data-url-expected.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<style>
+body { padding: 50px; }
+
+#expected {
+  width: 100px;
+  height: 100px;
+  background-color: green;
+}
+</style>
+<body>
+<div id="expected"></div>
+</body>
diff --git a/third_party/blink/web_tests/http/tests/security/mask-image-cors-data-url.html b/third_party/blink/web_tests/http/tests/security/mask-image-cors-data-url.html
new file mode 100644
index 0000000..7525360
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/security/mask-image-cors-data-url.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<style>
+#target {
+  width: 200px;
+  height: 200px;
+  background-color: green;
+  /* Data URL access is OK. */
+  -webkit-mask-image: url('data:image/svg+xml;utf8, \
+    <svg xmlns="http://www.w3.org/2000/svg" width="200px" height="200px"> \
+      <rect x="50px" y="50px" width="100px" height="100px" fill="black" /> \
+    </svg>');
+}
+</style>
+<div id="target"></div>
diff --git a/third_party/blink/web_tests/http/tests/security/mask-image-cors-disallow-origin-expected.html b/third_party/blink/web_tests/http/tests/security/mask-image-cors-disallow-origin-expected.html
new file mode 100644
index 0000000..bd54434
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/security/mask-image-cors-disallow-origin-expected.html
@@ -0,0 +1,3 @@
+<!DOCTYPE html>
+<html>
+</html>
diff --git a/third_party/blink/web_tests/http/tests/security/mask-image-cors-disallow-origin-expected.txt b/third_party/blink/web_tests/http/tests/security/mask-image-cors-disallow-origin-expected.txt
new file mode 100644
index 0000000..27880ca
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/security/mask-image-cors-disallow-origin-expected.txt
@@ -0,0 +1 @@
+CONSOLE ERROR: Access to image at 'http://localhost:8080/security/resources/image-access-control.php?file=../../resources/square100.png&allow=false' from origin 'http://127.0.0.1:8000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
diff --git a/third_party/blink/web_tests/http/tests/security/mask-image-cors-disallow-origin.html b/third_party/blink/web_tests/http/tests/security/mask-image-cors-disallow-origin.html
new file mode 100644
index 0000000..9274a75
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/security/mask-image-cors-disallow-origin.html
@@ -0,0 +1,13 @@
+<!doctype html>
+<style>
+#target {
+  width: 200px;
+  height: 200px;
+  background-color: green;
+  /* Cross-origin request is not OK because a "Access-Control-Allow-Origin:" header is not returned. */
+  -webkit-mask-image: url("http://localhost:8080/security/resources/image-access-control.php?file=../../resources/square100.png&allow=false");
+  -webkit-mask-repeat: no-repeat;
+  -webkit-mask-position: center;
+}
+</style>
+<div id="target"></div>
diff --git a/third_party/blink/web_tests/http/tests/security/mask-image-cors-port-expected.html b/third_party/blink/web_tests/http/tests/security/mask-image-cors-port-expected.html
new file mode 100644
index 0000000..bd54434
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/security/mask-image-cors-port-expected.html
@@ -0,0 +1,3 @@
+<!DOCTYPE html>
+<html>
+</html>
diff --git a/third_party/blink/web_tests/http/tests/security/mask-image-cors-port-expected.txt b/third_party/blink/web_tests/http/tests/security/mask-image-cors-port-expected.txt
new file mode 100644
index 0000000..ad68390
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/security/mask-image-cors-port-expected.txt
@@ -0,0 +1 @@
+CONSOLE ERROR: Access to image at 'http://localhost:8080/resources/square100.png' from origin 'http://127.0.0.1:8000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
diff --git a/third_party/blink/web_tests/http/tests/security/mask-image-cors-port.html b/third_party/blink/web_tests/http/tests/security/mask-image-cors-port.html
new file mode 100644
index 0000000..37a0545
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/security/mask-image-cors-port.html
@@ -0,0 +1,13 @@
+<!doctype html>
+<style>
+#target {
+  width: 200px;
+  height: 200px;
+  background-color: green;
+  /* blocked because the URL's port number doesn't match this document's origin. */
+  -webkit-mask-image: url("http://localhost:8080/resources/square100.png");
+  -webkit-mask-repeat: no-repeat;
+  -webkit-mask-position: center;
+}
+</style>
+<div id="target"></div>
diff --git a/third_party/blink/web_tests/http/tests/security/mask-image-cors-same-origin-expected.html b/third_party/blink/web_tests/http/tests/security/mask-image-cors-same-origin-expected.html
new file mode 100644
index 0000000..83364b7
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/security/mask-image-cors-same-origin-expected.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<style>
+body { padding: 50px; }
+
+#expected {
+  width: 100px;
+  height: 100px;
+  background-color: green;
+}
+</style>
+<body>
+<div id="expected"></div>
+</body>
diff --git a/third_party/blink/web_tests/http/tests/security/mask-image-cors-same-origin.html b/third_party/blink/web_tests/http/tests/security/mask-image-cors-same-origin.html
new file mode 100644
index 0000000..046654fe
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/security/mask-image-cors-same-origin.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<style>
+#target {
+  width: 200px;
+  height: 200px;
+  background-color: green;
+  /* Access is OK because the URL and this document have the same origin. */
+  -webkit-mask-image: url("/resources/square100.png");
+  -webkit-mask-repeat: no-repeat;
+  -webkit-mask-position: center;
+}
+</style>
+<div id="target"></div>
diff --git a/third_party/blink/web_tests/paint/invalidation/animation/opacity-animation-expected.txt b/third_party/blink/web_tests/paint/invalidation/animation/opacity-animation-expected.txt
index 65a0bce..799c1d9a 100644
--- a/third_party/blink/web_tests/paint/invalidation/animation/opacity-animation-expected.txt
+++ b/third_party/blink/web_tests/paint/invalidation/animation/opacity-animation-expected.txt
@@ -9,7 +9,6 @@
     {
       "name": "LayoutNGBlockFlow DIV id='target'",
       "bounds": [100, 100],
-      "opacity": 0.5,
       "contentsOpaque": true,
       "backgroundColor": "#808080",
       "transform": 1
diff --git a/third_party/blink/web_tests/paint/invalidation/compositing/fixed-pos-inside-composited-intermediate-layer-expected.txt b/third_party/blink/web_tests/paint/invalidation/compositing/fixed-pos-inside-composited-intermediate-layer-expected.txt
index 1ea9aad..74d61db 100644
--- a/third_party/blink/web_tests/paint/invalidation/compositing/fixed-pos-inside-composited-intermediate-layer-expected.txt
+++ b/third_party/blink/web_tests/paint/invalidation/compositing/fixed-pos-inside-composited-intermediate-layer-expected.txt
@@ -24,7 +24,6 @@
     {
       "name": "LayoutNGBlockFlow (positioned) DIV class='fixed'",
       "bounds": [50, 50],
-      "opacity": 0.990000009536743,
       "contentsOpaque": true,
       "backgroundColor": "#00FF00",
       "transform": 6
diff --git a/third_party/blink/web_tests/paint/invalidation/compositing/fixed-pos-with-abs-pos-child-scroll-expected.txt b/third_party/blink/web_tests/paint/invalidation/compositing/fixed-pos-with-abs-pos-child-scroll-expected.txt
index cc3a28f6..3b4c5e8 100644
--- a/third_party/blink/web_tests/paint/invalidation/compositing/fixed-pos-with-abs-pos-child-scroll-expected.txt
+++ b/third_party/blink/web_tests/paint/invalidation/compositing/fixed-pos-with-abs-pos-child-scroll-expected.txt
@@ -11,7 +11,6 @@
       "name": "LayoutNGBlockFlow (positioned) DIV id='fixed'",
       "position": [-360, -90],
       "bounds": [360, 90],
-      "opacity": 0.990000009536743,
       "contentsOpaque": true,
       "transform": 1
     }
diff --git a/third_party/blink/web_tests/paint/invalidation/compositing/fixed-scroll-in-empty-root-layer-expected.txt b/third_party/blink/web_tests/paint/invalidation/compositing/fixed-scroll-in-empty-root-layer-expected.txt
index ef2c609..ce27241 100644
--- a/third_party/blink/web_tests/paint/invalidation/compositing/fixed-scroll-in-empty-root-layer-expected.txt
+++ b/third_party/blink/web_tests/paint/invalidation/compositing/fixed-scroll-in-empty-root-layer-expected.txt
@@ -21,7 +21,6 @@
     {
       "name": "LayoutNGBlockFlow (positioned) DIV",
       "bounds": [100, 100],
-      "opacity": 0.990000009536743,
       "contentsOpaque": true,
       "backgroundColor": "#008000",
       "transform": 4
diff --git a/third_party/blink/web_tests/paint/invalidation/compositing/new-stacking-context-expected.txt b/third_party/blink/web_tests/paint/invalidation/compositing/new-stacking-context-expected.txt
index 27e85c5..300e926 100644
--- a/third_party/blink/web_tests/paint/invalidation/compositing/new-stacking-context-expected.txt
+++ b/third_party/blink/web_tests/paint/invalidation/compositing/new-stacking-context-expected.txt
@@ -38,7 +38,6 @@
     {
       "name": "LayoutNGBlockFlow DIV id='target'",
       "bounds": [200, 200],
-      "opacity": 0.5,
       "contentsOpaque": true,
       "backgroundColor": "#008000",
       "paintInvalidations": [
diff --git a/third_party/blink/web_tests/paint/invalidation/compositing/opacity-from-zero-to-non-zero-composited-expected.txt b/third_party/blink/web_tests/paint/invalidation/compositing/opacity-from-zero-to-non-zero-composited-expected.txt
index b51807a3..8be75aae 100644
--- a/third_party/blink/web_tests/paint/invalidation/compositing/opacity-from-zero-to-non-zero-composited-expected.txt
+++ b/third_party/blink/web_tests/paint/invalidation/compositing/opacity-from-zero-to-non-zero-composited-expected.txt
@@ -9,7 +9,6 @@
     {
       "name": "LayoutNGBlockFlow DIV id='target'",
       "bounds": [100, 100],
-      "opacity": 0.5,
       "contentsOpaque": true,
       "backgroundColor": "#008000",
       "transform": 1
diff --git a/third_party/blink/web_tests/paint/invalidation/compositing/should-not-repaint-composited-opacity-expected.txt b/third_party/blink/web_tests/paint/invalidation/compositing/should-not-repaint-composited-opacity-expected.txt
index ecc0fd9..8c3e221 100644
--- a/third_party/blink/web_tests/paint/invalidation/compositing/should-not-repaint-composited-opacity-expected.txt
+++ b/third_party/blink/web_tests/paint/invalidation/compositing/should-not-repaint-composited-opacity-expected.txt
@@ -9,7 +9,6 @@
     {
       "name": "LayoutNGBlockFlow DIV id='composited-box'",
       "bounds": [100, 100],
-      "opacity": 0.5,
       "contentsOpaque": true,
       "backfaceVisibility": "hidden",
       "backgroundColor": "#008000",
diff --git a/third_party/blink/web_tests/paint/invalidation/filters/filter-on-html-element-with-fixed-position-child-expected.txt b/third_party/blink/web_tests/paint/invalidation/filters/filter-on-html-element-with-fixed-position-child-expected.txt
index aab1401..9a5589ce 100644
--- a/third_party/blink/web_tests/paint/invalidation/filters/filter-on-html-element-with-fixed-position-child-expected.txt
+++ b/third_party/blink/web_tests/paint/invalidation/filters/filter-on-html-element-with-fixed-position-child-expected.txt
@@ -8,7 +8,6 @@
     {
       "name": "LayoutNGBlockFlow (positioned) DIV",
       "bounds": [100, 100],
-      "opacity": 0.990000009536743,
       "contentsOpaque": true,
       "backgroundColor": "#D3D3D3",
       "transform": 2
@@ -16,7 +15,6 @@
     {
       "name": "LayoutNGBlockFlow (positioned) DIV",
       "bounds": [100, 100],
-      "opacity": 0.990000009536743,
       "contentsOpaque": true,
       "backgroundColor": "#D3D3D3",
       "transform": 3
diff --git a/third_party/blink/web_tests/paint/invalidation/position/absolute-position-changed-expected.txt b/third_party/blink/web_tests/paint/invalidation/position/absolute-position-changed-expected.txt
index 8e8fcba..239bd1d 100644
--- a/third_party/blink/web_tests/paint/invalidation/position/absolute-position-changed-expected.txt
+++ b/third_party/blink/web_tests/paint/invalidation/position/absolute-position-changed-expected.txt
@@ -18,7 +18,6 @@
     {
       "name": "LayoutNGBlockFlow (positioned) DIV class='fixed red'",
       "bounds": [100, 100],
-      "opacity": 0.990000009536743,
       "contentsOpaque": true,
       "backgroundColor": "#FF0000",
       "transform": 2
diff --git a/third_party/blink/web_tests/paint/invalidation/scroll/fixed-child-move-after-scroll-expected.txt b/third_party/blink/web_tests/paint/invalidation/scroll/fixed-child-move-after-scroll-expected.txt
index bea1f40..251a021 100644
--- a/third_party/blink/web_tests/paint/invalidation/scroll/fixed-child-move-after-scroll-expected.txt
+++ b/third_party/blink/web_tests/paint/invalidation/scroll/fixed-child-move-after-scroll-expected.txt
@@ -11,7 +11,6 @@
     {
       "name": "LayoutNGBlockFlow (positioned) DIV id='toMove'",
       "bounds": [100, 100],
-      "opacity": 0.990000009536743,
       "contentsOpaque": true,
       "backgroundColor": "#008000",
       "paintInvalidations": [
diff --git a/third_party/blink/web_tests/paint/invalidation/scroll/fixed-child-of-fixed-move-after-scroll-expected.txt b/third_party/blink/web_tests/paint/invalidation/scroll/fixed-child-of-fixed-move-after-scroll-expected.txt
index bea1f40..251a021 100644
--- a/third_party/blink/web_tests/paint/invalidation/scroll/fixed-child-of-fixed-move-after-scroll-expected.txt
+++ b/third_party/blink/web_tests/paint/invalidation/scroll/fixed-child-of-fixed-move-after-scroll-expected.txt
@@ -11,7 +11,6 @@
     {
       "name": "LayoutNGBlockFlow (positioned) DIV id='toMove'",
       "bounds": [100, 100],
-      "opacity": 0.990000009536743,
       "contentsOpaque": true,
       "backgroundColor": "#008000",
       "paintInvalidations": [
diff --git a/third_party/blink/web_tests/paint/invalidation/scroll/fixed-move-after-scroll-expected.txt b/third_party/blink/web_tests/paint/invalidation/scroll/fixed-move-after-scroll-expected.txt
index bea1f40..251a021 100644
--- a/third_party/blink/web_tests/paint/invalidation/scroll/fixed-move-after-scroll-expected.txt
+++ b/third_party/blink/web_tests/paint/invalidation/scroll/fixed-move-after-scroll-expected.txt
@@ -11,7 +11,6 @@
     {
       "name": "LayoutNGBlockFlow (positioned) DIV id='toMove'",
       "bounds": [100, 100],
-      "opacity": 0.990000009536743,
       "contentsOpaque": true,
       "backgroundColor": "#008000",
       "paintInvalidations": [
diff --git a/third_party/blink/web_tests/paint/invalidation/scroll/fixed-under-composited-absolute-scrolled-expected.txt b/third_party/blink/web_tests/paint/invalidation/scroll/fixed-under-composited-absolute-scrolled-expected.txt
index 0562d89..2fdb9dd6 100644
--- a/third_party/blink/web_tests/paint/invalidation/scroll/fixed-under-composited-absolute-scrolled-expected.txt
+++ b/third_party/blink/web_tests/paint/invalidation/scroll/fixed-under-composited-absolute-scrolled-expected.txt
@@ -19,7 +19,6 @@
     {
       "name": "LayoutNGBlockFlow (positioned) DIV id='fixed'",
       "bounds": [100, 200],
-      "opacity": 0.990000009536743,
       "contentsOpaque": true,
       "backgroundColor": "#008000",
       "paintInvalidations": [
diff --git a/third_party/blink/web_tests/paint/invalidation/scroll/fixed-with-border-under-composited-absolute-scrolled-expected.txt b/third_party/blink/web_tests/paint/invalidation/scroll/fixed-with-border-under-composited-absolute-scrolled-expected.txt
index b42d36c..da04f4b 100644
--- a/third_party/blink/web_tests/paint/invalidation/scroll/fixed-with-border-under-composited-absolute-scrolled-expected.txt
+++ b/third_party/blink/web_tests/paint/invalidation/scroll/fixed-with-border-under-composited-absolute-scrolled-expected.txt
@@ -19,7 +19,6 @@
     {
       "name": "LayoutNGBlockFlow (positioned) DIV id='fixed'",
       "bounds": [120, 220],
-      "opacity": 0.990000009536743,
       "contentsOpaque": true,
       "backgroundColor": "#008000",
       "paintInvalidations": [
diff --git a/third_party/blink/web_tests/platform/linux/compositing/geometry/preserve-3d-switching-expected.txt b/third_party/blink/web_tests/platform/linux/compositing/geometry/preserve-3d-switching-expected.txt
index 618d13f..91dc27c4 100644
--- a/third_party/blink/web_tests/platform/linux/compositing/geometry/preserve-3d-switching-expected.txt
+++ b/third_party/blink/web_tests/platform/linux/compositing/geometry/preserve-3d-switching-expected.txt
@@ -24,7 +24,6 @@
     {
       "name": "LayoutNGBlockFlow (positioned) DIV",
       "bounds": [200, 200],
-      "opacity": 0.699999988079071,
       "contentsOpaque": true,
       "backgroundColor": "#008000",
       "transform": 9
diff --git a/third_party/blink/web_tests/platform/mac/compositing/geometry/preserve-3d-switching-expected.txt b/third_party/blink/web_tests/platform/mac/compositing/geometry/preserve-3d-switching-expected.txt
index 618d13f..91dc27c4 100644
--- a/third_party/blink/web_tests/platform/mac/compositing/geometry/preserve-3d-switching-expected.txt
+++ b/third_party/blink/web_tests/platform/mac/compositing/geometry/preserve-3d-switching-expected.txt
@@ -24,7 +24,6 @@
     {
       "name": "LayoutNGBlockFlow (positioned) DIV",
       "bounds": [200, 200],
-      "opacity": 0.699999988079071,
       "contentsOpaque": true,
       "backgroundColor": "#008000",
       "transform": 9
diff --git a/third_party/blink/web_tests/platform/mac/css3/blending/mix-blend-mode-composited-layers-expected.txt b/third_party/blink/web_tests/platform/mac/css3/blending/mix-blend-mode-composited-layers-expected.txt
index da137a6..243f13e2 100644
--- a/third_party/blink/web_tests/platform/mac/css3/blending/mix-blend-mode-composited-layers-expected.txt
+++ b/third_party/blink/web_tests/platform/mac/css3/blending/mix-blend-mode-composited-layers-expected.txt
@@ -7,7 +7,6 @@
     {
       "name": "LayoutNGBlockFlow DIV",
       "bounds": [10, 10],
-      "blendMode": "multiply",
       "backgroundColor": "#0000FF",
       "transform": 1
     },
diff --git a/third_party/blink/web_tests/platform/mac/paint/invalidation/position/fixed-expected.txt b/third_party/blink/web_tests/platform/mac/paint/invalidation/position/fixed-expected.txt
index e6dc4082..f93d48ba 100644
--- a/third_party/blink/web_tests/platform/mac/paint/invalidation/position/fixed-expected.txt
+++ b/third_party/blink/web_tests/platform/mac/paint/invalidation/position/fixed-expected.txt
@@ -11,7 +11,6 @@
     {
       "name": "LayoutNGBlockFlow (positioned) DIV id='t'",
       "bounds": [100, 100],
-      "opacity": 0.990000009536743,
       "contentsOpaque": true,
       "backgroundColor": "#008000",
       "paintInvalidations": [
diff --git a/third_party/blink/web_tests/platform/win/compositing/geometry/preserve-3d-switching-expected.txt b/third_party/blink/web_tests/platform/win/compositing/geometry/preserve-3d-switching-expected.txt
index fdafc92..e54214e2 100644
--- a/third_party/blink/web_tests/platform/win/compositing/geometry/preserve-3d-switching-expected.txt
+++ b/third_party/blink/web_tests/platform/win/compositing/geometry/preserve-3d-switching-expected.txt
@@ -24,7 +24,6 @@
     {
       "name": "LayoutNGBlockFlow (positioned) DIV",
       "bounds": [200, 200],
-      "opacity": 0.699999988079071,
       "contentsOpaque": true,
       "backgroundColor": "#008000",
       "transform": 9
diff --git a/third_party/blink/web_tests/platform/win/css3/blending/mix-blend-mode-composited-layers-expected.txt b/third_party/blink/web_tests/platform/win/css3/blending/mix-blend-mode-composited-layers-expected.txt
index 471d37b..8b7ef9a 100644
--- a/third_party/blink/web_tests/platform/win/css3/blending/mix-blend-mode-composited-layers-expected.txt
+++ b/third_party/blink/web_tests/platform/win/css3/blending/mix-blend-mode-composited-layers-expected.txt
@@ -7,7 +7,6 @@
     {
       "name": "LayoutNGBlockFlow DIV",
       "bounds": [10, 10],
-      "blendMode": "multiply",
       "backgroundColor": "#0000FF",
       "transform": 1
     },
diff --git a/third_party/blink/web_tests/platform/win/paint/invalidation/position/fixed-expected.txt b/third_party/blink/web_tests/platform/win/paint/invalidation/position/fixed-expected.txt
index 7a73cda..63227dc 100644
--- a/third_party/blink/web_tests/platform/win/paint/invalidation/position/fixed-expected.txt
+++ b/third_party/blink/web_tests/platform/win/paint/invalidation/position/fixed-expected.txt
@@ -11,7 +11,6 @@
     {
       "name": "LayoutNGBlockFlow (positioned) DIV id='t'",
       "bounds": [100, 100],
-      "opacity": 0.990000009536743,
       "contentsOpaque": true,
       "backgroundColor": "#008000",
       "paintInvalidations": [
diff --git a/third_party/blink/web_tests/serial/serialPort_readable.html b/third_party/blink/web_tests/serial/serialPort_readable.html
index 129a891..3b67e55 100644
--- a/third_party/blink/web_tests/serial/serialPort_readable.html
+++ b/third_party/blink/web_tests/serial/serialPort_readable.html
@@ -98,4 +98,23 @@
   assert_equals(undefined, value);
 }, 'Parity error closes readable and replaces it with a new stream');
 
+serial_test(async (t, fake) => {
+  const { port, fakePort } = await getFakeSerialPort(fake);
+  // Select a buffer size larger than the amount of data transferred.
+  await port.open({ baudrate: 9600, buffersize: 64 });
+
+  fakePort.write(new TextEncoder().encode("Hello world!"));
+
+  const reader = port.readable.pipeThrough(new TextDecoderStream()).getReader();
+  let { value, done } = await reader.read();
+  assert_false(done);
+  assert_equals("Hello world!", value);
+
+  port.close();
+
+  ({ value, done } = await reader.read());
+  assert_true(done);
+  assert_equals(undefined, value);
+}, 'Can pipe readable through a transform stream.')
+
 </script>
diff --git a/third_party/blink/web_tests/serial/serialPort_writable.html b/third_party/blink/web_tests/serial/serialPort_writable.html
index 12bec7d..6fa35d6c 100644
--- a/third_party/blink/web_tests/serial/serialPort_writable.html
+++ b/third_party/blink/web_tests/serial/serialPort_writable.html
@@ -68,4 +68,22 @@
   compareArrays(data, value);
 }, 'Can read a large amount of data');
 
+serial_test(async (t, fake) => {
+  const { port, fakePort } = await getFakeSerialPort(fake);
+
+  await port.open({ baudrate: 9600 });
+  assert_true(port.writable instanceof WritableStream);
+
+  const encoder = new TextEncoderStream();
+  encoder.readable.pipeTo(port.writable);
+  const writer = encoder.writable.getWriter();
+  let writePromise = writer.write("Hello world!");
+  let { value, done } = await fakePort.read();
+  await writePromise;
+  assert_equals("Hello world!", new TextDecoder().decode(value));
+
+  port.close();
+  assert_equals(null, port.writable);
+}, 'Can pipe a stream to writable');
+
 </script>
diff --git a/third_party/icu4j/OWNERS b/third_party/icu4j/OWNERS
index 7fcd92b..df1ed5b 100644
--- a/third_party/icu4j/OWNERS
+++ b/third_party/icu4j/OWNERS
@@ -1,3 +1,5 @@
 jbudorick@chromium.org
 mikecase@chromium.org
-yolandyan@chromium.org
\ No newline at end of file
+yolandyan@chromium.org
+
+# COMPONENT: Test>Android
diff --git a/tools/mb/mb_config.pyl b/tools/mb/mb_config.pyl
index f83dcb4..beb15a7 100644
--- a/tools/mb/mb_config.pyl
+++ b/tools/mb/mb_config.pyl
@@ -22,6 +22,7 @@
       'linux-chromeos-google-rel': 'official_goma_chromeos_minimal_symbols',
       'linux-google-rel': 'official_goma',
       'mac-google-rel': 'official_goma',
+      'win-celab-rel': 'official_celab_release_bot',
       'win-google-rel': 'official_goma_x86',
     },
 
@@ -733,6 +734,7 @@
       'linux-chrome': 'official_goma',
       'linux-chromeos-chrome': 'official_goma_chromeos_minimal_symbols',
       'win_chrome_official': 'official_goma_x86',
+      'win-celab-try-rel': 'official_celab_release_bot',
     },
 
     'tryserver.chromium.chromiumos': {
@@ -1781,6 +1783,10 @@
       'msan', 'release_bot',
     ],
 
+    'official_celab_release_bot': [
+      'official', 'release_bot', 'minimal_symbols',
+    ],
+
     # The cros chrome-sdk takes care of adding needed official/branding args
     # when using the sdk with '--internal'. So use the base cros_chrome_sdk
     # config here, but make sure the '--internal' argument is passed to the
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 84bdcb5..9285b31a 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -7718,6 +7718,7 @@
   <int value="1" label="Search provider change"/>
   <int value="2" label="Tab closed"/>
   <int value="3" label="Navigation"/>
+  <int value="4" label="Shutdown"/>
 </enum>
 
 <enum name="ChromeDownloadCountType">
@@ -18216,6 +18217,7 @@
   <int value="622" label="PrintingBackgroundGraphicsDefault"/>
   <int value="623" label="LegacySameSiteCookieBehaviorEnabled"/>
   <int value="624" label="LegacySameSiteCookieBehaviorEnabledForDomainList"/>
+  <int value="625" label="PrintJobHistoryExpirationPeriod"/>
 </enum>
 
 <enum name="EnterprisePolicyInvalidations">
@@ -24870,6 +24872,8 @@
   <int value="3046" label="PluginElementLoadedDocument"/>
   <int value="3047" label="PluginElementLoadedImage"/>
   <int value="3048" label="PluginElementLoadedExternal"/>
+  <int value="3049" label="RenderSubtreeAttribute"/>
+  <int value="3050" label="ARIAAnnotationRoles"/>
 </enum>
 
 <enum name="FeaturePolicyAllowlistType">
@@ -36112,6 +36116,7 @@
   <int value="-632030508" label="NativeWindowNavButtons:disabled"/>
   <int value="-631740127" label="inert-visual-viewport"/>
   <int value="-631614101" label="CameraSystemWebApp:enabled"/>
+  <int value="-625695291" label="AccessibilityExposeARIAAnnotations:enabled"/>
   <int value="-624221121" label="CommandLineOnNonRooted:enabled"/>
   <int value="-623364791" label="ClickToCallReceiver:disabled"/>
   <int value="-622685174" label="enable-pdf-material-ui"/>
@@ -37064,6 +37069,7 @@
   <int value="667643314" label="LitePageServerPreviews:enabled"/>
   <int value="673588373" label="OmniboxPedalSuggestions:disabled"/>
   <int value="677866592" label="ClickToCallUI:disabled"/>
+  <int value="679251577" label="AccessibilityExposeARIAAnnotations:disabled"/>
   <int value="679931272" label="DcheckIsFatal:enabled"/>
   <int value="680070635"
       label="enable-fullscreen-handwriting-virtual-keyboard:disabled"/>
@@ -53109,6 +53115,7 @@
   <int value="1" label="No Safety Tip"/>
   <int value="2" label="Safety Tip for bad reputation"/>
   <int value="3" label="Safety Tip for lookalike URL"/>
+  <int value="4" label="Safety Tip for a URL with a suspicious keyword"/>
 </enum>
 
 <enum name="SameSiteCookieContext">
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 0db195e..c3b17974 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -30671,6 +30671,21 @@
   <summary>Records the error returned from CreateFontFace.</summary>
 </histogram>
 
+<histogram name="DirectWrite.Fonts.Proxy.EmptyFamilyResultsRatio"
+    units="percentage of empty family results" expires_after="M82">
+  <owner>drott@chromium.org</owner>
+  <owner>layout-dev@chromium.org</owner>
+  <summary>
+    When scanning fonts for meta data as part of local matching, record the
+    ratio of empty per-family results to all per-family results as percentage.
+    Recorded in DWriteFontTableBuilder at the time of finishing scanning the
+    font files for their name meta information. A value of 100% means, all
+    per-family scan tasks came back empty. A value of 0% means, all per-family
+    tasks yielded at least one font file. Used for diagnosing
+    https://crbug.com/1009402.
+  </summary>
+</histogram>
+
 <histogram name="DirectWrite.Fonts.Proxy.Fallback.CacheSize" units="Count">
   <owner>drott@chromium.org</owner>
   <summary>Records how many font families are in the fallback cache.</summary>
@@ -36376,7 +36391,7 @@
   </summary>
 </histogram>
 
-<histogram name="Enterprise.CloudReportingRequestCount" units="units">
+<histogram name="Enterprise.CloudReportingRequestCount" units="requests">
   <owner>zmin@chromium.org</owner>
   <summary>
     The number of request for one Chrome browser cloud management report. A
@@ -98811,6 +98826,26 @@
   </summary>
 </histogram>
 
+<histogram base="true" name="PageLoad.HeavyAds.ComputedTypeWithThresholdNoise"
+    enum="HeavyAdStatus2" expires_after="2020-08-05">
+  <owner>johnidel@chromium.org</owner>
+  <owner>jkarlin@chromium.org</owner>
+  <summary>
+    Records heavy ad type for each ad frame, as determined by the first
+    threshold hit (see FrameData::HeavyadStatus). This is recorded regardless of
+    feature flag or other conditions that prevent the heavy ad intervention from
+    occuring. This includes 1 megabyte of random additive noise on the network
+    threshold. As such, it is possible for this histogram to record different
+    values for the same frame than PageLoad.HeavyAds.ComputedType2.
+
+    Recored for all ad frames with non-zero bytes. Recorded when the ad frame
+    destroyed or when the page is destroyed.
+
+    It is possible for multiple thresholds to be hit at the same time, with the
+    higher valued enums winning those race conditions.
+  </summary>
+</histogram>
+
 <histogram base="true" name="PageLoad.HeavyAds.InterventionType"
     enum="HeavyAdStatus" expires_after="2020-08-05">
   <obsolete>
@@ -163798,6 +163833,7 @@
   <affected-histogram name="PageLoad.FrameCounts.AnyParentFrame.AdFrames"/>
   <affected-histogram name="PageLoad.HeavyAds.ComputedType"/>
   <affected-histogram name="PageLoad.HeavyAds.ComputedType2"/>
+  <affected-histogram name="PageLoad.HeavyAds.ComputedTypeWithThresholdNoise"/>
   <affected-histogram name="PageLoad.HeavyAds.InterventionType"/>
   <affected-histogram name="PageLoad.HeavyAds.InterventionType2"/>
 </histogram_suffixes>
diff --git a/tools/perf/core/perf_data_generator.py b/tools/perf/core/perf_data_generator.py
index 5e18ba8..2473e9d3 100755
--- a/tools/perf/core/perf_data_generator.py
+++ b/tools/perf/core/perf_data_generator.py
@@ -681,11 +681,6 @@
             '--use-stub',
         ],
       },
-      {
-        'isolate': 'dawn_perf_tests',
-        'num_shards': 1,
-        'type': TEST_TYPES.GTEST,
-      },
     ],
     'platform': 'win',
     'target_bits': 64,
diff --git a/ui/accessibility/accessibility_features.cc b/ui/accessibility/accessibility_features.cc
index 97eee4d..7d4119f 100644
--- a/ui/accessibility/accessibility_features.cc
+++ b/ui/accessibility/accessibility_features.cc
@@ -9,6 +9,15 @@
 
 namespace features {
 
+// Allow use of ARIA roles from https://github.com/w3c/annotation-aria draft.
+const base::Feature kEnableAccessibilityExposeARIAAnnotations{
+    "AccessibilityExposeARIAAnnotations", base::FEATURE_DISABLED_BY_DEFAULT};
+
+bool IsAccessibilityExposeARIAAnnotationsEnabled() {
+  return base::FeatureList::IsEnabled(
+      ::features::kEnableAccessibilityExposeARIAAnnotations);
+}
+
 // Enable exposing "display: none" nodes to the browser process AXTree
 const base::Feature kEnableAccessibilityExposeDisplayNone{
     "AccessibilityExposeDisplayNone", base::FEATURE_DISABLED_BY_DEFAULT};
diff --git a/ui/accessibility/accessibility_features.h b/ui/accessibility/accessibility_features.h
index c2f5d55f..e3b35503 100644
--- a/ui/accessibility/accessibility_features.h
+++ b/ui/accessibility/accessibility_features.h
@@ -12,6 +12,11 @@
 
 namespace features {
 
+AX_EXPORT extern const base::Feature kEnableAccessibilityExposeARIAAnnotations;
+
+// Returns true if ARIA annotations should be exposed to the browser AX Tree.
+AX_EXPORT bool IsAccessibilityExposeARIAAnnotationsEnabled();
+
 AX_EXPORT extern const base::Feature kEnableAccessibilityExposeDisplayNone;
 
 // Returns true if "display: none" nodes should be exposed to the
diff --git a/ui/accessibility/platform/ax_platform_node_win.cc b/ui/accessibility/platform/ax_platform_node_win.cc
index e6d3de4..361e8d1 100644
--- a/ui/accessibility/platform/ax_platform_node_win.cc
+++ b/ui/accessibility/platform/ax_platform_node_win.cc
@@ -3462,10 +3462,41 @@
     LONG y,
     enum IA2CoordinateType coord_type,
     LONG* offset) {
+  WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_GET_OFFSET_AT_POINT);
   COM_OBJECT_VALIDATE_1_ARG(offset);
-  // We don't support this method, but we have to return something
-  // rather than E_NOTIMPL or screen readers will complain.
-  *offset = 0;
+
+  *offset = -1;
+
+  if (coord_type == IA2CoordinateType::IA2_COORDTYPE_PARENT_RELATIVE) {
+    // We don't support when the IA2 coordinate type is parent relative, but
+    // we have to return something rather than E_NOTIMPL or screen readers
+    // will complain.
+    NOTIMPLEMENTED_LOG_ONCE() << "See http://crbug.com/1010726";
+    return S_FALSE;
+  }
+
+  // We currently only handle IA2 screen relative coord type.
+  DCHECK_EQ(coord_type, IA2_COORDTYPE_SCREEN_RELATIVE);
+
+  const AXPlatformNodeWin* hit_child = static_cast<AXPlatformNodeWin*>(
+      FromNativeViewAccessible(GetDelegate()->HitTestSync(x, y)));
+
+  if (!hit_child || !hit_child->IsTextOnlyObject()) {
+    return S_FALSE;
+  }
+
+  for (int i = 0, text_length = hit_child->GetInnerText().length();
+       i < text_length; ++i) {
+    gfx::Rect char_bounds =
+        hit_child->GetDelegate()->GetInnerTextRangeBoundsRect(
+            i, i + 1, AXCoordinateSystem::kScreen,
+            AXClippingBehavior::kUnclipped);
+    if (char_bounds.Contains(x, y)) {
+      *offset = i;
+      break;
+    }
+  }
+
   return S_OK;
 }
 
diff --git a/ui/accessibility/platform/ax_platform_node_win_unittest.cc b/ui/accessibility/platform/ax_platform_node_win_unittest.cc
index 2ac248c..d607880 100644
--- a/ui/accessibility/platform/ax_platform_node_win_unittest.cc
+++ b/ui/accessibility/platform/ax_platform_node_win_unittest.cc
@@ -2878,6 +2878,154 @@
   EXPECT_EQ(4, count);
 }
 
+TEST_F(AXPlatformNodeWinTest, TestIAccessibleTextGetOffsetAtPoint) {
+  AXNodeData root;
+  root.id = 1;
+  root.role = ax::mojom::Role::kWebArea;
+  root.relative_bounds.bounds = gfx::RectF(0, 0, 300, 200);
+  root.child_ids = {2, 3};
+
+  AXNodeData button;
+  button.id = 2;
+  button.role = ax::mojom::Role::kButton;
+  button.SetName("button");
+  button.relative_bounds.bounds = gfx::RectF(20, 0, 10, 10);
+
+  AXNodeData static_text1;
+  static_text1.id = 3;
+  static_text1.role = ax::mojom::Role::kStaticText;
+  static_text1.SetName("line 1");
+  static_text1.relative_bounds.bounds = gfx::RectF(0, 20, 30, 10);
+  static_text1.child_ids = {4};
+
+  AXNodeData inline_box1;
+  inline_box1.id = 4;
+  inline_box1.role = ax::mojom::Role::kInlineTextBox;
+  inline_box1.SetName("line 1");
+  inline_box1.relative_bounds.bounds = gfx::RectF(0, 20, 30, 10);
+  std::vector<int32_t> character_offsets1;
+  // The width of each character is 5px, height is 10px.
+  character_offsets1.push_back(5);   // "l" {0, 20, 5x10}
+  character_offsets1.push_back(10);  // "i" {5, 20, 5x10}
+  character_offsets1.push_back(15);  // "n" {10, 20, 5x10}
+  character_offsets1.push_back(20);  // "e" {15, 20, 5x10}
+  character_offsets1.push_back(25);  // " " {20, 20, 5x10}
+  character_offsets1.push_back(30);  // "1" {25, 20, 5x10}
+
+  inline_box1.AddIntListAttribute(
+      ax::mojom::IntListAttribute::kCharacterOffsets, character_offsets1);
+  inline_box1.AddIntListAttribute(ax::mojom::IntListAttribute::kWordStarts,
+                                  std::vector<int32_t>{0, 5});
+  inline_box1.AddIntListAttribute(ax::mojom::IntListAttribute::kWordEnds,
+                                  std::vector<int32_t>{4, 6});
+
+  Init(root, button, static_text1, inline_box1);
+
+  LONG offset_result;
+  ComPtr<IAccessible> root_iaccessible(IAccessibleFromNode(GetRootNode()));
+  ASSERT_NE(nullptr, root_iaccessible.Get());
+
+  ComPtr<IAccessibleText> root_text;
+  root_iaccessible.As(&root_text);
+  ASSERT_NE(nullptr, root_text.Get());
+
+  // Test point(0, 0) with coordinate parent relative, which is not supported.
+  // Expected result: S_FALSE.
+  EXPECT_EQ(S_FALSE, root_text->get_offsetAtPoint(
+                         0, 0, IA2CoordinateType::IA2_COORDTYPE_PARENT_RELATIVE,
+                         &offset_result));
+  EXPECT_EQ(-1, offset_result);
+
+  // Test point(0, 0) retrieved from IAccessibleText of the root web area is
+  // outside of any text. Expected result: S_FALSE.
+  EXPECT_EQ(S_FALSE, root_text->get_offsetAtPoint(
+                         0, 0, IA2CoordinateType::IA2_COORDTYPE_SCREEN_RELATIVE,
+                         &offset_result));
+  EXPECT_EQ(-1, offset_result);
+
+  // Test point(25, 5) retrieved from IAccessibleText of the root web area is
+  // on button, and outside of any text. Expected result: S_FALSE.
+  EXPECT_EQ(S_FALSE,
+            root_text->get_offsetAtPoint(
+                25, 5, IA2CoordinateType::IA2_COORDTYPE_SCREEN_RELATIVE,
+                &offset_result));
+  EXPECT_EQ(-1, offset_result);
+
+  // Test point(0, 20) retrieved from IAccessibleText of the root web area is
+  // on "line 1", character="l". Expected result: S_OK, offset=0.
+  EXPECT_EQ(S_OK, root_text->get_offsetAtPoint(
+                      0, 20, IA2CoordinateType::IA2_COORDTYPE_SCREEN_RELATIVE,
+                      &offset_result));
+  EXPECT_EQ(0, offset_result);
+
+  AXNode* static_text1_node = GetRootNode()->children()[1];
+  ComPtr<IAccessible> text1_iaccessible(IAccessibleFromNode(static_text1_node));
+  ASSERT_NE(nullptr, text1_iaccessible.Get());
+
+  ComPtr<IAccessibleText> text1;
+  text1_iaccessible.As(&text1);
+  ASSERT_NE(nullptr, text1.Get());
+
+  // "l" 4 points of bounds {(0, 20), (5, 20), (0, 30), (5, 30)}
+  // "i" 4 points of bounds {(5, 20), (10, 20), (5, 30), (10, 30)}
+  // "n" 4 points of bounds {(10, 20), (15, 20), (10, 30), (15, 30)}
+  // "e" 4 points of bounds {(15, 20), (20, 20), (15, 30), (20, 30)}
+  // " " 4 points of bounds {(20, 20), (25, 20), (20, 30), (25, 30)}
+  // "1" 4 points of bounds {(25, 20), (30, 20), (25, 30), (30, 30)}
+
+  // Test point(0, 0) outside of any character bounds and text.
+  EXPECT_EQ(S_FALSE, text1->get_offsetAtPoint(
+                         0, 0, IA2CoordinateType::IA2_COORDTYPE_SCREEN_RELATIVE,
+                         &offset_result));
+  EXPECT_EQ(-1, offset_result);
+
+  // Test point(30, 30) outside of any character bounds but on the text.
+  EXPECT_EQ(S_OK, text1->get_offsetAtPoint(
+                      30, 30, IA2CoordinateType::IA2_COORDTYPE_SCREEN_RELATIVE,
+                      &offset_result));
+  EXPECT_EQ(-1, offset_result);
+
+  // Test point(0, 20) inside bounds of "l", text offset=0
+  // character bounds={(0, 20), (5, 20), (0, 30), (5, 30)}
+  EXPECT_HRESULT_SUCCEEDED(text1->get_offsetAtPoint(
+      0, 20, IA2CoordinateType::IA2_COORDTYPE_SCREEN_RELATIVE, &offset_result));
+  EXPECT_EQ(0, offset_result);
+
+  // Test point(9, 20) inside bounds of "i", text offset=1
+  // character bounds={(5, 20), (10, 20), (5, 30), (10, 30)}
+  EXPECT_HRESULT_SUCCEEDED(text1->get_offsetAtPoint(
+      9, 20, IA2CoordinateType::IA2_COORDTYPE_SCREEN_RELATIVE, &offset_result));
+  EXPECT_EQ(1, offset_result);
+
+  // Test point(10, 30) inside bounds of "n", text offset=2
+  // character bounds={(10, 20), (15, 20), (10, 30), (15, 30)}
+  EXPECT_HRESULT_SUCCEEDED(text1->get_offsetAtPoint(
+      10, 29, IA2CoordinateType::IA2_COORDTYPE_SCREEN_RELATIVE,
+      &offset_result));
+  EXPECT_EQ(2, offset_result);
+
+  // Test point(19, 29) inside bounds of "e", text offset=3
+  // character bounds={(15, 20), (20, 20), (15, 30), (20, 30)
+  EXPECT_HRESULT_SUCCEEDED(text1->get_offsetAtPoint(
+      19, 29, IA2CoordinateType::IA2_COORDTYPE_SCREEN_RELATIVE,
+      &offset_result));
+  EXPECT_EQ(3, offset_result);
+
+  // Test point(23, 25) inside bounds of " ", text offset=4
+  // character bounds={(20, 20), (25, 20), (20, 30), (25, 30)}
+  EXPECT_HRESULT_SUCCEEDED(text1->get_offsetAtPoint(
+      23, 25, IA2CoordinateType::IA2_COORDTYPE_SCREEN_RELATIVE,
+      &offset_result));
+  EXPECT_EQ(4, offset_result);
+
+  // Test point(25, 20) inside bounds of "1", text offset=5
+  // character bounds={(25, 20), (30, 20), (25, 30), (30, 30)}
+  EXPECT_HRESULT_SUCCEEDED(text1->get_offsetAtPoint(
+      25, 20, IA2CoordinateType::IA2_COORDTYPE_SCREEN_RELATIVE,
+      &offset_result));
+  EXPECT_EQ(5, offset_result);
+}
+
 TEST_F(AXPlatformNodeWinTest, TestIAccessibleTextTextFieldRemoveSelection) {
   Init(BuildTextFieldWithSelectionRange(1, 2));
 
diff --git a/ui/base/ime/win/tsf_text_store.cc b/ui/base/ime/win/tsf_text_store.cc
index 4329d18a..9711819d 100644
--- a/ui/base/ime/win/tsf_text_store.cc
+++ b/ui/base/ime/win/tsf_text_store.cc
@@ -626,20 +626,23 @@
   // (composition_string) is not the same as previous composition string
   // (prev_composition_string_) during same composition or the composition
   // string is the same for different composition or selection is changed during
-  // composition. If composition_string is empty and there is an existing
-  // composition going on, we still need to call into blink to complete the
-  // composition started by TSF.
+  // composition or IME spans are changed during same composition. If
+  // composition_string is empty and there is an existing composition going on,
+  // we still need to call into blink to complete the composition started by
+  // TSF.
   if ((has_composition_range_ &&
        (previous_composition_start_ != composition_range_.start() ||
         previous_composition_string_ != composition_string ||
         !previous_composition_selection_range_.EqualsIgnoringDirection(
-            selection_))) ||
+            selection_) ||
+        previous_text_spans_ != text_spans_)) ||
       ((wparam_keydown_fired_ != 0) &&
        text_input_client_->HasCompositionText() &&
        composition_string.empty())) {
     previous_composition_string_ = composition_string;
     previous_composition_start_ = composition_range_.start();
     previous_composition_selection_range_ = selection_;
+    previous_text_spans_ = text_spans_;
 
     // We need to remove replacing text first before starting new composition if
     // there are any.
@@ -910,6 +913,7 @@
     previous_composition_string_.clear();
     previous_composition_start_ = 0;
     previous_composition_selection_range_ = gfx::Range::InvalidRange();
+    previous_text_spans_.clear();
   }
 
   return S_OK;
@@ -1197,6 +1201,7 @@
   previous_composition_string_.clear();
   previous_composition_start_ = 0;
   previous_composition_selection_range_ = gfx::Range::InvalidRange();
+  previous_text_spans_.clear();
   string_pending_insertion_.clear();
   composition_start_ = selection_.end();
 
diff --git a/ui/base/ime/win/tsf_text_store.h b/ui/base/ime/win/tsf_text_store.h
index 4eed8c0..d459f06 100644
--- a/ui/base/ime/win/tsf_text_store.h
+++ b/ui/base/ime/win/tsf_text_store.h
@@ -350,10 +350,12 @@
   // composition string twice. |previous_composition_selection_range_| indicates
   // the selection range during composition. We want to send the selection
   // change to blink if IME only change the selection range but not the
-  // composition text.
+  // composition text. |previous_text_spans_| saves the IME spans in previous
+  // edit session during same composition.
   base::string16 previous_composition_string_;
   size_t previous_composition_start_ = 0;
   gfx::Range previous_composition_selection_range_ = gfx::Range::InvalidRange();
+  ImeTextSpans previous_text_spans_;
 
   // |new_text_inserted_| indicates there is text to be inserted
   // into blink during ITextStoreACP::SetText().
diff --git a/ui/base/ime/win/tsf_text_store_unittest.cc b/ui/base/ime/win/tsf_text_store_unittest.cc
index f49580c..fe4fdf1c 100644
--- a/ui/base/ime/win/tsf_text_store_unittest.cc
+++ b/ui/base/ime/win/tsf_text_store_unittest.cc
@@ -2991,5 +2991,153 @@
   EXPECT_EQ(S_OK, result);
 }
 
+// regression tests for crbug.com/1006067.
+// We should call |TextInputClient::SetCompositionText()| if ImeTextSpans are
+// changed from previous edit session during same composition even though
+// composition string and composition selection remain unchange.
+class RegressionTest5Callback : public TSFTextStoreTestCallback {
+ public:
+  explicit RegressionTest5Callback(TSFTextStore* text_store)
+      : TSFTextStoreTestCallback(text_store) {}
+
+  HRESULT LockGranted1(DWORD flags) {
+    SetTextTest(0, 0, L"aa", S_OK);
+    GetTextTest(0, -1, L"aa", 2);
+    SetSelectionTest(2, 2, S_OK);
+
+    text_spans()->clear();
+    ImeTextSpan text_span_1;
+    text_span_1.start_offset = 0;
+    text_span_1.end_offset = 1;
+    text_span_1.underline_color = SK_ColorBLACK;
+    text_span_1.thickness = ImeTextSpan::Thickness::kThick;
+    text_span_1.background_color = SK_ColorTRANSPARENT;
+    text_spans()->push_back(text_span_1);
+    ImeTextSpan text_span_2;
+    text_span_2.start_offset = 1;
+    text_span_2.end_offset = 2;
+    text_span_2.underline_color = SK_ColorBLACK;
+    text_span_2.thickness = ImeTextSpan::Thickness::kThin;
+    text_span_2.background_color = SK_ColorTRANSPARENT;
+    text_spans()->push_back(text_span_2);
+    *edit_flag() = true;
+    *composition_start() = 0;
+    composition_range()->set_start(0);
+    composition_range()->set_end(2);
+    text_store_->OnKeyTraceDown(65u, 1966081u);
+    *has_composition_range() = true;
+
+    return S_OK;
+  }
+
+  void SetCompositionText1(const ui::CompositionText& composition) {
+    EXPECT_EQ(L"aa", composition.text);
+    EXPECT_EQ(2u, composition.selection.start());
+    EXPECT_EQ(2u, composition.selection.end());
+    ASSERT_EQ(2u, composition.ime_text_spans.size());
+    EXPECT_EQ(ImeTextSpan::Thickness::kThick,
+              composition.ime_text_spans[0].thickness);
+    EXPECT_EQ(ImeTextSpan::Thickness::kThin,
+              composition.ime_text_spans[1].thickness);
+    SetHasCompositionText(true);
+  }
+
+  // Only change underline thickness in IME spans. Other states (composition
+  // string, selection) remain unchange.
+  HRESULT LockGranted2(DWORD flags) {
+    GetTextTest(0, -1, L"aa", 2);
+
+    text_spans()->clear();
+    ImeTextSpan text_span_1;
+    text_span_1.start_offset = 0;
+    text_span_1.end_offset = 1;
+    text_span_1.underline_color = SK_ColorBLACK;
+    text_span_1.thickness = ImeTextSpan::Thickness::kThin;
+    text_span_1.background_color = SK_ColorTRANSPARENT;
+    text_spans()->push_back(text_span_1);
+    ImeTextSpan text_span_2;
+    text_span_2.start_offset = 1;
+    text_span_2.end_offset = 2;
+    text_span_2.underline_color = SK_ColorBLACK;
+    text_span_2.thickness = ImeTextSpan::Thickness::kThick;
+    text_span_2.background_color = SK_ColorTRANSPARENT;
+    text_spans()->push_back(text_span_2);
+
+    *edit_flag() = true;
+    *composition_start() = 0;
+    composition_range()->set_start(0);
+    composition_range()->set_end(2);
+
+    text_store_->OnKeyTraceUp(65u, 1966081u);
+    text_store_->OnKeyTraceDown(65u, 1966081u);
+    return S_OK;
+  }
+
+  void SetCompositionText2(const ui::CompositionText& composition) {
+    EXPECT_EQ(L"aa", composition.text);
+    EXPECT_EQ(2u, composition.selection.start());
+    EXPECT_EQ(2u, composition.selection.end());
+    ASSERT_EQ(2u, composition.ime_text_spans.size());
+    EXPECT_EQ(ImeTextSpan::Thickness::kThin,
+              composition.ime_text_spans[0].thickness);
+    EXPECT_EQ(ImeTextSpan::Thickness::kThick,
+              composition.ime_text_spans[1].thickness);
+    SetHasCompositionText(true);
+  }
+
+  HRESULT LockGranted3(DWORD flags) {
+    GetTextTest(0, -1, L"aa", 2);
+
+    text_spans()->clear();
+    *edit_flag() = true;
+    *composition_start() = 2;
+    composition_range()->set_start(0);
+    composition_range()->set_end(0);
+
+    *has_composition_range() = false;
+    text_store_->OnKeyTraceUp(65u, 1966081u);
+    return S_OK;
+  }
+
+  void InsertText3(const base::string16& text) {
+    EXPECT_EQ(L"aa", text);
+    SetHasCompositionText(false);
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(RegressionTest5Callback);
+};
+
+TEST_F(TSFTextStoreTest, RegressionTest5) {
+  RegressionTest5Callback callback(text_store_.get());
+  EXPECT_CALL(text_input_client_, SetCompositionText(_))
+      .WillOnce(
+          Invoke(&callback, &RegressionTest5Callback::SetCompositionText1))
+      .WillOnce(
+          Invoke(&callback, &RegressionTest5Callback::SetCompositionText2));
+
+  EXPECT_CALL(text_input_client_, InsertText(_))
+      .WillOnce(Invoke(&callback, &RegressionTest5Callback::InsertText3));
+
+  EXPECT_CALL(*sink_, OnLockGranted(_))
+      .WillOnce(Invoke(&callback, &RegressionTest5Callback::LockGranted1))
+      .WillOnce(Invoke(&callback, &RegressionTest5Callback::LockGranted2))
+      .WillOnce(Invoke(&callback, &RegressionTest5Callback::LockGranted3));
+
+  ON_CALL(text_input_client_, HasCompositionText())
+      .WillByDefault(
+          Invoke(&callback, &TSFTextStoreTestCallback::HasCompositionText));
+
+  HRESULT result = kInvalidResult;
+  EXPECT_EQ(S_OK, text_store_->RequestLock(TS_LF_READWRITE, &result));
+  EXPECT_EQ(S_OK, result);
+  result = kInvalidResult;
+  EXPECT_EQ(S_OK, text_store_->RequestLock(TS_LF_READWRITE, &result));
+  EXPECT_EQ(S_OK, result);
+  result = kInvalidResult;
+  EXPECT_EQ(S_OK, text_store_->RequestLock(TS_LF_READWRITE, &result));
+  EXPECT_EQ(S_OK, result);
+}
+
 }  // namespace
 }  // namespace ui
diff --git a/ui/ozone/platform/headless/headless_surface_factory.cc b/ui/ozone/platform/headless/headless_surface_factory.cc
index 2ecd82c..2829df0 100644
--- a/ui/ozone/platform/headless/headless_surface_factory.cc
+++ b/ui/ozone/platform/headless/headless_surface_factory.cc
@@ -34,12 +34,34 @@
 
 const base::FilePath::CharType kDevNull[] = FILE_PATH_LITERAL("/dev/null");
 
+base::FilePath GetPathForWidget(const base::FilePath& base_path,
+                                gfx::AcceleratedWidget widget) {
+  if (base_path.empty() || base_path == base::FilePath(kDevNull))
+    return base_path;
+
+    // Disambiguate multiple window output files with the window id.
+#if defined(OS_WIN)
+  std::string path =
+      base::NumberToString(reinterpret_cast<int>(widget)) + ".png";
+  std::wstring wpath(path.begin(), path.end());
+  return base_path.Append(wpath);
+#else
+  return base_path.Append(base::NumberToString(widget) + ".png");
+#endif
+}
+
 void WriteDataToFile(const base::FilePath& location, const SkBitmap& bitmap) {
   DCHECK(!location.empty());
   std::vector<unsigned char> png_data;
   gfx::PNGCodec::FastEncodeBGRASkBitmap(bitmap, true, &png_data);
-  base::WriteFile(location, reinterpret_cast<const char*>(png_data.data()),
-                  png_data.size());
+  if (base::WriteFile(location, reinterpret_cast<const char*>(png_data.data()),
+                      png_data.size()) < 0) {
+    static bool logged_once = false;
+    LOG_IF(ERROR, !logged_once)
+        << "Failed to write frame to file. "
+           "If running with the GPU process try --no-sandbox.";
+    logged_once = true;
+  }
 }
 
 // TODO(altimin): Find a proper way to capture rendering output.
@@ -78,6 +100,40 @@
   sk_sp<SkSurface> surface_;
 };
 
+class FileGLSurface : public GLSurfaceEglReadback {
+ public:
+  explicit FileGLSurface(const base::FilePath& location)
+      : location_(location) {}
+
+ private:
+  ~FileGLSurface() override = default;
+
+  // GLSurfaceEglReadback:
+  bool HandlePixels(uint8_t* pixels) override {
+    if (location_.empty())
+      return true;
+
+    const gfx::Size size = GetSize();
+    SkImageInfo info = SkImageInfo::MakeN32Premul(size.width(), size.height());
+    SkPixmap pixmap(info, pixels, info.minRowBytes());
+
+    SkBitmap bitmap;
+    bitmap.allocPixels(info);
+    if (!bitmap.writePixels(pixmap))
+      return false;
+
+    base::PostTask(FROM_HERE,
+                   {base::ThreadPool(), base::MayBlock(),
+                    base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
+                   base::BindOnce(&WriteDataToFile, location_, bitmap));
+    return true;
+  }
+
+  base::FilePath location_;
+
+  DISALLOW_COPY_AND_ASSIGN(FileGLSurface);
+};
+
 class TestPixmap : public gfx::NativePixmap {
  public:
   explicit TestPixmap(gfx::BufferFormat format) : format_(format) {}
@@ -117,15 +173,14 @@
 
 class GLOzoneEGLHeadless : public GLOzoneEGL {
  public:
-  GLOzoneEGLHeadless() = default;
+  GLOzoneEGLHeadless(const base::FilePath& base_path) : base_path_(base_path) {}
   ~GLOzoneEGLHeadless() override = default;
 
   // GLOzone:
   scoped_refptr<gl::GLSurface> CreateViewGLSurface(
       gfx::AcceleratedWidget window) override {
-    // TODO(kylechar): Extend GLSurface implementation to write to PNG file.
-    return gl::InitializeGLSurface(
-        base::MakeRefCounted<GLSurfaceEglReadback>());
+    return gl::InitializeGLSurface(base::MakeRefCounted<FileGLSurface>(
+        GetPathForWidget(base_path_, window)));
   }
 
   scoped_refptr<gl::GLSurface> CreateOffscreenGLSurface(
@@ -143,6 +198,8 @@
   }
 
  private:
+  base::FilePath base_path_;
+
   DISALLOW_COPY_AND_ASSIGN(GLOzoneEGLHeadless);
 };
 
@@ -150,28 +207,13 @@
 
 HeadlessSurfaceFactory::HeadlessSurfaceFactory(base::FilePath base_path)
     : base_path_(base_path),
-      swiftshader_implementation_(std::make_unique<GLOzoneEGLHeadless>()) {
+      swiftshader_implementation_(
+          std::make_unique<GLOzoneEGLHeadless>(base_path)) {
   CheckBasePath();
 }
 
 HeadlessSurfaceFactory::~HeadlessSurfaceFactory() = default;
 
-base::FilePath HeadlessSurfaceFactory::GetPathForWidget(
-    gfx::AcceleratedWidget widget) {
-  if (base_path_.empty() || base_path_ == base::FilePath(kDevNull))
-    return base_path_;
-
-    // Disambiguate multiple window output files with the window id.
-#if defined(OS_WIN)
-  std::string path =
-      base::NumberToString(reinterpret_cast<int>(widget)) + ".png";
-  std::wstring wpath(path.begin(), path.end());
-  return base_path_.Append(wpath);
-#else
-  return base_path_.Append(base::NumberToString(widget) + ".png");
-#endif
-}
-
 std::vector<gl::GLImplementation>
 HeadlessSurfaceFactory::GetAllowedGLImplementations() {
   return std::vector<gl::GLImplementation>{gl::kGLImplementationSwiftShaderGL};
@@ -192,7 +234,7 @@
 std::unique_ptr<SurfaceOzoneCanvas>
 HeadlessSurfaceFactory::CreateCanvasForWidget(gfx::AcceleratedWidget widget,
                                               base::TaskRunner* task_runner) {
-  return std::make_unique<FileSurface>(GetPathForWidget(widget));
+  return std::make_unique<FileSurface>(GetPathForWidget(base_path_, widget));
 }
 
 scoped_refptr<gfx::NativePixmap> HeadlessSurfaceFactory::CreateNativePixmap(
diff --git a/ui/ozone/platform/headless/headless_surface_factory.h b/ui/ozone/platform/headless/headless_surface_factory.h
index 67fe161..8270d643 100644
--- a/ui/ozone/platform/headless/headless_surface_factory.h
+++ b/ui/ozone/platform/headless/headless_surface_factory.h
@@ -20,8 +20,6 @@
   explicit HeadlessSurfaceFactory(base::FilePath base_path);
   ~HeadlessSurfaceFactory() override;
 
-  base::FilePath GetPathForWidget(gfx::AcceleratedWidget widget);
-
   // SurfaceFactoryOzone:
   std::vector<gl::GLImplementation> GetAllowedGLImplementations() override;
   GLOzone* GetGLOzone(gl::GLImplementation implementation) override;
diff --git a/ui/views/bubble/bubble_frame_view.cc b/ui/views/bubble/bubble_frame_view.cc
index 2e2a9a4..f939395 100644
--- a/ui/views/bubble/bubble_frame_view.cc
+++ b/ui/views/bubble/bubble_frame_view.cc
@@ -66,6 +66,10 @@
          std::max(0, window_bounds.right() - available_bounds.right());
 }
 
+// The height of the progress indicator shown at the top of the bubble frame
+// view.
+constexpr int kProgressIndicatorHeight = 4;
+
 }  // namespace
 
 // static
@@ -91,6 +95,12 @@
   close->SetTooltipText(base::string16());
 #endif
   close_ = AddChildView(std::move(close));
+
+  auto progress_indicator = std::make_unique<ProgressBar>(
+      kProgressIndicatorHeight, /*allow_round_corner=*/false);
+  progress_indicator->SetBackgroundColor(SK_ColorTRANSPARENT);
+  progress_indicator->SetVisible(false);
+  progress_indicator_ = AddChildView(std::move(progress_indicator));
 }
 
 BubbleFrameView::~BubbleFrameView() = default;
@@ -188,7 +198,7 @@
 
   if (point.y() < title()->bounds().bottom()) {
     auto* dialog_delegate = GetWidget()->widget_delegate()->AsDialogDelegate();
-    if (dialog_delegate && dialog_delegate->IsDialogDraggable()) {
+    if (dialog_delegate && dialog_delegate->draggable()) {
       return HTCAPTION;
     }
   }
@@ -265,6 +275,12 @@
   AddChildViewAt(title_view.release(), GetIndexOf(title_icon_) + 1);
 }
 
+void BubbleFrameView::SetProgress(base::Optional<double> progress) {
+  progress_indicator_->SetVisible(progress.has_value());
+  if (progress)
+    progress_indicator_->SetValue(progress.value());
+}
+
 const char* BubbleFrameView::GetClassName() const {
   return kViewClassName;
 }
@@ -313,6 +329,11 @@
          (!custom_title_ && !default_title_->GetVisible()));
 
   const gfx::Rect contents_bounds = GetContentsBounds();
+
+  progress_indicator_->SetBounds(contents_bounds.x(), contents_bounds.y(),
+                                 contents_bounds.width(),
+                                 kProgressIndicatorHeight);
+
   gfx::Rect bounds = contents_bounds;
   bounds.Inset(title_margins_);
 
diff --git a/ui/views/bubble/bubble_frame_view.h b/ui/views/bubble/bubble_frame_view.h
index 403410f..e5b454f 100644
--- a/ui/views/bubble/bubble_frame_view.h
+++ b/ui/views/bubble/bubble_frame_view.h
@@ -16,6 +16,7 @@
 #include "ui/views/bubble/bubble_border.h"
 #include "ui/views/controls/button/button.h"
 #include "ui/views/controls/label.h"
+#include "ui/views/controls/progress_bar.h"
 #include "ui/views/input_event_activation_protector.h"
 #include "ui/views/window/non_client_view.h"
 
@@ -60,6 +61,10 @@
   // label. If there is an existing title view it will be deleted.
   void SetTitleView(std::unique_ptr<View> title_view);
 
+  // Updates the current progress value of |progress_indicator_|. If progress is
+  // absent, hides |the progress_indicator|.
+  void SetProgress(base::Optional<double> progress);
+
   // View:
   const char* GetClassName() const override;
   gfx::Size CalculatePreferredSize() const override;
@@ -167,6 +172,7 @@
  private:
   FRIEND_TEST_ALL_PREFIXES(BubbleFrameViewTest, RemoveFootnoteView);
   FRIEND_TEST_ALL_PREFIXES(BubbleFrameViewTest, LayoutWithIcon);
+  FRIEND_TEST_ALL_PREFIXES(BubbleFrameViewTest, LayoutWithProgressIndicator);
   FRIEND_TEST_ALL_PREFIXES(BubbleFrameViewTest, IgnorePossiblyUnintendedClicks);
   FRIEND_TEST_ALL_PREFIXES(BubbleDelegateTest, CloseReasons);
   FRIEND_TEST_ALL_PREFIXES(BubbleDialogDelegateViewTest, CloseMethods);
@@ -234,6 +240,10 @@
   // The optional close button (the X).
   Button* close_ = nullptr;
 
+  // The optional progress bar. Used to indicate bubble pending state. By
+  // default it is invisible.
+  ProgressBar* progress_indicator_ = nullptr;
+
   // The optional header view.
   View* header_view_ = nullptr;
 
diff --git a/ui/views/bubble/bubble_frame_view_unittest.cc b/ui/views/bubble/bubble_frame_view_unittest.cc
index cf879870..53db9bd 100644
--- a/ui/views/bubble/bubble_frame_view_unittest.cc
+++ b/ui/views/bubble/bubble_frame_view_unittest.cc
@@ -1265,4 +1265,24 @@
   EXPECT_TRUE(bubble->IsClosed());
 }
 
+// Ensures that layout is correct when the progress indicator is visible.
+TEST_F(BubbleFrameViewTest, LayoutWithProgressIndicator) {
+  TestBubbleDialogDelegateView delegate;
+  TestAnchor anchor(CreateParams(Widget::InitParams::TYPE_WINDOW));
+  delegate.SetAnchorView(anchor.widget().GetContentsView());
+  Widget* bubble = BubbleDialogDelegateView::CreateBubble(&delegate);
+  bubble->Show();
+
+  BubbleFrameView* frame = delegate.GetBubbleFrameView();
+  frame->SetProgress(/*infinite animation*/ -1);
+  View* progress_indicator = frame->progress_indicator_;
+
+  // Ensures the progress indicator is visible and takes full widget width.
+  EXPECT_TRUE(progress_indicator->GetVisible());
+  EXPECT_EQ(progress_indicator->x(), 0);
+  EXPECT_EQ(progress_indicator->y(), 0);
+  EXPECT_EQ(progress_indicator->width(),
+            bubble->GetWindowBoundsInScreen().width());
+}
+
 }  // namespace views
diff --git a/ui/views/window/dialog_delegate.cc b/ui/views/window/dialog_delegate.cc
index 11e9df5..e9b59cfb 100644
--- a/ui/views/window/dialog_delegate.cc
+++ b/ui/views/window/dialog_delegate.cc
@@ -165,10 +165,6 @@
   return Accept();
 }
 
-bool DialogDelegate::IsDialogDraggable() const {
-  return false;
-}
-
 void DialogDelegate::UpdateButton(LabelButton* button, ui::DialogButton type) {
   button->SetText(GetDialogButtonLabel(type));
   button->SetEnabled(IsDialogButtonEnabled(type));
diff --git a/ui/views/window/dialog_delegate.h b/ui/views/window/dialog_delegate.h
index 32b2b96f..28ff960 100644
--- a/ui/views/window/dialog_delegate.h
+++ b/ui/views/window/dialog_delegate.h
@@ -40,6 +40,7 @@
     ~Params();
     base::Optional<int> default_button = base::nullopt;
     bool round_corners = true;
+    bool draggable = false;
   };
 
   DialogDelegate();
@@ -113,10 +114,6 @@
   // must remain open.
   virtual bool Close();
 
-  // Dialogs should not be draggable unless the dialog can be created with no
-  // parent browser window.
-  virtual bool IsDialogDraggable() const;
-
   // Updates the properties and appearance of |button| which has been created
   // for type |type|. Override to do special initialization above and beyond
   // the typical.
@@ -156,6 +153,8 @@
 
   void set_default_button(int button) { params_.default_button = button; }
   void set_use_round_corners(bool round) { params_.round_corners = round; }
+  void set_draggable(bool draggable) { params_.draggable = draggable; }
+  bool draggable() const { return params_.draggable; }
 
  protected:
   ~DialogDelegate() override;
diff --git a/ui/views/window/dialog_delegate_unittest.cc b/ui/views/window/dialog_delegate_unittest.cc
index 4c39bfa..8cd18bd8e 100644
--- a/ui/views/window/dialog_delegate_unittest.cc
+++ b/ui/views/window/dialog_delegate_unittest.cc
@@ -29,7 +29,10 @@
 
 class TestDialog : public DialogDelegateView {
  public:
-  TestDialog() : input_(new views::Textfield()) { AddChildView(input_); }
+  TestDialog() : input_(new views::Textfield()) {
+    DialogDelegate::set_draggable(true);
+    AddChildView(input_);
+  }
   ~TestDialog() override = default;
 
   void Init() {
@@ -57,7 +60,6 @@
     closed_ = true;
     return closeable_;
   }
-  bool IsDialogDraggable() const override { return true; }
 
   gfx::Size CalculatePreferredSize() const override {
     return gfx::Size(200, 200);