diff --git a/AUTHORS b/AUTHORS
index 171836c..c556566 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -992,6 +992,7 @@
 Thiago Marcos P. Santos <thiago.santos@intel.com>
 Thomas Butter <tbutter@gmail.com>
 Thomas Conti <tomc@amazon.com>
+Thomas Nguyen <haitung.nguyen@avast.com>
 Thomas White <im.toms.inbox@gmail.com>
 Tiago Vignatti <tiago.vignatti@intel.com>
 Tibor Dusnoki <tibor.dusnoki.91@gmail.com>
diff --git a/DEPS b/DEPS
index 7b58126..6378e4f 100644
--- a/DEPS
+++ b/DEPS
@@ -195,11 +195,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': '747a820a7d69c1e969cf4a1108958b2818ded3f6',
+  'skia_revision': 'd7c4f9d06425d8ed5134475e94fb34f265fa1fbc',
   # 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': 'c07c0c9311b44b0d618819b7b916d154326ee177',
+  'v8_revision': '759f70344a9a68576605324f760c5942981ea9fd',
   # 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.
@@ -207,7 +207,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': '19a1943b27a0b9fc821dcaeb5726acbcf4342494',
+  'angle_revision': 'be774187b1cefe94f4717cbd747ebdea0b349478',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
@@ -266,7 +266,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling devtools-frontend
   # and whatever else without interference from each other.
-  'devtools_frontend_revision': 'f70087925046672bfdd8277b17fabb31ef0795b3',
+  'devtools_frontend_revision': '3186e69eb7995747d51b2c7126b8fef05c5032d1',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libprotobuf-mutator
   # and whatever else without interference from each other.
@@ -322,7 +322,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'quiche_revision': '74ee66401627cc3ac60a153dfc57ecfb9650b347',
+  'quiche_revision': '2146ce8055a5e96371eb31a169c46f322afac527',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ios_webkit
   # and whatever else without interference from each other.
@@ -875,7 +875,7 @@
 
   # Build tools for Chrome OS.
   'src/third_party/chromite': {
-      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + 'bf47d2ef673b889870f4169e10a20f9c04d9db45',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + 'de0ed93bda6323335cf84bc3d33e3aa848786a65',
       'condition': 'checkout_chromeos',
   },
 
@@ -1248,7 +1248,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' + 'f6c424bb59238a75fbb10d4d802b5ad31107a3a3',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' + '22b8657a5558de83e88093a85d302f0d0773bb9f',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '6f3e5028eb65d0b4c5fdd792106ac4c84eee1eb3',
@@ -1465,7 +1465,7 @@
   },
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + 'ee8c246be71e4dd8d4ca34011d3b3e40faf576a2',
+    Var('webrtc_git') + '/src.git' + '@' + '81bbd7199a2e97680a4488c2da4f8248137e12e0',
 
   'src/third_party/libgifcodec':
      Var('skia_git') + '/libgifcodec' + '@'+  Var('libgifcodec_revision'),
@@ -1537,7 +1537,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@f8ef67b87b415744c3325cb484a38dce93e5bacb',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@08057456d373475cd7e551639c334f360b1d4f92',
     'condition': 'checkout_src_internal',
   },
 
@@ -4097,8 +4097,12 @@
   '+third_party/icu/source/i18n/unicode',
   '+url',
 
-  # Chromium cannot directly depend on Abseil.
+  # Abseil features must be allowlisted explicitly for now. See
+  # //styleguide/c++/c++11.html. Allowed features' headers will be listed
+  # explicitly here.
   '-absl',
+  '-third_party/abseil-cpp',
+  '+third_party/abseil-cpp/absl/types/variant.h',
 ]
 
 
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsTest.java
index 7c88ee5..365b2b7 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsTest.java
@@ -37,6 +37,7 @@
 import org.chromium.android_webview.test.TestAwContentsClient.OnDownloadStartHelper;
 import org.chromium.android_webview.test.util.CommonResources;
 import org.chromium.base.BuildInfo;
+import org.chromium.base.Log;
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.base.test.util.CallbackHelper;
 import org.chromium.base.test.util.CommandLineFlags;
@@ -55,6 +56,7 @@
 import java.util.Map;
 import java.util.concurrent.Semaphore;
 import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
 import java.util.concurrent.atomic.AtomicInteger;
 
 /**
@@ -62,6 +64,8 @@
  */
 @RunWith(AwJUnit4ClassRunner.class)
 public class AwContentsTest {
+    private static final String TAG = "AwContentsTest";
+
     @Rule
     public AwActivityTestRule mActivityTestRule = new AwActivityTestRule();
 
@@ -837,7 +841,12 @@
                     callbackHelper.notifyCalled();
                 });
             });
-            callbackHelper.waitForFirst();
+            try {
+                callbackHelper.waitForFirst();
+            } catch (TimeoutException e) {
+                Log.w(TAG, "Timeout", e);
+                continue;
+            }
             int[] quadrantColors = (int[]) resultHolder[0];
             lastQuadrantColors = quadrantColors;
             if (quadrantColors != null && Color.rgb(255, 0, 0) == quadrantColors[0]
diff --git a/ash/BUILD.gn b/ash/BUILD.gn
index c2fabf06..67fa7f7 100644
--- a/ash/BUILD.gn
+++ b/ash/BUILD.gn
@@ -1944,6 +1944,7 @@
     "system/message_center/ash_message_popup_collection_unittest.cc",
     "system/message_center/inactive_user_notification_blocker_unittest.cc",
     "system/message_center/message_center_ui_controller_unittest.cc",
+    "system/message_center/notification_swipe_control_view_unittest.cc",
     "system/message_center/notifier_settings_view_unittest.cc",
     "system/message_center/session_state_notification_blocker_unittest.cc",
     "system/message_center/unified_message_center_bubble_unittest.cc",
diff --git a/ash/app_list/views/assistant/assistant_page_view.cc b/ash/app_list/views/assistant/assistant_page_view.cc
index d8becd6..fe3a302 100644
--- a/ash/app_list/views/assistant/assistant_page_view.cc
+++ b/ash/app_list/views/assistant/assistant_page_view.cc
@@ -22,9 +22,11 @@
 #include "ash/public/cpp/assistant/controller/assistant_ui_controller.h"
 #include "ash/public/cpp/view_shadow.h"
 #include "ash/strings/grit/ash_strings.h"
+#include "base/metrics/histogram_functions.h"
 #include "base/strings/utf_string_conversions.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/chromeos/search_box/search_box_constants.h"
+#include "ui/compositor/animation_throughput_reporter.h"
 #include "ui/compositor/scoped_layer_animation_settings.h"
 #include "ui/compositor_extra/shadow.h"
 #include "ui/views/background.h"
@@ -282,6 +284,14 @@
   // of a bounds animation.
   {
     auto settings = contents_view()->CreateTransitionAnimationSettings(layer());
+
+    ui::AnimationThroughputReporter reporter(
+        settings->GetAnimator(),
+        metrics_util::ForSmoothness(base::BindRepeating([](int value) {
+          base::UmaHistogramPercentage(
+              assistant::ui::kAssistantResizePageViewHistogram, value);
+        })));
+
     layer()->SetClipRect(gfx::Rect(to_rect.size()));
 
     // Also animate corner radius for the view.
diff --git a/ash/assistant/ui/BUILD.gn b/ash/assistant/ui/BUILD.gn
index ee005b3..07cd341 100644
--- a/ash/assistant/ui/BUILD.gn
+++ b/ash/assistant/ui/BUILD.gn
@@ -70,6 +70,8 @@
     "main_stage/assistant_text_element_view.h",
     "main_stage/assistant_ui_element_view.cc",
     "main_stage/assistant_ui_element_view.h",
+    "main_stage/assistant_ui_element_view_animator.cc",
+    "main_stage/assistant_ui_element_view_animator.h",
     "main_stage/assistant_ui_element_view_factory.cc",
     "main_stage/assistant_ui_element_view_factory.h",
     "main_stage/assistant_zero_state_view.cc",
diff --git a/ash/assistant/ui/assistant_ui_constants.h b/ash/assistant/ui/assistant_ui_constants.h
index 8667802..40335e6f 100644
--- a/ash/assistant/ui/assistant_ui_constants.h
+++ b/ash/assistant/ui/assistant_ui_constants.h
@@ -43,6 +43,16 @@
 
 constexpr int kWarmerWelcomesMaxTimesTriggered = 3;
 
+// Histogram names for measuring animation performance.
+constexpr char kAssistantCardElementHistogram[] =
+    "Ash.Assistant.AnimationSmoothness.CardElement";
+constexpr char kAssistantTextElementHistogram[] =
+    "Ash.Assistant.AnimationSmoothness.TextElement";
+constexpr char kAssistantResizePageViewHistogram[] =
+    "Ash.Assistant.AnimationSmoothness.ResizeAssistantPageView";
+constexpr char kAssistantSuggestionChipHistogram[] =
+    "Ash.Assistant.AnimationSmoothness.SuggestionChip";
+
 }  // namespace ui
 }  // namespace assistant
 
diff --git a/ash/assistant/ui/main_stage/assistant_card_element_view.cc b/ash/assistant/ui/main_stage/assistant_card_element_view.cc
index 492927f..f94c76a5 100644
--- a/ash/assistant/ui/main_stage/assistant_card_element_view.cc
+++ b/ash/assistant/ui/main_stage/assistant_card_element_view.cc
@@ -9,6 +9,7 @@
 #include "ash/assistant/model/ui/assistant_card_element.h"
 #include "ash/assistant/ui/assistant_ui_constants.h"
 #include "ash/assistant/ui/assistant_view_delegate.h"
+#include "ash/assistant/ui/main_stage/assistant_ui_element_view_animator.h"
 #include "ash/assistant/util/deep_link_util.h"
 #include "ash/public/cpp/assistant/controller/assistant_controller.h"
 #include "ui/aura/window.h"
@@ -214,4 +215,9 @@
   GetViewAccessibility().OverrideName(card_element_->fallback());
 }
 
+std::unique_ptr<ElementAnimator> AssistantCardElementView::CreateAnimator() {
+  return std::make_unique<AssistantUiElementViewAnimator>(
+      this, assistant::ui::kAssistantCardElementHistogram);
+}
+
 }  // namespace ash
diff --git a/ash/assistant/ui/main_stage/assistant_card_element_view.h b/ash/assistant/ui/main_stage/assistant_card_element_view.h
index 4d2d61a..df26e6b 100644
--- a/ash/assistant/ui/main_stage/assistant_card_element_view.h
+++ b/ash/assistant/ui/main_stage/assistant_card_element_view.h
@@ -35,6 +35,7 @@
   void ChildPreferredSizeChanged(views::View* child) override;
   void OnGestureEvent(ui::GestureEvent* event) override;
   void ScrollRectToVisible(const gfx::Rect& rect) override;
+  std::unique_ptr<ElementAnimator> CreateAnimator() override;
 
   // AssistantWebView::Observer:
   void DidSuppressNavigation(const GURL& url,
diff --git a/ash/assistant/ui/main_stage/assistant_text_element_view.cc b/ash/assistant/ui/main_stage/assistant_text_element_view.cc
index 0e1173e..4913803 100644
--- a/ash/assistant/ui/main_stage/assistant_text_element_view.cc
+++ b/ash/assistant/ui/main_stage/assistant_text_element_view.cc
@@ -8,6 +8,7 @@
 
 #include "ash/assistant/model/ui/assistant_text_element.h"
 #include "ash/assistant/ui/assistant_ui_constants.h"
+#include "ash/assistant/ui/main_stage/assistant_ui_element_view_animator.h"
 #include "base/strings/utf_string_conversions.h"
 #include "ui/views/background.h"
 #include "ui/views/controls/label.h"
@@ -65,4 +66,9 @@
   label_->SetMultiLine(true);
 }
 
+std::unique_ptr<ElementAnimator> AssistantTextElementView::CreateAnimator() {
+  return std::make_unique<AssistantUiElementViewAnimator>(
+      this, assistant::ui::kAssistantTextElementHistogram);
+}
+
 }  // namespace ash
diff --git a/ash/assistant/ui/main_stage/assistant_text_element_view.h b/ash/assistant/ui/main_stage/assistant_text_element_view.h
index 05c1a24e..ad5e005 100644
--- a/ash/assistant/ui/main_stage/assistant_text_element_view.h
+++ b/ash/assistant/ui/main_stage/assistant_text_element_view.h
@@ -18,6 +18,7 @@
 namespace ash {
 
 class AssistantTextElement;
+class ElementAnimator;
 
 // AssistantTextElementView is the visual representation of an
 // AssistantTextElement. It is a child view of UiElementContainerView.
@@ -35,6 +36,7 @@
   ui::Layer* GetLayerForAnimating() override;
   std::string ToStringForTesting() const override;
   void ChildPreferredSizeChanged(views::View* child) override;
+  std::unique_ptr<ElementAnimator> CreateAnimator() override;
 
  private:
   void InitLayout(const std::string& text);
diff --git a/ash/assistant/ui/main_stage/assistant_ui_element_view.cc b/ash/assistant/ui/main_stage/assistant_ui_element_view.cc
index b8d4c60..28ac5ff 100644
--- a/ash/assistant/ui/main_stage/assistant_ui_element_view.cc
+++ b/ash/assistant/ui/main_stage/assistant_ui_element_view.cc
@@ -4,65 +4,8 @@
 
 #include "ash/assistant/ui/main_stage/assistant_ui_element_view.h"
 
-#include "ash/assistant/ui/main_stage/element_animator.h"
-#include "ash/assistant/util/animation_util.h"
-#include "ui/compositor/callback_layer_animation_observer.h"
-#include "ui/compositor/layer_animation_element.h"
-#include "ui/compositor/layer_animator.h"
-
 namespace ash {
 
-namespace {
-
-using assistant::util::CreateLayerAnimationSequence;
-using assistant::util::CreateOpacityElement;
-using assistant::util::StartLayerAnimationSequence;
-
-// Animation.
-constexpr base::TimeDelta kAnimateInDuration =
-    base::TimeDelta::FromMilliseconds(250);
-constexpr base::TimeDelta kAnimateOutDuration =
-    base::TimeDelta::FromMilliseconds(200);
-
-// AssistantUiElementViewAnimator ----------------------------------------------
-
-class AssistantUiElementViewAnimator : public ElementAnimator {
- public:
-  explicit AssistantUiElementViewAnimator(AssistantUiElementView* view)
-      : ElementAnimator(view), view_(view) {}
-  explicit AssistantUiElementViewAnimator(
-      AssistantUiElementViewAnimator& copy) = delete;
-  AssistantUiElementViewAnimator& operator=(
-      AssistantUiElementViewAnimator& assign) = delete;
-  ~AssistantUiElementViewAnimator() override = default;
-
-  // ElementAnimator:
-  void AnimateIn(ui::CallbackLayerAnimationObserver* observer) override {
-    StartLayerAnimationSequence(
-        layer()->GetAnimator(),
-        CreateLayerAnimationSequence(CreateOpacityElement(
-            1.f, kAnimateInDuration, gfx::Tween::Type::FAST_OUT_SLOW_IN)),
-        observer);
-  }
-
-  void AnimateOut(ui::CallbackLayerAnimationObserver* observer) override {
-    StartLayerAnimationSequence(
-        layer()->GetAnimator(),
-        CreateLayerAnimationSequence(CreateOpacityElement(
-            kMinimumAnimateOutOpacity, kAnimateOutDuration)),
-        observer);
-  }
-
-  ui::Layer* layer() const override { return view_->GetLayerForAnimating(); }
-
- private:
-  AssistantUiElementView* const view_;
-};
-
-}  // namespace
-
-// AssistantUiElementView ------------------------------------------------------
-
 AssistantUiElementView::AssistantUiElementView() = default;
 
 AssistantUiElementView::~AssistantUiElementView() = default;
@@ -71,8 +14,4 @@
   return "AssistantUiElementView";
 }
 
-std::unique_ptr<ElementAnimator> AssistantUiElementView::CreateAnimator() {
-  return std::make_unique<AssistantUiElementViewAnimator>(this);
-}
-
 }  // namespace ash
diff --git a/ash/assistant/ui/main_stage/assistant_ui_element_view.h b/ash/assistant/ui/main_stage/assistant_ui_element_view.h
index ca36f22..18e8aa5 100644
--- a/ash/assistant/ui/main_stage/assistant_ui_element_view.h
+++ b/ash/assistant/ui/main_stage/assistant_ui_element_view.h
@@ -35,7 +35,7 @@
 
   // Returns a newly created animator which is used by UiElementContainerView
   // to animate this view on/off stage in sync with Assistant response events.
-  virtual std::unique_ptr<ElementAnimator> CreateAnimator();
+  virtual std::unique_ptr<ElementAnimator> CreateAnimator() = 0;
 
  protected:
   AssistantUiElementView();
diff --git a/ash/assistant/ui/main_stage/assistant_ui_element_view_animator.cc b/ash/assistant/ui/main_stage/assistant_ui_element_view_animator.cc
new file mode 100644
index 0000000..152f514
--- /dev/null
+++ b/ash/assistant/ui/main_stage/assistant_ui_element_view_animator.cc
@@ -0,0 +1,66 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ash/assistant/ui/main_stage/assistant_ui_element_view_animator.h"
+
+#include "ash/assistant/ui/main_stage/assistant_ui_element_view.h"
+#include "ash/assistant/ui/main_stage/element_animator.h"
+#include "ash/assistant/util/animation_util.h"
+#include "base/metrics/histogram_functions.h"
+#include "ui/compositor/callback_layer_animation_observer.h"
+#include "ui/gfx/animation/tween.h"
+
+namespace ash {
+
+namespace {
+
+using assistant::util::CreateLayerAnimationSequence;
+using assistant::util::CreateOpacityElement;
+using assistant::util::StartLayerAnimationSequence;
+
+// Animation.
+constexpr base::TimeDelta kAnimateInDuration =
+    base::TimeDelta::FromMilliseconds(250);
+constexpr base::TimeDelta kAnimateOutDuration =
+    base::TimeDelta::FromMilliseconds(200);
+
+}  // namespace
+
+AssistantUiElementViewAnimator::AssistantUiElementViewAnimator(
+    AssistantUiElementView* view,
+    const char* animation_smoothness_histogram)
+    : ElementAnimator(view),
+      view_(view),
+      animation_smoothness_histogram_(animation_smoothness_histogram) {}
+
+AssistantUiElementViewAnimator::AnimationSmoothnessCallback
+AssistantUiElementViewAnimator::GetAnimationSmoothnessCallback() const {
+  return base::BindRepeating<void(const std::string&, int value)>(
+      base::UmaHistogramPercentage, animation_smoothness_histogram_);
+}
+
+// ElementAnimator:
+void AssistantUiElementViewAnimator::AnimateIn(
+    ui::CallbackLayerAnimationObserver* observer) {
+  StartLayerAnimationSequence(
+      layer()->GetAnimator(),
+      CreateLayerAnimationSequence(CreateOpacityElement(
+          1.f, kAnimateInDuration, gfx::Tween::Type::FAST_OUT_SLOW_IN)),
+      observer, GetAnimationSmoothnessCallback());
+}
+
+void AssistantUiElementViewAnimator::AnimateOut(
+    ui::CallbackLayerAnimationObserver* observer) {
+  StartLayerAnimationSequence(
+      layer()->GetAnimator(),
+      CreateLayerAnimationSequence(
+          CreateOpacityElement(kMinimumAnimateOutOpacity, kAnimateOutDuration)),
+      observer, GetAnimationSmoothnessCallback());
+}
+
+ui::Layer* AssistantUiElementViewAnimator::layer() const {
+  return view_->GetLayerForAnimating();
+}
+
+}  // namespace ash
diff --git a/ash/assistant/ui/main_stage/assistant_ui_element_view_animator.h b/ash/assistant/ui/main_stage/assistant_ui_element_view_animator.h
new file mode 100644
index 0000000..ff0c122
--- /dev/null
+++ b/ash/assistant/ui/main_stage/assistant_ui_element_view_animator.h
@@ -0,0 +1,49 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef ASH_ASSISTANT_UI_MAIN_STAGE_ASSISTANT_UI_ELEMENT_VIEW_ANIMATOR_H_
+#define ASH_ASSISTANT_UI_MAIN_STAGE_ASSISTANT_UI_ELEMENT_VIEW_ANIMATOR_H_
+
+#include "ash/assistant/ui/main_stage/element_animator.h"
+#include "ash/assistant/util/animation_util.h"
+#include "base/callback.h"
+
+namespace ui {
+class CallbackLayerAnimationObserver;
+class Layer;
+}  // namespace ui
+
+namespace ash {
+
+class AssistantUiElementView;
+
+class AssistantUiElementViewAnimator : public ElementAnimator {
+ public:
+  using AnimationSmoothnessCallback =
+      assistant::util::AnimationSmoothnessCallback;
+
+  AssistantUiElementViewAnimator(AssistantUiElementView* view,
+                                 const char* animation_smoothness_histogram);
+
+  explicit AssistantUiElementViewAnimator(
+      AssistantUiElementViewAnimator& copy) = delete;
+  AssistantUiElementViewAnimator& operator=(
+      AssistantUiElementViewAnimator& assign) = delete;
+  ~AssistantUiElementViewAnimator() override = default;
+
+  // ElementAnimator:
+  void AnimateIn(ui::CallbackLayerAnimationObserver* observer) override;
+  void AnimateOut(ui::CallbackLayerAnimationObserver* observer) override;
+  ui::Layer* layer() const override;
+
+  AnimationSmoothnessCallback GetAnimationSmoothnessCallback() const;
+
+ private:
+  AssistantUiElementView* const view_;
+  std::string const animation_smoothness_histogram_;
+};
+
+}  // namespace ash
+
+#endif  // ASH_ASSISTANT_UI_MAIN_STAGE_ASSISTANT_UI_ELEMENT_VIEW_ANIMATOR_H_
diff --git a/ash/assistant/ui/main_stage/element_animator.h b/ash/assistant/ui/main_stage/element_animator.h
index a88e40a..d74da75e 100644
--- a/ash/assistant/ui/main_stage/element_animator.h
+++ b/ash/assistant/ui/main_stage/element_animator.h
@@ -34,7 +34,7 @@
   // value, it should be safe to circumnavigate this DCHECK.
   constexpr static float kMinimumAnimateOutOpacity = 0.0001f;
 
-  ElementAnimator(views::View* animated_view);
+  explicit ElementAnimator(views::View* animated_view);
   virtual ~ElementAnimator() = default;
 
   // Fade out the current element, meaning it will still be visible but
diff --git a/ash/assistant/ui/main_stage/suggestion_container_view.cc b/ash/assistant/ui/main_stage/suggestion_container_view.cc
index a16de97..2a23b68 100644
--- a/ash/assistant/ui/main_stage/suggestion_container_view.cc
+++ b/ash/assistant/ui/main_stage/suggestion_container_view.cc
@@ -22,6 +22,7 @@
 #include "ash/public/cpp/assistant/controller/assistant_suggestions_controller.h"
 #include "ash/public/cpp/assistant/controller/assistant_ui_controller.h"
 #include "base/bind.h"
+#include "base/metrics/histogram_functions.h"
 #include "ui/compositor/callback_layer_animation_observer.h"
 #include "ui/compositor/layer_animation_element.h"
 #include "ui/views/layout/box_layout.h"
@@ -55,13 +56,19 @@
   ~SuggestionChipAnimator() override = default;
 
   void AnimateIn(ui::CallbackLayerAnimationObserver* observer) override {
-    StartLayerAnimationSequence(layer()->GetAnimator(),
-                                CreateAnimateInAnimation(), observer);
+    StartLayerAnimationSequence(
+        layer()->GetAnimator(), CreateAnimateInAnimation(), observer,
+        base::BindRepeating<void(const std::string&, int)>(
+            base::UmaHistogramPercentage,
+            assistant::ui::kAssistantSuggestionChipHistogram));
   }
 
   void AnimateOut(ui::CallbackLayerAnimationObserver* observer) override {
-    StartLayerAnimationSequence(layer()->GetAnimator(),
-                                CreateAnimateOutAnimation(), observer);
+    StartLayerAnimationSequence(
+        layer()->GetAnimator(), CreateAnimateOutAnimation(), observer,
+        base::BindRepeating<void(const std::string&, int)>(
+            base::UmaHistogramPercentage,
+            assistant::ui::kAssistantSuggestionChipHistogram));
   }
 
   void FadeOut(ui::CallbackLayerAnimationObserver* observer) override {
@@ -74,9 +81,8 @@
   bool IsSelectedChip() const { return view() == parent_->selected_chip(); }
 
   ui::LayerAnimationSequence* CreateAnimateInAnimation() const {
-    return CreateLayerAnimationSequence(
-        CreateOpacityElement(1.f, kChipFadeInDuration,
-                             gfx::Tween::Type::FAST_OUT_SLOW_IN));
+    return CreateLayerAnimationSequence(CreateOpacityElement(
+        1.f, kChipFadeInDuration, gfx::Tween::Type::FAST_OUT_SLOW_IN));
   }
 
   ui::LayerAnimationSequence* CreateAnimateOutAnimation() const {
diff --git a/ash/assistant/util/BUILD.gn b/ash/assistant/util/BUILD.gn
index aa4c1331..31a4675 100644
--- a/ash/assistant/util/BUILD.gn
+++ b/ash/assistant/util/BUILD.gn
@@ -26,6 +26,7 @@
 
   deps = [
     "//ash/assistant/model",
+    "//ash/public/cpp",
     "//ash/public/cpp/vector_icons",
     "//base",
     "//base:i18n",
diff --git a/ash/assistant/util/DEPS b/ash/assistant/util/DEPS
index 19d3edc5..8bae261 100644
--- a/ash/assistant/util/DEPS
+++ b/ash/assistant/util/DEPS
@@ -2,7 +2,7 @@
   "-ash",
   "+ash/assistant/model",
   "+ash/assistant/util",
-  "+ash/public/cpp/vector_icons",
+  "+ash/public/cpp",
   "+net",
   "+ui/base",
   "+ui/gfx",
diff --git a/ash/assistant/util/animation_util.cc b/ash/assistant/util/animation_util.cc
index 5d4e01612..46fb357 100644
--- a/ash/assistant/util/animation_util.cc
+++ b/ash/assistant/util/animation_util.cc
@@ -4,7 +4,9 @@
 
 #include "ash/assistant/util/animation_util.h"
 
+#include "ash/public/cpp/metrics_util.h"
 #include "base/time/time.h"
+#include "ui/compositor/animation_throughput_reporter.h"
 #include "ui/compositor/callback_layer_animation_observer.h"
 #include "ui/compositor/layer_animation_element.h"
 #include "ui/compositor/layer_animation_observer.h"
@@ -112,19 +114,28 @@
 void StartLayerAnimationSequence(
     ::ui::LayerAnimator* layer_animator,
     ::ui::LayerAnimationSequence* layer_animation_sequence,
-    ::ui::LayerAnimationObserver* observer) {
+    ::ui::LayerAnimationObserver* observer,
+    base::Optional<AnimationSmoothnessCallback> smoothness_callback) {
   if (observer)
     layer_animation_sequence->AddObserver(observer);
+
+  base::Optional<ui::AnimationThroughputReporter> reporter;
+  if (smoothness_callback) {
+    reporter.emplace(layer_animator, ash::metrics_util::ForSmoothness(
+                                         smoothness_callback.value()));
+  }
   layer_animator->StartAnimation(layer_animation_sequence);
 }
 
 void StartLayerAnimationSequence(
     views::View* view,
     ::ui::LayerAnimationSequence* layer_animation_sequence,
-    ::ui::LayerAnimationObserver* observer) {
+    ::ui::LayerAnimationObserver* observer,
+    base::Optional<AnimationSmoothnessCallback> smoothness_callback) {
   DCHECK(view->layer());
   StartLayerAnimationSequence(view->layer()->GetAnimator(),
-                              layer_animation_sequence, observer);
+                              layer_animation_sequence, observer,
+                              smoothness_callback);
 }
 
 void StartLayerAnimationSequencesTogether(
diff --git a/ash/assistant/util/animation_util.h b/ash/assistant/util/animation_util.h
index 25483f6..cea08bc 100644
--- a/ash/assistant/util/animation_util.h
+++ b/ash/assistant/util/animation_util.h
@@ -8,7 +8,9 @@
 #include <memory>
 #include <vector>
 
+#include "base/callback.h"
 #include "base/component_export.h"
+#include "base/optional.h"
 #include "ui/gfx/animation/tween.h"
 
 namespace base {
@@ -36,6 +38,8 @@
   bool is_cyclic = false;
 };
 
+using AnimationSmoothnessCallback = base::RepeatingCallback<void(int)>;
+
 // Creates a LayerAnimationSequence containing the specified
 // LayerAnimationElements with the given |params|. The method caller assumes
 // ownership of the returned pointer.
@@ -91,21 +95,27 @@
 
 // Starts the specified |layer_animation_sequence| on the given
 // |layer_animator|. If an optional |observer| is supplied, it will be added to
-// the sequence.
+// the sequence. If an optional |smoothness_callback| is supplied, it
+// will be attached to the animation to measure performance.
 COMPONENT_EXPORT(ASSISTANT_UTIL)
 void StartLayerAnimationSequence(
     ::ui::LayerAnimator* layer_animator,
     ::ui::LayerAnimationSequence* layer_animation_sequence,
-    ::ui::LayerAnimationObserver* observer = nullptr);
+    ::ui::LayerAnimationObserver* observer = nullptr,
+    base::Optional<AnimationSmoothnessCallback> smoothness_callback =
+        base::nullopt);
 
 // Starts the specified |layer_animation_sequence| on the layer of the given
 // |view|. If an optional |observer| is supplied, it will be added to the
-// sequence.
+// sequence. If an optional |smoothness_callback| is supplied, it will be
+// attached to the animation to measure performance.
 COMPONENT_EXPORT(ASSISTANT_UTIL)
 void StartLayerAnimationSequence(
     views::View* view,
     ::ui::LayerAnimationSequence* layer_animation_sequence,
-    ::ui::LayerAnimationObserver* observer = nullptr);
+    ::ui::LayerAnimationObserver* observer = nullptr,
+    base::Optional<AnimationSmoothnessCallback> animation_smoothness_callback =
+        base::nullopt);
 
 // Starts the specified |layer_animation_sequences| together on the given
 // |layer_animator|. If an optional |observer| is supplied, it will be added
diff --git a/ash/login/ui/lock_contents_view.cc b/ash/login/ui/lock_contents_view.cc
index 7ce1da1288..9f1d001d 100644
--- a/ash/login/ui/lock_contents_view.cc
+++ b/ash/login/ui/lock_contents_view.cc
@@ -1773,6 +1773,7 @@
       UserState* state = FindStateForUser(
           view->auth_user()->current_user().basic_user_info.account_id);
       uint32_t to_update_auth;
+      bool show_pinpad = false;
       if (state->force_online_sign_in) {
         to_update_auth = LoginAuthUserView::AUTH_ONLINE_SIGN_IN;
       } else if (state->disable_auth) {
@@ -1789,16 +1790,16 @@
         // not interfere with PIN entry.
         const bool is_keyboard_visible_for_view =
             GetKeyboardControllerForView() ? keyboard_shown_ : false;
-        if ((state->show_pin || state->show_pin_pad_for_password) &&
-            !is_keyboard_visible_for_view) {
+        show_pinpad = !is_keyboard_visible_for_view &&
+                      (state->show_pin || state->show_pin_pad_for_password);
+        if (state->show_pin)
           to_update_auth |= LoginAuthUserView::AUTH_PIN;
-        }
         if (state->enable_tap_auth)
           to_update_auth |= LoginAuthUserView::AUTH_TAP;
         if (state->fingerprint_state != FingerprintState::UNAVAILABLE)
           to_update_auth |= LoginAuthUserView::AUTH_FINGERPRINT;
       }
-      view->auth_user()->SetAuthMethods(to_update_auth, state->show_pin);
+      view->auth_user()->SetAuthMethods(to_update_auth, show_pinpad);
     } else if (view->public_account()) {
       view->public_account()->SetAuthEnabled(true /*enabled*/, animate);
     }
@@ -1809,7 +1810,7 @@
       return;
     if (view->auth_user()) {
       view->auth_user()->SetAuthMethods(LoginAuthUserView::AUTH_NONE,
-                                        false /*can_use_pin*/);
+                                        false /*show_pinpad*/);
     } else if (view->public_account()) {
       view->public_account()->SetAuthEnabled(false /*enabled*/, animate);
     }
diff --git a/ash/login/ui/login_auth_user_view.cc b/ash/login/ui/login_auth_user_view.cc
index f697ef6..875560a 100644
--- a/ash/login/ui/login_auth_user_view.cc
+++ b/ash/login/ui/login_auth_user_view.cc
@@ -745,16 +745,14 @@
     non_pin_y_start_in_screen = view->GetBoundsInScreen().y();
     pin_start_in_screen = view->pin_view_->GetBoundsInScreen().origin();
 
-    had_pin = (view->auth_methods() & LoginAuthUserView::AUTH_PIN) != 0;
-    had_password =
-        (view->auth_methods() & LoginAuthUserView::AUTH_PASSWORD) != 0;
-    had_fingerprint =
-        (view->auth_methods() & LoginAuthUserView::AUTH_FINGERPRINT) != 0;
+    had_pinpad = view->show_pinpad_;
+    had_password = view->HasAuthMethod(LoginAuthUserView::AUTH_PASSWORD);
+    had_fingerprint = view->HasAuthMethod(LoginAuthUserView::AUTH_FINGERPRINT);
   }
 
   int non_pin_y_start_in_screen = 0;
   gfx::Point pin_start_in_screen;
-  bool had_pin = false;
+  bool had_pinpad = false;
   bool had_password = false;
   bool had_fingerprint = false;
 };
@@ -980,20 +978,20 @@
   add_padding(kDistanceFromPinKeyboardToBigUserViewBottomDp);
 
   // Update authentication UI.
-  SetAuthMethods(auth_methods_, false /*can_use_pin*/);
+  SetAuthMethods(auth_methods_, false /*show_pinpad*/);
   user_view_->UpdateForUser(user, false /*animate*/);
 }
 
 LoginAuthUserView::~LoginAuthUserView() = default;
 
 void LoginAuthUserView::SetAuthMethods(uint32_t auth_methods,
-                                       bool can_use_pin) {
-  can_use_pin_ = can_use_pin;
+                                       bool show_pinpad) {
+  show_pinpad_ = show_pinpad;
   bool had_password = HasAuthMethod(AUTH_PASSWORD);
 
   auth_methods_ = static_cast<AuthMethods>(auth_methods);
   bool has_password = HasAuthMethod(AUTH_PASSWORD);
-  bool has_pin_pad = HasAuthMethod(AUTH_PIN);
+  bool has_pin = HasAuthMethod(AUTH_PIN);
   bool has_tap = HasAuthMethod(AUTH_TAP);
   bool force_online_sign_in = HasAuthMethod(AUTH_ONLINE_SIGN_IN);
   bool has_fingerprint = HasAuthMethod(AUTH_FINGERPRINT);
@@ -1010,7 +1008,7 @@
   // Adjust the PIN keyboard visibility before the password textfield's one, so
   // that when both are about to be hidden the focus doesn't jump to the "1"
   // keyboard button, causing unexpected accessibility effects.
-  pin_view_->SetVisible(has_pin_pad);
+  pin_view_->SetVisible(show_pinpad);
 
   password_view_->SetEnabled(has_password);
   password_view_->SetEnabledOnEmptyPassword(has_tap);
@@ -1023,25 +1021,25 @@
     password_view_->RequestFocus();
 
   fingerprint_view_->SetVisible(has_fingerprint);
-  fingerprint_view_->SetCanUsePin(can_use_pin);
+  fingerprint_view_->SetCanUsePin(has_pin);
   challenge_response_view_->SetVisible(has_challenge_response);
 
   int padding_view_height = kDistanceBetweenPasswordFieldAndPinKeyboardDp;
-  if (has_fingerprint && !has_pin_pad) {
+  if (has_fingerprint && !show_pinpad) {
     padding_view_height = kDistanceBetweenPasswordFieldAndFingerprintViewDp;
-  } else if (has_challenge_response && !has_pin_pad) {
+  } else if (has_challenge_response && !show_pinpad) {
     padding_view_height =
         kDistanceBetweenPasswordFieldAndChallengeResponseViewDp;
   }
   padding_below_password_view_->SetPreferredSize(
       gfx::Size(kNonEmptyWidthDp, padding_view_height));
 
-  // Note: |has_tap| must have higher priority than |has_pin_pad| when
+  // Note: |has_tap| must have higher priority than |has_pin| when
   // determining the placeholder.
   if (has_tap) {
     password_view_->SetPlaceholderText(
         l10n_util::GetStringUTF16(IDS_ASH_LOGIN_POD_PASSWORD_TAP_PLACEHOLDER));
-  } else if (can_use_pin) {
+  } else if (has_pin) {
     password_view_->SetPlaceholderText(
         l10n_util::GetStringUTF16(IDS_ASH_LOGIN_POD_PASSWORD_PIN_PLACEHOLDER));
   } else {
@@ -1107,9 +1105,9 @@
 void LoginAuthUserView::ApplyAnimationPostLayout() {
   DCHECK(cached_animation_state_);
 
-  bool has_password = (auth_methods() & AUTH_PASSWORD) != 0;
-  bool has_pin = (auth_methods() & AUTH_PIN) != 0;
-  bool has_fingerprint = (auth_methods() & AUTH_FINGERPRINT) != 0;
+  bool has_password = HasAuthMethod(AUTH_PASSWORD);
+  bool has_pinpad = show_pinpad_;
+  bool has_fingerprint = HasAuthMethod(AUTH_FINGERPRINT);
 
   ////////
   // Animate the user info (ie, icon, name) up or down the screen.
@@ -1169,8 +1167,8 @@
   ////////
   // Grow/shrink the PIN keyboard if it is being hidden or shown.
 
-  if (cached_animation_state_->had_pin != has_pin) {
-    if (!has_pin) {
+  if (cached_animation_state_->had_pinpad != has_pinpad) {
+    if (!has_pinpad) {
       gfx::Point pin_end_in_screen = pin_view_->GetBoundsInScreen().origin();
       gfx::Rect pin_bounds = pin_view_->bounds();
       pin_bounds.set_x(cached_animation_state_->pin_start_in_screen.x() -
@@ -1185,7 +1183,7 @@
     }
 
     auto transition = std::make_unique<PinKeyboardAnimation>(
-        has_pin /*grow*/, pin_view_->height(),
+        has_pinpad /*grow*/, pin_view_->height(),
         // TODO(https://crbug.com/955119): Implement proper animation.
         base::TimeDelta::FromMilliseconds(
             login_constants::kChangeUserAnimationDurationMs / 2.0f),
@@ -1193,7 +1191,7 @@
     auto* sequence = new ui::LayerAnimationSequence(std::move(transition));
 
     // Hide the PIN keyboard after animation if needed.
-    if (!has_pin) {
+    if (!has_pinpad) {
       auto* observer = BuildObserverToHideView(pin_view_);
       sequence->AddObserver(observer);
       observer->SetActive();
@@ -1291,7 +1289,7 @@
   password_view_->SetReadOnly(true);
   Shell::Get()->login_screen_controller()->AuthenticateUserWithPasswordOrPin(
       current_user().basic_user_info.account_id, base::UTF16ToUTF8(password),
-      can_use_pin_,
+      HasAuthMethod(AUTH_PIN),
       base::BindOnce(&LoginAuthUserView::OnAuthComplete,
                      weak_factory_.GetWeakPtr()));
 }
diff --git a/ash/login/ui/login_auth_user_view.h b/ash/login/ui/login_auth_user_view.h
index 5f05d3f..33aca56 100644
--- a/ash/login/ui/login_auth_user_view.h
+++ b/ash/login/ui/login_auth_user_view.h
@@ -104,9 +104,9 @@
   ~LoginAuthUserView() override;
 
   // Set the displayed set of auth methods. |auth_methods| contains or-ed
-  // together AuthMethod values. |can_use_pin| should be true if the user can
-  // authenticate using PIN, even if the PIN keyboard is not displayed.
-  void SetAuthMethods(uint32_t auth_methods, bool can_use_pin);
+  // together AuthMethod values. |show_pinpad| determines whether the pin pad
+  // should be visible.
+  void SetAuthMethods(uint32_t auth_methods, bool show_pinpad);
   AuthMethods auth_methods() const { return auth_methods_; }
 
   // Add an easy unlock icon.
@@ -184,10 +184,9 @@
   void AttemptAuthenticateWithChallengeResponse();
 
   AuthMethods auth_methods_ = AUTH_NONE;
-  // True if the user's password might be a PIN. PIN is hashed differently from
-  // password. The PIN keyboard may not always be visible even when the user
-  // wants to submit a PIN, eg. the virtual keyboard hides the PIN keyboard.
-  bool can_use_pin_ = false;
+
+  // Whether to show the pinpad. Sometimes hidden by the on screen keyboard
+  bool show_pinpad_ = false;
   LoginUserView* user_view_ = nullptr;
   LoginPasswordView* password_view_ = nullptr;
   NonAccessibleView* password_view_container_ = nullptr;
diff --git a/ash/login/ui/login_auth_user_view_unittest.cc b/ash/login/ui/login_auth_user_view_unittest.cc
index f3bef543..cc3f8d2 100644
--- a/ash/login/ui/login_auth_user_view_unittest.cc
+++ b/ash/login/ui/login_auth_user_view_unittest.cc
@@ -57,9 +57,8 @@
     SetWidget(CreateWidgetWithContent(container_));
   }
 
-  void SetAuthMethods(uint32_t auth_methods) {
-    bool can_use_pin = (auth_methods & LoginAuthUserView::AUTH_PIN) != 0;
-    view_->SetAuthMethods(auth_methods, can_use_pin);
+  void SetAuthMethods(uint32_t auth_methods, bool show_pinpad) {
+    view_->SetAuthMethods(auth_methods, show_pinpad);
   }
 
   LoginUserInfo user_;
@@ -75,7 +74,7 @@
 // Verifies showing the PIN keyboard makes the user view grow.
 TEST_F(LoginAuthUserViewUnittest, ShowingPinExpandsView) {
   gfx::Size start_size = view_->size();
-  SetAuthMethods(LoginAuthUserView::AUTH_PIN);
+  SetAuthMethods(LoginAuthUserView::AUTH_PIN, true /*show_pinpad*/);
   container_->Layout();
   gfx::Size expanded_size = view_->size();
   EXPECT_GT(expanded_size.height(), start_size.height());
@@ -95,11 +94,11 @@
   EXPECT_FALSE(auth_test.user_view()->HasFocus());
 
   // If the user view is showing a password it must be opaque.
-  SetAuthMethods(LoginAuthUserView::AUTH_PASSWORD);
+  SetAuthMethods(LoginAuthUserView::AUTH_PASSWORD, false /*show_pinpad*/);
   EXPECT_TRUE(user_test.is_opaque());
-  SetAuthMethods(LoginAuthUserView::AUTH_NONE);
+  SetAuthMethods(LoginAuthUserView::AUTH_NONE, false /*show_pinpad*/);
   EXPECT_FALSE(user_test.is_opaque());
-  SetAuthMethods(LoginAuthUserView::AUTH_PASSWORD);
+  SetAuthMethods(LoginAuthUserView::AUTH_PASSWORD, false /*show_pinpad*/);
   EXPECT_TRUE(user_test.is_opaque());
 }
 
@@ -119,8 +118,8 @@
   EXPECT_CALL(*client,
               AuthenticateUserWithEasyUnlock(
                   user_view->current_user().basic_user_info.account_id));
-  SetAuthMethods(LoginAuthUserView::AUTH_PASSWORD |
-                 LoginAuthUserView::AUTH_TAP);
+  SetAuthMethods(LoginAuthUserView::AUTH_PASSWORD | LoginAuthUserView::AUTH_TAP,
+                 false /*show_pinpad*/);
   password_view->Clear();
 
   generator->PressKey(ui::KeyboardCode::VKEY_RETURN, 0);
@@ -138,7 +137,7 @@
 
   // When auth method is |AUTH_ONLINE_SIGN_IN|, the online sign-in message is
   // visible. The password field and PIN keyboard are invisible.
-  SetAuthMethods(LoginAuthUserView::AUTH_ONLINE_SIGN_IN);
+  SetAuthMethods(LoginAuthUserView::AUTH_ONLINE_SIGN_IN, false /*show_pinpad*/);
   EXPECT_TRUE(online_sign_in_message->GetVisible());
   EXPECT_FALSE(password_view->GetVisible());
   EXPECT_FALSE(pin_view->GetVisible());
@@ -153,13 +152,13 @@
   base::RunLoop().RunUntilIdle();
 
   // The online sign-in message is invisible for all other auth methods.
-  SetAuthMethods(LoginAuthUserView::AUTH_NONE);
+  SetAuthMethods(LoginAuthUserView::AUTH_NONE, false /*show_pinpad*/);
   EXPECT_FALSE(online_sign_in_message->GetVisible());
-  SetAuthMethods(LoginAuthUserView::AUTH_PASSWORD);
+  SetAuthMethods(LoginAuthUserView::AUTH_PASSWORD, false /*show_pinpad*/);
   EXPECT_FALSE(online_sign_in_message->GetVisible());
-  SetAuthMethods(LoginAuthUserView::AUTH_PIN);
+  SetAuthMethods(LoginAuthUserView::AUTH_PIN, true /*show_pinpad*/);
   EXPECT_FALSE(online_sign_in_message->GetVisible());
-  SetAuthMethods(LoginAuthUserView::AUTH_TAP);
+  SetAuthMethods(LoginAuthUserView::AUTH_TAP, false /*show_pinpad*/);
   EXPECT_FALSE(online_sign_in_message->GetVisible());
 }
 
@@ -172,20 +171,20 @@
   };
 
   // Set a password.
-  SetAuthMethods(LoginAuthUserView::AUTH_PASSWORD);
+  SetAuthMethods(LoginAuthUserView::AUTH_PASSWORD, false /*show_pinpad*/);
   password_test.textfield()->SetText(base::ASCIIToUTF16("Hello"));
 
   // Enable some other auth method (PIN), password is not cleared.
   view_->CaptureStateForAnimationPreLayout();
-  SetAuthMethods(LoginAuthUserView::AUTH_PASSWORD |
-                 LoginAuthUserView::AUTH_PIN);
+  SetAuthMethods(LoginAuthUserView::AUTH_PASSWORD | LoginAuthUserView::AUTH_PIN,
+                 true /*show_pinpad*/);
   EXPECT_TRUE(has_password());
   view_->ApplyAnimationPostLayout();
   EXPECT_TRUE(has_password());
 
   // Disable password, password is cleared.
   view_->CaptureStateForAnimationPreLayout();
-  SetAuthMethods(LoginAuthUserView::AUTH_NONE);
+  SetAuthMethods(LoginAuthUserView::AUTH_NONE, false /*show_pinpad*/);
   EXPECT_TRUE(has_password());
   view_->ApplyAnimationPostLayout();
   EXPECT_FALSE(has_password());
diff --git a/ash/system/message_center/notification_swipe_control_view.cc b/ash/system/message_center/notification_swipe_control_view.cc
index 45e5d018..203ae61 100644
--- a/ash/system/message_center/notification_swipe_control_view.cc
+++ b/ash/system/message_center/notification_swipe_control_view.cc
@@ -185,17 +185,25 @@
 void NotificationSwipeControlView::ButtonPressed(views::Button* sender,
                                                  const ui::Event& event) {
   DCHECK(sender);
+  std::string notification_id = message_view_->notification_id();
+  auto weak_this = weak_factory_.GetWeakPtr();
+
   if (sender == settings_button_) {
     message_view_->OnSettingsButtonPressed(event);
-    metrics_utils::LogSettingsShown(message_view_->notification_id(),
+    metrics_utils::LogSettingsShown(notification_id,
                                     /*is_slide_controls=*/true,
                                     /*is_popup=*/false);
   } else if (sender == snooze_button_) {
     message_view_->OnSnoozeButtonPressed(event);
-    metrics_utils::LogSnoozed(message_view_->notification_id(),
+    metrics_utils::LogSnoozed(notification_id,
                               /*is_slide_controls=*/true,
                               /*is_popup=*/false);
   }
+
+  // Button handlers of |message_view_| may have closed |this|.
+  if (!weak_this)
+    return;
+
   HideButtons();
 
   // Closing the swipe control is done in these button pressed handlers.
diff --git a/ash/system/message_center/notification_swipe_control_view.h b/ash/system/message_center/notification_swipe_control_view.h
index 84bb0bc..d78f06bc 100644
--- a/ash/system/message_center/notification_swipe_control_view.h
+++ b/ash/system/message_center/notification_swipe_control_view.h
@@ -6,7 +6,7 @@
 #define ASH_SYSTEM_MESSAGE_CENTER_NOTIFICATION_SWIPE_CONTROL_VIEW_H_
 
 #include "ash/ash_export.h"
-#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
 #include "base/observer_list.h"
 #include "base/observer_list_types.h"
 #include "third_party/skia/include/core/SkColor.h"
@@ -35,6 +35,9 @@
 
   explicit NotificationSwipeControlView(
       message_center::MessageView* message_view);
+  NotificationSwipeControlView(const NotificationSwipeControlView&) = delete;
+  NotificationSwipeControlView& operator=(const NotificationSwipeControlView&) =
+      delete;
   ~NotificationSwipeControlView() override;
 
   // views::View
@@ -50,6 +53,11 @@
   void UpdateCornerRadius(int top_radius, int bottom_radius);
 
  private:
+  FRIEND_TEST_ALL_PREFIXES(NotificationSwipeControlViewTest,
+                           DeleteOnSettingsButtonPressed);
+  FRIEND_TEST_ALL_PREFIXES(NotificationSwipeControlViewTest,
+                           DeleteOnSnoozeButtonPressed);
+
   // Change the visibility of the settings button.
   void ShowButtons(ButtonPosition button_position,
                    bool has_settings,
@@ -68,7 +76,7 @@
   views::ImageButton* settings_button_ = nullptr;
   views::ImageButton* snooze_button_ = nullptr;
 
-  DISALLOW_COPY_AND_ASSIGN(NotificationSwipeControlView);
+  base::WeakPtrFactory<NotificationSwipeControlView> weak_factory_{this};
 };
 
 }  // namespace ash
diff --git a/ash/system/message_center/notification_swipe_control_view_unittest.cc b/ash/system/message_center/notification_swipe_control_view_unittest.cc
new file mode 100644
index 0000000..813b7aa8
--- /dev/null
+++ b/ash/system/message_center/notification_swipe_control_view_unittest.cc
@@ -0,0 +1,151 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ash/system/message_center/notification_swipe_control_view.h"
+
+#include <memory>
+
+#include "base/strings/string16.h"
+#include "base/strings/utf_string_conversions.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/events/base_event_utils.h"
+#include "ui/gfx/geometry/point_f.h"
+#include "ui/gfx/image/image.h"
+#include "ui/message_center/lock_screen/fake_lock_screen_controller.h"
+#include "ui/message_center/message_center.h"
+#include "ui/message_center/public/cpp/notification.h"
+#include "ui/message_center/views/message_view.h"
+#include "ui/message_center/views/notification_control_buttons_view.h"
+#include "url/gurl.h"
+
+namespace {
+
+class MockMessageView : public message_center::MessageView {
+ public:
+  explicit MockMessageView(const message_center::Notification& notification)
+      : message_center::MessageView(notification),
+        buttons_view_(
+            std::make_unique<message_center::NotificationControlButtonsView>(
+                this)) {
+    buttons_view_->ShowSettingsButton(
+        notification.should_show_settings_button());
+    buttons_view_->ShowSnoozeButton(notification.should_show_snooze_button());
+  }
+  ~MockMessageView() override = default;
+
+  message_center::NotificationControlButtonsView* GetControlButtonsView()
+      const override {
+    return buttons_view_.get();
+  }
+
+  MOCK_METHOD(void,
+              OnSettingsButtonPressed,
+              (const ui::Event& event),
+              (override));
+  MOCK_METHOD(void,
+              OnSnoozeButtonPressed,
+              (const ui::Event& event),
+              (override));
+
+ private:
+  std::unique_ptr<message_center::NotificationControlButtonsView> buttons_view_;
+};
+
+}  // namespace
+
+namespace ash {
+
+class NotificationSwipeControlViewTest : public testing::Test {
+ public:
+  NotificationSwipeControlViewTest() = default;
+  ~NotificationSwipeControlViewTest() override = default;
+
+  void SetUp() override {
+    message_center::MessageCenter::Initialize(
+        std::make_unique<message_center::FakeLockScreenController>());
+
+    message_center::RichNotificationData rich_data;
+    rich_data.settings_button_handler =
+        message_center::SettingsButtonHandler::DELEGATE;
+    rich_data.should_show_snooze_button = true;
+    message_center::Notification notification(
+        message_center::NOTIFICATION_TYPE_SIMPLE, "id",
+        base::UTF8ToUTF16("title"), base::UTF8ToUTF16("id"), gfx::Image(),
+        base::string16(), GURL(),
+        message_center::NotifierId(message_center::NotifierType::APPLICATION,
+                                   "notifier_id"),
+        rich_data, nullptr);
+
+    message_view_ = std::make_unique<MockMessageView>(notification);
+  }
+
+  void TearDown() override { message_center::MessageCenter::Shutdown(); }
+
+ protected:
+  MockMessageView* message_view() { return message_view_.get(); }
+
+ private:
+  std::unique_ptr<MockMessageView> message_view_;
+};
+
+TEST_F(NotificationSwipeControlViewTest, DeleteOnSettingsButtonPressed) {
+  auto swipe_control_view =
+      std::make_unique<NotificationSwipeControlView>(message_view());
+
+  EXPECT_CALL(*message_view(), OnSettingsButtonPressed(testing::_))
+      .WillOnce(testing::DoDefault())
+      .WillOnce(
+          testing::InvokeWithoutArgs([&]() { swipe_control_view.reset(); }));
+
+  ui::MouseEvent press(ui::ET_MOUSE_PRESSED, gfx::PointF(), gfx::PointF(),
+                       ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
+                       ui::EF_NONE);
+
+  // First click will do nothing, expect that to work.
+  swipe_control_view->ShowButtons(
+      NotificationSwipeControlView::ButtonPosition::LEFT,
+      /*has_settings_button=*/true, /*has_snooze_button=*/true);
+  swipe_control_view->ButtonPressed(swipe_control_view->settings_button_,
+                                    press);
+  EXPECT_TRUE(swipe_control_view);
+
+  // Second click deletes |swipe_control_view| in the handler.
+  swipe_control_view->ShowButtons(
+      NotificationSwipeControlView::ButtonPosition::LEFT,
+      /*has_settings_button=*/true, /*has_snooze_button=*/true);
+  swipe_control_view->ButtonPressed(swipe_control_view->settings_button_,
+                                    press);
+  EXPECT_FALSE(swipe_control_view);
+}
+
+TEST_F(NotificationSwipeControlViewTest, DeleteOnSnoozeButtonPressed) {
+  auto swipe_control_view =
+      std::make_unique<NotificationSwipeControlView>(message_view());
+
+  EXPECT_CALL(*message_view(), OnSnoozeButtonPressed(testing::_))
+      .WillOnce(testing::DoDefault())
+      .WillOnce(
+          testing::InvokeWithoutArgs([&]() { swipe_control_view.reset(); }));
+
+  ui::MouseEvent press(ui::ET_MOUSE_PRESSED, gfx::PointF(), gfx::PointF(),
+                       ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
+                       ui::EF_NONE);
+
+  // First click will do nothing, expect that to work.
+  swipe_control_view->ShowButtons(
+      NotificationSwipeControlView::ButtonPosition::LEFT,
+      /*has_settings_button=*/true, /*has_snooze_button=*/true);
+  swipe_control_view->ButtonPressed(swipe_control_view->snooze_button_, press);
+  EXPECT_TRUE(swipe_control_view);
+
+  // Second click deletes |swipe_control_view| in the handler.
+  swipe_control_view->ShowButtons(
+      NotificationSwipeControlView::ButtonPosition::LEFT,
+      /*has_settings_button=*/true, /*has_snooze_button=*/true);
+  swipe_control_view->ButtonPressed(swipe_control_view->snooze_button_, press);
+  EXPECT_FALSE(swipe_control_view);
+}
+
+}  // namespace ash
diff --git a/base/memory/weak_ptr.h b/base/memory/weak_ptr.h
index 42aa341..d977a0da 100644
--- a/base/memory/weak_ptr.h
+++ b/base/memory/weak_ptr.h
@@ -324,7 +324,7 @@
 
   ~WeakPtrFactory() = default;
 
-  WeakPtr<T> GetWeakPtr() {
+  WeakPtr<T> GetWeakPtr() const {
     return WeakPtr<T>(weak_reference_owner_.GetRef(),
                       reinterpret_cast<T*>(ptr_));
   }
diff --git a/build/android/pylib/local/emulator/avd.py b/build/android/pylib/local/emulator/avd.py
index 4f8103f..a638f387 100644
--- a/build/android/pylib/local/emulator/avd.py
+++ b/build/android/pylib/local/emulator/avd.py
@@ -263,6 +263,9 @@
             'hw.lcd.width': width,
         })
 
+        if self.avd_settings.ram_size:
+          config_ini_contents['hw.ramSize'] = self.avd_settings.ram_size
+
       # Start & stop the AVD.
       self._Initialize()
       instance = _AvdInstance(self._emulator_path, self._emulator_home,
diff --git a/build/android/pylib/local/emulator/proto/avd.proto b/build/android/pylib/local/emulator/proto/avd.proto
index bbcdd530..b06da490 100644
--- a/build/android/pylib/local/emulator/proto/avd.proto
+++ b/build/android/pylib/local/emulator/proto/avd.proto
@@ -50,6 +50,9 @@
   // See https://bit.ly/2P1qK2X for all the available keys.
   // The values should be on, off, default, or null
   map<string, string> advanced_features = 3;
+
+  // The physical RAM size on the device, in megabytes.
+  uint32 ram_size = 4;
 }
 
 message Avd {
diff --git a/build/android/pylib/local/emulator/proto/avd_pb2.py b/build/android/pylib/local/emulator/proto/avd_pb2.py
index e48f3b05..49cc1aa8 100644
--- a/build/android/pylib/local/emulator/proto/avd_pb2.py
+++ b/build/android/pylib/local/emulator/proto/avd_pb2.py
@@ -18,7 +18,7 @@
   package='tools.android.avd.proto',
   syntax='proto3',
   serialized_options=None,
-  serialized_pb=b'\n\tavd.proto\x12\x17tools.android.avd.proto\"G\n\x0b\x43IPDPackage\x12\x14\n\x0cpackage_name\x18\x01 \x01(\t\x12\x0f\n\x07version\x18\x02 \x01(\t\x12\x11\n\tdest_path\x18\x03 \x01(\t\"@\n\x0eScreenSettings\x12\x0e\n\x06height\x18\x01 \x01(\r\x12\r\n\x05width\x18\x02 \x01(\r\x12\x0f\n\x07\x64\x65nsity\x18\x03 \x01(\r\"\x1e\n\x0eSdcardSettings\x12\x0c\n\x04size\x18\x01 \x01(\t\"\x8f\x02\n\x0b\x41vdSettings\x12\x37\n\x06screen\x18\x01 \x01(\x0b\x32\'.tools.android.avd.proto.ScreenSettings\x12\x37\n\x06sdcard\x18\x02 \x01(\x0b\x32\'.tools.android.avd.proto.SdcardSettings\x12U\n\x11\x61\x64vanced_features\x18\x03 \x03(\x0b\x32:.tools.android.avd.proto.AvdSettings.AdvancedFeaturesEntry\x1a\x37\n\x15\x41\x64vancedFeaturesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"\xad\x02\n\x03\x41vd\x12>\n\x10\x65mulator_package\x18\x01 \x01(\x0b\x32$.tools.android.avd.proto.CIPDPackage\x12\x42\n\x14system_image_package\x18\x02 \x01(\x0b\x32$.tools.android.avd.proto.CIPDPackage\x12\x19\n\x11system_image_name\x18\x03 \x01(\t\x12\x39\n\x0b\x61vd_package\x18\x04 \x01(\x0b\x32$.tools.android.avd.proto.CIPDPackage\x12\x10\n\x08\x61vd_name\x18\x05 \x01(\t\x12:\n\x0c\x61vd_settings\x18\x06 \x01(\x0b\x32$.tools.android.avd.proto.AvdSettingsb\x06proto3'
+  serialized_pb=b'\n\tavd.proto\x12\x17tools.android.avd.proto\"G\n\x0b\x43IPDPackage\x12\x14\n\x0cpackage_name\x18\x01 \x01(\t\x12\x0f\n\x07version\x18\x02 \x01(\t\x12\x11\n\tdest_path\x18\x03 \x01(\t\"@\n\x0eScreenSettings\x12\x0e\n\x06height\x18\x01 \x01(\r\x12\r\n\x05width\x18\x02 \x01(\r\x12\x0f\n\x07\x64\x65nsity\x18\x03 \x01(\r\"\x1e\n\x0eSdcardSettings\x12\x0c\n\x04size\x18\x01 \x01(\t\"\xa1\x02\n\x0b\x41vdSettings\x12\x37\n\x06screen\x18\x01 \x01(\x0b\x32\'.tools.android.avd.proto.ScreenSettings\x12\x37\n\x06sdcard\x18\x02 \x01(\x0b\x32\'.tools.android.avd.proto.SdcardSettings\x12U\n\x11\x61\x64vanced_features\x18\x03 \x03(\x0b\x32:.tools.android.avd.proto.AvdSettings.AdvancedFeaturesEntry\x12\x10\n\x08ram_size\x18\x04 \x01(\r\x1a\x37\n\x15\x41\x64vancedFeaturesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"\xad\x02\n\x03\x41vd\x12>\n\x10\x65mulator_package\x18\x01 \x01(\x0b\x32$.tools.android.avd.proto.CIPDPackage\x12\x42\n\x14system_image_package\x18\x02 \x01(\x0b\x32$.tools.android.avd.proto.CIPDPackage\x12\x19\n\x11system_image_name\x18\x03 \x01(\t\x12\x39\n\x0b\x61vd_package\x18\x04 \x01(\x0b\x32$.tools.android.avd.proto.CIPDPackage\x12\x10\n\x08\x61vd_name\x18\x05 \x01(\t\x12:\n\x0c\x61vd_settings\x18\x06 \x01(\x0b\x32$.tools.android.avd.proto.AvdSettingsb\x06proto3'
 )
 
 
@@ -178,8 +178,8 @@
   extension_ranges=[],
   oneofs=[
   ],
-  serialized_start=426,
-  serialized_end=481,
+  serialized_start=444,
+  serialized_end=499,
 )
 
 _AVDSETTINGS = _descriptor.Descriptor(
@@ -210,6 +210,13 @@
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
       serialized_options=None, file=DESCRIPTOR),
+    _descriptor.FieldDescriptor(
+      name='ram_size', full_name='tools.android.avd.proto.AvdSettings.ram_size', index=3,
+      number=4, type=13, cpp_type=3, label=1,
+      has_default_value=False, default_value=0,
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      serialized_options=None, file=DESCRIPTOR),
   ],
   extensions=[
   ],
@@ -223,7 +230,7 @@
   oneofs=[
   ],
   serialized_start=210,
-  serialized_end=481,
+  serialized_end=499,
 )
 
 
@@ -288,8 +295,8 @@
   extension_ranges=[],
   oneofs=[
   ],
-  serialized_start=484,
-  serialized_end=785,
+  serialized_start=502,
+  serialized_end=803,
 )
 
 _AVDSETTINGS_ADVANCEDFEATURESENTRY.containing_type = _AVDSETTINGS
diff --git a/build/config/compiler/BUILD.gn b/build/config/compiler/BUILD.gn
index 2202e3f..dbcddb3 100644
--- a/build/config/compiler/BUILD.gn
+++ b/build/config/compiler/BUILD.gn
@@ -694,6 +694,12 @@
     ldflags += [ "-Wl,--no-rosegment" ]
   }
 
+  # Don't do call-graph-sorted binary layout on Android, as that increases the
+  # binary size due to more thunks for long jumps.
+  if (use_lld && is_android) {
+    ldflags += [ "-Wl,--no-call-graph-profile-sort" ]
+  }
+
   # This flag enforces that member pointer base types are complete. It helps
   # prevent us from running into problems in the Microsoft C++ ABI (see
   # https://crbug.com/847724).
diff --git a/build/fuchsia/linux.sdk.sha1 b/build/fuchsia/linux.sdk.sha1
index a01346de..e2de5d0 100644
--- a/build/fuchsia/linux.sdk.sha1
+++ b/build/fuchsia/linux.sdk.sha1
@@ -1 +1 @@
-0.20200730.3.1
+0.20200731.1.1
diff --git a/build/fuchsia/mac.sdk.sha1 b/build/fuchsia/mac.sdk.sha1
index a01346de..e2de5d0 100644
--- a/build/fuchsia/mac.sdk.sha1
+++ b/build/fuchsia/mac.sdk.sha1
@@ -1 +1 @@
-0.20200730.3.1
+0.20200731.1.1
diff --git a/build_overrides/build.gni b/build_overrides/build.gni
index 497ebae..fbfaa3e 100644
--- a/build_overrides/build.gni
+++ b/build_overrides/build.gni
@@ -27,8 +27,7 @@
   # TODO(crbug/1006769): Switch to perfetto's client library.
   use_perfetto_client_library = false
 
-  # If true, it assumes that //third_party/abseil-cpp is an available
-  # dependency for googletest.
+  # Allows googletest to pretty-print various absl types.
   gtest_enable_absl_printers = false
 }
 
diff --git a/chrome/android/chrome_java_sources.gni b/chrome/android/chrome_java_sources.gni
index abec2ebe..846ab9a3 100644
--- a/chrome/android/chrome_java_sources.gni
+++ b/chrome/android/chrome_java_sources.gni
@@ -1485,6 +1485,7 @@
   "java/src/org/chromium/chrome/browser/sync/settings/SignInPreference.java",
   "java/src/org/chromium/chrome/browser/sync/settings/SyncAndServicesSettings.java",
   "java/src/org/chromium/chrome/browser/sync/settings/SyncOffPreference.java",
+  "java/src/org/chromium/chrome/browser/sync/settings/SyncPromoPreference.java",
   "java/src/org/chromium/chrome/browser/sync/settings/SyncSettingsUtils.java",
   "java/src/org/chromium/chrome/browser/sync/ui/PassphraseActivity.java",
   "java/src/org/chromium/chrome/browser/sync/ui/PassphraseCreationDialogFragment.java",
diff --git a/chrome/android/features/keyboard_accessory/javatests/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/PasswordAccessoryIntegrationTest.java b/chrome/android/features/keyboard_accessory/javatests/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/PasswordAccessoryIntegrationTest.java
index 221b8fb..8d824eb 100644
--- a/chrome/android/features/keyboard_accessory/javatests/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/PasswordAccessoryIntegrationTest.java
+++ b/chrome/android/features/keyboard_accessory/javatests/src/org/chromium/chrome/browser/keyboard_accessory/sheet_tabs/PasswordAccessoryIntegrationTest.java
@@ -40,6 +40,7 @@
 import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
 import org.chromium.chrome.test.util.browser.Features.EnableFeatures;
 import org.chromium.content_public.browser.test.util.CriteriaHelper;
+import org.chromium.ui.test.util.UiDisableIf;
 
 import java.util.concurrent.TimeoutException;
 
@@ -109,6 +110,7 @@
 
     @Test
     @SmallTest
+    @DisableIf.Device(type = {UiDisableIf.TABLET}) // https://crbug.com/1111770
     public void testFillsPasswordOnTap() throws TimeoutException {
         mHelper.loadTestPage(false);
         mHelper.cacheCredentials("mpark@abc.com", "ShorterPassword");
diff --git a/chrome/android/java/res/drawable/ic_expand_more_in_circle_24dp.xml b/chrome/android/java/res/drawable/ic_expand_more_in_circle_24dp.xml
index 08c0a50..1a240f3 100644
--- a/chrome/android/java/res/drawable/ic_expand_more_in_circle_24dp.xml
+++ b/chrome/android/java/res/drawable/ic_expand_more_in_circle_24dp.xml
@@ -11,9 +11,9 @@
   <path
       android:strokeWidth="1"
       android:pathData="M23.5,11.9999C23.5,18.3512 18.3513,23.4999 12,23.4999C5.6487,23.4999 0.5,18.3512 0.5,11.9999C0.5,5.6486 5.6487,0.4999 12,0.4999C18.3513,0.4999 23.5,5.6486 23.5,11.9999Z"
-      android:strokeColor="@color/default_chip_outline_color_light"/>
+      android:strokeColor="@color/hairline_stroke_color"/>
   <path
       android:pathData="M12.5892,14.7487C12.4383,14.9039 12.23,14.9999 12,14.9999C11.77,14.9999 11.5617,14.9039 11.4108,14.7487L7.2442,10.463C7.0933,10.3079 7,10.0936 7,9.857C7,9.3839 7.3733,8.9999 7.8333,8.9999C8.0633,8.9999 8.2717,9.0959 8.4225,9.251L12,12.9307L15.5775,9.251C15.7283,9.0959 15.9367,8.9999 16.1667,8.9999C16.6267,8.9999 17,9.3839 17,9.857C17,10.0936 16.9067,10.3079 16.7558,10.463L12.5892,14.7487Z"
-      android:fillColor="@color/default_chip_outline_color_dark"
+      android:fillColor="@color/default_control_color_normal"
       android:fillType="evenOdd"/>
 </vector>
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/IncognitoDescriptionView.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/IncognitoDescriptionView.java
index 7fa753cd..50d55a5 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/IncognitoDescriptionView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/IncognitoDescriptionView.java
@@ -356,7 +356,7 @@
         String addition;
         switch (enforcement) {
             case CookieControlsEnforcement.ENFORCED_BY_POLICY:
-                iconRes = R.drawable.controlled_setting_mandatory;
+                iconRes = R.drawable.ic_business_small;
                 addition = resources.getString(R.string.managed_by_your_organization);
                 break;
             case CookieControlsEnforcement.ENFORCED_BY_COOKIE_SETTING:
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/AndroidPaymentAppFinder.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/AndroidPaymentAppFinder.java
index c5fd6a5..7d03a23 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/AndroidPaymentAppFinder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/AndroidPaymentAppFinder.java
@@ -308,7 +308,9 @@
                 ChromeActivity.fromWebContents(mFactoryDelegate.getParams().getWebContents());
         if (!PaymentOptionsUtils.requestAnyInformation(
                     mFactoryDelegate.getParams().getPaymentOptions())
-                && activity != null) {
+                && activity != null
+                && PaymentFeatureList.isEnabled(
+                        PaymentFeatureList.WEB_PAYMENTS_APP_STORE_BILLING)) {
             findAppStoreBillingApp(activity, allInstalledPaymentApps);
         }
 
@@ -359,6 +361,14 @@
             GURL defaultUrlMethod = null;
             if (!TextUtils.isEmpty(defaultMethod)) {
                 defaultUrlMethod = new GURL(defaultMethod);
+
+                // Do not download any manifests for the app whose default payment method identifier
+                // is an app store payment method identifier, because app store method URLs are used
+                // only for identification and do not host manifest files.
+                if (mAppStores.values().contains(defaultUrlMethod)) {
+                    continue;
+                }
+
                 if (UrlUtils.isURLValid(defaultUrlMethod)) {
                     defaultMethod = urlToStringWithoutTrailingSlash(defaultUrlMethod);
                 }
@@ -394,6 +404,12 @@
                     continue;
                 }
 
+                // Ignore payment method identifiers of app stores, because app store method URLs
+                // are used only for identification and do not host manifest files.
+                if (mAppStores.values().contains(supportedUrlMethod)) {
+                    continue;
+                }
+
                 if (!methodToAppsMapping.containsKey(supportedMethod)) {
                     methodToAppsMapping.put(supportedMethod, new HashSet<ResolveInfo>());
                 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/sync/settings/SyncPromoPreference.java b/chrome/android/java/src/org/chromium/chrome/browser/sync/settings/SyncPromoPreference.java
new file mode 100644
index 0000000..e15b38a4
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/sync/settings/SyncPromoPreference.java
@@ -0,0 +1,216 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.sync.settings;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.View;
+
+import androidx.annotation.IntDef;
+import androidx.annotation.Nullable;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceViewHolder;
+
+import org.chromium.chrome.R;
+import org.chromium.chrome.browser.firstrun.FirstRunSignInProcessor;
+import org.chromium.chrome.browser.flags.ChromeFeatureList;
+import org.chromium.chrome.browser.preferences.ChromePreferenceKeys;
+import org.chromium.chrome.browser.preferences.SharedPreferencesManager;
+import org.chromium.chrome.browser.profiles.Profile;
+import org.chromium.chrome.browser.signin.IdentityServicesProvider;
+import org.chromium.chrome.browser.signin.PersonalizedSigninPromoView;
+import org.chromium.chrome.browser.signin.ProfileDataCache;
+import org.chromium.chrome.browser.signin.SigninPromoController;
+import org.chromium.chrome.browser.signin.SigninPromoUtil;
+import org.chromium.chrome.browser.sync.AndroidSyncSettings;
+import org.chromium.chrome.browser.sync.ProfileSyncService;
+import org.chromium.chrome.browser.sync.ProfileSyncService.SyncStateChangedListener;
+import org.chromium.components.signin.AccountManagerFacade;
+import org.chromium.components.signin.AccountManagerFacadeProvider;
+import org.chromium.components.signin.AccountsChangeObserver;
+import org.chromium.components.signin.identitymanager.ConsentLevel;
+import org.chromium.components.signin.identitymanager.IdentityManager;
+import org.chromium.components.signin.metrics.SigninAccessPoint;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * A preference that displays Personalized Sync Promo when the user is not syncing.
+ */
+// TODO(https://crbug.com/1110889): Move all promos from SigninPreference to this class.
+public class SyncPromoPreference extends Preference
+        implements ProfileDataCache.Observer, AndroidSyncSettings.AndroidSyncSettingsObserver,
+                   SyncStateChangedListener, AccountsChangeObserver {
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({State.PROMO_HIDDEN, State.PERSONALIZED_SYNC_PROMO})
+    public @interface State {
+        int PROMO_HIDDEN = 0;
+        int PERSONALIZED_SYNC_PROMO = 1;
+    }
+
+    private final ProfileDataCache mProfileDataCache;
+    private final AccountManagerFacade mAccountManagerFacade;
+    private @SignInPreference.State int mState;
+    private @Nullable SigninPromoController mSigninPromoController;
+
+    /**
+     * Constructor for inflating from XML.
+     */
+    public SyncPromoPreference(Context context, AttributeSet attrs) {
+        super(context, attrs);
+
+        mProfileDataCache =
+                ProfileDataCache.createProfileDataCache(context, R.drawable.ic_sync_badge_off_20dp);
+        mAccountManagerFacade = AccountManagerFacadeProvider.getInstance();
+
+        // State will be updated in registerForUpdates.
+        mState = State.PROMO_HIDDEN;
+        setVisible(false);
+    }
+
+    /**
+     * Starts listening for updates to the sign-in and sync state.
+     * TODO(https://crbug.com/1109713): Remove this method and use onAttached() instead
+     */
+    public void registerForUpdates() {
+        mAccountManagerFacade.addObserver(this);
+        mProfileDataCache.addObserver(this);
+        FirstRunSignInProcessor.updateSigninManagerFirstRunCheckDone();
+        AndroidSyncSettings.get().registerObserver(this);
+        ProfileSyncService syncService = ProfileSyncService.get();
+        if (syncService != null) {
+            syncService.addSyncStateChangedListener(this);
+        }
+
+        update();
+    }
+
+    /**
+     * Stops listening for updates to the sign-in and sync state. Every call to registerForUpdates()
+     * must be matched with a call to this method.
+     * TODO(https://crbug.com/1109713): Remove this method and use onAttached() instead
+     */
+    public void unregisterForUpdates() {
+        mAccountManagerFacade.removeObserver(this);
+        mProfileDataCache.removeObserver(this);
+        AndroidSyncSettings.get().unregisterObserver(this);
+        ProfileSyncService syncService = ProfileSyncService.get();
+        if (syncService != null) {
+            syncService.removeSyncStateChangedListener(this);
+        }
+    }
+
+    /**
+     * Should be called when the {@link PreferenceFragmentCompat} which used {@link
+     * SyncPromoPreference} gets destroyed. Used to record "ImpressionsTilDismiss" histogram.
+     */
+    public void onPreferenceFragmentDestroyed() {
+        if (mSigninPromoController != null) {
+            mSigninPromoController.onPromoDestroyed();
+        }
+    }
+
+    /** Returns the state of the preference. Not valid until registerForUpdates is called. */
+    @State
+    public int getState() {
+        return mState;
+    }
+
+    /** Updates the title, summary, and image based on the current sign-in state. */
+    private void update() {
+        // If feature is not enabled keep the preference at the default PROMO_NONE state.
+        if (!ChromeFeatureList.isEnabled(ChromeFeatureList.MOBILE_IDENTITY_CONSISTENCY)) {
+            return;
+        }
+        boolean personalizedPromoDismissed = SharedPreferencesManager.getInstance().readBoolean(
+                ChromePreferenceKeys.SIGNIN_PROMO_SETTINGS_PERSONALIZED_DISMISSED, false);
+        if (isSignedInButNotSyncing() && !personalizedPromoDismissed
+                && SigninPromoController.hasNotReachedImpressionLimit(SigninAccessPoint.SETTINGS)) {
+            setupPersonalizedSyncPromo();
+            return;
+        }
+
+        if (mSigninPromoController != null) {
+            // Don't change the promo type if the new promo is already being shown.
+            setupPersonalizedSyncPromo();
+            return;
+        }
+
+        setupPromoHidden();
+    }
+
+    private void setupPersonalizedSyncPromo() {
+        mState = State.PERSONALIZED_SYNC_PROMO;
+        setLayoutResource(R.layout.personalized_signin_promo_view_settings);
+        setVisible(true);
+
+        if (mSigninPromoController == null) {
+            mSigninPromoController = new SigninPromoController(SigninAccessPoint.SETTINGS);
+        }
+
+        notifyChanged();
+    }
+
+    private void setupPromoHidden() {
+        mState = State.PROMO_HIDDEN;
+        mSigninPromoController = null;
+        setVisible(false);
+    }
+
+    private boolean isSignedInButNotSyncing() {
+        IdentityManager identityManager = IdentityServicesProvider.get().getIdentityManager(
+                Profile.getLastUsedRegularProfile());
+        return !identityManager.hasPrimaryAccount()
+                && identityManager.getPrimaryAccountInfo(ConsentLevel.NOT_REQUIRED) != null;
+    }
+
+    @Override
+    public void onBindViewHolder(PreferenceViewHolder holder) {
+        super.onBindViewHolder(holder);
+
+        if (mSigninPromoController == null) {
+            return;
+        }
+
+        PersonalizedSigninPromoView syncPromoView =
+                (PersonalizedSigninPromoView) holder.findViewById(R.id.signin_promo_view_container);
+        // TODO(https://crbug.com/1095628): Use setupPersonalizedSyncPromo here.
+        SigninPromoUtil.setupPromoViewFromCache(
+                mSigninPromoController, mProfileDataCache, syncPromoView, () -> {
+                    SharedPreferencesManager.getInstance().writeBoolean(
+                            ChromePreferenceKeys.SIGNIN_PROMO_SETTINGS_PERSONALIZED_DISMISSED,
+                            true);
+                    setupPromoHidden();
+                });
+        syncPromoView.getStatusMessage().setVisibility(View.VISIBLE);
+        syncPromoView.getChooseAccountButton().setVisibility(View.GONE);
+        syncPromoView.getSigninButton().setText(R.string.sync_promo_turn_on_sync);
+    }
+
+    // ProfileSyncServiceListener implementation.
+    @Override
+    public void syncStateChanged() {
+        update();
+    }
+
+    // ProfileDataCache.Observer implementation.
+    @Override
+    public void onProfileDataUpdated(String accountId) {
+        update();
+    }
+
+    // AndroidSyncSettings.AndroidSyncSettingsObserver implementation.
+    @Override
+    public void androidSyncSettingsChanged() {
+        update();
+    }
+
+    // AccountsChangeObserver implementation.
+    @Override
+    public void onAccountsChanged() {
+        update();
+    }
+}
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/signin/AccountPickerBottomSheetTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/signin/AccountPickerBottomSheetTest.java
index 1dd78e5..5c88cf11 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/signin/AccountPickerBottomSheetTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/signin/AccountPickerBottomSheetTest.java
@@ -38,6 +38,7 @@
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.flags.ChromeSwitches;
+import org.chromium.chrome.browser.night_mode.ChromeNightModeTestUtils;
 import org.chromium.chrome.browser.signin.account_picker.AccountPickerBottomSheetCoordinator;
 import org.chromium.chrome.browser.signin.account_picker.AccountPickerDelegate;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
@@ -132,6 +133,18 @@
 
     @Test
     @MediumTest
+    @Feature("RenderTest")
+    public void testCollapsedSheetWithAccountViewDarkMode() throws IOException {
+        TestThreadUtils.runOnUiThreadBlocking(
+                () -> { ChromeNightModeTestUtils.setUpNightModeForChromeActivity(true); });
+        mRenderTestRule.setNightModeEnabled(true);
+        buildAndShowCollapsedBottomSheet();
+        mRenderTestRule.render(
+                mCoordinator.getBottomSheetViewForTesting(), "collapsed_sheet_with_account_dark");
+    }
+
+    @Test
+    @MediumTest
     public void testExpandedSheet() {
         buildAndShowExpandedBottomSheet();
         onView(allOf(withText(PROFILE_DATA1.getAccountName()), withEffectiveVisibility(VISIBLE)))
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/tracing/settings/TracingSettingsTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/tracing/settings/TracingSettingsTest.java
index a5b5cbfd..61fe976c 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/tracing/settings/TracingSettingsTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/tracing/settings/TracingSettingsTest.java
@@ -10,6 +10,7 @@
 import android.app.PendingIntent;
 import android.content.Context;
 import android.content.Intent;
+import android.os.Build.VERSION_CODES;
 import android.support.test.InstrumentationRegistry;
 import android.util.Pair;
 
@@ -31,6 +32,7 @@
 
 import org.chromium.base.test.util.CallbackHelper;
 import org.chromium.base.test.util.CommandLineFlags;
+import org.chromium.base.test.util.DisabledTest;
 import org.chromium.base.test.util.DisableIf;
 import org.chromium.base.test.util.Feature;
 import org.chromium.chrome.browser.ChromeActivity;
@@ -144,7 +146,9 @@
     @Test
     @MediumTest
     @Feature({"Preferences"})
-    @DisableIf.Build(sdk_is_less_than = 21, message = "crbug.com/899894")
+    @DisabledTest
+    @DisableIf.Build(sdk_is_less_than = VERSION_CODES.M,
+            message = "crbug.com/899894 (for <L), crbug.com/1111816 (for L)")
     public void testRecordTrace() throws Exception {
         mActivityTestRule.startMainActivityOnBlankPage();
         mSettingsActivityTestRule.startSettingsActivity();
diff --git a/chrome/app/nearby_share_strings.grdp b/chrome/app/nearby_share_strings.grdp
index ad48cea..d36cffee 100644
--- a/chrome/app/nearby_share_strings.grdp
+++ b/chrome/app/nearby_share_strings.grdp
@@ -14,6 +14,9 @@
   <message name="IDS_NEARBY_FILE_ATTACHMENTS_VIDEOS" desc="Describes one or more videos being transfered via Nearby Share.">
     {COUNT, plural, =1 {1 video} other {# videos}}
   </message>
+  <message name="IDS_NEARBY_SECURE_CONNECTION_ID" desc="Description shown on both sender and receiver devices, to remind users to verify/compare tokens on both devices for security before sending/receiving data.">
+    Secure connection ID: <ph name="TOKEN">$1<ex>1234</ex></ph>
+  </message>
   <message name="IDS_NEARBY_TEXT_ATTACHMENTS_ADDRESSES" desc="Describes one or more addresses being transfered via Nearby Share.">
     {COUNT, plural, =1 {1 address} other {# addresses}}
   </message>
@@ -49,12 +52,24 @@
   <message name="IDS_NEARBY_NOTIFICATION_RECEIVE_ACTION" desc="Text shown on the notification action to accept receiving data via Nearby Share.">
     Receive
   </message>
+  <message name="IDS_NEARBY_NOTIFICATION_RECEIVE_FAILURE_TITLE" desc="Text shown as the title of a notfication when receiving data via Nearby Share failed.">
+    Failed to receive <ph name="ATTACHMENTS">$1<ex>3 items</ex></ph> from <ph name="DEVICE_NAME">$2<ex>Ted's Pixel 2</ex></ph>
+  </message>
   <message name="IDS_NEARBY_NOTIFICATION_RECEIVE_PROGRESS_TITLE" desc="Text shown as the title of a notfication when receiving data via Nearby Share.">
     Receiving <ph name="ATTACHMENTS">$1<ex>3 items</ex></ph> from <ph name="DEVICE_NAME">$2<ex>Ted's Pixel 2</ex></ph>
   </message>
+  <message name="IDS_NEARBY_NOTIFICATION_RECEIVE_SUCCESS_TITLE" desc="Text shown as the title of a notfication when data was sucessfully received via Nearby Share.">
+    <ph name="ATTACHMENTS">$1<ex>3 items</ex></ph> received from <ph name="DEVICE_NAME">$2<ex>Ted's Pixel 2</ex></ph>
+  </message>
+  <message name="IDS_NEARBY_NOTIFICATION_SEND_FAILURE_TITLE" desc="Text shown as the title of a notfication when sending data via Nearby Share failed.">
+    Failed to send <ph name="ATTACHMENTS">$1<ex>3 items</ex></ph> to <ph name="DEVICE_NAME">$2<ex>Ted's Pixel 2</ex></ph>
+  </message>
   <message name="IDS_NEARBY_NOTIFICATION_SEND_PROGRESS_TITLE" desc="Text shown as the title of a notfication when sending data via Nearby Share.">
     Sending <ph name="ATTACHMENTS">$1<ex>3 items</ex></ph> to <ph name="DEVICE_NAME">$2<ex>Ted's Pixel 2</ex></ph>
   </message>
+  <message name="IDS_NEARBY_NOTIFICATION_SEND_SUCCESS_TITLE" desc="Text shown as the title of a notfication when data was successfully sent via Nearby Share.">
+    <ph name="ATTACHMENTS">$1<ex>3 items</ex></ph> successfully sent to <ph name="DEVICE_NAME">$2<ex>Ted's Pixel 2</ex></ph>
+  </message>
   <message name="IDS_NEARBY_NOTIFICATION_SOURCE" desc="Text shown as the source of a Nearby Share notification.">
     Nearby Share
   </message>
diff --git a/chrome/app/nearby_share_strings_grdp/IDS_NEARBY_NOTIFICATION_RECEIVE_FAILURE_TITLE.png.sha1 b/chrome/app/nearby_share_strings_grdp/IDS_NEARBY_NOTIFICATION_RECEIVE_FAILURE_TITLE.png.sha1
new file mode 100644
index 0000000..2a267cc
--- /dev/null
+++ b/chrome/app/nearby_share_strings_grdp/IDS_NEARBY_NOTIFICATION_RECEIVE_FAILURE_TITLE.png.sha1
@@ -0,0 +1 @@
+7e4a3b15e25d39a118de05867196d7cb632030b0
\ No newline at end of file
diff --git a/chrome/app/nearby_share_strings_grdp/IDS_NEARBY_NOTIFICATION_RECEIVE_SUCCESS_TITLE.png.sha1 b/chrome/app/nearby_share_strings_grdp/IDS_NEARBY_NOTIFICATION_RECEIVE_SUCCESS_TITLE.png.sha1
new file mode 100644
index 0000000..f9fc8f2
--- /dev/null
+++ b/chrome/app/nearby_share_strings_grdp/IDS_NEARBY_NOTIFICATION_RECEIVE_SUCCESS_TITLE.png.sha1
@@ -0,0 +1 @@
+7837c6e2c7ab0d48d50035a70e1cf1cc5a8d1da5
\ No newline at end of file
diff --git a/chrome/app/nearby_share_strings_grdp/IDS_NEARBY_NOTIFICATION_SEND_FAILURE_TITLE.png.sha1 b/chrome/app/nearby_share_strings_grdp/IDS_NEARBY_NOTIFICATION_SEND_FAILURE_TITLE.png.sha1
new file mode 100644
index 0000000..2c85a72
--- /dev/null
+++ b/chrome/app/nearby_share_strings_grdp/IDS_NEARBY_NOTIFICATION_SEND_FAILURE_TITLE.png.sha1
@@ -0,0 +1 @@
+09f41715f5eb5479ca90cf91f6c56985356ee03c
\ No newline at end of file
diff --git a/chrome/app/nearby_share_strings_grdp/IDS_NEARBY_NOTIFICATION_SEND_SUCCESS_TITLE.png.sha1 b/chrome/app/nearby_share_strings_grdp/IDS_NEARBY_NOTIFICATION_SEND_SUCCESS_TITLE.png.sha1
new file mode 100644
index 0000000..8cba3e4
--- /dev/null
+++ b/chrome/app/nearby_share_strings_grdp/IDS_NEARBY_NOTIFICATION_SEND_SUCCESS_TITLE.png.sha1
@@ -0,0 +1 @@
+e469e9b7fe91b0ce18f6624ef3d13276cb47f459
\ No newline at end of file
diff --git a/chrome/app/nearby_share_strings_grdp/IDS_NEARBY_SECURE_CONNECTION_ID.png.sha1 b/chrome/app/nearby_share_strings_grdp/IDS_NEARBY_SECURE_CONNECTION_ID.png.sha1
new file mode 100644
index 0000000..a56b14fd
--- /dev/null
+++ b/chrome/app/nearby_share_strings_grdp/IDS_NEARBY_SECURE_CONNECTION_ID.png.sha1
@@ -0,0 +1 @@
+0c6d5805e7d2fca601540782782590f7fee3733c
\ No newline at end of file
diff --git a/chrome/app/os_settings_strings.grdp b/chrome/app/os_settings_strings.grdp
index aa35b88..42e4c7aa 100644
--- a/chrome/app/os_settings_strings.grdp
+++ b/chrome/app/os_settings_strings.grdp
@@ -18,6 +18,15 @@
   <message name="IDS_SETTINGS_SECONDARY_USER_BANNER" desc="Banner displayed in settings page when the user is secondary in a multi-profile session.">
     Some settings belonging to <ph name="PRIMARY_EMAIL">$1<ex>john@google.com</ex></ph> are being shared with you. These settings only affect your account when using multiple sign-in.
   </message>
+  <message name="IDS_SETTINGS_UPDATE_REQUIRED_EOL_BANNER_DAYS" desc="Banner displayed in OS settings page in case update is required by policy but device has reached end-of-life and the days remaining to return the device back to the enterprise is greater than one and less than seven.">
+      <ph name="DOMAIN">$1<ex>example.com</ex></ph> requires you to back up your data and return this device within <ph name="DAYS_COUNT">$2<ex>10</ex></ph> days.<ph name="LINK_BEGIN">&lt;a target="_blank" href="$3<ex>https://google.com/</ex>"&gt;</ph>See details<ph name="LINK_END">&lt;/a&gt;</ph>
+  </message>
+  <message name="IDS_SETTINGS_UPDATE_REQUIRED_EOL_BANNER_LAST_DAY" desc="Banner displayed in OS settings page in case update is required by policy but device has reached end-of-life and it is the last day to return the device back to the enterprise.">
+      <ph name="DOMAIN">$1<ex>example.com</ex></ph> requires you to back up your data and return this device today.<ph name="LINK_BEGIN">&lt;a target="_blank" href="$2<ex>https://google.com/</ex>"&gt;</ph>See details<ph name="LINK_END">&lt;/a&gt;</ph>
+  </message>
+  <message name="IDS_SETTINGS_UPDATE_REQUIRED_EOL_BANNER_ONE_WEEK" desc="Banner displayed in OS settings page in case update is required by policy but device has reached end-of-life and the days remaining to return the device back to the enterprise is equal to seven.">
+      <ph name="DOMAIN">$1<ex>example.com</ex></ph> requires you to back up your data and return this device within 1 week.<ph name="LINK_BEGIN">&lt;a target="_blank" href="$2<ex>https://google.com/</ex>"&gt;</ph>See details<ph name="LINK_END">&lt;/a&gt;</ph>
+  </message>
 
   <!-- Settings Search Box -->
   <message name="IDS_OS_SEARCH_RESULT_ROW_A11Y_RESULT_SELECTED" desc="ChromeVox alert to indicate the position number of a selected result in a list of search results and the selected result text itself, and that the user can press enter to navigate to section described by the search result.">
diff --git a/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_UPDATE_REQUIRED_EOL_BANNER_DAYS.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_UPDATE_REQUIRED_EOL_BANNER_DAYS.png.sha1
new file mode 100644
index 0000000..9e106a1
--- /dev/null
+++ b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_UPDATE_REQUIRED_EOL_BANNER_DAYS.png.sha1
@@ -0,0 +1 @@
+932a1b84fef78d74fa7802ad184487feaa2e9e5f
\ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_UPDATE_REQUIRED_EOL_BANNER_LAST_DAY.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_UPDATE_REQUIRED_EOL_BANNER_LAST_DAY.png.sha1
new file mode 100644
index 0000000..e226813
--- /dev/null
+++ b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_UPDATE_REQUIRED_EOL_BANNER_LAST_DAY.png.sha1
@@ -0,0 +1 @@
+836fdeaa65fa654bd342623dd680ee6ff378b295
\ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_UPDATE_REQUIRED_EOL_BANNER_ONE_WEEK.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_UPDATE_REQUIRED_EOL_BANNER_ONE_WEEK.png.sha1
new file mode 100644
index 0000000..49bf1e6
--- /dev/null
+++ b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_UPDATE_REQUIRED_EOL_BANNER_ONE_WEEK.png.sha1
@@ -0,0 +1 @@
+6dcd6792924c36dc064038d41aba50c07f105a6f
\ No newline at end of file
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 934c20a..b3d125d 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -1257,6 +1257,8 @@
     "prefs/profile_pref_store_manager.h",
     "prefs/session_startup_pref.cc",
     "prefs/session_startup_pref.h",
+    "prerender/chrome_prerender_contents_delegate.cc",
+    "prerender/chrome_prerender_contents_delegate.h",
     "prerender/chrome_prerender_manager_delegate.cc",
     "prerender/chrome_prerender_manager_delegate.h",
     "prerender/isolated/isolated_prerender_features.cc",
@@ -1290,6 +1292,7 @@
     "prerender/prerende_manager_delegate.h",
     "prerender/prerender_contents.cc",
     "prerender/prerender_contents.h",
+    "prerender/prerender_contents_delegate.h",
     "prerender/prerender_field_trial.cc",
     "prerender/prerender_field_trial.h",
     "prerender/prerender_handle.cc",
@@ -3310,11 +3313,8 @@
       "metrics/tab_stats_tracker.cc",
       "metrics/tab_stats_tracker.h",
       "metrics/tab_stats_tracker_delegate.h",
-      "nearby_sharing/attachment.h",
       "nearby_sharing/fast_initiation_manager.cc",
       "nearby_sharing/fast_initiation_manager.h",
-      "nearby_sharing/file_attachment.cc",
-      "nearby_sharing/file_attachment.h",
       "nearby_sharing/incoming_frames_reader.cc",
       "nearby_sharing/incoming_frames_reader.h",
       "nearby_sharing/incoming_share_target_info.cc",
@@ -3354,10 +3354,10 @@
       "nearby_sharing/share_target.cc",
       "nearby_sharing/share_target.h",
       "nearby_sharing/share_target_discovered_callback.h",
-      "nearby_sharing/text_attachment.cc",
-      "nearby_sharing/text_attachment.h",
       "nearby_sharing/transfer_metadata.cc",
       "nearby_sharing/transfer_metadata.h",
+      "nearby_sharing/transfer_metadata_builder.cc",
+      "nearby_sharing/transfer_metadata_builder.h",
       "nearby_sharing/transfer_update_callback.h",
       "nearby_sharing/webrtc_signaling_messenger.cc",
       "nearby_sharing/webrtc_signaling_messenger.h",
@@ -3442,8 +3442,6 @@
       "resource_coordinator/discard_metrics_lifecycle_unit_observer.h",
       "resource_coordinator/intervention_policy_database.cc",
       "resource_coordinator/intervention_policy_database.h",
-      "resource_coordinator/leveldb_site_characteristics_database.cc",
-      "resource_coordinator/leveldb_site_characteristics_database.h",
       "resource_coordinator/lifecycle_unit.cc",
       "resource_coordinator/lifecycle_unit.h",
       "resource_coordinator/lifecycle_unit_base.cc",
@@ -3454,28 +3452,6 @@
       "resource_coordinator/lifecycle_unit_source_base.cc",
       "resource_coordinator/lifecycle_unit_source_base.h",
       "resource_coordinator/lifecycle_unit_source_observer.h",
-      "resource_coordinator/local_site_characteristics_data_impl.cc",
-      "resource_coordinator/local_site_characteristics_data_impl.h",
-      "resource_coordinator/local_site_characteristics_data_reader.cc",
-      "resource_coordinator/local_site_characteristics_data_reader.h",
-      "resource_coordinator/local_site_characteristics_data_store.cc",
-      "resource_coordinator/local_site_characteristics_data_store.h",
-      "resource_coordinator/local_site_characteristics_data_store_factory.cc",
-      "resource_coordinator/local_site_characteristics_data_store_factory.h",
-      "resource_coordinator/local_site_characteristics_data_store_inspector.cc",
-      "resource_coordinator/local_site_characteristics_data_store_inspector.h",
-      "resource_coordinator/local_site_characteristics_data_writer.cc",
-      "resource_coordinator/local_site_characteristics_data_writer.h",
-      "resource_coordinator/local_site_characteristics_database.h",
-      "resource_coordinator/local_site_characteristics_non_recording_data_store.cc",
-      "resource_coordinator/local_site_characteristics_non_recording_data_store.h",
-      "resource_coordinator/local_site_characteristics_noop_data_writer.cc",
-      "resource_coordinator/local_site_characteristics_noop_data_writer.h",
-      "resource_coordinator/local_site_characteristics_webcontents_observer.cc",
-      "resource_coordinator/local_site_characteristics_webcontents_observer.h",
-      "resource_coordinator/site_characteristics_data_reader.h",
-      "resource_coordinator/site_characteristics_data_store.h",
-      "resource_coordinator/site_characteristics_data_writer.h",
       "resource_coordinator/tab_activity_watcher.cc",
       "resource_coordinator/tab_activity_watcher.h",
       "resource_coordinator/tab_lifecycle_observer.h",
@@ -3770,6 +3746,7 @@
       "//base/util/timer",
       "//chrome/app/vector_icons",
       "//chrome/browser/media/kaleidoscope/mojom",
+      "//chrome/browser/nearby_sharing:share_target",
       "//chrome/browser/nearby_sharing/certificates",
       "//chrome/browser/nearby_sharing/client",
       "//chrome/browser/nearby_sharing/common",
diff --git a/chrome/browser/accessibility/caption_controller.cc b/chrome/browser/accessibility/caption_controller.cc
index edb1a4d..d9d11a7f 100644
--- a/chrome/browser/accessibility/caption_controller.cc
+++ b/chrome/browser/accessibility/caption_controller.cc
@@ -161,6 +161,15 @@
   caption_bubble_controllers_.erase(browser);
 }
 
+bool CaptionController::OnSpeechRecognitionReady(
+    content::WebContents* web_contents) {
+  Browser* browser = chrome::FindBrowserWithWebContents(web_contents);
+  if (!browser || !caption_bubble_controllers_.count(browser))
+    return false;
+  return caption_bubble_controllers_[browser]->OnSpeechRecognitionReady(
+      web_contents);
+}
+
 bool CaptionController::DispatchTranscription(
     content::WebContents* web_contents,
     const chrome::mojom::TranscriptionResultPtr& transcription_result) {
diff --git a/chrome/browser/accessibility/caption_controller.h b/chrome/browser/accessibility/caption_controller.h
index 274b46a..af1422d 100644
--- a/chrome/browser/accessibility/caption_controller.h
+++ b/chrome/browser/accessibility/caption_controller.h
@@ -64,6 +64,12 @@
 
   void Init();
 
+  // Alerts the CaptionBubbleController that belongs to the appropriate browser
+  // that speech recognition is ready to start for the given web contents.
+  // Returns whether this message was routed successfully. Transcriptions will
+  // not proceed if this returns false.
+  bool OnSpeechRecognitionReady(content::WebContents* web_contents);
+
   // Routes a transcription to the CaptionBubbleController that belongs to the
   // appropriate browser. Returns whether the transcription result was routed
   // successfully. Transcriptions will halt if this returns false.
diff --git a/chrome/browser/accessibility/caption_controller_browsertest.cc b/chrome/browser/accessibility/caption_controller_browsertest.cc
index 968dad09..157e7ed 100644
--- a/chrome/browser/accessibility/caption_controller_browsertest.cc
+++ b/chrome/browser/accessibility/caption_controller_browsertest.cc
@@ -83,6 +83,21 @@
         ->GetCaptionBubbleControllerForBrowser(browser);
   }
 
+  bool OnSpeechRecognitionReady() {
+    return OnSpeechRecognitionReadyOnBrowser(browser());
+  }
+
+  bool OnSpeechRecognitionReadyOnBrowser(Browser* browser) {
+    return OnSpeechRecognitionReadyOnBrowserForProfile(browser,
+                                                       browser->profile());
+  }
+
+  bool OnSpeechRecognitionReadyOnBrowserForProfile(Browser* browser,
+                                                   Profile* profile) {
+    return GetControllerForProfile(profile)->OnSpeechRecognitionReady(
+        browser->tab_strip_model()->GetActiveWebContents());
+  }
+
   bool DispatchTranscription(std::string text) {
     return DispatchTranscriptionToBrowser(text, browser());
   }
@@ -173,9 +188,8 @@
 IN_PROC_BROWSER_TEST_F(CaptionControllerTest,
                        LiveCaptionEnabledChanged_BubbleVisible) {
   SetLiveCaptionEnabled(true);
-  // Make the bubble visible by dispatching a transcription.
-  DispatchTranscription(
-      "In Switzerland it is illegal to own just one guinea pig.");
+  // Make the bubble visible.
+  OnSpeechRecognitionReady();
 // The CaptionBubbleController is currently only implemented in Views.
 #if defined(TOOLKIT_VIEWS)
   EXPECT_TRUE(IsWidgetVisible());
@@ -248,9 +262,8 @@
             controller->GetCaptionBubbleControllerForBrowser(browser4));
   EXPECT_EQ(3, NumBubbleControllers());
 
-  // Make the bubble on browser3 visible by dispatching a transcription.
-  DispatchTranscriptionToBrowser(
-      "If you lift a kangaroo's tail off the ground it can't hop.", browser3);
+  // Make the bubble on browser3 visible.
+  OnSpeechRecognitionReadyOnBrowser(browser3);
 // The CaptionBubbleController is currently only implemented in Views.
 #if defined(TOOLKIT_VIEWS)
   EXPECT_TRUE(IsWidgetVisibleOnBrowser(browser3));
@@ -263,9 +276,8 @@
             controller->GetCaptionBubbleControllerForBrowser(browser3));
   EXPECT_EQ(2, NumBubbleControllers());
 
-  // Make the bubble on browser2 visible by dispatching a transcription.
-  DispatchTranscriptionToBrowser(
-      "A lion's roar can be heard from 5 miles away.", browser2);
+  // Make the bubble on browser2 visible.
+  OnSpeechRecognitionReadyOnBrowser(browser2);
 // The CaptionBubbleController is currently only implemented in Views.
 #if defined(TOOLKIT_VIEWS)
   EXPECT_TRUE(IsWidgetVisibleOnBrowser(browser2));
@@ -301,11 +313,8 @@
                          incognito_browser2));
   EXPECT_EQ(2, NumBubbleControllers());
 
-  // Make the bubble on incognito_browser1 visible by dispatching a
-  // transcription.
-  DispatchTranscriptionToBrowser(
-      "If you lift a kangaroo's tail off the ground it can't hop.",
-      incognito_browser1);
+  // Make the bubble on incognito_browser1 visible.
+  OnSpeechRecognitionReadyOnBrowser(incognito_browser1);
 // The CaptionBubbleController is currently only implemented in Views.
 #if defined(TOOLKIT_VIEWS)
   EXPECT_TRUE(IsWidgetVisibleOnBrowser(incognito_browser1));
@@ -319,12 +328,77 @@
   EXPECT_EQ(1, NumBubbleControllers());
 }
 
+IN_PROC_BROWSER_TEST_F(CaptionControllerTest, OnSpeechRecognitionReady) {
+  bool success = OnSpeechRecognitionReady();
+  EXPECT_FALSE(success);
+  EXPECT_EQ(0, NumBubbleControllers());
+
+  SetLiveCaptionEnabled(true);
+  success = OnSpeechRecognitionReady();
+  EXPECT_TRUE(success);
+// The CaptionBubbleController is currently only implemented in Views.
+#if defined(TOOLKIT_VIEWS)
+  EXPECT_TRUE(IsWidgetVisible());
+#else
+  EXPECT_FALSE(IsWidgetVisible());
+#endif
+
+  SetLiveCaptionEnabled(false);
+  success = OnSpeechRecognitionReady();
+  EXPECT_FALSE(success);
+  EXPECT_EQ(0, NumBubbleControllers());
+}
+
+IN_PROC_BROWSER_TEST_F(CaptionControllerTest,
+                       OnSpeechRecognitionReady_MultipleBrowsers) {
+  Browser* browser1 = browser();
+  Browser* browser2 = CreateBrowser(browser()->profile());
+  Browser* incognito_browser = CreateIncognitoBrowser();
+  SetLiveCaptionEnabled(true);
+
+  // OnSpeechRecognitionReady routes to the right browser.
+  bool success = OnSpeechRecognitionReadyOnBrowser(browser1);
+  EXPECT_TRUE(success);
+// The CaptionBubbleController is currently only implemented in Views.
+#if defined(TOOLKIT_VIEWS)
+  EXPECT_TRUE(IsWidgetVisibleOnBrowser(browser1));
+  EXPECT_FALSE(IsWidgetVisibleOnBrowser(browser2));
+  EXPECT_FALSE(IsWidgetVisibleOnBrowser(incognito_browser));
+#else
+  EXPECT_FALSE(IsWidgetVisibleOnBrowser(browser1));
+#endif
+
+  success = OnSpeechRecognitionReadyOnBrowser(browser2);
+  EXPECT_TRUE(success);
+// The CaptionBubbleController is currently only implemented in Views.
+#if defined(TOOLKIT_VIEWS)
+  EXPECT_TRUE(IsWidgetVisibleOnBrowser(browser1));
+  EXPECT_TRUE(IsWidgetVisibleOnBrowser(browser2));
+  EXPECT_FALSE(IsWidgetVisibleOnBrowser(incognito_browser));
+#else
+  EXPECT_FALSE(IsWidgetVisibleOnBrowser(browser2));
+#endif
+
+  success = OnSpeechRecognitionReadyOnBrowser(incognito_browser);
+  EXPECT_TRUE(success);
+// The CaptionBubbleController is currently only implemented in Views.
+#if defined(TOOLKIT_VIEWS)
+  EXPECT_TRUE(IsWidgetVisibleOnBrowser(browser1));
+  EXPECT_TRUE(IsWidgetVisibleOnBrowser(browser2));
+  EXPECT_TRUE(IsWidgetVisibleOnBrowser(incognito_browser));
+#else
+  EXPECT_FALSE(IsWidgetVisibleOnBrowser(incognito_browser));
+#endif
+}
+
 IN_PROC_BROWSER_TEST_F(CaptionControllerTest, DispatchTranscription) {
+  OnSpeechRecognitionReady();
   bool success = DispatchTranscription("A baby spider is called a spiderling.");
   EXPECT_FALSE(success);
   EXPECT_EQ(0, NumBubbleControllers());
 
   SetLiveCaptionEnabled(true);
+  OnSpeechRecognitionReady();
   success = DispatchTranscription(
       "A baby octopus is about the size of a flea when it is born.");
   EXPECT_TRUE(success);
@@ -338,6 +412,7 @@
 #endif
 
   SetLiveCaptionEnabled(false);
+  OnSpeechRecognitionReady();
   success = DispatchTranscription(
       "Approximately 10-20% of power outages in the US are caused by "
       "squirrels.");
@@ -353,6 +428,7 @@
   SetLiveCaptionEnabled(true);
 
   // Dispatch transcription routes the transcription to the right browser.
+  OnSpeechRecognitionReadyOnBrowser(browser1);
   bool success = DispatchTranscriptionToBrowser(
       "Honeybees can recognize human faces.", browser1);
   EXPECT_TRUE(success);
@@ -369,6 +445,7 @@
   EXPECT_FALSE(IsWidgetVisibleOnBrowser(browser1));
 #endif
 
+  OnSpeechRecognitionReadyOnBrowser(browser2);
   success = DispatchTranscriptionToBrowser(
       "A blue whale's heart is the size of a small car.", browser2);
   EXPECT_TRUE(success);
@@ -386,6 +463,7 @@
   EXPECT_FALSE(IsWidgetVisibleOnBrowser(browser2));
 #endif
 
+  OnSpeechRecognitionReadyOnBrowser(incognito_browser);
   success = DispatchTranscriptionToBrowser(
       "Squirrels forget where they hide about half of their nuts.",
       incognito_browser);
@@ -501,10 +579,8 @@
   EXPECT_EQ(1, NumBubbleControllersForProfile(profile1));
   EXPECT_EQ(0, NumBubbleControllersForProfile(profile2));
 
-  // Make the bubble on incognito_browser1 visible by dispatching a
-  // transcription.
-  DispatchTranscriptionToBrowser(
-      "If you lift a kangaroo's tail off the ground it can't hop.", browser1);
+  // Make the bubble on incognito_browser1 visible.
+  OnSpeechRecognitionReadyOnBrowser(browser1);
 // The CaptionBubbleController is currently only implemented in Views.
 #if defined(TOOLKIT_VIEWS)
   EXPECT_TRUE(IsWidgetVisibleOnBrowser(browser1));
@@ -520,6 +596,52 @@
 }
 
 IN_PROC_BROWSER_TEST_F(CaptionControllerTest,
+                       OnSpeechRecognitionReady_MultipleProfiles) {
+  Profile* profile1 = browser()->profile();
+  Profile* profile2 = CreateProfile();
+  Browser* browser1 = browser();
+  Browser* browser2 = CreateBrowser(profile2);
+
+  // Enable live caption on both profiles.
+  SetLiveCaptionEnabled(true);
+  profile2->GetPrefs()->SetBoolean(prefs::kLiveCaptionEnabled, true);
+
+  // OnSpeechRecognitionReady routes to the right browser on the right profile.
+  bool success =
+      OnSpeechRecognitionReadyOnBrowserForProfile(browser1, profile1);
+  EXPECT_TRUE(success);
+// The CaptionBubbleController is currently only implemented in Views.
+#if defined(TOOLKIT_VIEWS)
+  EXPECT_TRUE(IsWidgetVisibleOnBrowser(browser1));
+  EXPECT_FALSE(IsWidgetVisibleOnBrowser(browser2));
+#else
+  EXPECT_FALSE(IsWidgetVisibleOnBrowser(browser1));
+#endif
+
+  success = OnSpeechRecognitionReadyOnBrowserForProfile(browser2, profile2);
+  EXPECT_TRUE(success);
+// The CaptionBubbleController is currently only implemented in Views.
+#if defined(TOOLKIT_VIEWS)
+  EXPECT_TRUE(IsWidgetVisibleOnBrowser(browser1));
+  EXPECT_TRUE(IsWidgetVisibleOnBrowser(browser2));
+#else
+  EXPECT_FALSE(IsWidgetVisibleOnBrowser(browser1));
+#endif
+
+  // OnSpeechRecognitionReady returns false for browsers on different profiles.
+  success = OnSpeechRecognitionReadyOnBrowserForProfile(browser1, profile2);
+  EXPECT_FALSE(success);
+// The CaptionBubbleController is currently only implemented in Views.
+#if defined(TOOLKIT_VIEWS)
+  EXPECT_TRUE(IsWidgetVisibleOnBrowser(browser1));
+  EXPECT_TRUE(IsWidgetVisibleOnBrowser(browser2));
+#else
+  EXPECT_FALSE(IsWidgetVisibleOnBrowser(browser1));
+  EXPECT_FALSE(IsWidgetVisibleOnBrowser(browser2));
+#endif
+}
+
+IN_PROC_BROWSER_TEST_F(CaptionControllerTest,
                        DispatchTranscription_MultipleProfiles) {
   Profile* profile1 = browser()->profile();
   Profile* profile2 = CreateProfile();
@@ -532,6 +654,7 @@
 
   // Dispatch transcription routes the transcription to the right browser on the
   // right profile.
+  OnSpeechRecognitionReadyOnBrowserForProfile(browser1, profile1);
   bool success = DispatchTranscriptionToBrowserForProfile(
       "Only female mosquitos bite.", browser1, profile1);
   EXPECT_TRUE(success);
@@ -546,6 +669,7 @@
   EXPECT_FALSE(IsWidgetVisibleOnBrowser(browser1));
 #endif
 
+  OnSpeechRecognitionReadyOnBrowserForProfile(browser2, profile2);
   success = DispatchTranscriptionToBrowserForProfile(
       "Mosquitos were around at the time of the dinosaurs.", browser2,
       profile2);
diff --git a/chrome/browser/accessibility/caption_host_impl.cc b/chrome/browser/accessibility/caption_host_impl.cc
index deaa6ec..b549c9bb 100644
--- a/chrome/browser/accessibility/caption_host_impl.cc
+++ b/chrome/browser/accessibility/caption_host_impl.cc
@@ -39,6 +39,29 @@
 
 CaptionHostImpl::~CaptionHostImpl() = default;
 
+void CaptionHostImpl::OnSpeechRecognitionReady(
+    OnSpeechRecognitionReadyCallback reply) {
+  if (!frame_host_) {
+    std::move(reply).Run(false);
+    return;
+  }
+  content::WebContents* web_contents =
+      content::WebContents::FromRenderFrameHost(frame_host_);
+  if (!web_contents) {
+    frame_host_ = nullptr;
+    std::move(reply).Run(false);
+    return;
+  }
+  Profile* profile =
+      Profile::FromBrowserContext(web_contents->GetBrowserContext());
+  if (!profile) {
+    std::move(reply).Run(false);
+    return;
+  }
+  std::move(reply).Run(CaptionControllerFactory::GetForProfile(profile)
+                           ->OnSpeechRecognitionReady(web_contents));
+}
+
 void CaptionHostImpl::OnTranscription(
     chrome::mojom::TranscriptionResultPtr transcription_result,
     OnTranscriptionCallback reply) {
diff --git a/chrome/browser/accessibility/caption_host_impl.h b/chrome/browser/accessibility/caption_host_impl.h
index 3e3615e6..23be8ed 100644
--- a/chrome/browser/accessibility/caption_host_impl.h
+++ b/chrome/browser/accessibility/caption_host_impl.h
@@ -37,6 +37,8 @@
       mojo::PendingReceiver<chrome::mojom::CaptionHost> receiver);
 
   // chrome::mojom::CaptionHost:
+  void OnSpeechRecognitionReady(
+      OnSpeechRecognitionReadyCallback reply) override;
   void OnTranscription(
       chrome::mojom::TranscriptionResultPtr transcription_result,
       OnTranscriptionCallback reply) override;
diff --git a/chrome/browser/apps/intent_helper/apps_navigation_throttle.cc b/chrome/browser/apps/intent_helper/apps_navigation_throttle.cc
index 1e4865f..4fcccfb 100644
--- a/chrome/browser/apps/intent_helper/apps_navigation_throttle.cc
+++ b/chrome/browser/apps/intent_helper/apps_navigation_throttle.cc
@@ -15,6 +15,7 @@
 #include "chrome/browser/apps/app_service/browser_app_launcher.h"
 #include "chrome/browser/apps/intent_helper/intent_picker_auto_display_service.h"
 #include "chrome/browser/apps/intent_helper/page_transition_util.h"
+#include "chrome/browser/prerender/chrome_prerender_contents_delegate.h"
 #include "chrome/browser/prerender/prerender_contents.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
@@ -253,7 +254,8 @@
   // Do not create the throttle if no apps can be installed.
   // Do not create the throttle in incognito or for a prerender navigation.
   if (web_contents->GetBrowserContext()->IsOffTheRecord() ||
-      prerender::PrerenderContents::FromWebContents(web_contents) != nullptr) {
+      prerender::ChromePrerenderContentsDelegate::FromWebContents(
+          web_contents) != nullptr) {
     return false;
   }
 
diff --git a/chrome/browser/apps/platform_apps/platform_app_navigation_redirector.cc b/chrome/browser/apps/platform_apps/platform_app_navigation_redirector.cc
index f60040d..e30a336 100644
--- a/chrome/browser/apps/platform_apps/platform_app_navigation_redirector.cc
+++ b/chrome/browser/apps/platform_apps/platform_app_navigation_redirector.cc
@@ -7,6 +7,7 @@
 #include "apps/launcher.h"
 #include "base/bind.h"
 #include "base/logging.h"
+#include "chrome/browser/prerender/chrome_prerender_contents_delegate.h"
 #include "chrome/browser/prerender/prerender_contents.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser_finder.h"
@@ -43,7 +44,7 @@
 
   // If prerendering, don't launch the app but abort the navigation.
   prerender::PrerenderContents* prerender_contents =
-      prerender::PrerenderContents::FromWebContents(source);
+      prerender::ChromePrerenderContentsDelegate::FromWebContents(source);
   if (prerender_contents) {
     prerender_contents->Destroy(prerender::FINAL_STATUS_NAVIGATION_INTERCEPTED);
     return true;
diff --git a/chrome/browser/browser_switcher/browser_switcher_navigation_throttle.cc b/chrome/browser/browser_switcher/browser_switcher_navigation_throttle.cc
index 7c7f17b..20c626ae 100644
--- a/chrome/browser/browser_switcher/browser_switcher_navigation_throttle.cc
+++ b/chrome/browser/browser_switcher/browser_switcher_navigation_throttle.cc
@@ -13,6 +13,7 @@
 #include "chrome/browser/browser_switcher/browser_switcher_service.h"
 #include "chrome/browser/browser_switcher/browser_switcher_service_factory.h"
 #include "chrome/browser/browser_switcher/browser_switcher_sitelist.h"
+#include "chrome/browser/prerender/chrome_prerender_contents_delegate.h"
 #include "chrome/browser/prerender/prerender_contents.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/common/webui_url_constants.h"
@@ -62,7 +63,7 @@
   // If prerendering, don't launch the alternative browser but abort the
   // navigation.
   prerender::PrerenderContents* prerender_contents =
-      prerender::PrerenderContents::FromWebContents(web_contents);
+      prerender::ChromePrerenderContentsDelegate::FromWebContents(web_contents);
   if (prerender_contents) {
     prerender_contents->Destroy(prerender::FINAL_STATUS_BROWSER_SWITCH);
     return true;
diff --git a/chrome/browser/chrome_browser_interface_binders.cc b/chrome/browser/chrome_browser_interface_binders.cc
index 59cc6a5b..e339ba9 100644
--- a/chrome/browser/chrome_browser_interface_binders.cc
+++ b/chrome/browser/chrome_browser_interface_binders.cc
@@ -20,6 +20,7 @@
 #include "chrome/browser/navigation_predictor/navigation_predictor.h"
 #include "chrome/browser/password_manager/chrome_password_manager_client.h"
 #include "chrome/browser/predictors/network_hints_handler_impl.h"
+#include "chrome/browser/prerender/chrome_prerender_contents_delegate.h"
 #include "chrome/browser/prerender/prerender_contents.h"
 #include "chrome/browser/prerender/prerender_processor_impl.h"
 #include "chrome/browser/profiles/profile.h"
@@ -284,7 +285,7 @@
     return;
 
   auto* prerender_contents =
-      prerender::PrerenderContents::FromWebContents(web_contents);
+      prerender::ChromePrerenderContentsDelegate::FromWebContents(web_contents);
   if (!prerender_contents)
     return;
   prerender_contents->AddPrerenderCancelerReceiver(std::move(receiver));
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index da405a9f3..96077da 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -91,6 +91,7 @@
 #include "chrome/browser/platform_util.h"
 #include "chrome/browser/plugins/pdf_iframe_navigation_throttle.h"
 #include "chrome/browser/plugins/plugin_utils.h"
+#include "chrome/browser/prerender/chrome_prerender_contents_delegate.h"
 #include "chrome/browser/prerender/isolated/isolated_prerender_features.h"
 #include "chrome/browser/prerender/isolated/isolated_prerender_service.h"
 #include "chrome/browser/prerender/isolated/isolated_prerender_service_factory.h"
@@ -945,7 +946,8 @@
 mojo::PendingRemote<prerender::mojom::PrerenderCanceler> GetPrerenderCanceler(
     base::OnceCallback<content::WebContents*()> wc_getter) {
   mojo::PendingRemote<prerender::mojom::PrerenderCanceler> canceler;
-  prerender::PrerenderContents::FromWebContents(std::move(wc_getter).Run())
+  prerender::ChromePrerenderContentsDelegate::FromWebContents(
+      std::move(wc_getter).Run())
       ->AddPrerenderCancelerReceiver(canceler.InitWithNewPipeAndPassReceiver());
   return canceler;
 }
@@ -980,7 +982,7 @@
 
   // Do not launch external requests attached to unswapped prerenders.
   prerender::PrerenderContents* prerender_contents =
-      prerender::PrerenderContents::FromWebContents(web_contents);
+      prerender::ChromePrerenderContentsDelegate::FromWebContents(web_contents);
   if (prerender_contents) {
     prerender_contents->Destroy(prerender::FINAL_STATUS_UNSUPPORTED_SCHEME);
     return;
@@ -2869,7 +2871,7 @@
 
   // If the tab is being prerendered, cancel the prerender and the request.
   prerender::PrerenderContents* prerender_contents =
-      prerender::PrerenderContents::FromWebContents(web_contents);
+      prerender::ChromePrerenderContentsDelegate::FromWebContents(web_contents);
   if (prerender_contents) {
     prerender_contents->Destroy(prerender::FINAL_STATUS_SSL_ERROR);
     if (!callback.is_null()) {
@@ -2960,7 +2962,7 @@
     net::ClientCertIdentityList client_certs,
     std::unique_ptr<content::ClientCertificateDelegate> delegate) {
   prerender::PrerenderContents* prerender_contents =
-      prerender::PrerenderContents::FromWebContents(web_contents);
+      prerender::ChromePrerenderContentsDelegate::FromWebContents(web_contents);
   if (prerender_contents) {
     prerender_contents->Destroy(
         prerender::FINAL_STATUS_SSL_CLIENT_CERTIFICATE_REQUESTED);
@@ -3141,7 +3143,8 @@
   }
 #endif
 
-  DCHECK(!prerender::PrerenderContents::FromWebContents(web_contents));
+  DCHECK(!prerender::ChromePrerenderContentsDelegate::FromWebContents(
+      web_contents));
 
   BlockedWindowParams blocked_params(
       target_url, source_origin, opener->GetSiteInstance(), referrer,
@@ -3946,7 +3949,8 @@
   // TODO(davidben): This is insufficient to integrate with prerender properly.
   // https://crbug.com/370595
   prerender::PrerenderContents* prerender_contents =
-      prerender::PrerenderContents::FromWebContents(handle->GetWebContents());
+      prerender::ChromePrerenderContentsDelegate::FromWebContents(
+          handle->GetWebContents());
   if (!prerender_contents && handle->IsInMainFrame()) {
     throttles.push_back(
         navigation_interception::InterceptNavigationDelegate::CreateThrottleFor(
diff --git a/chrome/browser/chrome_service_worker_browsertest.cc b/chrome/browser/chrome_service_worker_browsertest.cc
index 34d5d18..803c9ae 100644
--- a/chrome/browser/chrome_service_worker_browsertest.cc
+++ b/chrome/browser/chrome_service_worker_browsertest.cc
@@ -219,11 +219,11 @@
       embedded_test_server()->GetURL("/scope/done.html"));
   EXPECT_EQ(expected_title2, title_watcher2.WaitAndGetTitle());
 
-  content::WebContents* web_contents =
-      browser()->tab_strip_model()->GetActiveWebContents();
-  EXPECT_TRUE(content_settings::TabSpecificContentSettings::FromWebContents(
-                  web_contents)
-                  ->IsContentBlocked(ContentSettingsType::JAVASCRIPT));
+  content::RenderFrameHost* main_frame =
+      browser()->tab_strip_model()->GetActiveWebContents()->GetMainFrame();
+  EXPECT_TRUE(
+      content_settings::TabSpecificContentSettings::GetForFrame(main_frame)
+          ->IsContentBlocked(ContentSettingsType::JAVASCRIPT));
 }
 
 IN_PROC_BROWSER_TEST_F(ChromeServiceWorkerTest,
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn
index cc07952..a7d40e3 100644
--- a/chrome/browser/chromeos/BUILD.gn
+++ b/chrome/browser/chromeos/BUILD.gn
@@ -1990,6 +1990,10 @@
     "policy/display_rotation_default_handler.h",
     "policy/display_settings_handler.cc",
     "policy/display_settings_handler.h",
+    "policy/dlp/dlp_content_manager.cc",
+    "policy/dlp/dlp_content_manager.h",
+    "policy/dlp/dlp_content_tab_helper.cc",
+    "policy/dlp/dlp_content_tab_helper.h",
     "policy/dlp/enterprise_clipboard_dlp_controller.cc",
     "policy/dlp/enterprise_clipboard_dlp_controller.h",
     "policy/dm_token_storage.cc",
@@ -3279,6 +3283,8 @@
     "policy/device_dock_mac_address_source_handler_unittest.cc",
     "policy/device_local_account_policy_service_unittest.cc",
     "policy/device_policy_decoder_chromeos_unittest.cc",
+    "policy/dlp/dlp_content_manager_unittest.cc",
+    "policy/dlp/dlp_content_tab_helper_unittest.cc",
     "policy/dm_token_storage_unittest.cc",
     "policy/extension_cache_unittest.cc",
     "policy/extension_install_event_log_collector_unittest.cc",
diff --git a/chrome/browser/chromeos/policy/dlp/dlp_content_manager.cc b/chrome/browser/chromeos/policy/dlp/dlp_content_manager.cc
new file mode 100644
index 0000000..1844ddb
--- /dev/null
+++ b/chrome/browser/chromeos/policy/dlp/dlp_content_manager.cc
@@ -0,0 +1,107 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/chromeos/policy/dlp/dlp_content_manager.h"
+
+#include "base/stl_util.h"
+#include "content/public/browser/visibility.h"
+#include "content/public/browser/web_contents.h"
+#include "url/gurl.h"
+
+namespace policy {
+
+static DlpContentManager* g_dlp_content_manager = nullptr;
+
+// static
+DlpContentManager* DlpContentManager::Get() {
+  if (!g_dlp_content_manager)
+    g_dlp_content_manager = new DlpContentManager();
+  return g_dlp_content_manager;
+}
+
+bool DlpContentManager::IsWebContentsConfidential(
+    const content::WebContents* web_contents) const {
+  return base::Contains(confidential_web_contents_, web_contents);
+}
+
+bool DlpContentManager::IsConfidentialDataPresentOnScreen() const {
+  return is_confidential_web_contents_visible_;
+}
+
+/* static */
+void DlpContentManager::SetDlpContentManagerForTesting(
+    DlpContentManager* dlp_content_manager) {
+  if (g_dlp_content_manager)
+    delete g_dlp_content_manager;
+  g_dlp_content_manager = dlp_content_manager;
+}
+
+/* static */
+void DlpContentManager::ResetDlpContentManagerForTesting() {
+  g_dlp_content_manager = nullptr;
+}
+
+DlpContentManager::DlpContentManager() = default;
+
+DlpContentManager::~DlpContentManager() = default;
+
+void DlpContentManager::OnConfidentialityChanged(
+    content::WebContents* web_contents,
+    bool confidential) {
+  if (confidential) {
+    AddToConfidential(web_contents);
+  } else {
+    RemoveFromConfidential(web_contents);
+  }
+}
+
+void DlpContentManager::OnWebContentsDestroyed(
+    const content::WebContents* web_contents) {
+  RemoveFromConfidential(web_contents);
+}
+
+bool DlpContentManager::IsURLConfidential(const GURL& url) const {
+  // TODO(crbug/1109783): Implement based on the policy.
+  return false;
+}
+
+void DlpContentManager::OnVisibilityChanged(content::WebContents* web_contents,
+                                            bool visible) {
+  MaybeChangeVisibilityFlag();
+}
+
+void DlpContentManager::AddToConfidential(content::WebContents* web_contents) {
+  confidential_web_contents_.insert(web_contents);
+  if (web_contents->GetVisibility() == content::Visibility::VISIBLE) {
+    MaybeChangeVisibilityFlag();
+  }
+}
+
+void DlpContentManager::RemoveFromConfidential(
+    const content::WebContents* web_contents) {
+  confidential_web_contents_.erase(web_contents);
+  MaybeChangeVisibilityFlag();
+}
+
+void DlpContentManager::MaybeChangeVisibilityFlag() {
+  bool is_confidential_web_contents_currently_visible = false;
+  for (auto* web_contents : confidential_web_contents_) {
+    if (web_contents->GetVisibility() == content::Visibility::VISIBLE) {
+      is_confidential_web_contents_currently_visible = true;
+      break;
+    }
+  }
+  if (is_confidential_web_contents_visible_ !=
+      is_confidential_web_contents_currently_visible) {
+    is_confidential_web_contents_visible_ =
+        is_confidential_web_contents_currently_visible;
+    OnScreenConfidentialityStateChanged(is_confidential_web_contents_visible_);
+  }
+}
+
+void DlpContentManager::OnScreenConfidentialityStateChanged(bool visible) {
+  // TODO(crbug/1105991): Implement enforcing/releasing of restrictions.
+}
+
+}  // namespace policy
diff --git a/chrome/browser/chromeos/policy/dlp/dlp_content_manager.h b/chrome/browser/chromeos/policy/dlp/dlp_content_manager.h
new file mode 100644
index 0000000..3a353f8
--- /dev/null
+++ b/chrome/browser/chromeos/policy/dlp/dlp_content_manager.h
@@ -0,0 +1,87 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_CHROMEOS_POLICY_DLP_DLP_CONTENT_MANAGER_H_
+#define CHROME_BROWSER_CHROMEOS_POLICY_DLP_DLP_CONTENT_MANAGER_H_
+
+#include "base/containers/flat_set.h"
+
+class GURL;
+
+namespace content {
+class WebContents;
+}  // namespace content
+
+namespace policy {
+
+// System-wide class that tracks the set of currently known confidential
+// WebContents and whether any of them are currently visible.
+// If any confidential WebContents is visible, the corresponding restrictions
+// will be enforced according to the current enterprise policy.
+class DlpContentManager {
+ public:
+  // Creates the instance if not yet created.
+  // There will always be a single instance created on the first access.
+  static DlpContentManager* Get();
+
+  // Checks whether |web_contents| is confidential according to the policy.
+  bool IsWebContentsConfidential(
+      const content::WebContents* web_contents) const;
+
+  // Returns whether any WebContents with a confidential content is currently
+  // visible.
+  bool IsConfidentialDataPresentOnScreen() const;
+
+  // The caller (test) should manage |dlp_content_manager| lifetime.
+  // Reset doesn't delete the object.
+  static void SetDlpContentManagerForTesting(
+      DlpContentManager* dlp_content_manager);
+  static void ResetDlpContentManagerForTesting();
+
+ private:
+  friend class DlpContentManagerTest;
+  friend class DlpContentTabHelper;
+  friend class MockDlpContentManager;
+
+  DlpContentManager();
+  virtual ~DlpContentManager();
+  DlpContentManager(const DlpContentManager&) = delete;
+  DlpContentManager& operator=(const DlpContentManager&) = delete;
+
+  // Called from DlpContentTabHelper:
+  // Being called when confidentiality state changes for |web_contents|, e.g.
+  // because of navigation.
+  virtual void OnConfidentialityChanged(content::WebContents* web_contents,
+                                        bool confidential);
+  // Called when |web_contents| is about to be destroyed.
+  virtual void OnWebContentsDestroyed(const content::WebContents* web_contents);
+  // Should return whether |url| is considered as confidential according to
+  // the policies.
+  virtual bool IsURLConfidential(const GURL& url) const;
+  // Called when |web_contents| becomes visible or not.
+  virtual void OnVisibilityChanged(content::WebContents* web_contents,
+                                   bool visible);
+
+  // Helpers to add/remove WebContents from confidential sets.
+  void AddToConfidential(content::WebContents* web_contents);
+  void RemoveFromConfidential(const content::WebContents* web_contents);
+
+  // Updates |is_confidential_web_contents_visible_| and calls
+  // OnScreenConfidentialityStateChanged() if needed.
+  void MaybeChangeVisibilityFlag();
+
+  // Called when a confidential content becomes visible or all confidential
+  // content becomes not visible.
+  void OnScreenConfidentialityStateChanged(bool visible);
+
+  // Set of currently known confidential WebContents.
+  base::flat_set<content::WebContents*> confidential_web_contents_;
+  // Flag the indicates whether any confidential WebContents is currently
+  // visible or not.
+  bool is_confidential_web_contents_visible_ = false;
+};
+
+}  // namespace policy
+
+#endif  // CHROME_BROWSER_CHROMEOS_POLICY_DLP_DLP_CONTENT_MANAGER_H_
diff --git a/chrome/browser/chromeos/policy/dlp/dlp_content_manager_unittest.cc b/chrome/browser/chromeos/policy/dlp/dlp_content_manager_unittest.cc
new file mode 100644
index 0000000..b3cc879
--- /dev/null
+++ b/chrome/browser/chromeos/policy/dlp/dlp_content_manager_unittest.cc
@@ -0,0 +1,141 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/chromeos/policy/dlp/dlp_content_manager.h"
+
+#include "chrome/test/base/testing_profile.h"
+#include "content/public/test/browser_task_environment.h"
+#include "content/public/test/test_renderer_host.h"
+#include "content/public/test/web_contents_tester.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace policy {
+
+class DlpContentManagerTest : public testing::Test {
+ protected:
+  void SetUp() override {
+    testing::Test::SetUp();
+
+    profile_ = std::make_unique<TestingProfile>();
+  }
+
+  std::unique_ptr<content::WebContents> CreateWebContents() {
+    return content::WebContentsTester::CreateTestWebContents(profile_.get(),
+                                                             nullptr);
+  }
+
+  void ChangeConfidentiality(content::WebContents* web_contents,
+                             bool confidential) {
+    manager_.OnConfidentialityChanged(web_contents, confidential);
+  }
+
+  void ChangeVisibility(content::WebContents* web_contents, bool visible) {
+    if (visible) {
+      web_contents->WasShown();
+    } else {
+      web_contents->WasHidden();
+    }
+    manager_.OnVisibilityChanged(web_contents, visible);
+  }
+
+  void DestroyWebContents(content::WebContents* web_contents) {
+    manager_.OnWebContentsDestroyed(web_contents);
+  }
+
+  DlpContentManager manager_;
+
+ private:
+  content::BrowserTaskEnvironment task_environment_;
+  content::RenderViewHostTestEnabler rvh_test_enabler_;
+  std::unique_ptr<TestingProfile> profile_;
+};
+
+TEST_F(DlpContentManagerTest, NoConfidentialDataShown) {
+  std::unique_ptr<content::WebContents> web_contents = CreateWebContents();
+  EXPECT_FALSE(manager_.IsWebContentsConfidential(web_contents.get()));
+  EXPECT_FALSE(manager_.IsConfidentialDataPresentOnScreen());
+}
+
+TEST_F(DlpContentManagerTest, ConfidentialDataShown) {
+  std::unique_ptr<content::WebContents> web_contents = CreateWebContents();
+  EXPECT_FALSE(manager_.IsWebContentsConfidential(web_contents.get()));
+  EXPECT_FALSE(manager_.IsConfidentialDataPresentOnScreen());
+
+  ChangeConfidentiality(web_contents.get(), /*confidential=*/true);
+  EXPECT_TRUE(manager_.IsWebContentsConfidential(web_contents.get()));
+  EXPECT_TRUE(manager_.IsConfidentialDataPresentOnScreen());
+
+  DestroyWebContents(web_contents.get());
+  EXPECT_FALSE(manager_.IsWebContentsConfidential(web_contents.get()));
+  EXPECT_FALSE(manager_.IsConfidentialDataPresentOnScreen());
+}
+
+TEST_F(DlpContentManagerTest, ConfidentialDataVisibilityChanged) {
+  std::unique_ptr<content::WebContents> web_contents = CreateWebContents();
+  EXPECT_FALSE(manager_.IsWebContentsConfidential(web_contents.get()));
+  EXPECT_FALSE(manager_.IsConfidentialDataPresentOnScreen());
+
+  ChangeConfidentiality(web_contents.get(), /*confidential=*/true);
+  EXPECT_TRUE(manager_.IsWebContentsConfidential(web_contents.get()));
+  EXPECT_TRUE(manager_.IsConfidentialDataPresentOnScreen());
+
+  ChangeVisibility(web_contents.get(), /*visible=*/false);
+  EXPECT_TRUE(manager_.IsWebContentsConfidential(web_contents.get()));
+  EXPECT_FALSE(manager_.IsConfidentialDataPresentOnScreen());
+
+  ChangeVisibility(web_contents.get(), /*visible=*/true);
+  EXPECT_TRUE(manager_.IsWebContentsConfidential(web_contents.get()));
+  EXPECT_TRUE(manager_.IsConfidentialDataPresentOnScreen());
+
+  DestroyWebContents(web_contents.get());
+  EXPECT_FALSE(manager_.IsWebContentsConfidential(web_contents.get()));
+  EXPECT_FALSE(manager_.IsConfidentialDataPresentOnScreen());
+}
+
+TEST_F(DlpContentManagerTest,
+       TwoWebContentsVisibilityAndConfidentialityChanged) {
+  std::unique_ptr<content::WebContents> web_contents1 = CreateWebContents();
+  std::unique_ptr<content::WebContents> web_contents2 = CreateWebContents();
+  EXPECT_FALSE(manager_.IsWebContentsConfidential(web_contents1.get()));
+  EXPECT_FALSE(manager_.IsWebContentsConfidential(web_contents2.get()));
+  EXPECT_FALSE(manager_.IsConfidentialDataPresentOnScreen());
+
+  // WebContents 1 becomes confidential.
+  ChangeConfidentiality(web_contents1.get(), /*confidential=*/true);
+  EXPECT_TRUE(manager_.IsWebContentsConfidential(web_contents1.get()));
+  EXPECT_FALSE(manager_.IsWebContentsConfidential(web_contents2.get()));
+  EXPECT_TRUE(manager_.IsConfidentialDataPresentOnScreen());
+
+  // WebContents 2 is hidden.
+  ChangeVisibility(web_contents2.get(), /*visible=*/false);
+  EXPECT_TRUE(manager_.IsWebContentsConfidential(web_contents1.get()));
+  EXPECT_FALSE(manager_.IsWebContentsConfidential(web_contents2.get()));
+  EXPECT_TRUE(manager_.IsConfidentialDataPresentOnScreen());
+
+  // WebContents 1 becomes non-confidential.
+  ChangeConfidentiality(web_contents1.get(), /*confidential=*/false);
+  EXPECT_FALSE(manager_.IsWebContentsConfidential(web_contents1.get()));
+  EXPECT_FALSE(manager_.IsWebContentsConfidential(web_contents2.get()));
+  EXPECT_FALSE(manager_.IsConfidentialDataPresentOnScreen());
+
+  // WebContents 2 becomes confidential.
+  ChangeConfidentiality(web_contents2.get(), /*confidential=*/true);
+  EXPECT_FALSE(manager_.IsWebContentsConfidential(web_contents1.get()));
+  EXPECT_TRUE(manager_.IsWebContentsConfidential(web_contents2.get()));
+  EXPECT_FALSE(manager_.IsConfidentialDataPresentOnScreen());
+
+  // WebContents 2 is visible.
+  ChangeVisibility(web_contents2.get(), /*visible=*/true);
+  EXPECT_FALSE(manager_.IsWebContentsConfidential(web_contents1.get()));
+  EXPECT_TRUE(manager_.IsWebContentsConfidential(web_contents2.get()));
+  EXPECT_TRUE(manager_.IsConfidentialDataPresentOnScreen());
+
+  DestroyWebContents(web_contents1.get());
+  DestroyWebContents(web_contents2.get());
+  EXPECT_FALSE(manager_.IsWebContentsConfidential(web_contents1.get()));
+  EXPECT_FALSE(manager_.IsWebContentsConfidential(web_contents2.get()));
+  EXPECT_FALSE(manager_.IsConfidentialDataPresentOnScreen());
+}
+
+}  // namespace policy
diff --git a/chrome/browser/chromeos/policy/dlp/dlp_content_tab_helper.cc b/chrome/browser/chromeos/policy/dlp/dlp_content_tab_helper.cc
new file mode 100644
index 0000000..e5945a6
--- /dev/null
+++ b/chrome/browser/chromeos/policy/dlp/dlp_content_tab_helper.cc
@@ -0,0 +1,81 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/chromeos/policy/dlp/dlp_content_tab_helper.h"
+
+#include "chrome/browser/chromeos/policy/dlp/dlp_content_manager.h"
+#include "content/public/browser/navigation_handle.h"
+#include "content/public/browser/render_frame_host.h"
+#include "content/public/browser/web_contents.h"
+#include "url/gurl.h"
+
+namespace policy {
+
+DlpContentTabHelper::~DlpContentTabHelper() = default;
+
+void DlpContentTabHelper::RenderFrameCreated(
+    content::RenderFrameHost* render_frame_host) {
+  if (DlpContentManager::Get()->IsURLConfidential(
+          render_frame_host->GetLastCommittedURL())) {
+    const bool inserted = confidential_frames_.insert(render_frame_host).second;
+    if (inserted && confidential_frames_.size() == 1) {
+      DlpContentManager::Get()->OnConfidentialityChanged(web_contents(),
+                                                         /*confidential=*/true);
+    }
+  }
+}
+
+void DlpContentTabHelper::RenderFrameDeleted(
+    content::RenderFrameHost* render_frame_host) {
+  const bool erased = confidential_frames_.erase(render_frame_host);
+  if (erased && confidential_frames_.empty())
+    DlpContentManager::Get()->OnConfidentialityChanged(web_contents(),
+                                                       /*confidential=*/false);
+}
+
+void DlpContentTabHelper::DidFinishNavigation(
+    content::NavigationHandle* navigation_handle) {
+  if (!navigation_handle->HasCommitted() || navigation_handle->IsErrorPage())
+    return;
+  if (DlpContentManager::Get()->IsURLConfidential(
+          navigation_handle->GetURL())) {
+    const bool inserted =
+        confidential_frames_.insert(navigation_handle->GetRenderFrameHost())
+            .second;
+    if (inserted && confidential_frames_.size() == 1) {
+      DlpContentManager::Get()->OnConfidentialityChanged(web_contents(),
+                                                         /*confidential=*/true);
+    }
+  } else {
+    const bool erased =
+        confidential_frames_.erase(navigation_handle->GetRenderFrameHost());
+    if (erased && confidential_frames_.empty())
+      DlpContentManager::Get()->OnConfidentialityChanged(
+          web_contents(),
+          /*confidential=*/false);
+  }
+}
+
+void DlpContentTabHelper::WebContentsDestroyed() {
+  DlpContentManager::Get()->OnWebContentsDestroyed(web_contents());
+}
+
+void DlpContentTabHelper::OnVisibilityChanged(content::Visibility visibility) {
+  // DlpContentManager tracks visibility only for confidential WebContents.
+  if (!IsConfidential())
+    return;
+  DlpContentManager::Get()->OnVisibilityChanged(
+      web_contents(), visibility == content::Visibility::VISIBLE);
+}
+
+DlpContentTabHelper::DlpContentTabHelper(content::WebContents* web_contents)
+    : content::WebContentsObserver(web_contents) {}
+
+bool DlpContentTabHelper::IsConfidential() const {
+  return !confidential_frames_.empty();
+}
+
+WEB_CONTENTS_USER_DATA_KEY_IMPL(DlpContentTabHelper)
+
+}  // namespace policy
diff --git a/chrome/browser/chromeos/policy/dlp/dlp_content_tab_helper.h b/chrome/browser/chromeos/policy/dlp/dlp_content_tab_helper.h
new file mode 100644
index 0000000..bffff1f
--- /dev/null
+++ b/chrome/browser/chromeos/policy/dlp/dlp_content_tab_helper.h
@@ -0,0 +1,60 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_CHROMEOS_POLICY_DLP_DLP_CONTENT_TAB_HELPER_H_
+#define CHROME_BROWSER_CHROMEOS_POLICY_DLP_DLP_CONTENT_TAB_HELPER_H_
+
+#include "base/containers/flat_set.h"
+#include "content/public/browser/visibility.h"
+#include "content/public/browser/web_contents_observer.h"
+#include "content/public/browser/web_contents_user_data.h"
+
+namespace content {
+class NavigationHandle;
+class RenderFrameHost;
+class WebContents;
+}  // namespace content
+
+namespace policy {
+
+// DlpContentTabHelper attaches to relevant WebContents that are covered by
+// DLP (Data Leak Prevention) feature and observes navigation in all sub-frames
+// as well as visibility of the WebContents and reports it to system-wide
+// DlpContentManager.
+// WebContents is considered as confidential if either the main frame or any
+// of sub-frames are confidential according to the current policy.
+class DlpContentTabHelper
+    : public content::WebContentsUserData<DlpContentTabHelper>,
+      public content::WebContentsObserver {
+ public:
+  ~DlpContentTabHelper() override;
+
+  // content::WebContentsObserver:
+  void RenderFrameCreated(content::RenderFrameHost* render_frame_host) override;
+  void RenderFrameDeleted(content::RenderFrameHost* render_frame_host) override;
+  void DidFinishNavigation(
+      content::NavigationHandle* navigation_handle) override;
+  void WebContentsDestroyed() override;
+  void OnVisibilityChanged(content::Visibility visibility) override;
+
+ private:
+  friend class content::WebContentsUserData<DlpContentTabHelper>;
+
+  explicit DlpContentTabHelper(content::WebContents* web_contents);
+  DlpContentTabHelper(const DlpContentTabHelper&) = delete;
+  DlpContentTabHelper& operator=(const DlpContentTabHelper&) = delete;
+
+  // WebContents is considered as confidential if either the main frame or any
+  // of sub-frames are confidential.
+  bool IsConfidential() const;
+
+  // Set of the currently known confidential frames.
+  base::flat_set<content::RenderFrameHost*> confidential_frames_;
+
+  WEB_CONTENTS_USER_DATA_KEY_DECL();
+};
+
+}  // namespace policy
+
+#endif  // CHROME_BROWSER_CHROMEOS_POLICY_DLP_DLP_CONTENT_TAB_HELPER_H_
diff --git a/chrome/browser/chromeos/policy/dlp/dlp_content_tab_helper_unittest.cc b/chrome/browser/chromeos/policy/dlp/dlp_content_tab_helper_unittest.cc
new file mode 100644
index 0000000..1bf4eb8
--- /dev/null
+++ b/chrome/browser/chromeos/policy/dlp/dlp_content_tab_helper_unittest.cc
@@ -0,0 +1,176 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/chromeos/policy/dlp/dlp_content_tab_helper.h"
+
+#include "chrome/browser/chromeos/policy/dlp/dlp_content_manager.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/tabs/tab_activity_simulator.h"
+#include "chrome/test/base/chrome_render_view_host_test_harness.h"
+#include "chrome/test/base/test_browser_window.h"
+#include "content/public/browser/render_frame_host.h"
+#include "content/public/test/navigation_simulator.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace policy {
+
+using testing::_;
+using testing::Return;
+
+class MockDlpContentManager : public DlpContentManager {
+ public:
+  MOCK_METHOD2(OnConfidentialityChanged, void(content::WebContents*, bool));
+  MOCK_METHOD1(OnWebContentsDestroyed, void(const content::WebContents*));
+  MOCK_CONST_METHOD1(IsURLConfidential, bool(const GURL&));
+  MOCK_METHOD2(OnVisibilityChanged, void(content::WebContents*, bool));
+};
+
+class DlpContentTabHelperTest : public ChromeRenderViewHostTestHarness {
+ protected:
+  void SetUp() override {
+    ChromeRenderViewHostTestHarness::SetUp();
+
+    DlpContentManager::SetDlpContentManagerForTesting(
+        &mock_dlp_content_manager_);
+
+    // Initialize browser.
+    params_ = std::make_unique<Browser::CreateParams>(profile(),
+                                                      /*user_gesture=*/true);
+    browser_ = CreateBrowserWithTestWindowForParams(params_.get());
+    tab_strip_model_ = browser_->tab_strip_model();
+  }
+
+  void TearDown() override {
+    tab_strip_model_->CloseAllTabs();
+    browser_.reset();
+    params_.reset();
+
+    DlpContentManager::ResetDlpContentManagerForTesting();
+
+    ChromeRenderViewHostTestHarness::TearDown();
+  }
+
+  MockDlpContentManager mock_dlp_content_manager_;
+  TabActivitySimulator tab_activity_simulator_;
+  TabStripModel* tab_strip_model_;
+  std::unique_ptr<Browser::CreateParams> params_;
+  std::unique_ptr<Browser> browser_;
+};
+
+TEST_F(DlpContentTabHelperTest, SingleNotConfidentialWebContents) {
+  GURL kUrl = GURL("https://example.com");
+  EXPECT_CALL(mock_dlp_content_manager_, IsURLConfidential(GURL()))
+      .Times(1)
+      .WillOnce(Return(false));
+  EXPECT_CALL(mock_dlp_content_manager_, IsURLConfidential(kUrl))
+      .Times(1)
+      .WillOnce(Return(false));
+  EXPECT_CALL(mock_dlp_content_manager_, OnConfidentialityChanged(_, _))
+      .Times(0);
+  EXPECT_CALL(mock_dlp_content_manager_, OnVisibilityChanged(_, _)).Times(0);
+
+  content::WebContents* web_contents =
+      tab_activity_simulator_.AddWebContentsAndNavigate(tab_strip_model_, kUrl);
+  EXPECT_NE(nullptr, DlpContentTabHelper::FromWebContents(web_contents));
+
+  EXPECT_CALL(mock_dlp_content_manager_, OnWebContentsDestroyed(_)).Times(1);
+}
+
+TEST_F(DlpContentTabHelperTest, SingleConfidentialWebContents) {
+  GURL kUrl = GURL("https://example.com");
+  EXPECT_CALL(mock_dlp_content_manager_, IsURLConfidential(GURL()))
+      .Times(1)
+      .WillOnce(Return(false));
+  EXPECT_CALL(mock_dlp_content_manager_, IsURLConfidential(kUrl))
+      .Times(1)
+      .WillOnce(Return(true));
+  EXPECT_CALL(mock_dlp_content_manager_, OnConfidentialityChanged(_, true))
+      .Times(1);
+  EXPECT_CALL(mock_dlp_content_manager_, OnVisibilityChanged(_, _)).Times(0);
+
+  content::WebContents* web_contents =
+      tab_activity_simulator_.AddWebContentsAndNavigate(tab_strip_model_, kUrl);
+  EXPECT_NE(nullptr, DlpContentTabHelper::FromWebContents(web_contents));
+
+  EXPECT_CALL(mock_dlp_content_manager_, OnConfidentialityChanged(_, false))
+      .Times(1);
+  EXPECT_CALL(mock_dlp_content_manager_, OnWebContentsDestroyed(_)).Times(1);
+}
+
+TEST_F(DlpContentTabHelperTest, TwoWebContentsVisibilityChanged) {
+  GURL kUrl1 = GURL("https://example1.com");
+  GURL kUrl2 = GURL("https://example2.com");
+  EXPECT_CALL(mock_dlp_content_manager_, IsURLConfidential(GURL()))
+      .WillRepeatedly(Return(false));
+  EXPECT_CALL(mock_dlp_content_manager_, IsURLConfidential(kUrl1))
+      .Times(1)
+      .WillOnce(Return(true));
+  EXPECT_CALL(mock_dlp_content_manager_, IsURLConfidential(kUrl2))
+      .Times(1)
+      .WillOnce(Return(false));
+  EXPECT_CALL(mock_dlp_content_manager_, OnConfidentialityChanged(_, true))
+      .Times(1);
+  EXPECT_CALL(mock_dlp_content_manager_, OnVisibilityChanged(_, _)).Times(0);
+
+  content::WebContents* web_contents1 =
+      tab_activity_simulator_.AddWebContentsAndNavigate(tab_strip_model_,
+                                                        kUrl1);
+  content::WebContents* web_contents2 =
+      tab_activity_simulator_.AddWebContentsAndNavigate(tab_strip_model_,
+                                                        kUrl2);
+  EXPECT_NE(nullptr, DlpContentTabHelper::FromWebContents(web_contents1));
+  EXPECT_NE(nullptr, DlpContentTabHelper::FromWebContents(web_contents2));
+  EXPECT_CALL(mock_dlp_content_manager_, OnVisibilityChanged(_, false))
+      .Times(1);
+
+  tab_activity_simulator_.SwitchToTabAt(tab_strip_model_, 1);
+
+  EXPECT_CALL(mock_dlp_content_manager_, OnVisibilityChanged(_, true)).Times(1);
+
+  tab_activity_simulator_.SwitchToTabAt(tab_strip_model_, 0);
+
+  EXPECT_CALL(mock_dlp_content_manager_, OnConfidentialityChanged(_, false))
+      .Times(1);
+  EXPECT_CALL(mock_dlp_content_manager_, OnWebContentsDestroyed(_)).Times(2);
+}
+
+TEST_F(DlpContentTabHelperTest, SubFrameNavigation) {
+  GURL kNonConfidentialUrl = GURL("https://example.com");
+  GURL kConfidentialUrl = GURL("https://google.com");
+  EXPECT_CALL(mock_dlp_content_manager_, IsURLConfidential(GURL()))
+      .WillRepeatedly(Return(false));
+  EXPECT_CALL(mock_dlp_content_manager_, IsURLConfidential(kNonConfidentialUrl))
+      .WillRepeatedly(Return(false));
+  EXPECT_CALL(mock_dlp_content_manager_, IsURLConfidential(kConfidentialUrl))
+      .WillRepeatedly(Return(true));
+  EXPECT_CALL(mock_dlp_content_manager_, OnConfidentialityChanged(_, _))
+      .Times(0);
+  EXPECT_CALL(mock_dlp_content_manager_, OnVisibilityChanged(_, _)).Times(0);
+
+  // Create WebContents.
+  content::WebContents* web_contents =
+      tab_activity_simulator_.AddWebContentsAndNavigate(tab_strip_model_,
+                                                        kNonConfidentialUrl);
+  EXPECT_NE(nullptr, DlpContentTabHelper::FromWebContents(web_contents));
+
+  // Add subframe and navigate to confidential URL.
+  EXPECT_CALL(mock_dlp_content_manager_, OnConfidentialityChanged(_, true))
+      .Times(1);
+  content::RenderFrameHost* subframe =
+      content::NavigationSimulator::NavigateAndCommitFromDocument(
+          kConfidentialUrl,
+          content::RenderFrameHostTester::For(web_contents->GetMainFrame())
+              ->AppendChild("child"));
+
+  // Navigate away from confidential URL.
+  EXPECT_CALL(mock_dlp_content_manager_, OnConfidentialityChanged(_, false))
+      .Times(1);
+  content::NavigationSimulator::NavigateAndCommitFromDocument(
+      kNonConfidentialUrl, subframe);
+
+  EXPECT_CALL(mock_dlp_content_manager_, OnWebContentsDestroyed(_)).Times(1);
+}
+
+}  // namespace policy
diff --git a/chrome/browser/chromeos/policy/extension_install_event_log_collector.cc b/chrome/browser/chromeos/policy/extension_install_event_log_collector.cc
index 8e68bc6..aeb18c1 100644
--- a/chrome/browser/chromeos/policy/extension_install_event_log_collector.cc
+++ b/chrome/browser/chromeos/policy/extension_install_event_log_collector.cc
@@ -153,6 +153,38 @@
   }
 }
 
+// Helper method to convert ExtensionDownloaderDelegate::Stage to the
+// DownloadingStage proto.
+em::ExtensionInstallReportLogEvent_DownloadingStage
+ConvertDownloadingStageToProto(
+    extensions::ExtensionDownloaderDelegate::Stage stage) {
+  using DownloadingStage = extensions::ExtensionDownloaderDelegate::Stage;
+  switch (stage) {
+    case DownloadingStage::PENDING:
+      return em::ExtensionInstallReportLogEvent::DOWNLOAD_PENDING;
+    case DownloadingStage::QUEUED_FOR_MANIFEST:
+      return em::ExtensionInstallReportLogEvent::QUEUED_FOR_MANIFEST;
+    case DownloadingStage::DOWNLOADING_MANIFEST:
+      return em::ExtensionInstallReportLogEvent::DOWNLOADING_MANIFEST;
+    case DownloadingStage::DOWNLOADING_MANIFEST_RETRY:
+      return em::ExtensionInstallReportLogEvent::DOWNLOADING_MANIFEST_RETRY;
+    case DownloadingStage::PARSING_MANIFEST:
+      return em::ExtensionInstallReportLogEvent::PARSING_MANIFEST;
+    case DownloadingStage::MANIFEST_LOADED:
+      return em::ExtensionInstallReportLogEvent::MANIFEST_LOADED;
+    case DownloadingStage::QUEUED_FOR_CRX:
+      return em::ExtensionInstallReportLogEvent::QUEUED_FOR_CRX;
+    case DownloadingStage::DOWNLOADING_CRX:
+      return em::ExtensionInstallReportLogEvent::DOWNLOADING_CRX;
+    case DownloadingStage::DOWNLOADING_CRX_RETRY:
+      return em::ExtensionInstallReportLogEvent::DOWNLOADING_CRX_RETRY;
+    case DownloadingStage::FINISHED:
+      return em::ExtensionInstallReportLogEvent::FINISHED;
+    default:
+      NOTREACHED();
+  }
+}
+
 }  // namespace
 
 ExtensionInstallEventLogCollector::ExtensionInstallEventLogCollector(
@@ -248,6 +280,16 @@
   delegate_->Add(id, true /* gather_disk_space_info */, std::move(event));
 }
 
+void ExtensionInstallEventLogCollector::OnExtensionDownloadingStageChanged(
+    const extensions::ExtensionId& id,
+    extensions::ExtensionDownloaderDelegate::Stage stage) {
+  if (!delegate_->IsExtensionPending(id))
+    return;
+  auto event = std::make_unique<em::ExtensionInstallReportLogEvent>();
+  event->set_downloading_stage(ConvertDownloadingStageToProto(stage));
+  delegate_->Add(id, true /* gather_disk_space_info */, std::move(event));
+}
+
 void ExtensionInstallEventLogCollector::OnExtensionLoaded(
     content::BrowserContext* browser_context,
     const extensions::Extension* extension) {
diff --git a/chrome/browser/chromeos/policy/extension_install_event_log_collector.h b/chrome/browser/chromeos/policy/extension_install_event_log_collector.h
index 4428878..8676121 100644
--- a/chrome/browser/chromeos/policy/extension_install_event_log_collector.h
+++ b/chrome/browser/chromeos/policy/extension_install_event_log_collector.h
@@ -100,6 +100,9 @@
   void OnExtensionInstallationStageChanged(
       const extensions::ExtensionId& id,
       extensions::InstallStageTracker::Stage stage) override;
+  void OnExtensionDownloadingStageChanged(
+      const extensions::ExtensionId& id,
+      extensions::ExtensionDownloaderDelegate::Stage stage) override;
 
   // Reports success events for the extensions which are requested from policy
   // and are already loaded.
diff --git a/chrome/browser/chromeos/policy/extension_install_event_log_collector_unittest.cc b/chrome/browser/chromeos/policy/extension_install_event_log_collector_unittest.cc
index da44860..771155c 100644
--- a/chrome/browser/chromeos/policy/extension_install_event_log_collector_unittest.cc
+++ b/chrome/browser/chromeos/policy/extension_install_event_log_collector_unittest.cc
@@ -420,4 +420,42 @@
                                            0 /*expected_add_all_count*/));
 }
 
+// Verifies that a new event is created when the downloading stage is changed
+// during the downloading process.
+TEST_F(ExtensionInstallEventLogCollectorTest, DownloadingStageChanged) {
+  std::unique_ptr<ExtensionInstallEventLogCollector> collector =
+      std::make_unique<ExtensionInstallEventLogCollector>(
+          registry(), delegate(), profile());
+
+  auto ext = extensions::ExtensionBuilder(kExtensionName1)
+                 .SetID(kExtensionId1)
+                 .Build();
+  collector->OnExtensionDownloadingStageChanged(
+      kExtensionId1, extensions::ExtensionDownloaderDelegate::Stage::PENDING);
+  ASSERT_TRUE(VerifyEventAddedSuccessfully(1 /*expected_add_count*/,
+                                           0 /*expected_add_all_count*/));
+  EXPECT_EQ(em::ExtensionInstallReportLogEvent::DOWNLOAD_PENDING,
+            delegate()->last_request().event.downloading_stage());
+  collector->OnExtensionDownloadingStageChanged(
+      kExtensionId1,
+      extensions::ExtensionDownloaderDelegate::Stage::DOWNLOADING_MANIFEST);
+  ASSERT_TRUE(VerifyEventAddedSuccessfully(2 /*expected_add_count*/,
+                                           0 /*expected_add_all_count*/));
+  EXPECT_EQ(em::ExtensionInstallReportLogEvent::DOWNLOADING_MANIFEST,
+            delegate()->last_request().event.downloading_stage());
+  collector->OnExtensionDownloadingStageChanged(
+      kExtensionId1,
+      extensions::ExtensionDownloaderDelegate::Stage::DOWNLOADING_CRX);
+  ASSERT_TRUE(VerifyEventAddedSuccessfully(3 /*expected_add_count*/,
+                                           0 /*expected_add_all_count*/));
+  EXPECT_EQ(em::ExtensionInstallReportLogEvent::DOWNLOADING_CRX,
+            delegate()->last_request().event.downloading_stage());
+  collector->OnExtensionDownloadingStageChanged(
+      kExtensionId1, extensions::ExtensionDownloaderDelegate::Stage::FINISHED);
+  ASSERT_TRUE(VerifyEventAddedSuccessfully(4 /*expected_add_count*/,
+                                           0 /*expected_add_all_count*/));
+  EXPECT_EQ(em::ExtensionInstallReportLogEvent::FINISHED,
+            delegate()->last_request().event.downloading_stage());
+}
+
 }  // namespace policy
diff --git a/chrome/browser/chromeos/policy/install_event_log_util.cc b/chrome/browser/chromeos/policy/install_event_log_util.cc
index 89465c7..f321b2f 100644
--- a/chrome/browser/chromeos/policy/install_event_log_util.cc
+++ b/chrome/browser/chromeos/policy/install_event_log_util.cc
@@ -41,6 +41,7 @@
 // Chrome Reporting API.
 constexpr char kExtensionId[] = "extensionId";
 constexpr char kExtensionInstallEvent[] = "extensionAppInstallEvent";
+constexpr char kDownloadStage[] = "downloadStage";
 
 // Calculates hash for the given |event| and |context|, and stores the hash in
 // |hash|. Returns true if |event| and |context| are json serializable and
@@ -159,6 +160,11 @@
         extension_install_report_log_event.session_state_change_type());
   }
 
+  if (extension_install_report_log_event.has_downloading_stage()) {
+    event.SetIntKey(kDownloadStage,
+                    extension_install_report_log_event.downloading_stage());
+  }
+
   event.SetStringKey(kSerialNumber, GetSerialNumber());
 
   base::Value wrapper(base::Value::Type::DICTIONARY);
diff --git a/chrome/browser/chromeos/policy/minimum_version_policy_handler.cc b/chrome/browser/chromeos/policy/minimum_version_policy_handler.cc
index 7a443bb..6c8b48d 100644
--- a/chrome/browser/chromeos/policy/minimum_version_policy_handler.cc
+++ b/chrome/browser/chromeos/policy/minimum_version_policy_handler.cc
@@ -102,10 +102,13 @@
 
 }  // namespace
 
+const char MinimumVersionPolicyHandler::kRequirements[] = "requirements";
 const char MinimumVersionPolicyHandler::kChromeOsVersion[] = "chromeos_version";
 const char MinimumVersionPolicyHandler::kWarningPeriod[] = "warning_period";
 const char MinimumVersionPolicyHandler::kEolWarningPeriod[] =
     "aue_warning_period";
+const char MinimumVersionPolicyHandler::kUnmanagedUserRestricted[] =
+    "unmanaged_user_restricted";
 
 MinimumVersionRequirement::MinimumVersionRequirement(
     const base::Version version,
@@ -218,16 +221,23 @@
     return;
   }
 
-  const base::ListValue* entries;
-  std::vector<std::unique_ptr<MinimumVersionRequirement>> configs;
-  if (!cros_settings_->GetList(chromeos::kDeviceMinimumVersion, &entries) ||
-      !entries->GetSize()) {
-    // Reset state and hide update required screen if policy is not set or set
-    // to empty list.
+  const base::DictionaryValue* policy_value;
+  if (!cros_settings_->GetDictionary(chromeos::kDeviceMinimumVersion,
+                                     &policy_value)) {
+    VLOG(1) << "Revoke policy - policy is unset or value is incorrect.";
     HandleUpdateNotRequired();
     return;
   }
+  const base::Value* entries = policy_value->FindListKey(kRequirements);
+  if (!entries || entries->GetList().empty()) {
+    VLOG(1) << "Revoke policy - empty policy requirements.";
+    HandleUpdateNotRequired();
+    return;
+  }
+  auto restricted = policy_value->FindBoolKey(kUnmanagedUserRestricted);
+  unmanaged_user_restricted_ = restricted.value_or(false);
 
+  std::vector<std::unique_ptr<MinimumVersionRequirement>> configs;
   for (const auto& item : entries->GetList()) {
     const base::DictionaryValue* dict;
     if (item.GetAsDictionary(&dict)) {
@@ -440,25 +450,29 @@
     build_state->AddObserver(this);
 }
 
-void MinimumVersionPolicyHandler::MaybeShowNotificationOnLogin() {
+base::Optional<int> MinimumVersionPolicyHandler::GetTimeRemainingInDays() {
   const base::Time now = clock_->Now();
-  // This should only be true if |update_required_deadline_timer_| expired while
+  if (!state_ || update_required_deadline_ <= now)
+    return base::nullopt;
+  base::TimeDelta time_remaining = update_required_deadline_ - now;
+  return GetDaysRounded(time_remaining);
+}
+
+void MinimumVersionPolicyHandler::MaybeShowNotificationOnLogin() {
+  // |days| could be null if |update_required_deadline_timer_| expired while
   // login was in progress, else we would have shown the update required screen
   // at startup.
-  if (update_required_deadline_ <= now)
-    return;
-
-  base::TimeDelta time_remaining = update_required_deadline_ - now;
-  int days_remaining = GetDaysRounded(time_remaining);
-  if (days_remaining <= 1)
-    MaybeShowNotification(base::TimeDelta::FromDays(days_remaining));
+  base::Optional<int> days = GetTimeRemainingInDays();
+  if (days && days.value() <= 1)
+    MaybeShowNotification(base::TimeDelta::FromDays(days.value()));
 }
 
 void MinimumVersionPolicyHandler::MaybeShowNotification(
     base::TimeDelta warning) {
   const NetworkStatus status = GetCurrentNetworkStatus();
   if ((!eol_reached_ && status == NetworkStatus::kAllowed) ||
-      !delegate_->IsUserLoggedIn() || !delegate_->IsUserManaged()) {
+      !delegate_->IsUserLoggedIn() ||
+      (!delegate_->IsUserEnterpriseManaged() && !unmanaged_user_restricted_)) {
     return;
   }
 
@@ -611,9 +625,12 @@
   if (delegate_->IsLoginSessionState() && !delegate_->IsLoginInProgress()) {
     // Show update required screen over the login screen.
     delegate_->ShowUpdateRequiredScreen();
-  } else if (delegate_->IsUserLoggedIn() && delegate_->IsUserManaged()) {
+  } else if (delegate_->IsUserLoggedIn() &&
+             (delegate_->IsUserEnterpriseManaged() ||
+              unmanaged_user_restricted_)) {
     // Terminate the current user session to show update required
-    // screen on the login screen if user is managed.
+    // screen on the login screen if the user is managed or
+    // |unmanaged_user_restricted_| is set to true.
     delegate_->RestartToLoginScreen();
   }
   // No action is required if -
diff --git a/chrome/browser/chromeos/policy/minimum_version_policy_handler.h b/chrome/browser/chromeos/policy/minimum_version_policy_handler.h
index 7b0a7bc9..3a841e6 100644
--- a/chrome/browser/chromeos/policy/minimum_version_policy_handler.h
+++ b/chrome/browser/chromeos/policy/minimum_version_policy_handler.h
@@ -24,11 +24,11 @@
 class Clock;
 class DictionaryValue;
 class Time;
-}
+}  // namespace base
 
 namespace chromeos {
 class UpdateRequiredNotification;
-}
+}  // namespace chromeos
 
 namespace policy {
 
@@ -44,9 +44,11 @@
       public chromeos::NetworkStateHandlerObserver,
       public chromeos::UpdateEngineClient::Observer {
  public:
+  static const char kRequirements[];
   static const char kChromeOsVersion[];
   static const char kWarningPeriod[];
   static const char kEolWarningPeriod[];
+  static const char kUnmanagedUserRestricted[];
 
   class Observer {
    public:
@@ -69,8 +71,8 @@
     // Checks if a user is logged in.
     virtual bool IsUserLoggedIn() const = 0;
 
-    // Checks if the user logged in is a managed user.
-    virtual bool IsUserManaged() const = 0;
+    // Checks if the user logged in is managed and not a child user.
+    virtual bool IsUserEnterpriseManaged() const = 0;
 
     // Checks if we are currently on the login screen.
     virtual bool IsLoginSessionState() const = 0;
@@ -156,13 +158,19 @@
 
   static void RegisterPrefs(PrefRegistrySimple* registry);
 
-  // Show notification on managed user login if it is the last day to deadline.
+  // Show notification on login if the user is managed or
+  // |unmanaged_user_restricted_| is set to true if it is the last day to
+  // deadline.
   void MaybeShowNotificationOnLogin();
 
   // Returns true if an update is required and the device has reached
   // End Of Life (Auto Update Expiration).
   bool IsUpdateRequiredEol() const;
 
+  // Returns the number of days to deadline if update is required and deadline
+  // has not been reached. Returns null if update is not required.
+  base::Optional<int> GetTimeRemainingInDays();
+
   // Callback used in tests and invoked after end-of-life status has been
   // fetched from the update_engine.
   void set_fetch_eol_callback_for_testing(base::OnceClosure callback) {
@@ -253,6 +261,10 @@
   // version in all the configurations.
   std::unique_ptr<MinimumVersionRequirement> state_;
 
+  // If this flag is true, unmanaged user sessions receive update required
+  // notifications and are force logged out when deadline is reached.
+  bool unmanaged_user_restricted_ = false;
+
   bool eol_reached_ = false;
 
   // If this flag is true, user should restricted to use the session by logging
diff --git a/chrome/browser/chromeos/policy/minimum_version_policy_handler_browsertest.cc b/chrome/browser/chromeos/policy/minimum_version_policy_handler_browsertest.cc
index d6afe5c..d5ca2a5d2 100644
--- a/chrome/browser/chromeos/policy/minimum_version_policy_handler_browsertest.cc
+++ b/chrome/browser/chromeos/policy/minimum_version_policy_handler_browsertest.cc
@@ -130,6 +130,9 @@
                                 int warning,
                                 int eol_warning) const;
 
+  base::Value CreatePolicyValue(base::Value requirements,
+                                bool unmanaged_user_restricted) const;
+
   void SetUpdateEngineStatus(update_engine::Operation operation);
 
  protected:
@@ -184,6 +187,17 @@
   return dict;
 }
 
+base::Value MinimumVersionPolicyTestBase::CreatePolicyValue(
+    base::Value requirements,
+    bool unmanaged_user_restricted) const {
+  base::Value dict(base::Value::Type::DICTIONARY);
+  dict.SetKey(MinimumVersionPolicyHandler::kRequirements,
+              std::move(requirements));
+  dict.SetBoolKey(MinimumVersionPolicyHandler::kUnmanagedUserRestricted,
+                  unmanaged_user_restricted);
+  return dict;
+}
+
 void MinimumVersionPolicyTestBase::SetUpdateEngineStatus(
     update_engine::Operation operation) {
   update_engine::StatusResult status;
@@ -273,21 +287,22 @@
   EXPECT_EQ(ash::LoginScreenTestApi::GetUsersCount(), 2);
   EXPECT_FALSE(ash::LoginScreenTestApi::IsOobeDialogVisible());
 
-  // Create policy value as a list of requirements.
+  // Create policy value.
   base::Value requirement_list(base::Value::Type::LIST);
-  base::Value new_version_no_warning =
-      CreateRequirement(kNewVersion, kNoWarning, kNoWarning);
-  requirement_list.Append(std::move(new_version_no_warning));
+  requirement_list.Append(
+      CreateRequirement(kNewVersion, kNoWarning, kNoWarning));
+  base::Value policy_value(CreatePolicyValue(
+      std::move(requirement_list), false /* unmanaged_user_restricted */));
 
   // Set new value for policy and check update required screen is shown on the
   // login screen.
-  SetDevicePolicyAndWaitForSettingChange(requirement_list);
+  SetDevicePolicyAndWaitForSettingChange(policy_value);
   chromeos::OobeScreenWaiter(chromeos::UpdateRequiredView::kScreenId).Wait();
   EXPECT_TRUE(ash::LoginScreenTestApi::IsOobeDialogVisible());
 
   // Revoke policy and check update required screen is hidden.
-  base::Value empty_list(base::Value::Type::LIST);
-  SetDevicePolicyAndWaitForSettingChange(empty_list);
+  base::Value empty_policy(base::Value::Type::DICTIONARY);
+  SetDevicePolicyAndWaitForSettingChange(empty_policy);
   chromeos::OobeScreenExitWaiter(chromeos::UpdateRequiredView::kScreenId)
       .Wait();
   EXPECT_FALSE(ash::LoginScreenTestApi::IsOobeDialogVisible());
@@ -297,11 +312,13 @@
   // Login the user into the session and mark as managed.
   LoginManagedUser();
 
-  // Create policy value as a list of requirements.
+  // Create policy value.
   base::Value requirement_list(base::Value::Type::LIST);
   base::Value new_version_no_warning =
       CreateRequirement(kNewVersion, kNoWarning, kNoWarning);
   requirement_list.Append(std::move(new_version_no_warning));
+  base::Value policy_value(CreatePolicyValue(
+      std::move(requirement_list), false /* unmanaged_user_restricted */));
 
   // Create waiter to observe termination notification.
   content::WindowedNotificationObserver termination_waiter(
@@ -309,7 +326,7 @@
       content::NotificationService::AllSources());
 
   // Set new value for policy and check that user is logged out of the session.
-  SetDevicePolicyAndWaitForSettingChange(requirement_list);
+  SetDevicePolicyAndWaitForSettingChange(policy_value);
   termination_waiter.Wait();
   EXPECT_TRUE(chrome::IsAttemptingShutdown());
 }
@@ -342,7 +359,10 @@
   base::Value requirement_short_warning(base::Value::Type::LIST);
   requirement_short_warning.Append(
       CreateRequirement(kNewVersion, kShortWarningInDays, kShortWarningInDays));
-  SetDevicePolicyAndWaitForSettingChange(requirement_short_warning);
+  base::Value policy_short_warning(
+      CreatePolicyValue(std::move(requirement_short_warning),
+                        false /* unmanaged_user_restricted */));
+  SetDevicePolicyAndWaitForSettingChange(policy_short_warning);
 
   // Policy handler sets the local state and starts the deadline timer.
   timer_start_time = prefs->GetTime(prefs::kUpdateRequiredTimerStartTime);
@@ -358,7 +378,10 @@
   base::Value requirement_long_warning(base::Value::Type::LIST);
   requirement_long_warning.Append(
       CreateRequirement(kNewVersion, kLongWarningInDays, kLongWarningInDays));
-  SetDevicePolicyAndWaitForSettingChange(requirement_long_warning);
+  base::Value policy_long_warning(
+      CreatePolicyValue(std::move(requirement_long_warning),
+                        false /* unmanaged_user_restricted */));
+  SetDevicePolicyAndWaitForSettingChange(policy_long_warning);
 
   // Warning time is increased but timer start time does not change.
   EXPECT_EQ(prefs->GetTime(prefs::kUpdateRequiredTimerStartTime),
@@ -372,7 +395,10 @@
   base::Value requirement_no_warning(base::Value::Type::LIST);
   requirement_no_warning.Append(
       CreateRequirement(kNewVersion, kNoWarning, kNoWarning));
-  SetDevicePolicyAndWaitForSettingChange(requirement_no_warning);
+  base::Value policy_no_warning(
+      CreatePolicyValue(std::move(requirement_no_warning),
+                        false /* unmanaged_user_restricted */));
+  SetDevicePolicyAndWaitForSettingChange(policy_no_warning);
 
   // Warning time is not reduced as policy does not allow to reduce deadline.
   EXPECT_EQ(prefs->GetTime(prefs::kUpdateRequiredTimerStartTime),
@@ -399,7 +425,10 @@
   base::Value requirement_very_long_warning(base::Value::Type::LIST);
   requirement_very_long_warning.Append(
       CreateRequirement(kNewVersion, kVeryLongWarningInDays, kNoWarning));
-  SetDevicePolicyAndWaitForSettingChange(requirement_very_long_warning);
+  base::Value policy_very_long_warning(
+      CreatePolicyValue(std::move(requirement_very_long_warning),
+                        false /* unmanaged_user_restricted */));
+  SetDevicePolicyAndWaitForSettingChange(policy_very_long_warning);
   EXPECT_EQ(prefs->GetTime(prefs::kUpdateRequiredTimerStartTime),
             timer_start_time);
   EXPECT_EQ(prefs->GetTimeDelta(prefs::kUpdateRequiredWarningPeriod),
@@ -417,7 +446,10 @@
   base::Value requirement_short_warning(base::Value::Type::LIST);
   requirement_short_warning.Append(
       CreateRequirement(kNewVersion, kShortWarningInDays, kShortWarningInDays));
-  SetDevicePolicyAndWaitForSettingChange(requirement_short_warning);
+  base::Value policy_value(
+      CreatePolicyValue(std::move(requirement_short_warning),
+                        false /* unmanaged_user_restricted */));
+  SetDevicePolicyAndWaitForSettingChange(policy_value);
 
   // Policy handler starts the deadline timer.
   EXPECT_TRUE(
@@ -460,33 +492,51 @@
   // Login the user into the session.
   LoginUnmanagedUser();
 
-  // Create policy value as a list of requirements.
+  // Create and set policy value.
   base::Value requirement_list(base::Value::Type::LIST);
-  base::Value new_version_no_warning =
-      CreateRequirement(kNewVersion, kNoWarning, kNoWarning);
-  requirement_list.Append(std::move(new_version_no_warning));
+  requirement_list.Append(
+      CreateRequirement(kNewVersion, kNoWarning, kNoWarning));
+  base::Value policy_value(CreatePolicyValue(
+      std::move(requirement_list), false /* unmanaged_user_restricted */));
 
   // Set new value for pref and check that user session is not terminated.
-  SetDevicePolicyAndWaitForSettingChange(requirement_list);
+  SetDevicePolicyAndWaitForSettingChange(policy_value);
   EXPECT_FALSE(chrome::IsAttemptingShutdown());
 }
 
+IN_PROC_BROWSER_TEST_F(MinimumVersionPolicyTest,
+                       CriticalUpdateInSessionUnmanagedUserEnabled) {
+  LoginUnmanagedUser();
+
+  // Create and set policy value.
+  base::Value requirement_list(base::Value::Type::LIST);
+  requirement_list.Append(
+      CreateRequirement(kNewVersion, kNoWarning, kNoWarning));
+  base::Value policy_value(CreatePolicyValue(
+      std::move(requirement_list), true /* unmanaged_user_restricted */));
+  SetDevicePolicyAndWaitForSettingChange(policy_value);
+
+  EXPECT_TRUE(chrome::IsAttemptingShutdown());
+}
+
 IN_PROC_BROWSER_TEST_F(MinimumVersionPolicyTest, NoNetworkNotificationClick) {
   // Login the user into the session.
   DisconectAllNetworks();
   LoginManagedUser();
 
-  // Create policy value as a list of requirements.
+  // Create policy value.
   base::Value requirement_list(base::Value::Type::LIST);
   requirement_list.Append(
       CreateRequirement(kNewVersion, kShortWarningInDays, kShortWarningInDays));
+  base::Value policy_value(CreatePolicyValue(
+      std::move(requirement_list), false /* unmanaged_user_restricted */));
 
   EXPECT_FALSE(
       display_service_tester_->GetNotification(kUpdateRequiredNotificationId));
   EXPECT_FALSE(tray_test_api_->IsTrayBubbleOpen());
 
   // Set new policy value and check that update required notification is shown.
-  SetDevicePolicyAndWaitForSettingChange(requirement_list);
+  SetDevicePolicyAndWaitForSettingChange(policy_value);
   EXPECT_TRUE(
       display_service_tester_->GetNotification(kUpdateRequiredNotificationId));
 
@@ -506,16 +556,18 @@
   DisconectAllNetworks();
   LoginManagedUser();
 
-  // Create policy value as a list of requirements.
+  // Create policy value.
   base::Value requirement_list(base::Value::Type::LIST);
   requirement_list.Append(
       CreateRequirement(kNewVersion, kShortWarningInDays, kShortWarningInDays));
+  base::Value policy_value(CreatePolicyValue(
+      std::move(requirement_list), false /* unmanaged_user_restricted */));
 
   EXPECT_FALSE(
       display_service_tester_->GetNotification(kUpdateRequiredNotificationId));
 
   // Set new policy value and check that update required notification is shown.
-  SetDevicePolicyAndWaitForSettingChange(requirement_list);
+  SetDevicePolicyAndWaitForSettingChange(policy_value);
   EXPECT_TRUE(
       display_service_tester_->GetNotification(kUpdateRequiredNotificationId));
 
@@ -535,11 +587,13 @@
   EXPECT_FALSE(
       display_service_tester_->GetNotification(kUpdateRequiredNotificationId));
 
-  // Create and set policy value as a list of requirements.
+  // Create and set policy value.
   base::Value requirement_list(base::Value::Type::LIST);
   requirement_list.Append(CreateRequirement(kNewVersion, kLastDayWarningInDays,
                                             kShortWarningInDays));
-  SetDevicePolicyAndWaitForSettingChange(requirement_list);
+  base::Value policy_value(CreatePolicyValue(
+      std::move(requirement_list), false /* unmanaged_user_restricted */));
+  SetDevicePolicyAndWaitForSettingChange(policy_value);
 
   // Login the user into the session and check that notification is shown.
   LoginManagedUser();
@@ -563,11 +617,13 @@
   EXPECT_FALSE(
       display_service_tester_->GetNotification(kUpdateRequiredNotificationId));
 
-  // Create and set policy value as a list of requirements.
+  // Create and set policy value.
   base::Value requirement_list(base::Value::Type::LIST);
   requirement_list.Append(CreateRequirement(kNewVersion, kLastDayWarningInDays,
                                             kShortWarningInDays));
-  SetDevicePolicyAndWaitForSettingChange(requirement_list);
+  base::Value policy_value(CreatePolicyValue(
+      std::move(requirement_list), false /* unmanaged_user_restricted */));
+  SetDevicePolicyAndWaitForSettingChange(policy_value);
 
   // Login the user into the session and check that notification is not shown
   // for unmanaged user.
@@ -576,16 +632,39 @@
       display_service_tester_->GetNotification(kUpdateRequiredNotificationId));
 }
 
+IN_PROC_BROWSER_TEST_F(MinimumVersionPolicyTest,
+                       NotificationOnUnmanagedUserEnabled) {
+  DisconectAllNetworks();
+  LoginUnmanagedUser();
+  EXPECT_FALSE(
+      display_service_tester_->GetNotification(kUpdateRequiredNotificationId));
+
+  // Create and set policy value.
+  base::Value requirement_list(base::Value::Type::LIST);
+  requirement_list.Append(
+      CreateRequirement(kNewVersion, kShortWarningInDays, kShortWarningInDays));
+  base::Value policy_value(CreatePolicyValue(
+      std::move(requirement_list), true /* unmanaged_user_restricted */));
+  SetDevicePolicyAndWaitForSettingChange(policy_value);
+
+  // Notifications should be shown to unmanaged user if it has been set in the
+  // policy.
+  EXPECT_TRUE(
+      display_service_tester_->GetNotification(kUpdateRequiredNotificationId));
+}
+
 IN_PROC_BROWSER_TEST_F(MinimumVersionPolicyTest, NotificationsOnLogin) {
   DisconectAllNetworks();
   EXPECT_FALSE(
       display_service_tester_->GetNotification(kUpdateRequiredNotificationId));
 
-  // Create policy value as a list of requirements.
+  // Create and set policy value.
   base::Value requirement_list(base::Value::Type::LIST);
   requirement_list.Append(
       CreateRequirement(kNewVersion, kShortWarningInDays, kShortWarningInDays));
-  SetDevicePolicyAndWaitForSettingChange(requirement_list);
+  base::Value policy_value(CreatePolicyValue(
+      std::move(requirement_list), false /* unmanaged_user_restricted */));
+  SetDevicePolicyAndWaitForSettingChange(policy_value);
 
   // Login the user into the session and check that notification is not shown as
   // it is not the last day to update device.
@@ -601,16 +680,18 @@
   ConnectCellularNetwork();
   LoginManagedUser();
 
-  // Create policy value as a list of requirements.
+  // Create policy value.
   base::Value requirement_list(base::Value::Type::LIST);
   requirement_list.Append(
       CreateRequirement(kNewVersion, kShortWarningInDays, kShortWarningInDays));
+  base::Value policy_value(CreatePolicyValue(
+      std::move(requirement_list), false /* unmanaged_user_restricted */));
 
   EXPECT_FALSE(
       display_service_tester_->GetNotification(kUpdateRequiredNotificationId));
 
   // Set new policy value and check that update required notification is shown.
-  SetDevicePolicyAndWaitForSettingChange(requirement_list);
+  SetDevicePolicyAndWaitForSettingChange(policy_value);
   EXPECT_TRUE(
       GetMinimumVersionPolicyHandler()->IsDeadlineTimerRunningForTesting());
   EXPECT_TRUE(
@@ -650,15 +731,17 @@
       base::DefaultClock::GetInstance()->Now() - base::TimeDelta::FromDays(1));
   LoginManagedUser();
 
-  // Create policy value as a list of requirements.
+  // Create policy value.
   base::Value requirement_list(base::Value::Type::LIST);
   requirement_list.Append(
       CreateRequirement(kNewVersion, kShortWarningInDays, kShortWarningInDays));
+  base::Value policy_value(CreatePolicyValue(
+      std::move(requirement_list), false /* unmanaged_user_restricted */));
   EXPECT_FALSE(
       display_service_tester_->GetNotification(kUpdateRequiredNotificationId));
 
   // Set new policy value and check that update required notification is shown.
-  SetDevicePolicyAndWaitForSettingChange(requirement_list);
+  SetDevicePolicyAndWaitForSettingChange(policy_value);
   EXPECT_TRUE(
       GetMinimumVersionPolicyHandler()->IsDeadlineTimerRunningForTesting());
   EXPECT_TRUE(
@@ -680,11 +763,14 @@
 IN_PROC_BROWSER_TEST_F(MinimumVersionPolicyTest, RelaunchNotificationOverride) {
   LoginManagedUser();
 
-  // Set policy value as a list of requirements.
+  // Create and set policy value.
   base::Value requirement_list(base::Value::Type::LIST);
   requirement_list.Append(
       CreateRequirement(kNewVersion, kShortWarningInDays, kShortWarningInDays));
-  SetDevicePolicyAndWaitForSettingChange(requirement_list);
+  base::Value policy_value(CreatePolicyValue(
+      std::move(requirement_list), false /* unmanaged_user_restricted */));
+  SetDevicePolicyAndWaitForSettingChange(policy_value);
+
   base::Time deadline =
       GetMinimumVersionPolicyHandler()->update_required_deadline_for_testing();
 
@@ -699,7 +785,8 @@
 
   // Revoking update required should reset the overridden the relaunch
   // notifications.
-  SetDevicePolicyAndWaitForSettingChange(base::Value(base::Value::Type::LIST));
+  SetDevicePolicyAndWaitForSettingChange(
+      base::Value(base::Value::Type::DICTIONARY));
   EXPECT_NE(upgrade_detector->GetHighAnnoyanceDeadline(), deadline);
 }
 
@@ -717,22 +804,22 @@
   chromeos::OobeScreenWaiter(chromeos::GaiaView::kScreenId).Wait();
   EXPECT_EQ(ash::LoginScreenTestApi::GetUsersCount(), 0);
 
-  // Create policy value as a list of requirements.
+  // Create and set policy value.
   base::Value requirement_list(base::Value::Type::LIST);
-  base::Value new_version_no_warning =
-      CreateRequirement(kNewVersion, kNoWarning, kNoWarning);
-  requirement_list.Append(std::move(new_version_no_warning));
+  requirement_list.Append(
+      CreateRequirement(kNewVersion, kNoWarning, kNoWarning));
+  base::Value policy_value(CreatePolicyValue(
+      std::move(requirement_list), false /* unmanaged_user_restricted */));
+  SetDevicePolicyAndWaitForSettingChange(policy_value);
 
-  // Set new value for policy and check update required screen is shown on the
-  // login screen.
-  SetDevicePolicyAndWaitForSettingChange(requirement_list);
+  // Check update required screen is shown on the login screen.
   chromeos::OobeScreenWaiter(chromeos::UpdateRequiredView::kScreenId).Wait();
   EXPECT_TRUE(ash::LoginScreenTestApi::IsOobeDialogVisible());
 
   // Revoke policy and check update required screen is hidden and gaia screen is
   // shown.
-  base::Value empty_list(base::Value::Type::LIST);
-  SetDevicePolicyAndWaitForSettingChange(empty_list);
+  base::Value empty_policy(base::Value::Type::DICTIONARY);
+  SetDevicePolicyAndWaitForSettingChange(empty_policy);
   chromeos::OobeScreenExitWaiter(chromeos::UpdateRequiredView::kScreenId)
       .Wait();
   chromeos::OobeScreenWaiter(chromeos::GaiaView::kScreenId).Wait();
@@ -746,14 +833,13 @@
   void SetUpInProcessBrowserTestFixture() override {
     MinimumVersionPolicyTestBase::SetUpInProcessBrowserTestFixture();
 
-    // Create policy value as a list of requirements.
+    // Create and set policy value.
     base::Value requirement_list(base::Value::Type::LIST);
-    base::Value new_version_no_warning =
-        CreateRequirement(kNewVersion, kNoWarning, kNoWarning);
-    requirement_list.Append(std::move(new_version_no_warning));
-
-    // Set new policy value.
-    SetAndRefreshMinimumChromeVersionPolicy(requirement_list);
+    requirement_list.Append(
+        CreateRequirement(kNewVersion, kNoWarning, kNoWarning));
+    base::Value policy_value(CreatePolicyValue(
+        std::move(requirement_list), false /* unmanaged_user_restricted */));
+    SetAndRefreshMinimumChromeVersionPolicy(policy_value);
   }
 };
 
@@ -875,11 +961,13 @@
   // MinimumVersionPolicyTestBase:
   void SetUpInProcessBrowserTestFixture() override {
     MinimumVersionPolicyTestBase::SetUpInProcessBrowserTestFixture();
-    // Create and set policy value as a list of requirements.
+    // Create and set policy value.
     base::Value requirement_list(base::Value::Type::LIST);
     requirement_list.Append(CreateRequirement(kNewVersion, kShortWarningInDays,
                                               kShortWarningInDays));
-    SetAndRefreshMinimumChromeVersionPolicy(requirement_list);
+    base::Value policy_value(CreatePolicyValue(
+        std::move(requirement_list), false /* unmanaged_user_restricted */));
+    SetAndRefreshMinimumChromeVersionPolicy(policy_value);
   }
 
  private:
diff --git a/chrome/browser/chromeos/policy/minimum_version_policy_handler_delegate_impl.cc b/chrome/browser/chromeos/policy/minimum_version_policy_handler_delegate_impl.cc
index 34db6580..d7f8943c 100644
--- a/chrome/browser/chromeos/policy/minimum_version_policy_handler_delegate_impl.cc
+++ b/chrome/browser/chromeos/policy/minimum_version_policy_handler_delegate_impl.cc
@@ -41,7 +41,7 @@
          user_manager::UserManager::Get()->IsUserLoggedIn();
 }
 
-bool MinimumVersionPolicyHandlerDelegateImpl::IsUserManaged() const {
+bool MinimumVersionPolicyHandlerDelegateImpl::IsUserEnterpriseManaged() const {
   if (!IsUserLoggedIn())
     return false;
   Profile* const profile = ProfileManager::GetPrimaryUserProfile();
@@ -50,7 +50,8 @@
   // TODO(https://crbug.com/1048607): Handle the case when |IsUserLoggedIn|
   // returns true after Auth success but |IsManaged| returns false before user
   // policy fetched.
-  return profile->GetProfilePolicyConnector()->IsManaged();
+  return profile->GetProfilePolicyConnector()->IsManaged() &&
+         !profile->IsChild();
 }
 
 bool MinimumVersionPolicyHandlerDelegateImpl::IsLoginSessionState() const {
diff --git a/chrome/browser/chromeos/policy/minimum_version_policy_handler_delegate_impl.h b/chrome/browser/chromeos/policy/minimum_version_policy_handler_delegate_impl.h
index 9653099..9dc955c 100644
--- a/chrome/browser/chromeos/policy/minimum_version_policy_handler_delegate_impl.h
+++ b/chrome/browser/chromeos/policy/minimum_version_policy_handler_delegate_impl.h
@@ -18,7 +18,7 @@
   bool IsKioskMode() const override;
   bool IsEnterpriseManaged() const override;
   bool IsUserLoggedIn() const override;
-  bool IsUserManaged() const override;
+  bool IsUserEnterpriseManaged() const override;
   bool IsLoginSessionState() const override;
   bool IsLoginInProgress() const override;
   void ShowUpdateRequiredScreen() override;
diff --git a/chrome/browser/chromeos/policy/minimum_version_policy_handler_unittest.cc b/chrome/browser/chromeos/policy/minimum_version_policy_handler_unittest.cc
index bc62f2f..1a48d45 100644
--- a/chrome/browser/chromeos/policy/minimum_version_policy_handler_unittest.cc
+++ b/chrome/browser/chromeos/policy/minimum_version_policy_handler_unittest.cc
@@ -67,7 +67,7 @@
   bool IsKioskMode() const;
   bool IsEnterpriseManaged() const;
   base::Version GetCurrentVersion() const;
-  bool IsUserManaged() const;
+  bool IsUserEnterpriseManaged() const;
   bool IsUserLoggedIn() const;
   bool IsLoginInProgress() const;
   MOCK_METHOD0(ShowUpdateRequiredScreen, void());
@@ -89,6 +89,9 @@
                                 int warning,
                                 int eol_warning) const;
 
+  base::Value CreatePolicyValue(base::Value requirements,
+                                bool unmanaged_user_restricted);
+
   void VerifyUpdateRequiredNotification(const base::string16& expected_title,
                                         const base::string16& expected_message);
 
@@ -184,7 +187,7 @@
   return true;
 }
 
-bool MinimumVersionPolicyHandlerTest::IsUserManaged() const {
+bool MinimumVersionPolicyHandlerTest::IsUserEnterpriseManaged() const {
   return user_managed_;
 }
 
@@ -222,6 +225,17 @@
   return dict;
 }
 
+base::Value MinimumVersionPolicyHandlerTest::CreatePolicyValue(
+    base::Value requirements,
+    bool unmanaged_user_restricted) {
+  base::Value dict(base::Value::Type::DICTIONARY);
+  dict.SetKey(MinimumVersionPolicyHandler::kRequirements,
+              std::move(requirements));
+  dict.SetBoolKey(MinimumVersionPolicyHandler::kUnmanagedUserRestricted,
+                  unmanaged_user_restricted);
+  return dict;
+}
+
 void MinimumVersionPolicyHandlerTest::VerifyUpdateRequiredNotification(
     const base::string16& expected_title,
     const base::string16& expected_message) {
@@ -236,6 +250,12 @@
   // No policy applied yet. Check requirements are satisfied.
   EXPECT_TRUE(GetMinimumVersionPolicyHandler()->RequirementsAreSatisfied());
   EXPECT_FALSE(GetState());
+  EXPECT_FALSE(GetMinimumVersionPolicyHandler()->GetTimeRemainingInDays());
+
+  // This is needed to wait till EOL status is fetched from the update_engine.
+  base::RunLoop run_loop;
+  GetMinimumVersionPolicyHandler()->set_fetch_eol_callback_for_testing(
+      run_loop.QuitClosure());
 
   // Create policy value as a list of requirements.
   base::Value requirement_list(base::Value::Type::LIST);
@@ -243,30 +263,34 @@
       CreateRequirement(kNewVersion, kShortWarning, kNoWarning);
   auto strongest_requirement = MinimumVersionRequirement::CreateInstanceIfValid(
       &base::Value::AsDictionaryValue(new_version_short_warning));
-  base::Value newer_version_long_warning =
-      CreateRequirement(kNewerVersion, kLongWarning, kNoWarning);
-  base::Value newest_version_no_warning =
-      CreateRequirement(kNewestVersion, kNoWarning, kNoWarning);
 
   requirement_list.Append(std::move(new_version_short_warning));
-  requirement_list.Append(std::move(newer_version_long_warning));
-  requirement_list.Append(std::move(newest_version_no_warning));
+  requirement_list.Append(
+      CreateRequirement(kNewerVersion, kLongWarning, kNoWarning));
+  requirement_list.Append(
+      CreateRequirement(kNewestVersion, kNoWarning, kNoWarning));
 
   // Set new value for pref and check that requirements are not satisfied.
   // The state in |MinimumVersionPolicyHandler| should be equal to the strongest
   // requirement as defined in the policy description.
-  SetPolicyPref(std::move(requirement_list));
+  SetPolicyPref(CreatePolicyValue(std::move(requirement_list),
+                                  false /* unmanaged_user_restricted */));
+  run_loop.Run();
 
   EXPECT_FALSE(GetMinimumVersionPolicyHandler()->RequirementsAreSatisfied());
   EXPECT_TRUE(GetState());
   EXPECT_TRUE(strongest_requirement);
   EXPECT_EQ(GetState()->Compare(strongest_requirement.get()), 0);
+  EXPECT_TRUE(GetMinimumVersionPolicyHandler()->GetTimeRemainingInDays());
+  EXPECT_EQ(GetMinimumVersionPolicyHandler()->GetTimeRemainingInDays().value(),
+            kShortWarning);
 
   // Reset the pref to empty list and verify state is reset.
   base::Value requirement_list2(base::Value::Type::LIST);
   SetPolicyPref(std::move(requirement_list2));
   EXPECT_TRUE(GetMinimumVersionPolicyHandler()->RequirementsAreSatisfied());
   EXPECT_FALSE(GetState());
+  EXPECT_FALSE(GetMinimumVersionPolicyHandler()->GetTimeRemainingInDays());
 }
 
 TEST_F(MinimumVersionPolicyHandlerTest, CriticalUpdates) {
@@ -300,7 +324,8 @@
   // Set new value for pref and check that requirements are not satisfied.
   // As the warning time is set to zero, the user should be logged out of the
   // session.
-  SetPolicyPref(std::move(requirement_list));
+  SetPolicyPref(CreatePolicyValue(std::move(requirement_list),
+                                  false /* unmanaged_user_restricted */));
   // Start the run loop to wait for EOL status fetch.
   run_loop.Run();
   EXPECT_FALSE(GetMinimumVersionPolicyHandler()->RequirementsAreSatisfied());
@@ -338,7 +363,8 @@
 
   // Set new value for pref and check that requirements are not satisfied.
   // Unmanaged user should not be logged out of the session.
-  SetPolicyPref(std::move(requirement_list));
+  SetPolicyPref(CreatePolicyValue(std::move(requirement_list),
+                                  false /* unmanaged_user_restricted */));
   // Start the run loop to wait for EOL status fetch.
   run_loop.Run();
   EXPECT_FALSE(GetMinimumVersionPolicyHandler()->RequirementsAreSatisfied());
@@ -361,7 +387,8 @@
 
   // Set new value for pref and check that requirements are still satisfied
   // as none of the requirements has version greater than current version.
-  SetPolicyPref(std::move(requirement_list));
+  SetPolicyPref(CreatePolicyValue(std::move(requirement_list),
+                                  false /* unmanaged_user_restricted */));
   EXPECT_TRUE(GetMinimumVersionPolicyHandler()->RequirementsAreSatisfied());
   EXPECT_FALSE(GetState());
 }
@@ -384,7 +411,8 @@
   base::Value requirement_list(base::Value::Type::LIST);
   requirement_list.Append(
       CreateRequirement(kNewVersion, kLongWarning, kLongWarning));
-  SetPolicyPref(std::move(requirement_list));
+  SetPolicyPref(CreatePolicyValue(std::move(requirement_list),
+                                  false /* unmanaged_user_restricted */));
 
   run_loop.Run();
   EXPECT_TRUE(
@@ -422,7 +450,8 @@
   base::Value requirement_list(base::Value::Type::LIST);
   requirement_list.Append(
       CreateRequirement(kNewVersion, kLongWarning, kLongWarning));
-  SetPolicyPref(std::move(requirement_list));
+  SetPolicyPref(CreatePolicyValue(std::move(requirement_list),
+                                  false /* unmanaged_user_restricted */));
 
   run_loop.Run();
   EXPECT_TRUE(
@@ -472,7 +501,8 @@
   base::Value requirement_list(base::Value::Type::LIST);
   requirement_list.Append(
       CreateRequirement(kNewVersion, kLongWarning, kLongWarning));
-  SetPolicyPref(std::move(requirement_list));
+  SetPolicyPref(CreatePolicyValue(std::move(requirement_list),
+                                  false /* unmanaged_user_restricted */));
   run_loop.Run();
   EXPECT_TRUE(
       GetMinimumVersionPolicyHandler()->IsDeadlineTimerRunningForTesting());
@@ -513,7 +543,8 @@
   base::Value requirement_list(base::Value::Type::LIST);
   requirement_list.Append(
       CreateRequirement(kNewVersion, kLongWarning, kLongWarning));
-  SetPolicyPref(std::move(requirement_list));
+  SetPolicyPref(CreatePolicyValue(std::move(requirement_list),
+                                  false /* unmanaged_user_restricted */));
   run_loop.Run();
   EXPECT_TRUE(
       GetMinimumVersionPolicyHandler()->IsDeadlineTimerRunningForTesting());
@@ -571,7 +602,8 @@
   base::Value requirement_list(base::Value::Type::LIST);
   requirement_list.Append(
       CreateRequirement(kNewVersion, kShortWarning, kShortWarning));
-  SetPolicyPref(std::move(requirement_list));
+  SetPolicyPref(CreatePolicyValue(std::move(requirement_list),
+                                  false /* unmanaged_user_restricted */));
   run_loop.Run();
   EXPECT_TRUE(
       GetMinimumVersionPolicyHandler()->IsDeadlineTimerRunningForTesting());
diff --git a/chrome/browser/client_hints/client_hints_browsertest.cc b/chrome/browser/client_hints/client_hints_browsertest.cc
index a8e7879..74673a05 100644
--- a/chrome/browser/client_hints/client_hints_browsertest.cc
+++ b/chrome/browser/client_hints/client_hints_browsertest.cc
@@ -300,15 +300,10 @@
   // Verify that the user is not notified that cookies or JavaScript were
   // blocked on the webpage due to the checks done by client hints.
   void VerifyContentSettingsNotNotified() const {
-    content::WebContents* web_contents =
-        browser()->tab_strip_model()->GetActiveWebContents();
-    EXPECT_FALSE(content_settings::TabSpecificContentSettings::FromWebContents(
-                     web_contents)
-                     ->IsContentBlocked(ContentSettingsType::COOKIES));
-
-    EXPECT_FALSE(content_settings::TabSpecificContentSettings::FromWebContents(
-                     web_contents)
-                     ->IsContentBlocked(ContentSettingsType::JAVASCRIPT));
+    auto* tscs = content_settings::TabSpecificContentSettings::GetForFrame(
+        browser()->tab_strip_model()->GetActiveWebContents()->GetMainFrame());
+    EXPECT_FALSE(tscs->IsContentBlocked(ContentSettingsType::COOKIES));
+    EXPECT_FALSE(tscs->IsContentBlocked(ContentSettingsType::JAVASCRIPT));
   }
 
   void SetExpectedEffectiveConnectionType(
diff --git a/chrome/browser/content_settings/content_settings_browsertest.cc b/chrome/browser/content_settings/content_settings_browsertest.cc
index 3b9c651..47b4ecb 100644
--- a/chrome/browser/content_settings/content_settings_browsertest.cc
+++ b/chrome/browser/content_settings/content_settings_browsertest.cc
@@ -75,16 +75,16 @@
 browsing_data::CannedCookieHelper* GetSiteSettingsCookieContainer(
     Browser* browser) {
   TabSpecificContentSettings* settings =
-      TabSpecificContentSettings::FromWebContents(
-          browser->tab_strip_model()->GetActiveWebContents());
+      TabSpecificContentSettings::GetForFrame(
+          browser->tab_strip_model()->GetActiveWebContents()->GetMainFrame());
   return settings->allowed_local_shared_objects().cookies();
 }
 
 browsing_data::CannedCookieHelper* GetSiteSettingsBlockedCookieContainer(
     Browser* browser) {
   TabSpecificContentSettings* settings =
-      TabSpecificContentSettings::FromWebContents(
-          browser->tab_strip_model()->GetActiveWebContents());
+      TabSpecificContentSettings::GetForFrame(
+          browser->tab_strip_model()->GetActiveWebContents()->GetMainFrame());
   return settings->blocked_local_shared_objects().cookies();
 }
 
@@ -730,8 +730,9 @@
 
   ASSERT_TRUE(::testing::Mock::VerifyAndClearExpectations(&observer));
 
-  EXPECT_TRUE(TabSpecificContentSettings::FromWebContents(web_contents)
-                  ->IsContentBlocked(ContentSettingsType::COOKIES));
+  EXPECT_TRUE(
+      TabSpecificContentSettings::GetForFrame(web_contents->GetMainFrame())
+          ->IsContentBlocked(ContentSettingsType::COOKIES));
 }
 
 // Any cookie access during a navigation does not end up in a new document (e.g.
@@ -751,8 +752,9 @@
 
   ui_test_utils::NavigateToURL(browser(), test_url);
 
-  EXPECT_FALSE(TabSpecificContentSettings::FromWebContents(web_contents)
-                   ->IsContentBlocked(ContentSettingsType::COOKIES));
+  EXPECT_FALSE(
+      TabSpecificContentSettings::GetForFrame(web_contents->GetMainFrame())
+          ->IsContentBlocked(ContentSettingsType::COOKIES));
 }
 
 class ContentSettingsBackForwardCacheBrowserTest : public ContentSettingsTest {
@@ -789,19 +791,22 @@
       browser()->tab_strip_model()->GetActiveWebContents();
   content::RenderFrameHost* main_frame = web_contents->GetMainFrame();
 
-  EXPECT_TRUE(TabSpecificContentSettings::FromWebContents(web_contents)
-                  ->IsContentBlocked(ContentSettingsType::COOKIES));
+  EXPECT_TRUE(
+      TabSpecificContentSettings::GetForFrame(web_contents->GetMainFrame())
+          ->IsContentBlocked(ContentSettingsType::COOKIES));
 
   ui_test_utils::NavigateToURL(browser(), other_url);
   EXPECT_TRUE(main_frame->IsInBackForwardCache());
-  EXPECT_FALSE(TabSpecificContentSettings::FromWebContents(web_contents)
-                   ->IsContentBlocked(ContentSettingsType::COOKIES));
+  EXPECT_FALSE(
+      TabSpecificContentSettings::GetForFrame(web_contents->GetMainFrame())
+          ->IsContentBlocked(ContentSettingsType::COOKIES));
 
   web_contents->GetController().GoBack();
   EXPECT_TRUE(WaitForLoadStop(web_contents));
   EXPECT_EQ(main_frame, web_contents->GetMainFrame());
-  EXPECT_TRUE(TabSpecificContentSettings::FromWebContents(web_contents)
-                  ->IsContentBlocked(ContentSettingsType::COOKIES));
+  EXPECT_TRUE(
+      TabSpecificContentSettings::GetForFrame(web_contents->GetMainFrame())
+          ->IsContentBlocked(ContentSettingsType::COOKIES));
 }
 
 IN_PROC_BROWSER_TEST_F(ContentSettingsBackForwardCacheBrowserTest,
@@ -817,12 +822,14 @@
       ->SetDefaultCookieSetting(CONTENT_SETTING_BLOCK);
 
   ui_test_utils::NavigateToURL(browser(), test_url);
-  EXPECT_TRUE(TabSpecificContentSettings::FromWebContents(web_contents)
-                  ->IsContentBlocked(ContentSettingsType::COOKIES));
+  EXPECT_TRUE(
+      TabSpecificContentSettings::GetForFrame(web_contents->GetMainFrame())
+          ->IsContentBlocked(ContentSettingsType::COOKIES));
 
   ui_test_utils::NavigateToURL(browser(), other_url);
-  EXPECT_FALSE(TabSpecificContentSettings::FromWebContents(web_contents)
-                   ->IsContentBlocked(ContentSettingsType::COOKIES));
+  EXPECT_FALSE(
+      TabSpecificContentSettings::GetForFrame(web_contents->GetMainFrame())
+          ->IsContentBlocked(ContentSettingsType::COOKIES));
 
   // This triggers a OnContentSettingChanged notification that should be
   // processed by the page in the cache.
@@ -831,8 +838,9 @@
 
   web_contents->GetController().GoBack();
   EXPECT_TRUE(WaitForLoadStop(web_contents));
-  EXPECT_FALSE(TabSpecificContentSettings::FromWebContents(web_contents)
-                   ->IsContentBlocked(ContentSettingsType::COOKIES));
+  EXPECT_FALSE(
+      TabSpecificContentSettings::GetForFrame(web_contents->GetMainFrame())
+          ->IsContentBlocked(ContentSettingsType::COOKIES));
 }
 
 // TODO(jww): This should be removed after strict secure cookies is enabled for
@@ -887,8 +895,9 @@
       browser()->tab_strip_model()->GetActiveWebContents();
   ASSERT_EQ(base::UTF8ToUTF16("Data URL"), web_contents->GetTitle());
 
-  EXPECT_TRUE(TabSpecificContentSettings::FromWebContents(web_contents)
-                  ->IsContentBlocked(ContentSettingsType::JAVASCRIPT));
+  EXPECT_TRUE(
+      TabSpecificContentSettings::GetForFrame(web_contents->GetMainFrame())
+          ->IsContentBlocked(ContentSettingsType::JAVASCRIPT));
 }
 
 // Tests that if redirect across origins occurs, the new process still gets the
@@ -912,8 +921,9 @@
   content::WebContents* web_contents =
       browser()->tab_strip_model()->GetActiveWebContents();
 
-  EXPECT_TRUE(TabSpecificContentSettings::FromWebContents(web_contents)
-                  ->IsContentBlocked(ContentSettingsType::COOKIES));
+  EXPECT_TRUE(
+      TabSpecificContentSettings::GetForFrame(web_contents->GetMainFrame())
+          ->IsContentBlocked(ContentSettingsType::COOKIES));
 }
 
 class ContentSettingsWorkerModulesBrowserTest : public ContentSettingsTest {
@@ -1051,8 +1061,9 @@
   // The import must be blocked.
   ui_test_utils::WaitForViewVisibility(
       browser(), VIEW_ID_CONTENT_SETTING_JAVASCRIPT, true);
-  EXPECT_TRUE(TabSpecificContentSettings::FromWebContents(web_contents)
-                  ->IsContentBlocked(ContentSettingsType::JAVASCRIPT));
+  EXPECT_TRUE(
+      TabSpecificContentSettings::GetForFrame(web_contents->GetMainFrame())
+          ->IsContentBlocked(ContentSettingsType::JAVASCRIPT));
   EXPECT_EQ(expected_title, title_watcher.WaitAndGetTitle());
 }
 
@@ -1200,7 +1211,8 @@
     ui_test_utils::NavigateToURL(browser(), url);
 
     EXPECT_EQ(expected_title, title_watcher.WaitAndGetTitle());
-    auto* tscs = TabSpecificContentSettings::FromWebContents(web_contents);
+    auto* tscs =
+        TabSpecificContentSettings::GetForFrame(web_contents->GetMainFrame());
     EXPECT_EQ(!expect_loaded,
               tscs && tscs->IsContentBlocked(ContentSettingsType::PLUGINS));
   }
@@ -1242,7 +1254,7 @@
     }
 
     TabSpecificContentSettings* tab_settings =
-        TabSpecificContentSettings::FromWebContents(web_contents);
+        TabSpecificContentSettings::GetForFrame(web_contents->GetMainFrame());
     EXPECT_EQ(expect_is_javascript_content_blocked,
               tab_settings->IsContentBlocked(ContentSettingsType::JAVASCRIPT));
     EXPECT_FALSE(tab_settings->IsContentBlocked(ContentSettingsType::PLUGINS));
diff --git a/chrome/browser/content_settings/sound_content_setting_observer.cc b/chrome/browser/content_settings/sound_content_setting_observer.cc
index e7db3b9..a8e905b 100644
--- a/chrome/browser/content_settings/sound_content_setting_observer.cc
+++ b/chrome/browser/content_settings/sound_content_setting_observer.cc
@@ -158,9 +158,12 @@
 void SoundContentSettingObserver::CheckSoundBlocked(bool is_audible) {
   if (is_audible && GetCurrentContentSetting() == CONTENT_SETTING_BLOCK) {
     // The tab has tried to play sound, but was muted.
+    // This is a page level event so it is OK to get the main frame here.
+    // TODO(https://crbug.com/1103176): We should figure a way of not having to
+    // use GetMainFrame here. (pass the source frame somehow)
     content_settings::TabSpecificContentSettings* settings =
-        content_settings::TabSpecificContentSettings::FromWebContents(
-            web_contents());
+        content_settings::TabSpecificContentSettings::GetForFrame(
+            web_contents()->GetMainFrame());
     if (settings)
       settings->OnAudioBlocked();
 
diff --git a/chrome/browser/content_settings/tab_specific_content_settings_delegate.cc b/chrome/browser/content_settings/tab_specific_content_settings_delegate.cc
index c2f8bcc..b4afc9c 100644
--- a/chrome/browser/content_settings/tab_specific_content_settings_delegate.cc
+++ b/chrome/browser/content_settings/tab_specific_content_settings_delegate.cc
@@ -43,12 +43,8 @@
 TabSpecificContentSettingsDelegate*
 TabSpecificContentSettingsDelegate::FromWebContents(
     content::WebContents* web_contents) {
-  auto* content_settings =
-      TabSpecificContentSettings::FromWebContents(web_contents);
-  if (!content_settings)
-    return nullptr;
   return static_cast<TabSpecificContentSettingsDelegate*>(
-      content_settings->delegate());
+      TabSpecificContentSettings::GetDelegateForWebContents(web_contents));
 }
 
 void TabSpecificContentSettingsDelegate::UpdateLocationBar() {
diff --git a/chrome/browser/engagement/site_engagement_helper.cc b/chrome/browser/engagement/site_engagement_helper.cc
index 655294c..68204cbc 100644
--- a/chrome/browser/engagement/site_engagement_helper.cc
+++ b/chrome/browser/engagement/site_engagement_helper.cc
@@ -10,6 +10,7 @@
 #include "base/stl_util.h"
 #include "base/time/time.h"
 #include "base/trace_event/trace_event.h"
+#include "chrome/browser/prerender/chrome_prerender_contents_delegate.h"
 #include "chrome/browser/prerender/prerender_contents.h"
 #include "chrome/browser/profiles/profile.h"
 #include "content/public/browser/navigation_handle.h"
@@ -232,7 +233,8 @@
   //
   // Prerenders trigger WasShown() when they are swapped in, so input engagement
   // will activate even if navigation engagement is not scored.
-  if (prerender::PrerenderContents::FromWebContents(web_contents()) != nullptr)
+  if (prerender::ChromePrerenderContentsDelegate::FromWebContents(
+          web_contents()) != nullptr)
     return;
 
   service_->HandleNavigation(web_contents(), handle->GetPageTransition());
diff --git a/chrome/browser/extensions/DEPS b/chrome/browser/extensions/DEPS
index cb789af..2c31981 100644
--- a/chrome/browser/extensions/DEPS
+++ b/chrome/browser/extensions/DEPS
@@ -1,6 +1,6 @@
 include_rules = [
   "+extensions/strings/grit/extensions_strings.h",
-  "+services/network",
+  "+services/network/public",
   "+third_party/ink/grit",
   "+ui/base",
 
@@ -23,12 +23,30 @@
   "extension_protocols_unittest\.cc": [
     "+services/network/test",
   ],
+  "extension_gcm_app_handler_unittest\.cc": [
+    "+services/network/test",
+  ],
   "zipfile_installer_unittest.cc": [
     "+services/data_decoder",
   ],
   "test_extension_system.cc": [
     "+services/data_decoder",
   ],
+  "updater/extension_updater_unittest\.cc": [
+    "+services/network/test",
+  ],
+
+  "chrome_extension_cookies\.h": [
+    # TODO(crbug.com/1049894): Remove.
+    "+services/network/cookie_settings.h",
+  ],
+
+  "chrome_extension_cookies\.cc": [
+    # TODO(crbug.com/1049894): Remove.
+    "+services/network/cookie_manager.h",
+    "+services/network/restricted_cookie_manager.h",
+  ],
+
   "webstore_private_apitest.cc" : [
     # TODO(crbug/1095814): Remove this layering violation.
     "+chrome/browser/ui/views/supervised_user/parent_permission_dialog_view.h",
diff --git a/chrome/browser/extensions/api/DEPS b/chrome/browser/extensions/api/DEPS
index 5dc13a5..5b31a7ff 100644
--- a/chrome/browser/extensions/api/DEPS
+++ b/chrome/browser/extensions/api/DEPS
@@ -5,7 +5,6 @@
    # Enable remote assistance on Chrome OS
   "+remoting/base",
   "+remoting/host",
-  "+services/network",
 ]
 
 specific_include_rules = {
@@ -13,4 +12,13 @@
     "+chrome/browser/ui/views/frame",
     "+components/captive_portal",
   ],
+  "tls_socket_unittest\.cc": [
+    "+services/network/network_context.h",
+  ],
+  "tcp_socket_unittest\.cc": [
+    "+services/network/network_context.h",
+  ],
+  "udp_socket_unittest\.cc": [
+    "+services/network/network_context.h",
+  ],
 }
diff --git a/chrome/browser/extensions/api/streams_private/streams_private_api.cc b/chrome/browser/extensions/api/streams_private/streams_private_api.cc
index 70a275c8..4b26fec 100644
--- a/chrome/browser/extensions/api/streams_private/streams_private_api.cc
+++ b/chrome/browser/extensions/api/streams_private/streams_private_api.cc
@@ -7,6 +7,7 @@
 #include <utility>
 
 #include "chrome/browser/extensions/extension_tab_util.h"
+#include "chrome/browser/prerender/chrome_prerender_contents_delegate.h"
 #include "chrome/browser/prerender/prerender_contents.h"
 #include "components/sessions/core/session_id.h"
 #include "content/public/browser/browser_thread.h"
@@ -45,7 +46,7 @@
   // continue. This is because plugins cancel prerender, see
   // http://crbug.com/343590.
   prerender::PrerenderContents* prerender_contents =
-      prerender::PrerenderContents::FromWebContents(web_contents);
+      prerender::ChromePrerenderContentsDelegate::FromWebContents(web_contents);
   if (prerender_contents) {
     prerender_contents->Destroy(prerender::FINAL_STATUS_DOWNLOAD);
     return;
diff --git a/chrome/browser/extensions/forced_extensions/install_stage_tracker.cc b/chrome/browser/extensions/forced_extensions/install_stage_tracker.cc
index c50a36c07..437367bc 100644
--- a/chrome/browser/extensions/forced_extensions/install_stage_tracker.cc
+++ b/chrome/browser/extensions/forced_extensions/install_stage_tracker.cc
@@ -152,7 +152,9 @@
     data.download_CRX_started_time = base::Time::Now();
   else if (stage == ExtensionDownloaderDelegate::Stage::FINISHED)
     data.download_CRX_finish_time = base::Time::Now();
+
   for (auto& observer : observers_) {
+    observer.OnExtensionDownloadingStageChanged(id, stage);
     observer.OnExtensionDataChangedForTesting(id, browser_context_, data);
   }
 }
diff --git a/chrome/browser/extensions/forced_extensions/install_stage_tracker.h b/chrome/browser/extensions/forced_extensions/install_stage_tracker.h
index 178f8fa..48e5c06 100644
--- a/chrome/browser/extensions/forced_extensions/install_stage_tracker.h
+++ b/chrome/browser/extensions/forced_extensions/install_stage_tracker.h
@@ -333,6 +333,11 @@
     // Called when installation stage of extension is updated.
     virtual void OnExtensionInstallationStageChanged(const ExtensionId& id,
                                                      Stage stage) {}
+
+    // Called when downloading stage of extension is updated.
+    virtual void OnExtensionDownloadingStageChanged(
+        const ExtensionId& id,
+        ExtensionDownloaderDelegate::Stage stage) {}
   };
 
   explicit InstallStageTracker(const content::BrowserContext* context);
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index 21dfb2a..dcef6f4 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -3834,6 +3834,11 @@
     "expiry_milestone": 90
   },
   {
+    "name": "scroll-to-text-ios",
+    "owners": [ "tmartino" ],
+    "expiry_milestone": 90
+  },
+  {
     "name": "scroll-unification",
     "owners": [ "bokan@chromium.org", "input-dev@chromium.org" ],
     "expiry_milestone": 90
diff --git a/chrome/browser/geolocation/geolocation_permission_context_delegate_unittest.cc b/chrome/browser/geolocation/geolocation_permission_context_delegate_unittest.cc
index 4d99d806..d09a3a6 100644
--- a/chrome/browser/geolocation/geolocation_permission_context_delegate_unittest.cc
+++ b/chrome/browser/geolocation/geolocation_permission_context_delegate_unittest.cc
@@ -77,8 +77,8 @@
   void CheckTabContentsState(const GURL& requesting_frame,
                              ContentSetting expected_content_setting) {
     content_settings::TabSpecificContentSettings* content_settings =
-        content_settings::TabSpecificContentSettings::FromWebContents(
-            web_contents());
+        content_settings::TabSpecificContentSettings::GetForFrame(
+            web_contents()->GetMainFrame());
     const ContentSettingsUsagesState::StateMap& state_map =
         content_settings->geolocation_usages_state().state_map();
     EXPECT_EQ(1U, state_map.count(requesting_frame.GetOrigin()));
diff --git a/chrome/browser/history/history_tab_helper.cc b/chrome/browser/history/history_tab_helper.cc
index 94af992..7c936dd 100644
--- a/chrome/browser/history/history_tab_helper.cc
+++ b/chrome/browser/history/history_tab_helper.cc
@@ -10,9 +10,6 @@
 #include "base/stl_util.h"
 #include "build/build_config.h"
 #include "chrome/browser/history/history_service_factory.h"
-#include "chrome/browser/prerender/prerender_contents.h"
-#include "chrome/browser/prerender/prerender_manager.h"
-#include "chrome/browser/prerender/prerender_manager_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "components/history/content/browser/history_context_helper.h"
 #include "components/history/core/browser/history_constants.h"
@@ -146,18 +143,6 @@
       web_contents()->GetLastCommittedURL(), last_committed->GetTimestamp(),
       last_committed->GetUniqueID(), navigation_handle);
 
-  prerender::PrerenderManager* prerender_manager =
-      prerender::PrerenderManagerFactory::GetForBrowserContext(
-          web_contents()->GetBrowserContext());
-  if (prerender_manager) {
-    prerender::PrerenderContents* prerender_contents =
-        prerender_manager->GetPrerenderContents(web_contents());
-    if (prerender_contents) {
-      prerender_contents->DidNavigate(add_page_args);
-      return;
-    }
-  }
-
 #if defined(OS_ANDROID)
   auto* background_tab_manager = BackgroundTabManager::GetInstance();
   if (background_tab_manager->IsBackgroundTab(web_contents())) {
diff --git a/chrome/browser/lookalikes/lookalike_url_navigation_throttle.cc b/chrome/browser/lookalikes/lookalike_url_navigation_throttle.cc
index 1541e85..2f4db69 100644
--- a/chrome/browser/lookalikes/lookalike_url_navigation_throttle.cc
+++ b/chrome/browser/lookalikes/lookalike_url_navigation_throttle.cc
@@ -22,6 +22,7 @@
 #include "chrome/browser/lookalikes/lookalike_url_controller_client.h"
 #include "chrome/browser/lookalikes/lookalike_url_service.h"
 #include "chrome/browser/lookalikes/lookalike_url_tab_storage.h"
+#include "chrome/browser/prerender/chrome_prerender_contents_delegate.h"
 #include "chrome/browser/prerender/prerender_contents.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/reputation/safety_tips_config.h"
@@ -255,7 +256,8 @@
     content::NavigationHandle* navigation_handle) {
   // If the tab is being prerendered, stop here before it breaks metrics
   content::WebContents* web_contents = navigation_handle->GetWebContents();
-  if (prerender::PrerenderContents::FromWebContents(web_contents)) {
+  if (prerender::ChromePrerenderContentsDelegate::FromWebContents(
+          web_contents)) {
     return nullptr;
   }
 
diff --git a/chrome/browser/media/media_engagement_service.cc b/chrome/browser/media/media_engagement_service.cc
index 62d8688..bf12369 100644
--- a/chrome/browser/media/media_engagement_service.cc
+++ b/chrome/browser/media/media_engagement_service.cc
@@ -15,6 +15,7 @@
 #include "chrome/browser/media/media_engagement_contents_observer.h"
 #include "chrome/browser/media/media_engagement_score.h"
 #include "chrome/browser/media/media_engagement_service_factory.h"
+#include "chrome/browser/prerender/chrome_prerender_contents_delegate.h"
 #include "chrome/browser/prerender/prerender_contents.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/common/pref_names.h"
@@ -83,7 +84,7 @@
   DCHECK(IsEnabled());
 
   // Ignore WebContents that are used for prerender/prefetch.
-  if (prerender::PrerenderContents::FromWebContents(web_contents))
+  if (prerender::ChromePrerenderContentsDelegate::FromWebContents(web_contents))
     return;
 
   MediaEngagementService* service =
diff --git a/chrome/browser/media/webrtc/media_stream_capture_indicator_unittest.cc b/chrome/browser/media/webrtc/media_stream_capture_indicator_unittest.cc
index a359972..9105781a 100644
--- a/chrome/browser/media/webrtc/media_stream_capture_indicator_unittest.cc
+++ b/chrome/browser/media/webrtc/media_stream_capture_indicator_unittest.cc
@@ -10,7 +10,7 @@
 #include "content/public/test/web_contents_tester.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/blink/public/common/tokens/portal_token.h"
+#include "third_party/blink/public/common/tokens/tokens.h"
 #include "third_party/blink/public/mojom/mediastream/media_stream.mojom-shared.h"
 
 namespace {
diff --git a/chrome/browser/media/webrtc/media_stream_devices_controller_browsertest.cc b/chrome/browser/media/webrtc/media_stream_devices_controller_browsertest.cc
index 24c7ed42..3914c71 100644
--- a/chrome/browser/media/webrtc/media_stream_devices_controller_browsertest.cc
+++ b/chrome/browser/media/webrtc/media_stream_devices_controller_browsertest.cc
@@ -68,7 +68,8 @@
   const GURL& example_url() const { return example_url_; }
 
   TabSpecificContentSettings* GetContentSettings() {
-    return TabSpecificContentSettings::FromWebContents(GetWebContents());
+    return TabSpecificContentSettings::GetForFrame(
+        GetWebContents()->GetMainFrame());
   }
 
   const std::string& example_audio_id() const { return example_audio_id_; }
diff --git a/chrome/browser/media/webrtc/permission_bubble_media_access_handler.cc b/chrome/browser/media/webrtc/permission_bubble_media_access_handler.cc
index 9f2f4cf1..883e426 100644
--- a/chrome/browser/media/webrtc/permission_bubble_media_access_handler.cc
+++ b/chrome/browser/media/webrtc/permission_bubble_media_access_handler.cc
@@ -67,9 +67,10 @@
   if (!web_contents)
     return;
 
+  // TODO(https://crbug.com/1103176): We should extract the frame from |request|
   auto* content_settings =
-      content_settings::TabSpecificContentSettings::FromWebContents(
-          web_contents);
+      content_settings::TabSpecificContentSettings::GetForFrame(
+          web_contents->GetMainFrame());
   if (!content_settings)
     return;
 
diff --git a/chrome/browser/nearby_sharing/BUILD.gn b/chrome/browser/nearby_sharing/BUILD.gn
new file mode 100644
index 0000000..2dc5a81
--- /dev/null
+++ b/chrome/browser/nearby_sharing/BUILD.gn
@@ -0,0 +1,22 @@
+# Copyright 2020 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+source_set("share_target") {
+  sources = [
+    "attachment.h",
+    "file_attachment.cc",
+    "file_attachment.h",
+    "share_target.cc",
+    "share_target.h",
+    "text_attachment.cc",
+    "text_attachment.h",
+  ]
+
+  deps = [
+    "//base",
+    "//chrome/browser/ui/webui/nearby_share:nearby_share_target_types",
+    "//chrome/services/sharing/public/mojom",
+    "//url",
+  ]
+}
diff --git a/chrome/browser/nearby_sharing/certificates/nearby_share_private_certificate.cc b/chrome/browser/nearby_sharing/certificates/nearby_share_private_certificate.cc
index 2d63936..5a6a24af 100644
--- a/chrome/browser/nearby_sharing/certificates/nearby_share_private_certificate.cc
+++ b/chrome/browser/nearby_sharing/certificates/nearby_share_private_certificate.cc
@@ -176,7 +176,35 @@
 }
 
 NearbySharePrivateCertificate::NearbySharePrivateCertificate(
-    NearbySharePrivateCertificate&&) = default;
+    const NearbySharePrivateCertificate& other) {
+  *this = other;
+}
+
+NearbySharePrivateCertificate& NearbySharePrivateCertificate::operator=(
+    const NearbySharePrivateCertificate& other) {
+  if (this == &other)
+    return *this;
+
+  visibility_ = other.visibility_;
+  not_before_ = other.not_before_;
+  not_after_ = other.not_after_;
+  key_pair_ = other.key_pair_->Copy();
+  secret_key_ = crypto::SymmetricKey::Import(
+      crypto::SymmetricKey::Algorithm::AES, other.secret_key_->key());
+  metadata_encryption_key_ = other.metadata_encryption_key_;
+  id_ = other.id_;
+  unencrypted_metadata_ = other.unencrypted_metadata_;
+  consumed_salts_ = other.consumed_salts_;
+  next_salts_for_testing_ = other.next_salts_for_testing_;
+  offset_for_testing_ = other.offset_for_testing_;
+  return *this;
+}
+
+NearbySharePrivateCertificate::NearbySharePrivateCertificate(
+    NearbySharePrivateCertificate&& other) = default;
+
+NearbySharePrivateCertificate& NearbySharePrivateCertificate::operator=(
+    NearbySharePrivateCertificate&& other) = default;
 
 NearbySharePrivateCertificate::~NearbySharePrivateCertificate() = default;
 
diff --git a/chrome/browser/nearby_sharing/certificates/nearby_share_private_certificate.h b/chrome/browser/nearby_sharing/certificates/nearby_share_private_certificate.h
index f9e8a85e..85d5d62 100644
--- a/chrome/browser/nearby_sharing/certificates/nearby_share_private_certificate.h
+++ b/chrome/browser/nearby_sharing/certificates/nearby_share_private_certificate.h
@@ -58,7 +58,12 @@
       nearbyshare::proto::EncryptedMetadata unencrypted_metadata,
       std::set<std::vector<uint8_t>> consumed_salts);
 
-  NearbySharePrivateCertificate(NearbySharePrivateCertificate&&);
+  NearbySharePrivateCertificate(const NearbySharePrivateCertificate& other);
+  NearbySharePrivateCertificate& operator=(
+      const NearbySharePrivateCertificate& other);
+  NearbySharePrivateCertificate(NearbySharePrivateCertificate&& other);
+  NearbySharePrivateCertificate& operator=(
+      NearbySharePrivateCertificate&& other);
 
   virtual ~NearbySharePrivateCertificate();
 
diff --git a/chrome/browser/nearby_sharing/fake_nearby_connection.cc b/chrome/browser/nearby_sharing/fake_nearby_connection.cc
new file mode 100644
index 0000000..93a07d3
--- /dev/null
+++ b/chrome/browser/nearby_sharing/fake_nearby_connection.cc
@@ -0,0 +1,46 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/nearby_sharing/fake_nearby_connection.h"
+
+FakeNearbyConnection::FakeNearbyConnection() = default;
+FakeNearbyConnection::~FakeNearbyConnection() = default;
+
+void FakeNearbyConnection::Read(ReadCallback callback) {
+  callback_ = std::move(callback);
+  MaybeRunCallback();
+}
+
+void FakeNearbyConnection::Write(std::vector<uint8_t> bytes,
+                                 WriteCallback callback) {
+  NOTIMPLEMENTED();
+}
+
+void FakeNearbyConnection::Close() {
+  closed_ = true;
+  if (callback_)
+    std::move(callback_).Run(base::nullopt);
+}
+
+bool FakeNearbyConnection::IsClosed() const {
+  return closed_;
+}
+
+void FakeNearbyConnection::RegisterForDisconnection(
+    base::OnceClosure callback) {
+  NOTIMPLEMENTED();
+}
+
+void FakeNearbyConnection::AppendReadableData(std::vector<uint8_t> bytes) {
+  data_.push(std::move(bytes));
+  MaybeRunCallback();
+}
+
+void FakeNearbyConnection::MaybeRunCallback() {
+  if (!callback_ || data_.empty())
+    return;
+  auto item = std::move(data_.front());
+  data_.pop();
+  std::move(callback_).Run(std::move(item));
+}
diff --git a/chrome/browser/nearby_sharing/fake_nearby_connection.h b/chrome/browser/nearby_sharing/fake_nearby_connection.h
new file mode 100644
index 0000000..9c3f188
--- /dev/null
+++ b/chrome/browser/nearby_sharing/fake_nearby_connection.h
@@ -0,0 +1,33 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_NEARBY_SHARING_FAKE_NEARBY_CONNECTION_H_
+#define CHROME_BROWSER_NEARBY_SHARING_FAKE_NEARBY_CONNECTION_H_
+
+#include <queue>
+#include <vector>
+
+#include "chrome/browser/nearby_sharing/nearby_connection.h"
+
+class FakeNearbyConnection : public NearbyConnection {
+ public:
+  FakeNearbyConnection();
+  ~FakeNearbyConnection() override;
+
+  void Read(ReadCallback callback) override;
+  void Write(std::vector<uint8_t> bytes, WriteCallback callback) override;
+  void Close() override;
+  bool IsClosed() const override;
+  void RegisterForDisconnection(base::OnceClosure callback) override;
+  void AppendReadableData(std::vector<uint8_t> bytes);
+
+ private:
+  void MaybeRunCallback();
+
+  bool closed_ = false;
+  ReadCallback callback_;
+  std::queue<std::vector<uint8_t>> data_;
+};
+
+#endif  // CHROME_BROWSER_NEARBY_SHARING_FAKE_NEARBY_CONNECTION_H_
diff --git a/chrome/browser/nearby_sharing/file_attachment.h b/chrome/browser/nearby_sharing/file_attachment.h
index 65ed319..19590c7 100644
--- a/chrome/browser/nearby_sharing/file_attachment.h
+++ b/chrome/browser/nearby_sharing/file_attachment.h
@@ -10,23 +10,13 @@
 #include "base/files/file_path.h"
 #include "base/optional.h"
 #include "chrome/browser/nearby_sharing/attachment.h"
+#include "chrome/services/sharing/public/mojom/nearby_decoder_types.mojom.h"
 
 // A single attachment to be sent by / received from a |ShareTarget|, can be
 // either a file or text.
 class FileAttachment : public Attachment {
  public:
-  // Different types are used to offer richer experiences on Receiver side,
-  // mainly for: 1. displaying notification of attachment types, 2. opening
-  // different types with different apps. Remember to update Notifications,
-  // ShareTarget, etc once more types are introduced here.
-  enum class Type {
-    kUnknown,
-    kImage,
-    kVideo,
-    kApp,
-    kAudio,
-    kMaxValue = kAudio
-  };
+  using Type = sharing::mojom::FileMetadata::Type;
 
   FileAttachment(std::string file_name,
                  Type type,
diff --git a/chrome/browser/nearby_sharing/incoming_frames_reader_unittest.cc b/chrome/browser/nearby_sharing/incoming_frames_reader_unittest.cc
index 5c0ca3a0..0a03a1a 100644
--- a/chrome/browser/nearby_sharing/incoming_frames_reader_unittest.cc
+++ b/chrome/browser/nearby_sharing/incoming_frames_reader_unittest.cc
@@ -4,15 +4,14 @@
 
 #include "chrome/browser/nearby_sharing/incoming_frames_reader.h"
 
-#include <queue>
 #include <vector>
 
 #include "base/run_loop.h"
 #include "base/test/bind_test_util.h"
 #include "base/time/time.h"
+#include "chrome/browser/nearby_sharing/fake_nearby_connection.h"
 #include "chrome/browser/nearby_sharing/mock_nearby_process_manager.h"
 #include "chrome/browser/nearby_sharing/mock_nearby_sharing_decoder.h"
-#include "chrome/browser/nearby_sharing/nearby_connection.h"
 #include "chrome/services/sharing/public/proto/wire_format.pb.h"
 #include "chrome/test/base/testing_profile.h"
 #include "content/public/test/browser_task_environment.h"
@@ -56,51 +55,6 @@
 
 }  // namespace
 
-class FakeNearbyConnection : public NearbyConnection {
- public:
-  FakeNearbyConnection() = default;
-  ~FakeNearbyConnection() override = default;
-
-  void Read(ReadCallback callback) override {
-    callback_ = std::move(callback);
-    MaybeRunCallback();
-  }
-
-  void Write(std::vector<uint8_t> bytes, WriteCallback callback) override {
-    NOTIMPLEMENTED();
-  }
-
-  void Close() override {
-    closed_ = true;
-    if (callback_)
-      std::move(callback_).Run(base::nullopt);
-  }
-
-  bool IsClosed() const override { return closed_; }
-
-  void RegisterForDisconnection(base::OnceClosure callback) override {
-    NOTIMPLEMENTED();
-  }
-
-  void AppendReadableData(std::vector<uint8_t> bytes) {
-    data_.push(std::move(bytes));
-    MaybeRunCallback();
-  }
-
- private:
-  void MaybeRunCallback() {
-    if (!callback_ || data_.empty())
-      return;
-    auto item = std::move(data_.front());
-    data_.pop();
-    std::move(callback_).Run(std::move(item));
-  }
-
-  bool closed_ = false;
-  ReadCallback callback_;
-  std::queue<std::vector<uint8_t>> data_;
-};
-
 class IncomingFramesReaderTest : public testing::Test {
  public:
   IncomingFramesReaderTest()
diff --git a/chrome/browser/nearby_sharing/nearby_notification_manager.cc b/chrome/browser/nearby_sharing/nearby_notification_manager.cc
index db2c504..baca16e 100644
--- a/chrome/browser/nearby_sharing/nearby_notification_manager.cc
+++ b/chrome/browser/nearby_sharing/nearby_notification_manager.cc
@@ -4,11 +4,11 @@
 
 #include "chrome/browser/nearby_sharing/nearby_notification_manager.h"
 
+#include "base/strings/strcat.h"
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/app/vector_icons/vector_icons.h"
 #include "chrome/browser/nearby_sharing/nearby_sharing_service.h"
 #include "chrome/browser/notifications/notification_display_service.h"
-#include "chrome/browser/notifications/notification_display_service_factory.h"
 #include "chrome/grit/generated_resources.h"
 #include "content/public/browser/browser_thread.h"
 #include "ui/base/l10n/l10n_util.h"
@@ -119,16 +119,44 @@
   return l10n_util::GetStringFUTF16(resource_id, attachments, device_name);
 }
 
+base::string16 GetSuccessNotificationTitle(const ShareTarget& share_target) {
+  int resource_id = share_target.is_incoming
+                        ? IDS_NEARBY_NOTIFICATION_RECEIVE_SUCCESS_TITLE
+                        : IDS_NEARBY_NOTIFICATION_SEND_SUCCESS_TITLE;
+  base::string16 attachments = GetAttachmentsString(share_target);
+  base::string16 device_name = base::ASCIIToUTF16(share_target.device_name);
+
+  return l10n_util::GetStringFUTF16(resource_id, attachments, device_name);
+}
+
+base::string16 GetFailureNotificationTitle(const ShareTarget& share_target) {
+  int resource_id = share_target.is_incoming
+                        ? IDS_NEARBY_NOTIFICATION_RECEIVE_FAILURE_TITLE
+                        : IDS_NEARBY_NOTIFICATION_SEND_FAILURE_TITLE;
+  base::string16 attachments = GetAttachmentsString(share_target);
+  base::string16 device_name = base::ASCIIToUTF16(share_target.device_name);
+
+  return l10n_util::GetStringFUTF16(resource_id, attachments, device_name);
+}
+
 base::string16 GetConnectionRequestNotificationMessage(
     const ShareTarget& share_target,
     const TransferMetadata& transfer_metadata) {
   base::string16 attachments = GetAttachmentsString(share_target);
   base::string16 device_name = base::ASCIIToUTF16(share_target.device_name);
-  // TODO(crbug.com/1102348): Show |transfer_metadata.token()| if present.
 
-  return l10n_util::GetStringFUTF16(
+  base::string16 message = l10n_util::GetStringFUTF16(
       IDS_NEARBY_NOTIFICATION_CONNECTION_REQUEST_MESSAGE, device_name,
       attachments);
+
+  if (transfer_metadata.token()) {
+    base::string16 token = l10n_util::GetStringFUTF16(
+        IDS_NEARBY_SECURE_CONNECTION_ID,
+        base::UTF8ToUTF16(*transfer_metadata.token()));
+    message = base::StrCat({message, base::UTF8ToUTF16("\n"), token});
+  }
+
+  return message;
 }
 
 gfx::Image GetImageFromShareTarget(const ShareTarget& share_target) {
@@ -139,10 +167,11 @@
 }  // namespace
 
 NearbyNotificationManager::NearbyNotificationManager(
-    Profile* profile,
+    NotificationDisplayService* notification_display_service,
     NearbySharingService* nearby_service)
-    : profile_(profile), nearby_service_(nearby_service) {
-  DCHECK(profile_);
+    : notification_display_service_(notification_display_service),
+      nearby_service_(nearby_service) {
+  DCHECK(notification_display_service_);
   DCHECK(nearby_service_);
   nearby_service_->RegisterReceiveSurface(
       this, NearbySharingService::ReceiveSurfaceState::kBackground);
@@ -220,7 +249,7 @@
   notification_actions.emplace_back(l10n_util::GetStringUTF16(IDS_APP_CANCEL));
   notification.set_buttons(notification_actions);
 
-  NotificationDisplayServiceFactory::GetForProfile(profile_)->Display(
+  notification_display_service_->Display(
       NotificationHandler::Type::NEARBY_SHARE, notification,
       /*metadata=*/nullptr);
 }
@@ -249,7 +278,7 @@
       l10n_util::GetStringUTF16(IDS_NEARBY_NOTIFICATION_DECLINE_ACTION));
   notification.set_buttons(notification_actions);
 
-  NotificationDisplayServiceFactory::GetForProfile(profile_)->Display(
+  notification_display_service_->Display(
       NotificationHandler::Type::NEARBY_SHARE, notification,
       /*metadata=*/nullptr);
 }
@@ -264,20 +293,38 @@
   notification.set_message(
       l10n_util::GetStringUTF16(IDS_NEARBY_NOTIFICATION_ONBOARDING_MESSAGE));
 
-  NotificationDisplayServiceFactory::GetForProfile(profile_)->Display(
+  notification_display_service_->Display(
       NotificationHandler::Type::NEARBY_SHARE, notification,
       /*metadata=*/nullptr);
 }
 
 void NearbyNotificationManager::ShowSuccess(const ShareTarget& share_target) {
-  // TODO(crbug.com/1102348): Show success notification.
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
+  message_center::Notification notification =
+      CreateNearbyNotification(kNearbyNotificationId);
+  notification.set_title(GetSuccessNotificationTitle(share_target));
+
+  // TODO(crbug.com/1102348): Show content specific actions and preview images.
+
+  notification_display_service_->Display(
+      NotificationHandler::Type::NEARBY_SHARE, notification,
+      /*metadata=*/nullptr);
 }
 
 void NearbyNotificationManager::ShowFailure(const ShareTarget& share_target) {
-  // TODO(crbug.com/1102348): Show failure notification.
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
+  message_center::Notification notification =
+      CreateNearbyNotification(kNearbyNotificationId);
+  notification.set_title(GetFailureNotificationTitle(share_target));
+
+  notification_display_service_->Display(
+      NotificationHandler::Type::NEARBY_SHARE, notification,
+      /*metadata=*/nullptr);
 }
 
 void NearbyNotificationManager::CloseTransfer() {
-  NotificationDisplayServiceFactory::GetForProfile(profile_)->Close(
-      NotificationHandler::Type::NEARBY_SHARE, kNearbyNotificationId);
+  notification_display_service_->Close(NotificationHandler::Type::NEARBY_SHARE,
+                                       kNearbyNotificationId);
 }
diff --git a/chrome/browser/nearby_sharing/nearby_notification_manager.h b/chrome/browser/nearby_sharing/nearby_notification_manager.h
index b9e7096d..adad78e 100644
--- a/chrome/browser/nearby_sharing/nearby_notification_manager.h
+++ b/chrome/browser/nearby_sharing/nearby_notification_manager.h
@@ -13,8 +13,8 @@
 #include "chrome/browser/nearby_sharing/transfer_metadata.h"
 #include "chrome/browser/nearby_sharing/transfer_update_callback.h"
 
-class Profile;
 class NearbySharingService;
+class NotificationDisplayService;
 
 // Manages notifications shown for Nearby Share. Only a single notification will
 // be shown as simultaneous connections are not supported. All methods should be
@@ -22,8 +22,9 @@
 class NearbyNotificationManager : public TransferUpdateCallback,
                                   public ShareTargetDiscoveredCallback {
  public:
-  NearbyNotificationManager(Profile* profile,
-                            NearbySharingService* nearby_service);
+  NearbyNotificationManager(
+      NotificationDisplayService* notification_display_service,
+      NearbySharingService* nearby_service);
   ~NearbyNotificationManager() override;
 
   // TransferUpdateCallback:
@@ -61,7 +62,7 @@
   void CloseTransfer();
 
  private:
-  Profile* profile_;
+  NotificationDisplayService* notification_display_service_;
   NearbySharingService* nearby_service_;
 };
 
diff --git a/chrome/browser/nearby_sharing/nearby_notification_manager_unittest.cc b/chrome/browser/nearby_sharing/nearby_notification_manager_unittest.cc
index a8f239c6..9011732 100644
--- a/chrome/browser/nearby_sharing/nearby_notification_manager_unittest.cc
+++ b/chrome/browser/nearby_sharing/nearby_notification_manager_unittest.cc
@@ -7,6 +7,7 @@
 #include <memory>
 #include <vector>
 
+#include "base/strings/strcat.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/scoped_feature_list.h"
 #include "chrome/app/vector_icons/vector_icons.h"
@@ -15,6 +16,7 @@
 #include "chrome/browser/nearby_sharing/share_target.h"
 #include "chrome/browser/nearby_sharing/transfer_metadata.h"
 #include "chrome/browser/nearby_sharing/transfer_metadata_builder.h"
+#include "chrome/browser/notifications/notification_display_service_factory.h"
 #include "chrome/browser/notifications/notification_display_service_tester.h"
 #include "chrome/grit/generated_resources.h"
 #include "chrome/test/base/testing_profile.h"
@@ -43,8 +45,7 @@
     scoped_feature_list_.InitAndEnableFeature(features::kNearbySharing);
     notification_tester_ =
         std::make_unique<NotificationDisplayServiceTester>(&profile_);
-    manager_ = std::make_unique<NearbyNotificationManager>(&profile_,
-                                                           &nearby_service_);
+    manager_ = CreateManager();
   }
   ~NearbyNotificationManagerTest() override = default;
 
@@ -55,6 +56,13 @@
         NotificationHandler::Type::NEARBY_SHARE);
   }
 
+  std::unique_ptr<NearbyNotificationManager> CreateManager() {
+    NotificationDisplayService* notification_display_service =
+        NotificationDisplayServiceFactory::GetForProfile(&profile_);
+    return std::make_unique<NearbyNotificationManager>(
+        notification_display_service, &nearby_service_);
+  }
+
  protected:
   base::test::ScopedFeatureList scoped_feature_list_;
   content::BrowserTaskEnvironment task_environment_;
@@ -64,13 +72,13 @@
   std::unique_ptr<NearbyNotificationManager> manager_;
 };
 
-struct ProgressNotificationTestParamInternal {
+struct AttachmentsTestParamInternal {
   std::vector<TextAttachment::Type> text_attachments;
   std::vector<FileAttachment::Type> file_attachments;
   int expected_resource_id;
 };
 
-ProgressNotificationTestParamInternal kProgressNotificationTestParams[] = {
+AttachmentsTestParamInternal kAttachmentsTestParams[] = {
     // No attachments.
     {{}, {}, IDS_NEARBY_UNKNOWN_ATTACHMENTS},
 
@@ -108,16 +116,17 @@
      IDS_NEARBY_FILE_ATTACHMENTS_UNKNOWN},
 };
 
-using ProgressNotificationTestParam =
-    std::tuple<ProgressNotificationTestParamInternal, bool>;
+using AttachmentsTestParam = std::tuple<AttachmentsTestParamInternal, bool>;
 
-class NearbyNotificationManagerProgressNotificationTest
+class NearbyNotificationManagerAttachmentsTest
     : public NearbyNotificationManagerTest,
-      public testing::WithParamInterface<ProgressNotificationTestParam> {};
+      public testing::WithParamInterface<AttachmentsTestParam> {};
+
+using ConnectionRequestTestParam = std::tuple<TransferMetadata::Status, bool>;
 
 class NearbyNotificationManagerConnectionRequestTest
     : public NearbyNotificationManagerTest,
-      public testing::WithParamInterface<TransferMetadata::Status> {};
+      public testing::WithParamInterface<ConnectionRequestTestParam> {};
 
 }  // namespace
 
@@ -142,8 +151,7 @@
           testing::SaveArg<0>(&send_transfer_callback),
           testing::SaveArg<1>(&send_discovery_callback),
           testing::Return(NearbySharingService::StatusCodes::kOk)));
-  manager_ =
-      std::make_unique<NearbyNotificationManager>(&profile_, &nearby_service_);
+  manager_ = CreateManager();
 
   EXPECT_EQ(manager(), receive_transfer_callback);
   EXPECT_EQ(manager(), send_transfer_callback);
@@ -221,8 +229,8 @@
   EXPECT_EQ(100.0 * progress, notification.progress());
 }
 
-TEST_P(NearbyNotificationManagerProgressNotificationTest, Test) {
-  const ProgressNotificationTestParamInternal& param = std::get<0>(GetParam());
+TEST_P(NearbyNotificationManagerAttachmentsTest, ShowProgress) {
+  const AttachmentsTestParamInternal& param = std::get<0>(GetParam());
   bool is_incoming = std::get<1>(GetParam());
 
   std::string device_name = "device";
@@ -256,21 +264,98 @@
   EXPECT_EQ(expected, notification.title());
 }
 
+TEST_P(NearbyNotificationManagerAttachmentsTest, ShowSuccess) {
+  const AttachmentsTestParamInternal& param = std::get<0>(GetParam());
+  bool is_incoming = std::get<1>(GetParam());
+
+  std::string device_name = "device";
+  ShareTarget share_target;
+  share_target.device_name = device_name;
+  share_target.is_incoming = is_incoming;
+
+  for (TextAttachment::Type type : param.text_attachments)
+    share_target.text_attachments.push_back(CreateTextAttachment(type));
+
+  for (FileAttachment::Type type : param.file_attachments)
+    share_target.file_attachments.push_back(CreateFileAttachment(type));
+
+  manager()->ShowSuccess(share_target);
+
+  int expected_resource_id = is_incoming
+                                 ? IDS_NEARBY_NOTIFICATION_RECEIVE_SUCCESS_TITLE
+                                 : IDS_NEARBY_NOTIFICATION_SEND_SUCCESS_TITLE;
+  size_t total = param.text_attachments.size() + param.file_attachments.size();
+  base::string16 expected = l10n_util::GetStringFUTF16(
+      expected_resource_id,
+      l10n_util::GetPluralStringFUTF16(param.expected_resource_id, total),
+      base::ASCIIToUTF16(device_name));
+
+  std::vector<message_center::Notification> notifications =
+      GetDisplayedNotifications();
+  ASSERT_EQ(1u, notifications.size());
+
+  const message_center::Notification& notification = notifications[0];
+  EXPECT_EQ(expected, notification.title());
+}
+
+TEST_P(NearbyNotificationManagerAttachmentsTest, ShowFailure) {
+  const AttachmentsTestParamInternal& param = std::get<0>(GetParam());
+  bool is_incoming = std::get<1>(GetParam());
+
+  std::string device_name = "device";
+  ShareTarget share_target;
+  share_target.device_name = device_name;
+  share_target.is_incoming = is_incoming;
+
+  for (TextAttachment::Type type : param.text_attachments)
+    share_target.text_attachments.push_back(CreateTextAttachment(type));
+
+  for (FileAttachment::Type type : param.file_attachments)
+    share_target.file_attachments.push_back(CreateFileAttachment(type));
+
+  manager()->ShowFailure(share_target);
+
+  int expected_resource_id = is_incoming
+                                 ? IDS_NEARBY_NOTIFICATION_RECEIVE_FAILURE_TITLE
+                                 : IDS_NEARBY_NOTIFICATION_SEND_FAILURE_TITLE;
+  size_t total = param.text_attachments.size() + param.file_attachments.size();
+  base::string16 expected = l10n_util::GetStringFUTF16(
+      expected_resource_id,
+      l10n_util::GetPluralStringFUTF16(param.expected_resource_id, total),
+      base::ASCIIToUTF16(device_name));
+
+  std::vector<message_center::Notification> notifications =
+      GetDisplayedNotifications();
+  ASSERT_EQ(1u, notifications.size());
+
+  const message_center::Notification& notification = notifications[0];
+  EXPECT_EQ(expected, notification.title());
+}
+
 INSTANTIATE_TEST_SUITE_P(
-    NearbyNotificationManagerProgressNotificationTest,
-    NearbyNotificationManagerProgressNotificationTest,
-    testing::Combine(testing::ValuesIn(kProgressNotificationTestParams),
+    NearbyNotificationManagerAttachmentsTest,
+    NearbyNotificationManagerAttachmentsTest,
+    testing::Combine(testing::ValuesIn(kAttachmentsTestParams),
                      testing::Bool()));
 
 TEST_P(NearbyNotificationManagerConnectionRequestTest,
        ShowConnectionRequest_ShowsNotification) {
+  TransferMetadata::Status status = std::get<0>(GetParam());
+  bool with_token = std::get<1>(GetParam());
+
   std::string device_name = "device";
+  std::string token = "3141";
+
   ShareTarget share_target;
   share_target.device_name = device_name;
   share_target.file_attachments.push_back(
       CreateFileAttachment(FileAttachment::Type::kImage));
-  TransferMetadata transfer_metadata =
-      TransferMetadataBuilder().set_status(GetParam()).build();
+
+  TransferMetadataBuilder transfer_metadata_builder;
+  transfer_metadata_builder.set_status(status);
+  if (with_token)
+    transfer_metadata_builder.set_token(token);
+  TransferMetadata transfer_metadata = transfer_metadata_builder.build();
 
   manager()->ShowConnectionRequest(share_target, transfer_metadata);
 
@@ -287,7 +372,13 @@
       IDS_NEARBY_NOTIFICATION_CONNECTION_REQUEST_MESSAGE,
       base::ASCIIToUTF16(device_name),
       l10n_util::GetPluralStringFUTF16(IDS_NEARBY_FILE_ATTACHMENTS_IMAGES, 1));
-  // TODO(crbug.com/1102348): Verify |transfer_metadata.token()| if present.
+
+  if (with_token) {
+    expected_message = base::StrCat(
+        {expected_message, base::UTF8ToUTF16("\n"),
+         l10n_util::GetStringFUTF16(IDS_NEARBY_SECURE_CONNECTION_ID,
+                                    base::UTF8ToUTF16(token))});
+  }
 
   EXPECT_EQ(message_center::NOTIFICATION_TYPE_SIMPLE, notification.type());
   EXPECT_EQ(expected_title, notification.title());
@@ -301,7 +392,7 @@
             notification.display_source());
 
   std::vector<base::string16> expected_button_titles;
-  if (GetParam() == TransferMetadata::Status::kAwaitingLocalConfirmation) {
+  if (status == TransferMetadata::Status::kAwaitingLocalConfirmation) {
     expected_button_titles.push_back(
         l10n_util::GetStringUTF16(IDS_NEARBY_NOTIFICATION_RECEIVE_ACTION));
   }
@@ -319,8 +410,10 @@
 INSTANTIATE_TEST_SUITE_P(
     NearbyNotificationManagerConnectionRequestTest,
     NearbyNotificationManagerConnectionRequestTest,
-    testing::Values(TransferMetadata::Status::kAwaitingLocalConfirmation,
-                    TransferMetadata::Status::kAwaitingRemoteAcceptance));
+    testing::Combine(
+        testing::Values(TransferMetadata::Status::kAwaitingLocalConfirmation,
+                        TransferMetadata::Status::kAwaitingRemoteAcceptance),
+        testing::Bool()));
 
 TEST_F(NearbyNotificationManagerTest, ShowOnboarding_ShowsNotification) {
   manager()->ShowOnboarding();
@@ -348,12 +441,44 @@
 
 TEST_F(NearbyNotificationManagerTest, ShowSuccess_ShowsNotification) {
   manager()->ShowSuccess(ShareTarget());
-  // TODO(crbug.com/1102348): Verify success notification.
+
+  std::vector<message_center::Notification> notifications =
+      GetDisplayedNotifications();
+  ASSERT_EQ(1u, notifications.size());
+
+  const message_center::Notification& notification = notifications[0];
+  EXPECT_EQ(message_center::NOTIFICATION_TYPE_SIMPLE, notification.type());
+
+  EXPECT_EQ(base::string16(), notification.message());
+  EXPECT_TRUE(notification.icon().IsEmpty());
+  EXPECT_EQ(GURL(), notification.origin_url());
+  EXPECT_FALSE(notification.never_timeout());
+  EXPECT_FALSE(notification.renotify());
+  EXPECT_EQ(&kNearbyShareIcon, &notification.vector_small_image());
+  EXPECT_EQ(l10n_util::GetStringUTF16(IDS_NEARBY_NOTIFICATION_SOURCE),
+            notification.display_source());
+  EXPECT_EQ(0u, notification.buttons().size());
 }
 
 TEST_F(NearbyNotificationManagerTest, ShowFailure_ShowsNotification) {
   manager()->ShowFailure(ShareTarget());
-  // TODO(crbug.com/1102348): Verify failure notification.
+
+  std::vector<message_center::Notification> notifications =
+      GetDisplayedNotifications();
+  ASSERT_EQ(1u, notifications.size());
+
+  const message_center::Notification& notification = notifications[0];
+  EXPECT_EQ(message_center::NOTIFICATION_TYPE_SIMPLE, notification.type());
+
+  EXPECT_EQ(base::string16(), notification.message());
+  EXPECT_TRUE(notification.icon().IsEmpty());
+  EXPECT_EQ(GURL(), notification.origin_url());
+  EXPECT_FALSE(notification.never_timeout());
+  EXPECT_FALSE(notification.renotify());
+  EXPECT_EQ(&kNearbyShareIcon, &notification.vector_small_image());
+  EXPECT_EQ(l10n_util::GetStringUTF16(IDS_NEARBY_NOTIFICATION_SOURCE),
+            notification.display_source());
+  EXPECT_EQ(0u, notification.buttons().size());
 }
 
 TEST_F(NearbyNotificationManagerTest,
diff --git a/chrome/browser/nearby_sharing/nearby_sharing_service_factory.cc b/chrome/browser/nearby_sharing/nearby_sharing_service_factory.cc
index 50830a1027..706d6e7 100644
--- a/chrome/browser/nearby_sharing/nearby_sharing_service_factory.cc
+++ b/chrome/browser/nearby_sharing/nearby_sharing_service_factory.cc
@@ -16,6 +16,7 @@
 #include "chrome/browser/nearby_sharing/nearby_connections_manager_impl.h"
 #include "chrome/browser/nearby_sharing/nearby_process_manager.h"
 #include "chrome/browser/nearby_sharing/nearby_sharing_service_impl.h"
+#include "chrome/browser/notifications/notification_display_service_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/signin/identity_manager_factory.h"
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
@@ -47,6 +48,7 @@
           kServiceName,
           BrowserContextDependencyManager::GetInstance()) {
   DependsOn(IdentityManagerFactory::GetInstance());
+  DependsOn(NotificationDisplayServiceFactory::GetInstance());
 }
 
 NearbySharingServiceFactory::~NearbySharingServiceFactory() = default;
@@ -59,15 +61,18 @@
     return nullptr;
   }
 
+  NearbyProcessManager& process_manager = NearbyProcessManager::GetInstance();
   Profile* profile = Profile::FromBrowserContext(context);
   PrefService* pref_service = profile->GetPrefs();
+  NotificationDisplayService* notification_display_service =
+      NotificationDisplayServiceFactory::GetForProfile(profile);
   auto nearby_connections_manager =
-      std::make_unique<NearbyConnectionsManagerImpl>(
-          &NearbyProcessManager::GetInstance(), profile);
+      std::make_unique<NearbyConnectionsManagerImpl>(&process_manager, profile);
 
   NS_LOG(VERBOSE) << __func__ << ": creating NearbySharingService.";
-  return new NearbySharingServiceImpl(pref_service, profile,
-                                      std::move(nearby_connections_manager));
+  return new NearbySharingServiceImpl(
+      pref_service, notification_display_service, profile,
+      std::move(nearby_connections_manager), &process_manager);
 }
 
 content::BrowserContext* NearbySharingServiceFactory::GetBrowserContextToUse(
diff --git a/chrome/browser/nearby_sharing/nearby_sharing_service_impl.cc b/chrome/browser/nearby_sharing/nearby_sharing_service_impl.cc
index 5a9dc3a8b..a5006b8 100644
--- a/chrome/browser/nearby_sharing/nearby_sharing_service_impl.cc
+++ b/chrome/browser/nearby_sharing/nearby_sharing_service_impl.cc
@@ -17,10 +17,9 @@
 #include "chrome/browser/nearby_sharing/local_device_data/nearby_share_local_device_data_manager_impl.h"
 #include "chrome/browser/nearby_sharing/logging/logging.h"
 #include "chrome/browser/nearby_sharing/nearby_connections_manager.h"
+#include "chrome/browser/nearby_sharing/transfer_metadata_builder.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/signin/identity_manager_factory.h"
-#include "chrome/browser/nearby_sharing/client/nearby_share_client_impl.h"
-#include "chrome/browser/profiles/profile.h"
 #include "chrome/services/sharing/public/cpp/advertisement.h"
 #include "chrome/services/sharing/public/mojom/nearby_connections_types.mojom.h"
 #include "components/prefs/pref_service.h"
@@ -31,6 +30,8 @@
 namespace {
 
 constexpr base::TimeDelta kReadFramesTimeout = base::TimeDelta::FromSeconds(15);
+constexpr base::TimeDelta kReadResponseFrameTimeout =
+    base::TimeDelta::FromSeconds(60);
 
 std::string ReceiveSurfaceStateToString(
     NearbySharingService::ReceiveSurfaceState state) {
@@ -123,12 +124,15 @@
 
 NearbySharingServiceImpl::NearbySharingServiceImpl(
     PrefService* prefs,
+    NotificationDisplayService* notification_display_service,
     Profile* profile,
-    std::unique_ptr<NearbyConnectionsManager> nearby_connections_manager)
+    std::unique_ptr<NearbyConnectionsManager> nearby_connections_manager,
+    NearbyProcessManager* process_manager)
     : prefs_(prefs),
       profile_(profile),
       settings_(prefs),
       nearby_connections_manager_(std::move(nearby_connections_manager)),
+      process_manager_(process_manager),
       http_client_factory_(std::make_unique<NearbyShareClientFactoryImpl>(
           IdentityManagerFactory::GetForProfile(profile),
           profile->GetURLLoaderFactory(),
@@ -144,21 +148,20 @@
   DCHECK(profile_);
   DCHECK(nearby_connections_manager_);
 
-  NearbyProcessManager& process_manager = NearbyProcessManager::GetInstance();
-  nearby_process_observer_.Add(&process_manager);
+  nearby_process_observer_.Add(process_manager_);
 
-  if (process_manager.IsActiveProfile(profile_)) {
+  if (process_manager_->IsActiveProfile(profile_)) {
     // TODO(crbug.com/1084576): Initialize NearbyConnectionsManager with
     // NearbyConnectionsMojom from |process_manager|:
-    // process_manager.GetOrStartNearbyConnections(profile_)
+    // process_manager_->GetOrStartNearbyConnections(profile_)
   }
 
   settings_.AddSettingsObserver(settings_receiver_.BindNewPipeAndPassRemote());
 
   GetBluetoothAdapter();
 
-  nearby_notification_manager_ =
-      std::make_unique<NearbyNotificationManager>(profile_, this);
+  nearby_notification_manager_ = std::make_unique<NearbyNotificationManager>(
+      notification_display_service, this);
 }
 
 NearbySharingServiceImpl::~NearbySharingServiceImpl() {
@@ -318,14 +321,12 @@
 }
 
 void NearbySharingServiceImpl::OnNearbyProcessStarted() {
-  NearbyProcessManager& process_manager = NearbyProcessManager::GetInstance();
-  if (process_manager.IsActiveProfile(profile_))
+  if (process_manager_->IsActiveProfile(profile_))
     NS_LOG(VERBOSE) << __func__ << ": Nearby process started!";
 }
 
 void NearbySharingServiceImpl::OnNearbyProcessStopped() {
-  NearbyProcessManager& process_manager = NearbyProcessManager::GetInstance();
-  if (process_manager.IsActiveProfile(profile_)) {
+  if (process_manager_->IsActiveProfile(profile_)) {
     // TODO(crbug.com/1084576): Check if process should be running and restart
     // it after a delay.
   }
@@ -336,7 +337,19 @@
     const std::vector<uint8_t>& endpoint_info,
     NearbyConnection* connection) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DCHECK(connection);
   // TODO(crbug/1085068): Handle incoming connection; use CertificateManager
+
+  // TODO(himanshujaju) - Update placeholder implementation
+  ShareTarget share_target;
+  share_target.is_incoming = true;
+
+  incoming_share_target_info_map_[share_target.id].set_connection(connection);
+  connection->RegisterForDisconnection(
+      base::BindOnce(&NearbySharingServiceImpl::UnregisterShareTarget,
+                     weak_ptr_factory_.GetWeakPtr(), share_target));
+
+  ReceiveIntroduction(std::move(share_target), /*token=*/base::nullopt);
 }
 
 void NearbySharingServiceImpl::OnEnabledChanged(bool enabled) {
@@ -671,12 +684,81 @@
 void NearbySharingServiceImpl::OnIncomingTransferUpdate(
     const ShareTarget& share_target,
     TransferMetadata metadata) {
-  // TODO(himanshujaju) - Implement.
+  if (metadata.status() != TransferMetadata::Status::kCancelled &&
+      metadata.status() != TransferMetadata::Status::kRejected) {
+    last_incoming_metadata_ = std::make_pair(share_target, metadata);
+  } else {
+    last_incoming_metadata_ = base::nullopt;
+  }
+
+  // TODO(himanshujaju) - Handle is_final_status()
+
+  base::ObserverList<TransferUpdateCallback>& transfer_callbacks =
+      foreground_receive_callbacks_.might_have_observers()
+          ? foreground_receive_callbacks_
+          : background_receive_callbacks_;
+
+  for (TransferUpdateCallback& callback : transfer_callbacks) {
+    callback.OnTransferUpdate(share_target, metadata);
+  }
+}
+
+void NearbySharingServiceImpl::WriteResponse(
+    NearbyConnection& connection,
+    sharing::nearby::ConnectionResponseFrame::Status status) {
+  sharing::nearby::Frame frame;
+  sharing::nearby::V1Frame* v1_frame = frame.mutable_v1();
+  v1_frame->mutable_connection_response()->set_status(status);
+
+  std::vector<uint8_t> data(frame.ByteSize());
+  frame.SerializeToArray(data.data(), frame.ByteSize());
+
+  connection.Write(std::move(data), base::DoNothing());
+}
+
+void NearbySharingServiceImpl::Fail(const ShareTarget& share_target,
+                                    TransferMetadata::Status status) {
+  NearbyConnection* connection = GetIncomingConnection(share_target);
+  if (!connection) {
+    NS_LOG(WARNING) << __func__ << ": Fail invoked for unknown share target.";
+    return;
+  }
+
+  // TODO(himanshujaju) - Create alarm and cancel in RegisterForDisconnection().
+
+  // Send response to remote device.
+  sharing::nearby::ConnectionResponseFrame::Status response_status;
+  switch (status) {
+    case TransferMetadata::Status::kNotEnoughSpace:
+      response_status =
+          sharing::nearby::ConnectionResponseFrame::NOT_ENOUGH_SPACE;
+      break;
+
+    case TransferMetadata::Status::kUnsupportedAttachmentType:
+      response_status =
+          sharing::nearby::ConnectionResponseFrame::UNSUPPORTED_ATTACHMENT_TYPE;
+      break;
+
+    case TransferMetadata::Status::kTimedOut:
+      response_status = sharing::nearby::ConnectionResponseFrame::TIMED_OUT;
+      break;
+
+    default:
+      response_status = sharing::nearby::ConnectionResponseFrame::UNKNOWN;
+      break;
+  }
+
+  WriteResponse(*connection, response_status);
+
+  if (incoming_share_target_info_map_.count(share_target.id)) {
+    OnIncomingTransferUpdate(
+        share_target, TransferMetadataBuilder().set_status(status).build());
+  }
 }
 
 void NearbySharingServiceImpl::ReceiveIntroduction(
-    const ShareTarget& share_target,
-    const std::string& token) {
+    ShareTarget share_target,
+    base::Optional<std::string> token) {
   NS_LOG(INFO) << __func__ << ": Receiving introduction from "
                << share_target.device_name;
 
@@ -689,20 +771,29 @@
   }
 
   auto frames_reader = std::make_unique<IncomingFramesReader>(
-      &NearbyProcessManager::GetInstance(), profile_, connection);
+      process_manager_, profile_, connection);
 
   frames_reader->ReadFrame(
       sharing::mojom::V1Frame::Tag::INTRODUCTION,
       base::BindOnce(&NearbySharingServiceImpl::OnReceivedIntroduction,
-                     weak_ptr_factory_.GetWeakPtr(), connection,
-                     std::move(frames_reader)),
+                     weak_ptr_factory_.GetWeakPtr(), std::move(share_target),
+                     std::move(token), std::move(frames_reader)),
       kReadFramesTimeout);
 }
 
 void NearbySharingServiceImpl::OnReceivedIntroduction(
-    NearbyConnection* connection,
+    ShareTarget share_target,
+    base::Optional<std::string> token,
     std::unique_ptr<IncomingFramesReader> frames_reader,
     base::Optional<sharing::mojom::V1FramePtr> frame) {
+  NearbyConnection* connection = GetIncomingConnection(share_target);
+  if (!connection) {
+    NS_LOG(WARNING)
+        << __func__
+        << ": Ignore received introduction, due to no connection established.";
+    return;
+  }
+
   if (!frame) {
     connection->Close();
     NS_LOG(WARNING) << __func__ << ": Invalid introduction frame";
@@ -710,7 +801,132 @@
   }
 
   NS_LOG(INFO) << __func__ << ": Successfully read the introduction frame.";
-  // TODO(himanshujaju) - Implement OnReceiveIntroduction
+
+  sharing::mojom::IntroductionFramePtr introduction_frame =
+      std::move((*frame)->get_introduction());
+  for (const auto& file : introduction_frame->file_metadata) {
+    if (file->size <= 0) {
+      Fail(share_target, TransferMetadata::Status::kUnsupportedAttachmentType);
+      NS_LOG(WARNING)
+          << __func__
+          << ": Ignore introduction, due to invalid attachment size";
+      return;
+    }
+
+    NS_LOG(VERBOSE) << __func__ << "Found file attachment " << file->name
+                    << " of type " << file->type << " with mimeType "
+                    << file->mime_type;
+    FileAttachment attachment(file->name, file->type, file->size,
+                              /*file_path=*/base::nullopt, file->mime_type);
+    // TODO(himanshujaju) - setAttachmentPayloadId();
+    share_target.file_attachments.push_back(std::move(attachment));
+  }
+
+  for (const auto& text : introduction_frame->text_metadata) {
+    if (text->size <= 0) {
+      Fail(share_target, TransferMetadata::Status::kUnsupportedAttachmentType);
+      NS_LOG(WARNING)
+          << __func__
+          << ": Ignore introduction, due to invalid attachment size";
+      return;
+    }
+
+    NS_LOG(VERBOSE) << __func__ << "Found text attachment " << text->text_title
+                    << " of type " << text->type;
+    TextAttachment attachment(text->text_title, text->type, text->size);
+    // TODO(himanshujaju) - setAttachmentPayloadId();
+    share_target.text_attachments.push_back(std::move(attachment));
+  }
+
+  if (!share_target.has_attachments()) {
+    NS_LOG(WARNING) << __func__
+                    << ": No attachment is found for this share target. It can "
+                       "be result of unrecognizable attachment type";
+    Fail(share_target, TransferMetadata::Status::kUnsupportedAttachmentType);
+
+    NS_LOG(VERBOSE) << __func__
+                    << ": We don't support the attachments sent by the sender. "
+                       "We have informed "
+                    << share_target.device_name;
+    return;
+  }
+
+  if (IsOutOfStorage(share_target)) {
+    Fail(share_target, TransferMetadata::Status::kNotEnoughSpace);
+    NS_LOG(WARNING) << __func__
+                    << ": Not enough space on the receiver. We have informed "
+                    << share_target.device_name;
+    return;
+  }
+
+  mutual_acceptance_timeout_alarm_.Reset(base::BindOnce(
+      &NearbySharingServiceImpl::OnIncomingMutualAcceptanceTimeout,
+      weak_ptr_factory_.GetWeakPtr(), share_target));
+
+  base::SequencedTaskRunnerHandle::Get()->PostDelayedTask(
+      FROM_HERE, base::BindOnce(mutual_acceptance_timeout_alarm_.callback()),
+      kReadResponseFrameTimeout);
+
+  OnIncomingTransferUpdate(
+      share_target,
+      TransferMetadataBuilder()
+          .set_status(TransferMetadata::Status::kAwaitingLocalConfirmation)
+          .build());
+
+  if (!incoming_share_target_info_map_.count(share_target.id)) {
+    connection->Close();
+    NS_LOG(VERBOSE) << __func__
+                    << ": IncomingShareTarget not found, disconnecting "
+                    << share_target.device_name;
+    return;
+  }
+
+  connection->RegisterForDisconnection(base::BindOnce(
+      &NearbySharingServiceImpl::OnIncomingConnectionDisconnected,
+      weak_ptr_factory_.GetWeakPtr(), share_target));
+
+  // TODO(himanshujaju) - start reader thread.
+}
+
+void NearbySharingServiceImpl::OnIncomingConnectionDisconnected(
+    const ShareTarget& share_target) {
+  OnIncomingTransferUpdate(share_target,
+                           TransferMetadataBuilder()
+                               .set_status(TransferMetadata::Status::kFailed)
+                               .build());
+  UnregisterShareTarget(share_target);
+}
+
+void NearbySharingServiceImpl::UnregisterShareTarget(
+    const ShareTarget& share_target) {
+  if (share_target.is_incoming) {
+    incoming_share_target_info_map_.erase(share_target.id);
+    nearby_connections_manager_->ClearIncomingPayloads();
+  } else {
+    // TODO(crbug.com/1084644) - Clear from outgoing map.
+  }
+  mutual_acceptance_timeout_alarm_.Cancel();
+}
+
+bool NearbySharingServiceImpl::IsOutOfStorage(const ShareTarget& share_target) {
+  // TODO(himanshujaju) - Check storage space based on file path.
+  return false;
+}
+
+void NearbySharingServiceImpl::OnIncomingMutualAcceptanceTimeout(
+    const ShareTarget& share_target) {
+  DCHECK(share_target.is_incoming);
+
+  NS_LOG(VERBOSE)
+      << __func__
+      << ": Incoming mutual acceptance timed out, closing connection for "
+      << share_target.device_name;
+
+  OnIncomingTransferUpdate(share_target,
+                           TransferMetadataBuilder()
+                               .set_status(TransferMetadata::Status::kTimedOut)
+                               .build());
+  Fail(share_target, TransferMetadata::Status::kTimedOut);
 }
 
 IncomingShareTargetInfo& NearbySharingServiceImpl::GetIncomingShareTargetInfo(
@@ -724,7 +940,7 @@
 }
 
 OutgoingShareTargetInfo& NearbySharingServiceImpl::GetOutgoingShareTargetInfo(
-    ShareTarget share_target) {
+    const ShareTarget& share_target) {
   return outgoing_share_target_info_map_[share_target.id];
 }
 
diff --git a/chrome/browser/nearby_sharing/nearby_sharing_service_impl.h b/chrome/browser/nearby_sharing/nearby_sharing_service_impl.h
index a621e2a..b1abe04 100644
--- a/chrome/browser/nearby_sharing/nearby_sharing_service_impl.h
+++ b/chrome/browser/nearby_sharing/nearby_sharing_service_impl.h
@@ -10,6 +10,7 @@
 #include <utility>
 #include <vector>
 
+#include "base/cancelable_callback.h"
 #include "base/containers/flat_map.h"
 #include "base/memory/ptr_util.h"
 #include "base/memory/weak_ptr.h"
@@ -27,8 +28,10 @@
 #include "chrome/browser/nearby_sharing/nearby_sharing_service.h"
 #include "chrome/browser/nearby_sharing/outgoing_share_target_info.h"
 #include "chrome/browser/nearby_sharing/share_target.h"
+#include "chrome/browser/nearby_sharing/transfer_metadata.h"
 #include "chrome/browser/ui/webui/nearby_share/public/mojom/nearby_share_settings.mojom.h"
 #include "chrome/services/sharing/public/mojom/nearby_decoder_types.mojom.h"
+#include "chrome/services/sharing/public/proto/wire_format.pb.h"
 #include "components/keyed_service/core/keyed_service.h"
 #include "components/prefs/pref_change_registrar.h"
 
@@ -38,6 +41,7 @@
 class NearbyShareCertificateManager;
 class NearbyShareClientFactory;
 class NearbyShareLocalDeviceDataManager;
+class NotificationDisplayService;
 class PrefService;
 class Profile;
 
@@ -52,8 +56,10 @@
  public:
   explicit NearbySharingServiceImpl(
       PrefService* prefs,
+      NotificationDisplayService* notification_display_service,
       Profile* profile,
-      std::unique_ptr<NearbyConnectionsManager> nearby_connections_manager);
+      std::unique_ptr<NearbyConnectionsManager> nearby_connections_manager,
+      NearbyProcessManager* process_manager);
   ~NearbySharingServiceImpl() override;
 
   // NearbySharingService:
@@ -127,25 +133,37 @@
   void InvalidateReceiveSurfaceState();
   void InvalidateAdvertisingState();
   void StopAdvertising();
+  void WriteResponse(
+      NearbyConnection& connection,
+      sharing::nearby::ConnectionResponseFrame::Status reponse_status);
+  void Fail(const ShareTarget& share_target, TransferMetadata::Status status);
   void OnIncomingTransferUpdate(const ShareTarget& share_target,
                                 TransferMetadata metadata);
-  void ReceiveIntroduction(const ShareTarget& share_target,
-                           const std::string& token);
+  void ReceiveIntroduction(ShareTarget share_target,
+                           base::Optional<std::string> token);
   void OnReceivedIntroduction(
-      NearbyConnection* connection,
+      ShareTarget share_target,
+      base::Optional<std::string> token,
       std::unique_ptr<IncomingFramesReader> frames_reader,
       base::Optional<sharing::mojom::V1FramePtr> frame);
+  void OnIncomingConnectionDisconnected(const ShareTarget& share_target);
+  void UnregisterShareTarget(const ShareTarget& share_target);
+  bool IsOutOfStorage(const ShareTarget& share_target);
+
+  void OnIncomingMutualAcceptanceTimeout(const ShareTarget& share_target);
 
   IncomingShareTargetInfo& GetIncomingShareTargetInfo(
       const ShareTarget& share_target);
   NearbyConnection* GetIncomingConnection(const ShareTarget& share_target);
-  OutgoingShareTargetInfo& GetOutgoingShareTargetInfo(ShareTarget share_target);
+  OutgoingShareTargetInfo& GetOutgoingShareTargetInfo(
+      const ShareTarget& share_target);
   void ClearOutgoingShareTargetInfoMap();
 
   PrefService* prefs_;
   Profile* profile_;
   NearbyShareSettings settings_;
   std::unique_ptr<NearbyConnectionsManager> nearby_connections_manager_;
+  NearbyProcessManager* process_manager_;
   ScopedObserver<NearbyProcessManager, NearbyProcessManager::Observer>
       nearby_process_observer_{this};
   scoped_refptr<device::BluetoothAdapter> bluetooth_adapter_;
@@ -181,6 +199,10 @@
   base::flat_map<base::UnguessableToken, OutgoingShareTargetInfo>
       outgoing_share_target_info_map_;
 
+  // This alarm is used to disconnect the sharing connection if both sides do
+  // not press accept within the timeout.
+  base::CancelableOnceClosure mutual_acceptance_timeout_alarm_;
+
   // The current advertising power level. PowerLevel::kUnknown while not
   // advertising.
   PowerLevel advertising_power_level_ = PowerLevel::kUnknown;
diff --git a/chrome/browser/nearby_sharing/nearby_sharing_service_impl_unittest.cc b/chrome/browser/nearby_sharing/nearby_sharing_service_impl_unittest.cc
index 4aa859e..6c5bb0a 100644
--- a/chrome/browser/nearby_sharing/nearby_sharing_service_impl_unittest.cc
+++ b/chrome/browser/nearby_sharing/nearby_sharing_service_impl_unittest.cc
@@ -10,11 +10,18 @@
 
 #include "base/bind.h"
 #include "base/memory/ptr_util.h"
+#include "base/strings/string_number_conversions.h"
 #include "base/test/bind_test_util.h"
 #include "chrome/browser/nearby_sharing/common/nearby_share_prefs.h"
+#include "chrome/browser/nearby_sharing/fake_nearby_connection.h"
 #include "chrome/browser/nearby_sharing/fake_nearby_connections_manager.h"
 #include "chrome/browser/nearby_sharing/fast_initiation_manager.h"
+#include "chrome/browser/nearby_sharing/mock_nearby_process_manager.h"
+#include "chrome/browser/nearby_sharing/mock_nearby_sharing_decoder.h"
 #include "chrome/browser/nearby_sharing/nearby_connections_manager.h"
+#include "chrome/browser/notifications/notification_display_service_factory.h"
+#include "chrome/browser/notifications/notification_display_service_tester.h"
+#include "chrome/services/sharing/public/proto/wire_format.pb.h"
 #include "chrome/test/base/testing_browser_process.h"
 #include "chrome/test/base/testing_profile.h"
 #include "chrome/test/base/testing_profile_manager.h"
@@ -119,13 +126,15 @@
       this};
 };
 
-class FakeTransferUpdateCallback : public TransferUpdateCallback {
+class MockTransferUpdateCallback : public TransferUpdateCallback {
  public:
-  void OnTransferUpdate(const ShareTarget& shareTarget,
-                        const TransferMetadata& transferMetadata) override {
-    // TODO(crbug/1085068): Test transfer update callback when incoming
-    // connection is handled.
-  }
+  ~MockTransferUpdateCallback() override = default;
+
+  MOCK_METHOD(void,
+              OnTransferUpdate,
+              (const ShareTarget& shareTarget,
+               const TransferMetadata& transferMetadata),
+              (override));
 };
 
 namespace {
@@ -165,8 +174,14 @@
       const std::string& profile_name) {
     Profile* profile = profile_manager_.CreateTestingProfile(profile_name);
     fake_nearby_connections_manager_ = new FakeNearbyConnectionsManager();
+    notification_tester_ =
+        std::make_unique<NotificationDisplayServiceTester>(profile);
+    NotificationDisplayService* notification_display_service =
+        NotificationDisplayServiceFactory::GetForProfile(profile);
     auto service = std::make_unique<NearbySharingServiceImpl>(
-        &prefs_, profile, base::WrapUnique(fake_nearby_connections_manager_));
+        &prefs_, notification_display_service, profile,
+        base::WrapUnique(fake_nearby_connections_manager_),
+        &mock_nearby_process_manager_);
 
     // Allow the posted task to fetch the BluetoothAdapter to finish.
     base::RunLoop().RunUntilIdle();
@@ -196,12 +211,17 @@
         network_notifier_->GetConnectionType());
   }
 
+  NiceMock<MockNearbyProcessManager>& mock_nearby_process_manager() {
+    return mock_nearby_process_manager_;
+  }
+
  protected:
   content::BrowserTaskEnvironment task_environment_;
   ui::ScopedSetIdleState idle_state_{ui::IDLE_STATE_IDLE};
   TestingProfileManager profile_manager_{TestingBrowserProcess::GetGlobal()};
   sync_preferences::TestingPrefServiceSyncable prefs_;
   FakeNearbyConnectionsManager* fake_nearby_connections_manager_ = nullptr;
+  std::unique_ptr<NotificationDisplayServiceTester> notification_tester_;
   std::unique_ptr<NearbySharingServiceImpl> service_;
   std::unique_ptr<FakeFastInitiationManagerFactory>
       fast_initiation_manager_factory_;
@@ -209,6 +229,7 @@
   bool is_bluetooth_powered_ = true;
   device::BluetoothAdapter::Observer* adapter_observer_ = nullptr;
   scoped_refptr<NiceMock<device::MockBluetoothAdapter>> mock_bluetooth_adapter_;
+  NiceMock<MockNearbyProcessManager> mock_nearby_process_manager_;
   std::unique_ptr<net::test::MockNetworkChangeNotifier> network_notifier_ =
       net::test::MockNetworkChangeNotifier::Create();
 };
@@ -216,14 +237,13 @@
 }  // namespace
 
 TEST_F(NearbySharingServiceImplTest, AddsNearbyProcessObserver) {
-  NearbyProcessManager& manager = NearbyProcessManager::GetInstance();
-  EXPECT_TRUE(manager.observers_.HasObserver(service_.get()));
+  EXPECT_TRUE(
+      mock_nearby_process_manager().observers_.HasObserver(service_.get()));
 }
 
 TEST_F(NearbySharingServiceImplTest, RemovesNearbyProcessObserver) {
   service_.reset();
-  NearbyProcessManager& manager = NearbyProcessManager::GetInstance();
-  EXPECT_FALSE(manager.observers_.might_have_observers());
+  EXPECT_FALSE(mock_nearby_process_manager().observers_.might_have_observers());
 }
 
 TEST_F(NearbySharingServiceImplTest, DisableNearbyShutdownConnections) {
@@ -323,7 +343,7 @@
        ForegroundRegisterReceiveSurfaceIsAdvertising) {
   ui::ScopedSetIdleState unlocked(ui::IDLE_STATE_IDLE);
   SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
-  FakeTransferUpdateCallback callback;
+  MockTransferUpdateCallback callback;
   NearbySharingService::StatusCodes result = service_->RegisterReceiveSurface(
       &callback, NearbySharingService::ReceiveSurfaceState::kForeground);
   EXPECT_EQ(result, NearbySharingService::StatusCodes::kOk);
@@ -338,7 +358,7 @@
   SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
   prefs_.SetInteger(prefs::kNearbySharingBackgroundVisibilityName,
                     static_cast<int>(Visibility::kSelectedContacts));
-  FakeTransferUpdateCallback callback;
+  MockTransferUpdateCallback callback;
   NearbySharingService::StatusCodes result = service_->RegisterReceiveSurface(
       &callback, NearbySharingService::ReceiveSurfaceState::kBackground);
   EXPECT_EQ(result, NearbySharingService::StatusCodes::kOk);
@@ -351,7 +371,7 @@
        RegisterReceiveSurfaceTwiceSameCallbackKeepAdvertising) {
   ui::ScopedSetIdleState unlocked(ui::IDLE_STATE_IDLE);
   SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
-  FakeTransferUpdateCallback callback;
+  MockTransferUpdateCallback callback;
   NearbySharingService::StatusCodes result = service_->RegisterReceiveSurface(
       &callback, NearbySharingService::ReceiveSurfaceState::kForeground);
   EXPECT_EQ(result, NearbySharingService::StatusCodes::kOk);
@@ -367,13 +387,13 @@
        RegisterReceiveSurfaceTwiceKeepAdvertising) {
   ui::ScopedSetIdleState unlocked(ui::IDLE_STATE_IDLE);
   SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
-  FakeTransferUpdateCallback callback;
+  MockTransferUpdateCallback callback;
   NearbySharingService::StatusCodes result = service_->RegisterReceiveSurface(
       &callback, NearbySharingService::ReceiveSurfaceState::kForeground);
   EXPECT_EQ(result, NearbySharingService::StatusCodes::kOk);
   EXPECT_TRUE(fake_nearby_connections_manager_->IsAdvertising());
 
-  FakeTransferUpdateCallback callback2;
+  MockTransferUpdateCallback callback2;
   NearbySharingService::StatusCodes result2 = service_->RegisterReceiveSurface(
       &callback2, NearbySharingService::ReceiveSurfaceState::kForeground);
   EXPECT_EQ(result2, NearbySharingService::StatusCodes::kOk);
@@ -384,7 +404,7 @@
        ScreenLockedRegisterReceiveSurfaceNotAdvertising) {
   ui::ScopedSetIdleState locked(ui::IDLE_STATE_LOCKED);
   SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
-  FakeTransferUpdateCallback callback;
+  MockTransferUpdateCallback callback;
   NearbySharingService::StatusCodes result = service_->RegisterReceiveSurface(
       &callback, NearbySharingService::ReceiveSurfaceState::kForeground);
   EXPECT_EQ(result, NearbySharingService::StatusCodes::kOk);
@@ -400,7 +420,7 @@
   prefs_.SetInteger(prefs::kNearbySharingDataUsageName,
                     static_cast<int>(DataUsage::kOffline));
   service_->FlushMojoForTesting();
-  FakeTransferUpdateCallback callback;
+  MockTransferUpdateCallback callback;
   NearbySharingService::StatusCodes result = service_->RegisterReceiveSurface(
       &callback, NearbySharingService::ReceiveSurfaceState::kForeground);
   EXPECT_EQ(result, NearbySharingService::StatusCodes::kOk);
@@ -419,7 +439,7 @@
 TEST_F(NearbySharingServiceImplTest,
        NoNetworkRegisterReceiveSurfaceIsAdvertising) {
   ui::ScopedSetIdleState unlocked(ui::IDLE_STATE_IDLE);
-  FakeTransferUpdateCallback callback;
+  MockTransferUpdateCallback callback;
   NearbySharingService::StatusCodes result = service_->RegisterReceiveSurface(
       &callback, NearbySharingService::ReceiveSurfaceState::kForeground);
   EXPECT_EQ(result, NearbySharingService::StatusCodes::kOk);
@@ -431,7 +451,7 @@
        NoBluetoothNoNetworkRegisterReceiveSurfaceNotAdvertising) {
   ui::ScopedSetIdleState unlocked(ui::IDLE_STATE_IDLE);
   is_bluetooth_present_ = false;
-  FakeTransferUpdateCallback callback;
+  MockTransferUpdateCallback callback;
   NearbySharingService::StatusCodes result = service_->RegisterReceiveSurface(
       &callback, NearbySharingService::ReceiveSurfaceState::kForeground);
   EXPECT_EQ(result, NearbySharingService::StatusCodes::kOk);
@@ -442,7 +462,7 @@
 TEST_F(NearbySharingServiceImplTest, WifiRegisterReceiveSurfaceIsAdvertising) {
   ui::ScopedSetIdleState unlocked(ui::IDLE_STATE_IDLE);
   SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
-  FakeTransferUpdateCallback callback;
+  MockTransferUpdateCallback callback;
   NearbySharingService::StatusCodes result = service_->RegisterReceiveSurface(
       &callback, NearbySharingService::ReceiveSurfaceState::kForeground);
   EXPECT_EQ(result, NearbySharingService::StatusCodes::kOk);
@@ -453,7 +473,7 @@
        EthernetRegisterReceiveSurfaceIsAdvertising) {
   ui::ScopedSetIdleState unlocked(ui::IDLE_STATE_IDLE);
   SetConnectionType(net::NetworkChangeNotifier::CONNECTION_ETHERNET);
-  FakeTransferUpdateCallback callback;
+  MockTransferUpdateCallback callback;
   NearbySharingService::StatusCodes result = service_->RegisterReceiveSurface(
       &callback, NearbySharingService::ReceiveSurfaceState::kForeground);
   EXPECT_EQ(result, NearbySharingService::StatusCodes::kOk);
@@ -464,7 +484,7 @@
        ThreeGRegisterReceiveSurfaceIsAdvertising) {
   ui::ScopedSetIdleState unlocked(ui::IDLE_STATE_IDLE);
   SetConnectionType(net::NetworkChangeNotifier::CONNECTION_3G);
-  FakeTransferUpdateCallback callback;
+  MockTransferUpdateCallback callback;
   NearbySharingService::StatusCodes result = service_->RegisterReceiveSurface(
       &callback, NearbySharingService::ReceiveSurfaceState::kForeground);
   EXPECT_EQ(result, NearbySharingService::StatusCodes::kOk);
@@ -477,7 +497,7 @@
   ui::ScopedSetIdleState unlocked(ui::IDLE_STATE_IDLE);
   is_bluetooth_present_ = false;
   SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
-  FakeTransferUpdateCallback callback;
+  MockTransferUpdateCallback callback;
   NearbySharingService::StatusCodes result = service_->RegisterReceiveSurface(
       &callback, NearbySharingService::ReceiveSurfaceState::kForeground);
   EXPECT_EQ(result, NearbySharingService::StatusCodes::kOk);
@@ -489,7 +509,7 @@
   ui::ScopedSetIdleState unlocked(ui::IDLE_STATE_IDLE);
   is_bluetooth_present_ = false;
   SetConnectionType(net::NetworkChangeNotifier::CONNECTION_ETHERNET);
-  FakeTransferUpdateCallback callback;
+  MockTransferUpdateCallback callback;
   NearbySharingService::StatusCodes result = service_->RegisterReceiveSurface(
       &callback, NearbySharingService::ReceiveSurfaceState::kForeground);
   EXPECT_EQ(result, NearbySharingService::StatusCodes::kOk);
@@ -501,7 +521,7 @@
   ui::ScopedSetIdleState unlocked(ui::IDLE_STATE_IDLE);
   is_bluetooth_present_ = false;
   SetConnectionType(net::NetworkChangeNotifier::CONNECTION_3G);
-  FakeTransferUpdateCallback callback;
+  MockTransferUpdateCallback callback;
   NearbySharingService::StatusCodes result = service_->RegisterReceiveSurface(
       &callback, NearbySharingService::ReceiveSurfaceState::kForeground);
   EXPECT_EQ(result, NearbySharingService::StatusCodes::kOk);
@@ -515,7 +535,7 @@
   prefs_.SetBoolean(prefs::kNearbySharingEnabledPrefName, false);
   service_->FlushMojoForTesting();
   SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
-  FakeTransferUpdateCallback callback;
+  MockTransferUpdateCallback callback;
   NearbySharingService::StatusCodes result = service_->RegisterReceiveSurface(
       &callback, NearbySharingService::ReceiveSurfaceState::kForeground);
   EXPECT_EQ(result, NearbySharingService::StatusCodes::kOk);
@@ -527,7 +547,7 @@
        DisableFeatureReceiveSurfaceStopsAdvertising) {
   ui::ScopedSetIdleState unlocked(ui::IDLE_STATE_IDLE);
   SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
-  FakeTransferUpdateCallback callback;
+  MockTransferUpdateCallback callback;
   NearbySharingService::StatusCodes result = service_->RegisterReceiveSurface(
       &callback, NearbySharingService::ReceiveSurfaceState::kForeground);
   EXPECT_EQ(result, NearbySharingService::StatusCodes::kOk);
@@ -545,7 +565,7 @@
   SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
   prefs_.SetInteger(prefs::kNearbySharingBackgroundVisibilityName,
                     static_cast<int>(Visibility::kNoOne));
-  FakeTransferUpdateCallback callback;
+  MockTransferUpdateCallback callback;
   NearbySharingService::StatusCodes result = service_->RegisterReceiveSurface(
       &callback, NearbySharingService::ReceiveSurfaceState::kForeground);
   EXPECT_EQ(result, NearbySharingService::StatusCodes::kOk);
@@ -559,7 +579,7 @@
   prefs_.SetInteger(prefs::kNearbySharingBackgroundVisibilityName,
                     static_cast<int>(Visibility::kNoOne));
   service_->FlushMojoForTesting();
-  FakeTransferUpdateCallback callback;
+  MockTransferUpdateCallback callback;
   NearbySharingService::StatusCodes result = service_->RegisterReceiveSurface(
       &callback, NearbySharingService::ReceiveSurfaceState::kBackground);
   EXPECT_EQ(result, NearbySharingService::StatusCodes::kOk);
@@ -574,7 +594,7 @@
   prefs_.SetInteger(prefs::kNearbySharingBackgroundVisibilityName,
                     static_cast<int>(Visibility::kSelectedContacts));
   service_->FlushMojoForTesting();
-  FakeTransferUpdateCallback callback;
+  MockTransferUpdateCallback callback;
   NearbySharingService::StatusCodes result = service_->RegisterReceiveSurface(
       &callback, NearbySharingService::ReceiveSurfaceState::kBackground);
   EXPECT_EQ(result, NearbySharingService::StatusCodes::kOk);
@@ -594,7 +614,7 @@
   prefs_.SetInteger(prefs::kNearbySharingBackgroundVisibilityName,
                     static_cast<int>(Visibility::kNoOne));
   service_->FlushMojoForTesting();
-  FakeTransferUpdateCallback callback;
+  MockTransferUpdateCallback callback;
   NearbySharingService::StatusCodes result = service_->RegisterReceiveSurface(
       &callback, NearbySharingService::ReceiveSurfaceState::kBackground);
   EXPECT_EQ(result, NearbySharingService::StatusCodes::kOk);
@@ -613,7 +633,7 @@
   SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
   prefs_.SetInteger(prefs::kNearbySharingBackgroundVisibilityName,
                     static_cast<int>(Visibility::kSelectedContacts));
-  FakeTransferUpdateCallback callback;
+  MockTransferUpdateCallback callback;
   NearbySharingService::StatusCodes result = service_->RegisterReceiveSurface(
       &callback, NearbySharingService::ReceiveSurfaceState::kForeground);
   EXPECT_EQ(result, NearbySharingService::StatusCodes::kOk);
@@ -626,7 +646,7 @@
   SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
   prefs_.SetInteger(prefs::kNearbySharingBackgroundVisibilityName,
                     static_cast<int>(Visibility::kSelectedContacts));
-  FakeTransferUpdateCallback callback;
+  MockTransferUpdateCallback callback;
   NearbySharingService::StatusCodes result = service_->RegisterReceiveSurface(
       &callback, NearbySharingService::ReceiveSurfaceState::kBackground);
   EXPECT_EQ(result, NearbySharingService::StatusCodes::kOk);
@@ -639,7 +659,7 @@
   SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
   prefs_.SetInteger(prefs::kNearbySharingBackgroundVisibilityName,
                     static_cast<int>(Visibility::kAllContacts));
-  FakeTransferUpdateCallback callback;
+  MockTransferUpdateCallback callback;
   NearbySharingService::StatusCodes result = service_->RegisterReceiveSurface(
       &callback, NearbySharingService::ReceiveSurfaceState::kForeground);
   EXPECT_EQ(result, NearbySharingService::StatusCodes::kOk);
@@ -652,7 +672,7 @@
   SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
   prefs_.SetInteger(prefs::kNearbySharingBackgroundVisibilityName,
                     static_cast<int>(Visibility::kAllContacts));
-  FakeTransferUpdateCallback callback;
+  MockTransferUpdateCallback callback;
   NearbySharingService::StatusCodes result = service_->RegisterReceiveSurface(
       &callback, NearbySharingService::ReceiveSurfaceState::kBackground);
   EXPECT_EQ(result, NearbySharingService::StatusCodes::kOk);
@@ -662,7 +682,7 @@
 TEST_F(NearbySharingServiceImplTest, UnregisterReceiveSurfaceStopsAdvertising) {
   ui::ScopedSetIdleState unlocked(ui::IDLE_STATE_IDLE);
   SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
-  FakeTransferUpdateCallback callback;
+  MockTransferUpdateCallback callback;
   NearbySharingService::StatusCodes result = service_->RegisterReceiveSurface(
       &callback, NearbySharingService::ReceiveSurfaceState::kForeground);
   EXPECT_EQ(result, NearbySharingService::StatusCodes::kOk);
@@ -679,13 +699,13 @@
        UnregisterReceiveSurfaceDifferentCallbackKeepAdvertising) {
   ui::ScopedSetIdleState unlocked(ui::IDLE_STATE_IDLE);
   SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
-  FakeTransferUpdateCallback callback;
+  MockTransferUpdateCallback callback;
   NearbySharingService::StatusCodes result = service_->RegisterReceiveSurface(
       &callback, NearbySharingService::ReceiveSurfaceState::kForeground);
   EXPECT_EQ(result, NearbySharingService::StatusCodes::kOk);
   EXPECT_TRUE(fake_nearby_connections_manager_->IsAdvertising());
 
-  FakeTransferUpdateCallback callback2;
+  MockTransferUpdateCallback callback2;
   NearbySharingService::StatusCodes result2 =
       service_->UnregisterReceiveSurface(&callback2);
   EXPECT_EQ(result2, NearbySharingService::StatusCodes::kError);
@@ -695,9 +715,112 @@
 TEST_F(NearbySharingServiceImplTest, UnregisterReceiveSurfaceNeverRegistered) {
   ui::ScopedSetIdleState unlocked(ui::IDLE_STATE_IDLE);
   SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
-  FakeTransferUpdateCallback callback;
+  MockTransferUpdateCallback callback;
   NearbySharingService::StatusCodes result =
       service_->UnregisterReceiveSurface(&callback);
   EXPECT_EQ(result, NearbySharingService::StatusCodes::kError);
   EXPECT_FALSE(fake_nearby_connections_manager_->IsAdvertising());
 }
+
+TEST_F(NearbySharingServiceImplTest,
+       IncomingConnection_EmptyIntroductionFrame) {
+  std::string intro = "introduction_frame";
+  std::vector<uint8_t> bytes(intro.begin(), intro.end());
+  NiceMock<MockNearbySharingDecoder> mock_decoder;
+  EXPECT_CALL(mock_decoder, DecodeFrame(testing::Eq(bytes), testing::_))
+      .WillOnce(testing::Invoke(
+          [&](const std::vector<uint8_t>& data,
+              MockNearbySharingDecoder::DecodeFrameCallback callback) {
+            sharing::mojom::V1FramePtr mojo_v1frame =
+                sharing::mojom::V1Frame::New();
+            mojo_v1frame->set_introduction(
+                sharing::mojom::IntroductionFrame::New());
+
+            sharing::mojom::FramePtr mojo_frame = sharing::mojom::Frame::New();
+            mojo_frame->set_v1(std::move(mojo_v1frame));
+            std::move(callback).Run(std::move(mojo_frame));
+          }));
+
+  EXPECT_CALL(mock_nearby_process_manager(),
+              GetOrStartNearbySharingDecoder(testing::_))
+      .WillRepeatedly(testing::Return(&mock_decoder));
+
+  FakeNearbyConnection connection;
+  connection.AppendReadableData(bytes);
+  ui::ScopedSetIdleState unlocked(ui::IDLE_STATE_IDLE);
+  SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
+  NiceMock<MockTransferUpdateCallback> callback;
+  base::RunLoop run_loop;
+  EXPECT_CALL(callback, OnTransferUpdate(testing::_, testing::_))
+      .WillOnce(testing::Invoke([&run_loop](const ShareTarget& share_target,
+                                            TransferMetadata metadata) {
+        EXPECT_EQ(TransferMetadata::Status::kUnsupportedAttachmentType,
+                  metadata.status());
+        run_loop.Quit();
+      }));
+
+  NearbySharingService::StatusCodes result = service_->RegisterReceiveSurface(
+      &callback, NearbySharingService::ReceiveSurfaceState::kForeground);
+  EXPECT_EQ(result, NearbySharingService::StatusCodes::kOk);
+  EXPECT_TRUE(fake_nearby_connections_manager_->IsAdvertising());
+
+  service_->OnIncomingConnection("endpoint_id", {}, &connection);
+  run_loop.Run();
+}
+
+TEST_F(NearbySharingServiceImplTest,
+       IncomingConnection_ValidIntroductionFrame) {
+  std::string intro = "introduction_frame";
+  std::vector<uint8_t> bytes(intro.begin(), intro.end());
+  NiceMock<MockNearbySharingDecoder> mock_decoder;
+  EXPECT_CALL(mock_decoder, DecodeFrame(testing::Eq(bytes), testing::_))
+      .WillOnce(testing::Invoke(
+          [&](const std::vector<uint8_t>& data,
+              MockNearbySharingDecoder::DecodeFrameCallback callback) {
+            // TODO(himanshujaju) - Write helper functions for these.
+            std::vector<sharing::mojom::TextMetadataPtr> mojo_text_metadatas;
+            for (int i = 1; i <= 3; i++) {
+              mojo_text_metadatas.push_back(sharing::mojom::TextMetadata::New(
+                  "title " + base::NumberToString(i),
+                  static_cast<sharing::mojom::TextMetadata::Type>(i), i, i, i));
+            }
+
+            sharing::mojom::V1FramePtr mojo_v1frame =
+                sharing::mojom::V1Frame::New();
+            mojo_v1frame->set_introduction(
+                sharing::mojom::IntroductionFrame::New(
+                    std::vector<sharing::mojom::FileMetadataPtr>(),
+                    std::move(mojo_text_metadatas), base::nullopt,
+                    std::vector<sharing::mojom::WifiCredentialsMetadataPtr>()));
+
+            sharing::mojom::FramePtr mojo_frame = sharing::mojom::Frame::New();
+            mojo_frame->set_v1(std::move(mojo_v1frame));
+            std::move(callback).Run(std::move(mojo_frame));
+          }));
+
+  EXPECT_CALL(mock_nearby_process_manager(),
+              GetOrStartNearbySharingDecoder(testing::_))
+      .WillRepeatedly(testing::Return(&mock_decoder));
+
+  FakeNearbyConnection connection;
+  connection.AppendReadableData(bytes);
+  ui::ScopedSetIdleState unlocked(ui::IDLE_STATE_IDLE);
+  SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
+  NiceMock<MockTransferUpdateCallback> callback;
+  base::RunLoop run_loop;
+  EXPECT_CALL(callback, OnTransferUpdate(testing::_, testing::_))
+      .WillOnce(testing::Invoke([&run_loop](const ShareTarget& share_target,
+                                            TransferMetadata metadata) {
+        EXPECT_EQ(TransferMetadata::Status::kAwaitingLocalConfirmation,
+                  metadata.status());
+        run_loop.Quit();
+      }));
+
+  NearbySharingService::StatusCodes result = service_->RegisterReceiveSurface(
+      &callback, NearbySharingService::ReceiveSurfaceState::kForeground);
+  EXPECT_EQ(result, NearbySharingService::StatusCodes::kOk);
+  EXPECT_TRUE(fake_nearby_connections_manager_->IsAdvertising());
+
+  service_->OnIncomingConnection("endpoint_id", {}, &connection);
+  run_loop.Run();
+}
diff --git a/chrome/browser/nearby_sharing/share_target.h b/chrome/browser/nearby_sharing/share_target.h
index 67e7ca5..696f03a 100644
--- a/chrome/browser/nearby_sharing/share_target.h
+++ b/chrome/browser/nearby_sharing/share_target.h
@@ -12,7 +12,7 @@
 #include "base/unguessable_token.h"
 #include "chrome/browser/nearby_sharing/file_attachment.h"
 #include "chrome/browser/nearby_sharing/text_attachment.h"
-#include "chrome/browser/ui/webui/nearby_share/nearby_share.mojom-shared.h"
+#include "chrome/browser/ui/webui/nearby_share/nearby_share_target_types.mojom.h"
 #include "url/gurl.h"
 
 // A remote device.
@@ -33,6 +33,10 @@
   ShareTarget& operator=(ShareTarget&&);
   ~ShareTarget();
 
+  bool has_attachments() const {
+    return !text_attachments.empty() || !file_attachments.empty();
+  }
+
   base::UnguessableToken id = base::UnguessableToken::Create();
   std::string device_name;
   // Uri that points to an image of the ShareTarget, if one exists.
diff --git a/chrome/browser/nearby_sharing/text_attachment.h b/chrome/browser/nearby_sharing/text_attachment.h
index e9863ce..78ddacf 100644
--- a/chrome/browser/nearby_sharing/text_attachment.h
+++ b/chrome/browser/nearby_sharing/text_attachment.h
@@ -9,21 +9,12 @@
 
 #include "base/optional.h"
 #include "chrome/browser/nearby_sharing/attachment.h"
+#include "chrome/services/sharing/public/mojom/nearby_decoder_types.mojom.h"
 
 // Represents a text attachment.
 class TextAttachment : public Attachment {
  public:
-  // Different types are used to offer richer experiences on Receiver side,
-  // mainly for: 1. displaying notification of attachment types, 2. opening
-  // different types with different apps. Remember to update Notifications,
-  // ShareTarget, etc once more types are introduced here.
-  enum class Type {
-    kText,
-    kUrl,
-    kAddress,
-    kPhoneNumber,
-    kMaxValue = kPhoneNumber
-  };
+  using Type = sharing::mojom::TextMetadata::Type;
 
   TextAttachment(std::string text_body, Type type, int64_t size);
   ~TextAttachment() override;
diff --git a/chrome/browser/page_load_metrics/page_load_metrics_initialize.cc b/chrome/browser/page_load_metrics/page_load_metrics_initialize.cc
index 095c5f3..b435bc67 100644
--- a/chrome/browser/page_load_metrics/page_load_metrics_initialize.cc
+++ b/chrome/browser/page_load_metrics/page_load_metrics_initialize.cc
@@ -38,6 +38,7 @@
 #include "chrome/browser/page_load_metrics/observers/tab_restore_page_load_metrics_observer.h"
 #include "chrome/browser/page_load_metrics/observers/third_party_metrics_observer.h"
 #include "chrome/browser/page_load_metrics/observers/ukm_page_load_metrics_observer.h"
+#include "chrome/browser/prerender/chrome_prerender_contents_delegate.h"
 #include "chrome/browser/prerender/prerender_contents.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/search/search.h"
@@ -165,8 +166,8 @@
 }
 
 bool PageLoadMetricsEmbedder::IsPrerendering() const {
-  return prerender::PrerenderContents::FromWebContents(web_contents()) !=
-         nullptr;
+  return prerender::ChromePrerenderContentsDelegate::FromWebContents(
+             web_contents()) != nullptr;
 }
 
 bool PageLoadMetricsEmbedder::IsNewTabPageUrl(const GURL& url) {
@@ -178,7 +179,8 @@
 }
 
 bool PageLoadMetricsEmbedder::IsPrerender(content::WebContents* web_contents) {
-  return prerender::PrerenderContents::FromWebContents(web_contents);
+  return prerender::ChromePrerenderContentsDelegate::FromWebContents(
+      web_contents);
 }
 
 bool PageLoadMetricsEmbedder::IsExtensionUrl(const GURL& url) {
diff --git a/chrome/browser/password_check/android/internal/BUILD.gn b/chrome/browser/password_check/android/internal/BUILD.gn
index fc79408..65998f8 100644
--- a/chrome/browser/password_check/android/internal/BUILD.gn
+++ b/chrome/browser/password_check/android/internal/BUILD.gn
@@ -105,6 +105,7 @@
   deps = [ ":java_strings_grd" ]
   sources = [
     "java/res/layout/password_check_compromised_credential_item.xml",
+    "java/res/layout/password_check_compromised_credential_with_script_item.xml",
     "java/res/layout/password_check_header_item.xml",
     "java/res/values/dimens.xml",
   ]
diff --git a/chrome/browser/password_check/android/internal/java/res/layout/password_check_compromised_credential_item.xml b/chrome/browser/password_check/android/internal/java/res/layout/password_check_compromised_credential_item.xml
index 8d4d84e..83456ca 100644
--- a/chrome/browser/password_check/android/internal/java/res/layout/password_check_compromised_credential_item.xml
+++ b/chrome/browser/password_check/android/internal/java/res/layout/password_check_compromised_credential_item.xml
@@ -46,7 +46,6 @@
             android:layout_height="wrap_content"
             android:layout_width="wrap_content"
             android:text="@string/password_check_credential_row_change_button_caption"
-            android:visibility="gone"
             style="@style/FilledButton.Flat" />
 
     </LinearLayout>
diff --git a/chrome/browser/password_check/android/internal/java/res/layout/password_check_compromised_credential_with_script_item.xml b/chrome/browser/password_check/android/internal/java/res/layout/password_check_compromised_credential_with_script_item.xml
new file mode 100644
index 0000000..31005bb
--- /dev/null
+++ b/chrome/browser/password_check/android/internal/java/res/layout/password_check_compromised_credential_with_script_item.xml
@@ -0,0 +1,82 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2020 The Chromium Authors. All rights reserved.
+     Use of this source code is governed by a BSD-style license that can be
+     found in the LICENSE file. -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:fillViewport="true"
+    android:gravity="center"
+    android:layout_height="wrap_content"
+    android:layout_width="match_parent"
+    android:orientation="horizontal">
+
+    <!-- TODO(crbug.com/1106277): Remove the paddingStart if favicons fill that space. -->
+    <LinearLayout
+        android:gravity="start"
+        android:layout_gravity="top"
+        android:layout_height="wrap_content"
+        android:layout_width="0dp"
+        android:layout_weight="1"
+        android:orientation="vertical"
+        android:paddingStart="@dimen/compromised_credential_row_padding_start"
+        android:paddingBottom="@dimen/compromised_credential_row_padding_bottom"
+        android:paddingTop="@dimen/compromised_credential_row_padding_top">
+
+        <TextView
+            android:id="@+id/credential_origin"
+            android:layout_height="wrap_content"
+            android:layout_width="match_parent"
+            android:textAppearance="@style/TextAppearance.TextLarge.Primary" />
+
+        <TextView
+            android:id="@+id/compromised_username"
+            android:layout_height="wrap_content"
+            android:layout_width="match_parent"
+            android:textAppearance="@style/TextAppearance.TextMedium.Secondary" />
+
+        <TextView
+            android:id="@+id/compromised_reason"
+            android:layout_marginTop="@dimen/compromised_credential_row_reason_margin_top"
+            android:layout_height="wrap_content"
+            android:layout_width="match_parent"
+            android:textAppearance="@style/TextAppearance.TextSmall.Secondary" />
+
+        <!-- TODO(crbug.com/1086114): Correct the style of the button and explanation text. -->
+        <org.chromium.ui.widget.ButtonCompat
+            android:id="@+id/credential_change_button_with_script"
+            android:layout_marginTop="@dimen/compromised_credential_row_button_margin_top"
+            android:layout_height="wrap_content"
+            android:layout_width="wrap_content"
+            android:text="@string/password_check_credential_row_change_button_with_script_caption"
+            style="@style/FilledButton.Flat" />
+
+         <TextView
+            android:id="@+id/script_button_explanation"
+            android:layout_marginTop="@dimen/compromised_credential_row_reason_margin_top"
+            android:layout_height="wrap_content"
+            android:layout_width="match_parent"
+            android:text="@string/password_check_credential_row_script_button_explanation"
+            android:textAppearance="@style/TextAppearance.TextSmall.Secondary" />
+
+        <org.chromium.ui.widget.ButtonCompat
+            android:id="@+id/credential_change_button"
+            android:layout_marginTop="@dimen/compromised_credential_row_button_margin_top"
+            android:layout_height="wrap_content"
+            android:layout_width="wrap_content"
+            android:text="@string/password_check_credential_row_change_button_caption"  />
+
+    </LinearLayout>
+
+    <org.chromium.components.browser_ui.widget.listmenu.ListMenuButton
+        xmlns:app="http://schemas.android.com/apk/res-auto"
+        android:id="@+id/credential_menu_button"
+        android:background="@null"
+        android:contentDescription="@string/more"
+        android:layout_gravity="top"
+        android:layout_height="@dimen/compromised_credential_row_more_size"
+        android:layout_width="wrap_content"
+        android:paddingEnd="@dimen/compromised_credential_row_more_padding_end"
+        android:paddingStart="@dimen/compromised_credential_row_more_padding_start"
+        android:src="@drawable/ic_more_vert_24dp"
+        app:tint="@color/default_icon_color_tint_list" />
+
+</LinearLayout>
diff --git a/chrome/browser/password_check/android/internal/java/src/org/chromium/chrome/browser/password_check/PasswordCheckBridge.java b/chrome/browser/password_check/android/internal/java/src/org/chromium/chrome/browser/password_check/PasswordCheckBridge.java
index aa20591d..4a6a88e 100644
--- a/chrome/browser/password_check/android/internal/java/src/org/chromium/chrome/browser/password_check/PasswordCheckBridge.java
+++ b/chrome/browser/password_check/android/internal/java/src/org/chromium/chrome/browser/password_check/PasswordCheckBridge.java
@@ -62,8 +62,10 @@
     }
 
     private static void insertCredential(CompromisedCredential[] credentials, int index,
-            String originUrl, String username, String password, boolean phished) {
-        credentials[index] = new CompromisedCredential(originUrl, username, password, phished);
+            String originUrl, String username, String password, boolean phished,
+            boolean hasScript) {
+        credentials[index] =
+                new CompromisedCredential(originUrl, username, password, phished, hasScript);
     }
 
     /**
diff --git a/chrome/browser/password_check/android/internal/java/src/org/chromium/chrome/browser/password_check/PasswordCheckMediator.java b/chrome/browser/password_check/android/internal/java/src/org/chromium/chrome/browser/password_check/PasswordCheckMediator.java
index 99fed90..5dd28c63 100644
--- a/chrome/browser/password_check/android/internal/java/src/org/chromium/chrome/browser/password_check/PasswordCheckMediator.java
+++ b/chrome/browser/password_check/android/internal/java/src/org/chromium/chrome/browser/password_check/PasswordCheckMediator.java
@@ -35,7 +35,9 @@
         assert items.size() == 1;
 
         for (CompromisedCredential credential : credentials) {
-            items.add(new ListItem(PasswordCheckProperties.ItemType.COMPROMISED_CREDENTIAL,
+            items.add(new ListItem(credential.hasScript()
+                            ? PasswordCheckProperties.ItemType.COMPROMISED_CREDENTIAL_WITH_SCRIPT
+                            : PasswordCheckProperties.ItemType.COMPROMISED_CREDENTIAL,
                     new PropertyModel
                             .Builder(PasswordCheckProperties.CompromisedCredentialProperties
                                              .ALL_KEYS)
diff --git a/chrome/browser/password_check/android/internal/java/src/org/chromium/chrome/browser/password_check/PasswordCheckProperties.java b/chrome/browser/password_check/android/internal/java/src/org/chromium/chrome/browser/password_check/PasswordCheckProperties.java
index 85042ce..9881cc623 100644
--- a/chrome/browser/password_check/android/internal/java/src/org/chromium/chrome/browser/password_check/PasswordCheckProperties.java
+++ b/chrome/browser/password_check/android/internal/java/src/org/chromium/chrome/browser/password_check/PasswordCheckProperties.java
@@ -55,7 +55,8 @@
         private HeaderProperties() {}
     }
 
-    @IntDef({ItemType.HEADER, ItemType.COMPROMISED_CREDENTIAL})
+    @IntDef({ItemType.HEADER, ItemType.COMPROMISED_CREDENTIAL,
+            ItemType.COMPROMISED_CREDENTIAL_WITH_SCRIPT})
     @Retention(RetentionPolicy.SOURCE)
     @interface ItemType {
         /**
@@ -67,6 +68,12 @@
          * A section containing a user's name and password.
          */
         int COMPROMISED_CREDENTIAL = 2;
+
+        /**
+         * A section containing a user's name and password for a domain where a password change
+         * script is available.
+         */
+        int COMPROMISED_CREDENTIAL_WITH_SCRIPT = 3;
     }
 
     /**
diff --git a/chrome/browser/password_check/android/internal/java/src/org/chromium/chrome/browser/password_check/PasswordCheckViewBinder.java b/chrome/browser/password_check/android/internal/java/src/org/chromium/chrome/browser/password_check/PasswordCheckViewBinder.java
index 5a44706..1496562 100644
--- a/chrome/browser/password_check/android/internal/java/src/org/chromium/chrome/browser/password_check/PasswordCheckViewBinder.java
+++ b/chrome/browser/password_check/android/internal/java/src/org/chromium/chrome/browser/password_check/PasswordCheckViewBinder.java
@@ -71,6 +71,10 @@
                 return new PasswordCheckViewHolder(parent,
                         R.layout.password_check_compromised_credential_item,
                         PasswordCheckViewBinder::bindCredentialView);
+            case ItemType.COMPROMISED_CREDENTIAL_WITH_SCRIPT:
+                return new PasswordCheckViewHolder(parent,
+                        R.layout.password_check_compromised_credential_with_script_item,
+                        PasswordCheckViewBinder::bindCredentialView);
         }
         assert false : "Cannot create view for ItemType: " + itemType;
         return null;
@@ -113,6 +117,10 @@
             reason.setText(credential.isPhished()
                             ? R.string.password_check_credential_row_reason_phished
                             : R.string.password_check_credential_row_reason_leaked);
+            if (credential.hasScript()) {
+                assert view.findViewById(R.id.credential_change_button_with_script) != null;
+                assert view.findViewById(R.id.script_button_explanation) != null;
+            }
 
             ListMenuButton more = view.findViewById(R.id.credential_menu_button);
             more.setDelegate(() -> {
diff --git a/chrome/browser/password_check/android/internal/java/strings/android_password_check_strings.grd b/chrome/browser/password_check/android/internal/java/strings/android_password_check_strings.grd
index 8b2593b..a5a305e 100644
--- a/chrome/browser/password_check/android/internal/java/strings/android_password_check_strings.grd
+++ b/chrome/browser/password_check/android/internal/java/strings/android_password_check_strings.grd
@@ -175,13 +175,18 @@
       <message name="IDS_PASSWORD_CHECK_CREDENTIAL_ROW_CHANGE_BUTTON_CAPTION" desc="Caption for the button that links to a site where the user can change a compromised credential.">
         Change password
       </message>
+      <message name="IDS_PASSWORD_CHECK_CREDENTIAL_ROW_CHANGE_BUTTON_WITH_SCRIPT_CAPTION" desc="Caption for the button that starts a script that changes a compromised credential.">
+        Change password automatically
+      </message>
       <message name="IDS_PASSWORD_CHECK_CREDENTIAL_ROW_REASON_LEAKED" desc="Small description explaining that a credential is compromised because it was part of a data breach.">
         Found in data breach
       </message>
       <message name="IDS_PASSWORD_CHECK_CREDENTIAL_ROW_REASON_PHISHED" desc="Small description explaining that a credential is compromised because it was entered on a deceptive site.">
         Entered on a deceptive site
       </message>
-
+      <message name="IDS_PASSWORD_CHECK_CREDENTIAL_ROW_SCRIPT_BUTTON_EXPLANATION" desc="Small description explaining that an automated password change can be done with a script.">
+        Let Google Assistant help you change your password
+      </message>
       <!-- Accessibility strings -->
       <message name="IDS_ACCESSIBILITY_PASSWORD_CHECK_RESTART_BUTTON" desc="Content description for the button to restart the password check.">
         Restart password check
diff --git a/chrome/browser/password_check/android/internal/java/strings/android_password_check_strings_grd/IDS_PASSWORD_CHECK_CREDENTIAL_ROW_CHANGE_BUTTON_WITH_SCRIPT_CAPTION.png.sha1 b/chrome/browser/password_check/android/internal/java/strings/android_password_check_strings_grd/IDS_PASSWORD_CHECK_CREDENTIAL_ROW_CHANGE_BUTTON_WITH_SCRIPT_CAPTION.png.sha1
new file mode 100644
index 0000000..353e7a92
--- /dev/null
+++ b/chrome/browser/password_check/android/internal/java/strings/android_password_check_strings_grd/IDS_PASSWORD_CHECK_CREDENTIAL_ROW_CHANGE_BUTTON_WITH_SCRIPT_CAPTION.png.sha1
@@ -0,0 +1 @@
+68c54c7c1d83797946c4652935284033a9ed10be
\ No newline at end of file
diff --git a/chrome/browser/password_check/android/internal/java/strings/android_password_check_strings_grd/IDS_PASSWORD_CHECK_CREDENTIAL_ROW_SCRIPT_BUTTON_EXPLANATION.png.sha1 b/chrome/browser/password_check/android/internal/java/strings/android_password_check_strings_grd/IDS_PASSWORD_CHECK_CREDENTIAL_ROW_SCRIPT_BUTTON_EXPLANATION.png.sha1
new file mode 100644
index 0000000..353e7a92
--- /dev/null
+++ b/chrome/browser/password_check/android/internal/java/strings/android_password_check_strings_grd/IDS_PASSWORD_CHECK_CREDENTIAL_ROW_SCRIPT_BUTTON_EXPLANATION.png.sha1
@@ -0,0 +1 @@
+68c54c7c1d83797946c4652935284033a9ed10be
\ No newline at end of file
diff --git a/chrome/browser/password_check/android/java/src/org/chromium/chrome/browser/password_check/CompromisedCredential.java b/chrome/browser/password_check/android/java/src/org/chromium/chrome/browser/password_check/CompromisedCredential.java
index bbcd4fe..b23793b 100644
--- a/chrome/browser/password_check/android/java/src/org/chromium/chrome/browser/password_check/CompromisedCredential.java
+++ b/chrome/browser/password_check/android/java/src/org/chromium/chrome/browser/password_check/CompromisedCredential.java
@@ -15,18 +15,20 @@
     private final String mPassword;
     private final String mOriginUrl;
     private final boolean mPhished;
+    private final boolean mHasScript;
 
     /**
      * @param username Username shown to the user.
      * @param originUrl Origin URL shown to the user in case this credential is a PSL match.
      */
-    public CompromisedCredential(
-            String originUrl, String username, String password, boolean phished) {
+    public CompromisedCredential(String originUrl, String username, String password,
+            boolean phished, boolean hasScript) {
         assert originUrl != null : "Credential origin is null! Pass an empty one instead.";
         mOriginUrl = originUrl;
         mUsername = username;
         mPassword = password;
         mPhished = phished;
+        mHasScript = hasScript;
     }
 
     public String getOriginUrl() {
@@ -41,18 +43,22 @@
     public boolean isPhished() {
         return mPhished;
     }
+    public boolean hasScript() {
+        return mHasScript;
+    }
 
     @Override
     public boolean equals(Object o) {
         if (this == o) return true;
         if (o == null || getClass() != o.getClass()) return false;
         CompromisedCredential that = (CompromisedCredential) o;
-        return mPhished == that.mPhished && mUsername.equals(that.mUsername)
-                && mPassword.equals(that.mPassword) && mOriginUrl.equals(that.mOriginUrl);
+        return mPhished == that.mPhished && mHasScript == that.mHasScript
+                && mUsername.equals(that.mUsername) && mPassword.equals(that.mPassword)
+                && mOriginUrl.equals(that.mOriginUrl);
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(mUsername, mPassword, mOriginUrl, mPhished);
+        return Objects.hash(mUsername, mPassword, mOriginUrl, mPhished, mHasScript);
     }
 }
diff --git a/chrome/browser/password_check/android/javatests/src/org/chromium/chrome/browser/password_check/PasswordCheckViewTest.java b/chrome/browser/password_check/android/javatests/src/org/chromium/chrome/browser/password_check/PasswordCheckViewTest.java
index a5fae1d7..a0940ea8 100644
--- a/chrome/browser/password_check/android/javatests/src/org/chromium/chrome/browser/password_check/PasswordCheckViewTest.java
+++ b/chrome/browser/password_check/android/javatests/src/org/chromium/chrome/browser/password_check/PasswordCheckViewTest.java
@@ -60,11 +60,13 @@
 @CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
 public class PasswordCheckViewTest {
     private static final CompromisedCredential ANA =
-            new CompromisedCredential("some-url.com", "Ana", "password", false);
+            new CompromisedCredential("some-url.com", "Ana", "password", false, false);
     private static final CompromisedCredential PHISHED =
-            new CompromisedCredential("example.com", "Baub", "DoSomething", true);
+            new CompromisedCredential("example.com", "Baub", "DoSomething", true, false);
     private static final CompromisedCredential LEAKED =
-            new CompromisedCredential("some-other-url.com", "AZiegler", "N0M3rcy", false);
+            new CompromisedCredential("some-other-url.com", "AZiegler", "N0M3rcy", false, false);
+    private static final CompromisedCredential SCRIPTED =
+            new CompromisedCredential("script.com", "Charlie", "secret", false, true);
 
     private PropertyModel mModel = PasswordCheckProperties.createDefaultModel();
     private PasswordCheckFragmentView mPasswordCheckView;
@@ -102,8 +104,7 @@
         pollUiThread(() -> Criteria.checkThat(getPasswordCheckViewList().getChildCount(), is(2)));
         // Has a change passwords button.
         assertNotNull(getCredentialChangeButtonAt(1));
-        // TODO(crbug.com/1092444): Ensure the button is visible as soon as it does something!
-        assertThat(getCredentialChangeButtonAt(1).getVisibility(), is(View.GONE));
+        assertThat(getCredentialChangeButtonAt(1).getVisibility(), is(View.VISIBLE));
         assertThat(getCredentialChangeButtonAt(1).getText(),
                 is(getString(R.string.password_check_credential_row_change_button_caption)));
 
@@ -154,6 +155,37 @@
 
     @Test
     @MediumTest
+    public void testCrendentialDisplaysChangeButtonWithScript() {
+        runOnUiThreadBlocking(() -> { mModel.get(ITEMS).add(buildCredentialItem(SCRIPTED)); });
+        pollUiThread(() -> Criteria.checkThat(getPasswordCheckViewList().getChildCount(), is(1)));
+
+        // Origin and username.
+        assertThat(getCredentialOriginAt(0).getText(), is(SCRIPTED.getOriginUrl()));
+        assertThat(getCredentialUserAt(0).getText(), is(SCRIPTED.getUsername()));
+
+        // Reason to show credential.
+        assertThat(getCredentialReasonAt(0).getText(),
+                is(getString(R.string.password_check_credential_row_reason_leaked)));
+
+        // Change button with script.
+        assertNotNull(getCredentialChangeButtonWithScriptAt(0));
+        assertThat(getCredentialChangeButtonWithScriptAt(0).getText(),
+                is(getString(
+                        R.string.password_check_credential_row_change_button_with_script_caption)));
+
+        // Explanation for change button with script.
+        assertNotNull(getCredentialChangeButtonWithScriptExplanationAt(0));
+        assertThat(getCredentialChangeButtonWithScriptExplanationAt(0).getText(),
+                is(getString(R.string.password_check_credential_row_script_button_explanation)));
+
+        // Change button without script.
+        assertNotNull(getCredentialChangeButtonAt(0));
+        assertThat(getCredentialChangeButtonAt(0).getText(),
+                is(getString(R.string.password_check_credential_row_change_button_caption)));
+    }
+
+    @Test
+    @MediumTest
     public void testClickingDeleteInMoreMenuTriggersHandler() {
         runOnUiThreadBlocking(() -> mModel.get(ITEMS).add(buildCredentialItem(ANA)));
         pollUiThread(() -> Criteria.checkThat(getPasswordCheckViewList().getChildCount(), is(1)));
@@ -176,7 +208,9 @@
     }
 
     private MVCListAdapter.ListItem buildCredentialItem(CompromisedCredential credential) {
-        return new MVCListAdapter.ListItem(PasswordCheckProperties.ItemType.COMPROMISED_CREDENTIAL,
+        return new MVCListAdapter.ListItem(credential.hasScript()
+                        ? PasswordCheckProperties.ItemType.COMPROMISED_CREDENTIAL_WITH_SCRIPT
+                        : PasswordCheckProperties.ItemType.COMPROMISED_CREDENTIAL,
                 new PropertyModel
                         .Builder(PasswordCheckProperties.CompromisedCredentialProperties.ALL_KEYS)
                         .with(COMPROMISED_CREDENTIAL, credential)
@@ -220,6 +254,16 @@
                 R.id.credential_change_button);
     }
 
+    private ButtonCompat getCredentialChangeButtonWithScriptAt(int index) {
+        return getPasswordCheckViewList().getChildAt(index).findViewById(
+                R.id.credential_change_button_with_script);
+    }
+
+    private TextView getCredentialChangeButtonWithScriptExplanationAt(int index) {
+        return getPasswordCheckViewList().getChildAt(index).findViewById(
+                R.id.script_button_explanation);
+    }
+
     private ListMenuButton getCredentialMoreButtonAt(int index) {
         return getPasswordCheckViewList().getChildAt(index).findViewById(
                 R.id.credential_menu_button);
diff --git a/chrome/browser/password_check/android/junit/src/org/chromium/chrome/browser/password_check/PasswordCheckControllerTest.java b/chrome/browser/password_check/android/junit/src/org/chromium/chrome/browser/password_check/PasswordCheckControllerTest.java
index 528c1a26..8bbbecc 100644
--- a/chrome/browser/password_check/android/junit/src/org/chromium/chrome/browser/password_check/PasswordCheckControllerTest.java
+++ b/chrome/browser/password_check/android/junit/src/org/chromium/chrome/browser/password_check/PasswordCheckControllerTest.java
@@ -37,9 +37,9 @@
 @RunWith(BaseRobolectricTestRunner.class)
 public class PasswordCheckControllerTest {
     private static final CompromisedCredential ANA =
-            new CompromisedCredential("https://m.a.xyz/", "Ana", "password", false);
+            new CompromisedCredential("https://m.a.xyz/", "Ana", "password", false, false);
     private static final CompromisedCredential BOB =
-            new CompromisedCredential("https://www.b.ch/", "Baub", "DoneSth", true);
+            new CompromisedCredential("https://www.b.ch/", "Baub", "DoneSth", true, false);
 
     @Mock
     private PasswordCheckComponentUi.Delegate mDelegate;
diff --git a/chrome/browser/pdf/pdf_extension_test.cc b/chrome/browser/pdf/pdf_extension_test.cc
index 55517ef..7d57fe2 100644
--- a/chrome/browser/pdf/pdf_extension_test.cc
+++ b/chrome/browser/pdf/pdf_extension_test.cc
@@ -860,6 +860,12 @@
   RunTestsInJsModule("viewer_pdf_toolbar_new_test.js", "test.pdf");
 }
 
+IN_PROC_BROWSER_TEST_F(PDFExtensionJSTest, ViewerPdfSidenav) {
+  // Although this test file does not require a PDF to be loaded, loading the
+  // elements without loading a PDF is difficult.
+  RunTestsInJsModule("viewer_pdf_sidenav_test.js", "test.pdf");
+}
+
 IN_PROC_BROWSER_TEST_F(PDFExtensionJSTest, ToolbarManager) {
   RunTestsInJsModule("toolbar_manager_test.js", "test.pdf");
 }
diff --git a/chrome/browser/performance_manager/persistence/site_data/site_data_cache_facade_factory.cc b/chrome/browser/performance_manager/persistence/site_data/site_data_cache_facade_factory.cc
index 845aa13..4a7dbb8 100644
--- a/chrome/browser/performance_manager/persistence/site_data/site_data_cache_facade_factory.cc
+++ b/chrome/browser/performance_manager/persistence/site_data/site_data_cache_facade_factory.cc
@@ -5,6 +5,7 @@
 #include "chrome/browser/performance_manager/persistence/site_data/site_data_cache_facade_factory.h"
 
 #include "base/memory/ptr_util.h"
+#include "base/run_loop.h"
 #include "chrome/browser/history/history_service_factory.h"
 #include "chrome/browser/performance_manager/persistence/site_data/site_data_cache_facade.h"
 #include "chrome/browser/profiles/incognito_helpers.h"
@@ -21,13 +22,8 @@
 
 SiteDataCacheFacadeFactory* g_instance = nullptr;
 
-}
-
-// static
-SiteDataCacheFacade* SiteDataCacheFacadeFactory::GetForProfile(
-    Profile* profile) {
-  return static_cast<SiteDataCacheFacade*>(
-      GetInstance()->GetServiceForBrowserContext(profile, true));
+// Tests that want to use this factory will have to explicitly enable it.
+bool g_enable_for_testing = false;
 }
 
 SiteDataCacheFacadeFactory* SiteDataCacheFacadeFactory::GetInstance() {
@@ -37,6 +33,31 @@
   return g_instance;
 }
 
+// static
+std::unique_ptr<base::AutoReset<bool>>
+SiteDataCacheFacadeFactory::EnableForTesting() {
+  // Only one AutoReset served by this function can exists, otherwise the first
+  // one being released would set g_enable_for_testing to false while there's
+  // other AutoReset still existing.
+  DCHECK(!g_enable_for_testing);
+  return std::make_unique<base::AutoReset<bool>>(&g_enable_for_testing, true);
+}
+
+// static
+void SiteDataCacheFacadeFactory::DisassociateForTesting(Profile* profile) {
+  GetInstance()->Disassociate(profile);
+}
+
+// static
+void SiteDataCacheFacadeFactory::ReleaseInstanceForTesting() {
+  base::RunLoop run_loop;
+  g_instance->cache_factory()->ResetWithCallbackAfterDestruction(
+      run_loop.QuitClosure());
+  run_loop.Run();
+  delete g_instance;
+  DCHECK(!g_instance);
+}
+
 SiteDataCacheFacadeFactory::SiteDataCacheFacadeFactory()
     : BrowserContextKeyedServiceFactory(
           "SiteDataCacheFacadeFactory",
@@ -69,8 +90,7 @@
 }
 
 bool SiteDataCacheFacadeFactory::ServiceIsNULLWhileTesting() const {
-  // Tests that want to use this factory will have to explicitly enable it.
-  return true;
+  return !g_enable_for_testing;
 }
 
 }  // namespace performance_manager
diff --git a/chrome/browser/performance_manager/persistence/site_data/site_data_cache_facade_factory.h b/chrome/browser/performance_manager/persistence/site_data/site_data_cache_facade_factory.h
index a4a4ec5..0c86f15 100644
--- a/chrome/browser/performance_manager/persistence/site_data/site_data_cache_facade_factory.h
+++ b/chrome/browser/performance_manager/persistence/site_data/site_data_cache_facade_factory.h
@@ -5,6 +5,7 @@
 #ifndef CHROME_BROWSER_PERFORMANCE_MANAGER_PERSISTENCE_SITE_DATA_SITE_DATA_CACHE_FACADE_FACTORY_H_
 #define CHROME_BROWSER_PERFORMANCE_MANAGER_PERSISTENCE_SITE_DATA_SITE_DATA_CACHE_FACADE_FACTORY_H_
 
+#include "base/auto_reset.h"
 #include "base/memory/weak_ptr.h"
 #include "base/no_destructor.h"
 #include "base/threading/sequence_bound.h"
@@ -49,9 +50,12 @@
  public:
   ~SiteDataCacheFacadeFactory() override;
 
-  static SiteDataCacheFacade* GetForProfile(Profile* profile);
   static SiteDataCacheFacadeFactory* GetInstance();
 
+  static std::unique_ptr<base::AutoReset<bool>> EnableForTesting();
+  static void DisassociateForTesting(Profile* profile);
+  static void ReleaseInstanceForTesting();
+
  protected:
   friend class base::NoDestructor<SiteDataCacheFacadeFactory>;
   friend class SiteDataCacheFacade;
diff --git a/chrome/browser/performance_manager/persistence/site_data/site_data_cache_facade_unittest.cc b/chrome/browser/performance_manager/persistence/site_data/site_data_cache_facade_unittest.cc
index 359e1ff..93c0a0814 100644
--- a/chrome/browser/performance_manager/persistence/site_data/site_data_cache_facade_unittest.cc
+++ b/chrome/browser/performance_manager/persistence/site_data/site_data_cache_facade_unittest.cc
@@ -106,15 +106,14 @@
   void SetUp() override {
     testing::TestWithPerformanceManager::SetUp();
     profile_ = std::make_unique<TestingProfile>();
-    facade_factory_ = base::WrapUnique(new SiteDataCacheFacadeFactory());
     use_in_memory_db_for_testing_ =
         LevelDBSiteDataStore::UseInMemoryDBForTesting();
   }
 
   void TearDown() override {
     use_in_memory_db_for_testing_.reset();
-    facade_factory_.reset();
     profile_.reset();
+    SiteDataCacheFacadeFactory::ReleaseInstanceForTesting();
     testing::TestWithPerformanceManager::TearDown();
   }
 
@@ -144,7 +143,6 @@
 
  private:
   std::unique_ptr<TestingProfile> profile_;
-  std::unique_ptr<SiteDataCacheFacadeFactory> facade_factory_;
   std::unique_ptr<base::AutoReset<bool>> use_in_memory_db_for_testing_;
 };
 
diff --git a/chrome/browser/performance_manager/test_support/site_data_utils.cc b/chrome/browser/performance_manager/test_support/site_data_utils.cc
new file mode 100644
index 0000000..d127f83
--- /dev/null
+++ b/chrome/browser/performance_manager/test_support/site_data_utils.cc
@@ -0,0 +1,117 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/performance_manager/test_support/site_data_utils.h"
+#include <memory>
+
+#include "base/callback_forward.h"
+#include "base/memory/weak_ptr.h"
+#include "base/run_loop.h"
+#include "base/test/bind_test_util.h"
+#include "chrome/browser/performance_manager/persistence/site_data/site_data_cache_facade_factory.h"
+#include "components/performance_manager/persistence/site_data/leveldb_site_data_store.h"
+#include "components/performance_manager/persistence/site_data/site_data_impl.h"
+#include "components/performance_manager/persistence/site_data/site_data_writer.h"
+#include "components/performance_manager/public/decorators/site_data_recorder.h"
+#include "components/performance_manager/public/performance_manager.h"
+
+namespace performance_manager {
+
+SiteDataTestHarness::SiteDataTestHarness()
+    : use_in_memory_db_for_testing_(
+          LevelDBSiteDataStore::UseInMemoryDBForTesting()),
+      enable_cache_factory_for_testing_(
+          SiteDataCacheFacadeFactory::EnableForTesting()) {}
+
+SiteDataTestHarness::~SiteDataTestHarness() = default;
+
+void SiteDataTestHarness::SetUp() {
+  PerformanceManagerTestHarnessHelper::SetUp();
+  base::RunLoop run_loop;
+  auto quit_closure = run_loop.QuitClosure();
+  auto recorder = std::make_unique<SiteDataRecorder>();
+  PerformanceManager::CallOnGraph(
+      FROM_HERE,
+      base::BindLambdaForTesting([&](performance_manager::Graph* graph) {
+        graph->PassToGraph(std::move(recorder));
+        std::move(quit_closure).Run();
+      }));
+  run_loop.Run();
+}
+
+void SiteDataTestHarness::TearDown(Profile* profile) {
+  SiteDataCacheFacadeFactory::DisassociateForTesting(profile);
+  TearDown();
+}
+
+void SiteDataTestHarness::TearDown() {
+  SiteDataCacheFacadeFactory::ReleaseInstanceForTesting();
+  PerformanceManagerTestHarnessHelper::TearDown();
+}
+
+internal::SiteDataImpl* GetSiteDataImplForPageNode(PageNode* page_node) {
+  auto* writer = SiteDataRecorder::Data::FromPageNode(page_node)->writer();
+
+  if (!writer)
+    return nullptr;
+
+  return writer->impl_for_testing();
+}
+
+void MarkWebContentsAsLoadedInBackgroundInSiteDataDb(
+    content::WebContents* web_contents) {
+  base::RunLoop run_loop;
+  PerformanceManager::CallOnGraph(
+      FROM_HERE,
+      base::BindOnce(
+          [](base::WeakPtr<PageNode> page_node, base::OnceClosure closure) {
+            DCHECK(page_node);
+            auto* impl = GetSiteDataImplForPageNode(page_node.get());
+            DCHECK(impl);
+            impl->NotifySiteLoaded();
+            impl->NotifyLoadedSiteBackgrounded();
+            std::move(closure).Run();
+          },
+          PerformanceManager::GetPageNodeForWebContents(web_contents),
+          run_loop.QuitClosure()));
+  run_loop.Run();
+}
+
+void MarkWebContentsAsUnloadedInBackgroundInSiteDataDb(
+    content::WebContents* web_contents) {
+  base::RunLoop run_loop;
+  PerformanceManager::CallOnGraph(
+      FROM_HERE,
+      base::BindOnce(
+          [](base::WeakPtr<PageNode> page_node, base::OnceClosure closure) {
+            DCHECK(page_node);
+            auto* impl = GetSiteDataImplForPageNode(page_node.get());
+            DCHECK(impl);
+            impl->NotifySiteUnloaded(TabVisibility::kBackground);
+            std::move(closure).Run();
+          },
+          PerformanceManager::GetPageNodeForWebContents(web_contents),
+          run_loop.QuitClosure()));
+  run_loop.Run();
+}
+
+void ExpireSiteDataObservationWindowsForWebContents(
+    content::WebContents* web_contents) {
+  base::RunLoop run_loop;
+  PerformanceManager::CallOnGraph(
+      FROM_HERE,
+      base::BindOnce(
+          [](base::WeakPtr<PageNode> page_node, base::OnceClosure closure) {
+            DCHECK(page_node);
+            auto* impl = GetSiteDataImplForPageNode(page_node.get());
+            DCHECK(impl);
+            impl->ExpireAllObservationWindowsForTesting();
+            std::move(closure).Run();
+          },
+          PerformanceManager::GetPageNodeForWebContents(web_contents),
+          run_loop.QuitClosure()));
+  run_loop.Run();
+}
+
+}  // namespace performance_manager
diff --git a/chrome/browser/performance_manager/test_support/site_data_utils.h b/chrome/browser/performance_manager/test_support/site_data_utils.h
new file mode 100644
index 0000000..e384a16
--- /dev/null
+++ b/chrome/browser/performance_manager/test_support/site_data_utils.h
@@ -0,0 +1,74 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_PERFORMANCE_MANAGER_TEST_SUPPORT_SITE_DATA_UTILS_H_
+#define CHROME_BROWSER_PERFORMANCE_MANAGER_TEST_SUPPORT_SITE_DATA_UTILS_H_
+
+#include <memory>
+#include "base/auto_reset.h"
+#include "components/performance_manager/test_support/test_harness_helper.h"
+
+class Profile;
+
+namespace content {
+class WebContents;
+}
+
+namespace performance_manager {
+
+namespace internal {
+class SiteDataImpl;
+}
+
+class PageNode;
+
+// A test harness that initializes all the components required to use the
+// SiteData database in unit_tests. This doesn't support tests that use multiple
+// profiles. See comment in the PerformanceManagerTestHarnessHelper class for
+// directions on how this should be used in tests.
+class SiteDataTestHarness : public PerformanceManagerTestHarnessHelper {
+ public:
+  SiteDataTestHarness();
+  SiteDataTestHarness(const SiteDataTestHarness& other) = delete;
+  SiteDataTestHarness& operator=(const SiteDataTestHarness&) = delete;
+  ~SiteDataTestHarness() override;
+
+  void SetUp() override;
+  // During tear down it's necessary to release the global
+  // SiteDataCacheFacadeFactory instance to ensure that it doesn't get reused
+  // from tests to tests. Before doing this it's necessary to disassociate it
+  // from any profile. This harness assumes that tests are only using one
+  // profile.
+  void TearDown(Profile* profile);
+
+ private:
+  void TearDown() override;
+
+  // Use an in memory database to avoid creating some unnecessary files on disk.
+  std::unique_ptr<base::AutoReset<bool>> use_in_memory_db_for_testing_;
+  std::unique_ptr<base::AutoReset<bool>> enable_cache_factory_for_testing_;
+};
+
+// Return the SiteDataImpl instance backing a PageNode, this might be null if
+// this PageNode isn't loaded with a valid URL.
+//
+// This function can only be called from the graph's sequence.
+internal::SiteDataImpl* GetSiteDataImplForPageNode(PageNode* page_node);
+
+// Pretend that this WebContents has been loaded and is in background.
+void MarkWebContentsAsLoadedInBackgroundInSiteDataDb(
+    content::WebContents* web_contents);
+
+// Pretend that this WebContents has been unloaded and is in background.
+void MarkWebContentsAsUnloadedInBackgroundInSiteDataDb(
+    content::WebContents* web_contents);
+
+// Expire all the Site Data Database observation windows for a given
+// WebContents.
+void ExpireSiteDataObservationWindowsForWebContents(
+    content::WebContents* web_contents);
+
+}  // namespace performance_manager
+
+#endif  // CHROME_BROWSER_PERFORMANCE_MANAGER_TEST_SUPPORT_SITE_DATA_UTILS_H_
diff --git a/chrome/browser/plugins/flash_download_interception.cc b/chrome/browser/plugins/flash_download_interception.cc
index d2fa416..689c74c2 100644
--- a/chrome/browser/plugins/flash_download_interception.cc
+++ b/chrome/browser/plugins/flash_download_interception.cc
@@ -82,13 +82,16 @@
   if (flash_setting == CONTENT_SETTING_DETECT_IMPORTANT_CONTENT) {
     permissions::PermissionManager* manager =
         PermissionManagerFactory::GetForProfile(profile);
+    // TODO(https://crbug.com/1103176): Plumb the actual frame reference here
+    // (FlashDownloadInterception is created only for renderer-initiated
+    // navigations, so we always should have a reference to the originating
+    // RenderFrameHost)
     manager->RequestPermission(
         ContentSettingsType::PLUGINS, web_contents->GetMainFrame(),
         web_contents->GetLastCommittedURL(), true, base::DoNothing());
   } else if (flash_setting == CONTENT_SETTING_BLOCK) {
-    auto* settings =
-        content_settings::TabSpecificContentSettings::FromWebContents(
-            web_contents);
+    auto* settings = content_settings::TabSpecificContentSettings::GetForFrame(
+        web_contents->GetMainFrame());
     if (settings)
       settings->FlashDownloadBlocked();
   }
diff --git a/chrome/browser/prerender/chrome_prerender_contents_delegate.cc b/chrome/browser/prerender/chrome_prerender_contents_delegate.cc
new file mode 100644
index 0000000..cb5204c
--- /dev/null
+++ b/chrome/browser/prerender/chrome_prerender_contents_delegate.cc
@@ -0,0 +1,51 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/prerender/chrome_prerender_contents_delegate.h"
+
+#include "chrome/browser/chrome_notification_types.h"
+#include "chrome/browser/prerender/prerender_contents.h"
+#include "chrome/browser/prerender/prerender_manager.h"
+#include "chrome/browser/prerender/prerender_manager_factory.h"
+#include "chrome/browser/task_manager/web_contents_tags.h"
+#include "chrome/browser/ui/tab_helpers.h"
+#include "chrome/common/chrome_render_frame.mojom.h"
+#include "components/prerender/browser/prerender_histograms.h"
+#include "content/public/browser/web_contents.h"
+
+namespace prerender {
+
+// static
+PrerenderContents* ChromePrerenderContentsDelegate::FromWebContents(
+    content::WebContents* web_contents) {
+  if (!web_contents)
+    return nullptr;
+  PrerenderManager* prerender_manager =
+      PrerenderManagerFactory::GetForBrowserContext(
+          web_contents->GetBrowserContext());
+  if (!prerender_manager)
+    return nullptr;
+  return prerender_manager->GetPrerenderContents(web_contents);
+}
+
+void ChromePrerenderContentsDelegate::OnPrerenderContentsCreated(
+    content::WebContents* web_contents) {
+  DCHECK(web_contents);
+  TabHelpers::AttachTabHelpers(web_contents);
+
+  // Tag the prerender contents with the task manager specific prerender tag, so
+  // that it shows up in the task manager.
+  task_manager::WebContentsTags::CreateForPrerenderContents(web_contents);
+}
+
+void ChromePrerenderContentsDelegate::ReleasePrerenderContents(
+    content::WebContents* web_contents) {
+  DCHECK(web_contents);
+
+  // Clear the task manager tag we added earlier to our
+  // WebContents since it's no longer a prerender contents.
+  task_manager::WebContentsTags::ClearTag(web_contents);
+}
+
+}  // namespace prerender
diff --git a/chrome/browser/prerender/chrome_prerender_contents_delegate.h b/chrome/browser/prerender/chrome_prerender_contents_delegate.h
new file mode 100644
index 0000000..fe90149a
--- /dev/null
+++ b/chrome/browser/prerender/chrome_prerender_contents_delegate.h
@@ -0,0 +1,36 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+#ifndef CHROME_BROWSER_PRERENDER_CHROME_PRERENDER_CONTENTS_DELEGATE_H_
+#define CHROME_BROWSER_PRERENDER_CHROME_PRERENDER_CONTENTS_DELEGATE_H_
+
+#include "chrome/browser/prerender/prerender_contents_delegate.h"
+#include "mojo/public/cpp/bindings/associated_remote.h"
+#include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
+
+namespace content {
+class WebContents;
+}
+
+namespace prerender {
+
+class PrerenderContents;
+
+class ChromePrerenderContentsDelegate : public PrerenderContentsDelegate {
+ public:
+  // Returns a PrerenderContents from the given web_contents, if it's used for
+  // prerendering. Otherwise returns nullptr. Handles a nullptr input for
+  // convenience.
+  static PrerenderContents* FromWebContents(content::WebContents* web_contents);
+
+  ChromePrerenderContentsDelegate() = default;
+  ~ChromePrerenderContentsDelegate() override = default;
+
+  // PrerenderContentsDelegate overrides.
+  void OnPrerenderContentsCreated(content::WebContents* web_contents) override;
+  void ReleasePrerenderContents(content::WebContents* web_contents) override;
+};
+
+}  // namespace prerender
+
+#endif  // CHROME_BROWSER_PRERENDER_CHROME_PRERENDER_CONTENTS_DELEGATE_H_
diff --git a/chrome/browser/prerender/prerender_contents.cc b/chrome/browser/prerender/prerender_contents.cc
index 8279210..f265b722 100644
--- a/chrome/browser/prerender/prerender_contents.cc
+++ b/chrome/browser/prerender/prerender_contents.cc
@@ -15,24 +15,17 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/task/post_task.h"
 #include "build/build_config.h"
-#include "chrome/browser/chrome_notification_types.h"
-#include "chrome/browser/history/history_tab_helper.h"
-#include "chrome/browser/prerender/prerender_field_trial.h"
-#include "chrome/browser/prerender/prerender_handle.h"
+#include "chrome/browser/prerender/prerender_contents_delegate.h"
 #include "chrome/browser/prerender/prerender_manager.h"
-#include "chrome/browser/prerender/prerender_manager_factory.h"
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/task_manager/web_contents_tags.h"
-#include "chrome/browser/ui/tab_helpers.h"
-#include "chrome/common/chrome_render_frame.mojom.h"
-#include "components/history/core/browser/history_types.h"
 #include "components/prerender/common/prerender_final_status.h"
 #include "components/prerender/common/prerender_util.h"
 #include "components/prerender/common/render_frame_prerender_messages.mojom.h"
+#include "content/public/browser/browser_context.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/navigation_handle.h"
 #include "content/public/browser/notification_service.h"
+#include "content/public/browser/notification_types.h"
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/render_process_host.h"
 #include "content/public/browser/render_view_host.h"
@@ -41,7 +34,6 @@
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_contents_delegate.h"
 #include "content/public/common/frame_navigate_params.h"
-#include "mojo/public/cpp/bindings/associated_remote.h"
 #include "net/http/http_response_headers.h"
 #include "services/resource_coordinator/public/cpp/memory_instrumentation/memory_instrumentation.h"
 #include "services/service_manager/public/cpp/binder_registry.h"
@@ -60,13 +52,15 @@
 class PrerenderContentsFactoryImpl : public PrerenderContents::Factory {
  public:
   PrerenderContents* CreatePrerenderContents(
+      std::unique_ptr<PrerenderContentsDelegate> delegate,
       PrerenderManager* prerender_manager,
-      Profile* profile,
+      content::BrowserContext* browser_context,
       const GURL& url,
       const content::Referrer& referrer,
       const base::Optional<url::Origin>& initiator_origin,
       Origin origin) override {
-    return new PrerenderContents(prerender_manager, profile, url, referrer,
+    return new PrerenderContents(std::move(delegate), prerender_manager,
+                                 browser_context, url, referrer,
                                  initiator_origin, origin);
   }
 };
@@ -129,8 +123,9 @@
 PrerenderContents::Observer::~Observer() {}
 
 PrerenderContents::PrerenderContents(
+    std::unique_ptr<PrerenderContentsDelegate> delegate,
     PrerenderManager* prerender_manager,
-    Profile* profile,
+    content::BrowserContext* browser_context,
     const GURL& url,
     const content::Referrer& referrer,
     const base::Optional<url::Origin>& initiator_origin,
@@ -138,10 +133,11 @@
     : prerender_mode_(prerender::mojom::PrerenderMode::kNoPrerender),
       prerendering_has_started_(false),
       prerender_manager_(prerender_manager),
+      delegate_(std::move(delegate)),
       prerender_url_(url),
       referrer_(referrer),
       initiator_origin_(initiator_origin),
-      profile_(profile),
+      browser_context_(browser_context),
       has_finished_loading_(false),
       final_status_(FINAL_STATUS_UNKNOWN),
       prerendering_has_been_cancelled_(false),
@@ -187,23 +183,10 @@
   return new PrerenderContentsFactoryImpl();
 }
 
-// static
-PrerenderContents* PrerenderContents::FromWebContents(
-    content::WebContents* web_contents) {
-  if (!web_contents)
-    return NULL;
-  PrerenderManager* prerender_manager =
-      PrerenderManagerFactory::GetForBrowserContext(
-          web_contents->GetBrowserContext());
-  if (!prerender_manager)
-    return NULL;
-  return prerender_manager->GetPrerenderContents(web_contents);
-}
-
 void PrerenderContents::StartPrerendering(
     const gfx::Rect& bounds,
     SessionStorageNamespace* session_storage_namespace) {
-  DCHECK(profile_);
+  DCHECK(browser_context_);
   DCHECK(!bounds.IsEmpty());
   DCHECK(!prerendering_has_started_);
   DCHECK(!prerender_contents_);
@@ -218,13 +201,8 @@
   prerendering_has_started_ = true;
 
   prerender_contents_ = CreateWebContents(session_storage_namespace);
-  TabHelpers::AttachTabHelpers(prerender_contents_.get());
   content::WebContentsObserver::Observe(prerender_contents_.get());
-
-  // Tag the prerender contents with the task manager specific prerender tag, so
-  // that it shows up in the task manager.
-  task_manager::WebContentsTags::CreateForPrerenderContents(
-      prerender_contents_.get());
+  delegate_->OnPrerenderContentsCreated(prerender_contents_.get());
 
   web_contents_delegate_.reset(new WebContentsDelegateImpl(this));
   prerender_contents_.get()->SetDelegate(web_contents_delegate_.get());
@@ -244,10 +222,6 @@
 
   NotifyPrerenderStart();
 
-  // Close ourselves when the application is shutting down.
-  notification_registrar_.Add(this, chrome::NOTIFICATION_APP_TERMINATING,
-                              content::NotificationService::AllSources());
-
   // Register to inform new RenderViews that we're prerendering.
   notification_registrar_.Add(
       this, content::NOTIFICATION_WEB_CONTENTS_RENDER_VIEW_HOST_CREATED,
@@ -320,12 +294,6 @@
                                 const content::NotificationSource& source,
                                 const content::NotificationDetails& details) {
   switch (type) {
-    // TODO(davidben): Try to remove this in favor of relying on
-    // FINAL_STATUS_PROFILE_DESTROYED.
-    case chrome::NOTIFICATION_APP_TERMINATING:
-      Destroy(FINAL_STATUS_APP_TERMINATING);
-      return;
-
     case content::NOTIFICATION_WEB_CONTENTS_RENDER_VIEW_HOST_CREATED: {
       if (prerender_contents_.get()) {
         DCHECK_EQ(content::Source<WebContents>(source).ptr(),
@@ -362,7 +330,8 @@
   content::SessionStorageNamespaceMap session_storage_namespace_map;
   session_storage_namespace_map[std::string()] = session_storage_namespace;
   return WebContents::CreateWithSessionStorage(
-      WebContents::CreateParams(profile_), session_storage_namespace_map);
+      WebContents::CreateParams(browser_context_),
+      session_storage_namespace_map);
 }
 
 void PrerenderContents::NotifyPrerenderStart() {
@@ -595,9 +564,7 @@
   prerender_contents_->SetDelegate(nullptr);
   content::WebContentsObserver::Observe(nullptr);
 
-  // Clear the task manager tag we added earlier to our
-  // WebContents since it's no longer a prerender contents.
-  task_manager::WebContentsTags::ClearTag(prerender_contents_.get());
+  delegate_->ReleasePrerenderContents(prerender_contents_.get());
 
   return std::move(prerender_contents_);
 }
@@ -607,17 +574,6 @@
                              : nullptr;
 }
 
-void PrerenderContents::DidNavigate(
-    const history::HistoryAddPageArgs& add_page_args) {
-  add_page_vector_.push_back(add_page_args);
-}
-
-void PrerenderContents::CommitHistory(WebContents* tab) {
-  HistoryTabHelper* history_tab_helper = HistoryTabHelper::FromWebContents(tab);
-  for (size_t i = 0; i < add_page_vector_.size(); ++i)
-    history_tab_helper->UpdateHistoryForNavigation(add_page_vector_[i]);
-}
-
 std::unique_ptr<base::DictionaryValue> PrerenderContents::GetAsValue() const {
   if (!prerender_contents_)
     return nullptr;
diff --git a/chrome/browser/prerender/prerender_contents.h b/chrome/browser/prerender/prerender_contents.h
index 15cde43..516e2b92 100644
--- a/chrome/browser/prerender/prerender_contents.h
+++ b/chrome/browser/prerender/prerender_contents.h
@@ -18,6 +18,7 @@
 #include "base/optional.h"
 #include "base/time/time.h"
 #include "base/values.h"
+#include "chrome/browser/prerender/prerender_contents_delegate.h"
 #include "components/prerender/common/prerender_canceler.mojom.h"
 #include "components/prerender/common/prerender_final_status.h"
 #include "components/prerender/common/prerender_origin.h"
@@ -30,22 +31,17 @@
 #include "ui/gfx/geometry/rect.h"
 #include "url/origin.h"
 
-class Profile;
-
 namespace base {
 class ProcessMetrics;
 }
 
 namespace content {
+class BrowserContext;
 class RenderViewHost;
 class SessionStorageNamespace;
 class WebContents;
 }  // namespace content
 
-namespace history {
-struct HistoryAddPageArgs;
-}
-
 namespace memory_instrumentation {
 class GlobalMemoryDump;
 }
@@ -65,11 +61,12 @@
     Factory() {}
     virtual ~Factory() {}
 
-    // Ownership is not transfered through this interface as prerender_manager
-    // and profile are stored as weak pointers.
+    // Ownership is not transferred through this interface as prerender_manager
+    // and browser_context are stored as weak pointers.
     virtual PrerenderContents* CreatePrerenderContents(
+        std::unique_ptr<PrerenderContentsDelegate> delegate,
         PrerenderManager* prerender_manager,
-        Profile* profile,
+        content::BrowserContext* browser_context,
         const GURL& url,
         const content::Referrer& referrer,
         const base::Optional<url::Origin>& initiator_origin,
@@ -123,11 +120,6 @@
 
   static Factory* CreateFactory();
 
-  // Returns a PrerenderContents from the given web_contents, if it's used for
-  // prerendering. Otherwise returns NULL. Handles a NULL input for
-  // convenience.
-  static PrerenderContents* FromWebContents(content::WebContents* web_contents);
-
   // Start rendering the contents in the prerendered state. If
   // |is_control_group| is true, this will go through some of the mechanics of
   // starting a prerender, without actually creating the RenderView. |bounds|
@@ -210,15 +202,6 @@
   // PrerenderManager's pending deletes list.
   void Destroy(FinalStatus reason);
 
-  // Called by the history tab helper with the information that it woudl have
-  // added to the history service had this web contents not been used for
-  // prerendering.
-  void DidNavigate(const history::HistoryAddPageArgs& add_page_args);
-
-  // Applies all the URL history encountered during prerendering to the
-  // new tab.
-  void CommitHistory(content::WebContents* tab);
-
   std::unique_ptr<base::DictionaryValue> GetAsValue() const;
 
   // Marks prerender as used and releases any throttled resource requests.
@@ -238,8 +221,9 @@
       mojo::PendingReceiver<prerender::mojom::PrerenderCanceler> receiver);
 
  protected:
-  PrerenderContents(PrerenderManager* prerender_manager,
-                    Profile* profile,
+  PrerenderContents(std::unique_ptr<PrerenderContentsDelegate> delegate,
+                    PrerenderManager* prerender_manager,
+                    content::BrowserContext* browser_context,
                     const GURL& url,
                     const content::Referrer& referrer,
                     const base::Optional<url::Origin>& initiator_origin,
@@ -304,6 +288,9 @@
   // The prerender manager owning this object.
   PrerenderManager* prerender_manager_;
 
+  // The delegate that content embedders use to override this class's logic.
+  std::unique_ptr<PrerenderContentsDelegate> delegate_;
+
   // The URL being prerendered.
   GURL prerender_url_;
 
@@ -314,8 +301,8 @@
   // is browser initiated.
   base::Optional<url::Origin> initiator_origin_;
 
-  // The profile being used
-  Profile* profile_;
+  // The browser context being used
+  content::BrowserContext* browser_context_;
 
   content::NotificationRegistrar notification_registrar_;
 
@@ -349,11 +336,6 @@
   // The bounds of the WebView from the launching page.
   gfx::Rect bounds_;
 
-  typedef std::vector<history::HistoryAddPageArgs> AddPageVector;
-
-  // Caches pages to be added to the history.
-  AddPageVector add_page_vector_;
-
   // A running tally of the number of bytes this prerender has caused to be
   // transferred over the network for resources.  Updated with AddNetworkBytes.
   int64_t network_bytes_;
diff --git a/chrome/browser/prerender/prerender_contents_delegate.h b/chrome/browser/prerender/prerender_contents_delegate.h
new file mode 100644
index 0000000..df22257
--- /dev/null
+++ b/chrome/browser/prerender/prerender_contents_delegate.h
@@ -0,0 +1,33 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_PRERENDER_PRERENDER_CONTENTS_DELEGATE_H_
+#define CHROME_BROWSER_PRERENDER_PRERENDER_CONTENTS_DELEGATE_H_
+
+#include "components/prerender/common/prerender_types.mojom.h"
+#include "content/public/browser/render_frame_host.h"
+
+namespace content {
+class WebContents;
+}
+
+namespace prerender {
+
+// PrerenderContentsDelegate allows a content embedder to customize
+// PrerenderContents logic.
+class PrerenderContentsDelegate {
+ public:
+  virtual ~PrerenderContentsDelegate() = default;
+
+  // Handle creation of new PrerenderContents.
+  virtual void OnPrerenderContentsCreated(
+      content::WebContents* web_contents) = 0;
+
+  // Prepare for |web_contents| to no longer be PrerenderContents.
+  virtual void ReleasePrerenderContents(content::WebContents* web_contents) = 0;
+};
+
+}  // namespace prerender
+
+#endif  // CHROME_BROWSER_PRERENDER_PRERENDER_CONTENTS_DELEGATE_H_
diff --git a/chrome/browser/prerender/prerender_manager.cc b/chrome/browser/prerender/prerender_manager.cc
index e112b7c9..e5c55ba0 100644
--- a/chrome/browser/prerender/prerender_manager.cc
+++ b/chrome/browser/prerender/prerender_manager.cc
@@ -32,11 +32,13 @@
 #include "base/timer/elapsed_timer.h"
 #include "base/values.h"
 #include "chrome/browser/net/prediction_options.h"
+#include "chrome/browser/predictors/loading_predictor.h"
+#include "chrome/browser/predictors/loading_predictor_factory.h"
+#include "chrome/browser/prerender/chrome_prerender_contents_delegate.h"
 #include "chrome/browser/prerender/prerender_contents.h"
 #include "chrome/browser/prerender/prerender_field_trial.h"
 #include "chrome/browser/prerender/prerender_handle.h"
 #include "chrome/browser/prerender/prerender_manager_delegate.h"
-#include "chrome/browser/prerender/prerender_manager_factory.h"
 #include "chrome/browser/prerender/prerender_tab_helper.h"
 #include "chrome/browser/prerender/prerender_util.h"
 #include "chrome/browser/profiles/profile.h"
@@ -793,7 +795,8 @@
     Origin origin) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   return base::WrapUnique(prerender_contents_factory_->CreatePrerenderContents(
-      this, profile_, url, referrer, initiator_origin, origin));
+      std::make_unique<ChromePrerenderContentsDelegate>(), this, profile_, url,
+      referrer, initiator_origin, origin));
 }
 
 void PrerenderManager::SortActivePrerenders() {
diff --git a/chrome/browser/prerender/prerender_test_utils.cc b/chrome/browser/prerender/prerender_test_utils.cc
index 20136364..0ac1a9b 100644
--- a/chrome/browser/prerender/prerender_test_utils.cc
+++ b/chrome/browser/prerender/prerender_test_utils.cc
@@ -137,15 +137,16 @@
 
 TestPrerenderContents::TestPrerenderContents(
     PrerenderManager* prerender_manager,
-    Profile* profile,
+    content::BrowserContext* browser_context,
     const GURL& url,
     const content::Referrer& referrer,
     const base::Optional<url::Origin>& initiator_origin,
     Origin origin,
     FinalStatus expected_final_status,
     bool ignore_final_status)
-    : PrerenderContents(prerender_manager,
-                        profile,
+    : PrerenderContents(std::make_unique<ChromePrerenderContentsDelegate>(),
+                        prerender_manager,
+                        browser_context,
                         url,
                         referrer,
                         initiator_origin,
@@ -388,8 +389,9 @@
 }
 
 PrerenderContents* TestPrerenderContentsFactory::CreatePrerenderContents(
+    std::unique_ptr<PrerenderContentsDelegate> delegate,
     PrerenderManager* prerender_manager,
-    Profile* profile,
+    content::BrowserContext* browser_context,
     const GURL& url,
     const content::Referrer& referrer,
     const base::Optional<url::Origin>& initiator_origin,
@@ -400,8 +402,8 @@
     expected_contents_queue_.pop_front();
   }
   TestPrerenderContents* contents = new TestPrerenderContents(
-      prerender_manager, profile, url, referrer, initiator_origin, origin,
-      expected.final_status, expected.ignore);
+      prerender_manager, browser_context, url, referrer, initiator_origin,
+      origin, expected.final_status, expected.ignore);
   if (expected.handle)
     expected.handle->OnPrerenderCreated(contents);
   return contents;
diff --git a/chrome/browser/prerender/prerender_test_utils.h b/chrome/browser/prerender/prerender_test_utils.h
index 3c0b13c..5f6a0bd 100644
--- a/chrome/browser/prerender/prerender_test_utils.h
+++ b/chrome/browser/prerender/prerender_test_utils.h
@@ -20,7 +20,9 @@
 #include "base/synchronization/lock.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "chrome/browser/external_protocol/external_protocol_handler.h"
+#include "chrome/browser/prerender/chrome_prerender_contents_delegate.h"
 #include "chrome/browser/prerender/prerender_contents.h"
+#include "chrome/browser/prerender/prerender_contents_delegate.h"
 #include "chrome/browser/prerender/prerender_manager.h"
 #include "chrome/browser/safe_browsing/test_safe_browsing_service.h"
 #include "chrome/test/base/in_process_browser_test.h"
@@ -81,7 +83,7 @@
                               public content::RenderWidgetHostObserver {
  public:
   TestPrerenderContents(PrerenderManager* prerender_manager,
-                        Profile* profile,
+                        content::BrowserContext* browser_context,
                         const GURL& url,
                         const content::Referrer& referrer,
                         const base::Optional<url::Origin>& initiator_origin,
@@ -261,8 +263,9 @@
   void IgnorePrerenderContents();
 
   PrerenderContents* CreatePrerenderContents(
+      std::unique_ptr<PrerenderContentsDelegate> delegate,
       PrerenderManager* prerender_manager,
-      Profile* profile,
+      content::BrowserContext* browser_context,
       const GURL& url,
       const content::Referrer& referrer,
       const base::Optional<url::Origin>& initiator_origin,
diff --git a/chrome/browser/prerender/prerender_unittest.cc b/chrome/browser/prerender/prerender_unittest.cc
index 1e73ab2..b9ef96d8 100644
--- a/chrome/browser/prerender/prerender_unittest.cc
+++ b/chrome/browser/prerender/prerender_unittest.cc
@@ -309,7 +309,8 @@
     Origin origin,
     const base::Optional<url::Origin>& initiator_origin,
     FinalStatus expected_final_status)
-    : PrerenderContents(test_prerender_manager,
+    : PrerenderContents(std::make_unique<ChromePrerenderContentsDelegate>(),
+                        test_prerender_manager,
                         nullptr,
                         url,
                         Referrer(),
diff --git a/chrome/browser/profiles/DEPS b/chrome/browser/profiles/DEPS
new file mode 100644
index 0000000..0a1ba26
--- /dev/null
+++ b/chrome/browser/profiles/DEPS
@@ -0,0 +1,7 @@
+specific_include_rules = {
+  "chrome_browser_main_extra_parts_profiles.cc": [
+    # This is required to create the SiteDataCacheFacadeFactory when a profile
+    # is created.
+    "+chrome/browser/performance_manager/persistence/site_data/site_data_cache_facade_factory.h"
+  ],
+}
diff --git a/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc b/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc
index ea3d371..6dc8c6a 100644
--- a/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc
+++ b/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc
@@ -110,8 +110,8 @@
 #include "chrome/browser/feedback/feedback_uploader_factory_chrome.h"
 #include "chrome/browser/media/feeds/media_feeds_service_factory.h"
 #include "chrome/browser/metrics/desktop_session_duration/desktop_profile_session_durations_service_factory.h"
+#include "chrome/browser/performance_manager/persistence/site_data/site_data_cache_facade_factory.h"
 #include "chrome/browser/profiles/profile_theme_update_service_factory.h"
-#include "chrome/browser/resource_coordinator/local_site_characteristics_data_store_factory.h"
 #include "chrome/browser/search/instant_service_factory.h"
 #include "chrome/browser/storage/storage_notification_service_factory.h"
 #include "chrome/browser/ui/global_error/global_error_service_factory.h"
@@ -335,7 +335,7 @@
   ProtocolHandlerRegistryFactory::GetInstance();
   RendererUpdaterFactory::GetInstance();
 #if !defined(OS_ANDROID)
-  resource_coordinator::LocalSiteCharacteristicsDataStoreFactory::GetInstance();
+  performance_manager::SiteDataCacheFacadeFactory::GetInstance();
 #endif
 #if BUILDFLAG(FULL_SAFE_BROWSING)
   safe_browsing::AdvancedProtectionStatusManagerFactory::GetInstance();
diff --git a/chrome/browser/profiles/profile_attributes_entry.cc b/chrome/browser/profiles/profile_attributes_entry.cc
index c0d9664..6b79bd1c 100644
--- a/chrome/browser/profiles/profile_attributes_entry.cc
+++ b/chrome/browser/profiles/profile_attributes_entry.cc
@@ -85,6 +85,19 @@
 
 }  // namespace
 
+bool ProfileThemeColors::operator==(const ProfileThemeColors& other) const {
+  return std::tie(this->profile_highlight_color,
+                  this->default_avatar_fill_color,
+                  this->default_avatar_stroke_color) ==
+         std::tie(other.profile_highlight_color,
+                  other.default_avatar_fill_color,
+                  other.default_avatar_stroke_color);
+}
+
+bool ProfileThemeColors::operator!=(const ProfileThemeColors& other) const {
+  return !(*this == other);
+}
+
 const char ProfileAttributesEntry::kSupervisedUserId[] = "managed_user_id";
 const char ProfileAttributesEntry::kIsOmittedFromProfileListKey[] =
     "is_omitted_from_profile_list";
diff --git a/chrome/browser/profiles/profile_attributes_entry.h b/chrome/browser/profiles/profile_attributes_entry.h
index 1575109..36426b28 100644
--- a/chrome/browser/profiles/profile_attributes_entry.h
+++ b/chrome/browser/profiles/profile_attributes_entry.h
@@ -46,6 +46,10 @@
   SkColor profile_highlight_color;
   SkColor default_avatar_fill_color;
   SkColor default_avatar_stroke_color;
+
+  // Equality operators for testing.
+  bool operator==(const ProfileThemeColors& other) const;
+  bool operator!=(const ProfileThemeColors& other) const;
 };
 
 class ProfileAttributesEntry {
diff --git a/chrome/browser/profiles/profile_attributes_storage_unittest.cc b/chrome/browser/profiles/profile_attributes_storage_unittest.cc
index 09ca6bd..19a97d2 100644
--- a/chrome/browser/profiles/profile_attributes_storage_unittest.cc
+++ b/chrome/browser/profiles/profile_attributes_storage_unittest.cc
@@ -931,12 +931,7 @@
   base::Optional<ProfileThemeColors> actual_colors =
       entry->GetProfileThemeColors();
   ASSERT_TRUE(actual_colors.has_value());
-  EXPECT_EQ(colors.profile_highlight_color,
-            actual_colors->profile_highlight_color);
-  EXPECT_EQ(colors.default_avatar_fill_color,
-            actual_colors->default_avatar_fill_color);
-  EXPECT_EQ(colors.default_avatar_stroke_color,
-            actual_colors->default_avatar_stroke_color);
+  EXPECT_EQ(colors, actual_colors);
 
   entry->SetProfileThemeColors(base::nullopt);
   EXPECT_EQ(base::nullopt, entry->GetProfileThemeColors());
diff --git a/chrome/browser/profiles/profile_theme_update_service_browsertest.cc b/chrome/browser/profiles/profile_theme_update_service_browsertest.cc
new file mode 100644
index 0000000..f60ee2f
--- /dev/null
+++ b/chrome/browser/profiles/profile_theme_update_service_browsertest.cc
@@ -0,0 +1,85 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/optional.h"
+#include "chrome/browser/profiles/profile_theme_update_service.h"
+
+#include "base/test/scoped_feature_list.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/profiles/profile_attributes_entry.h"
+#include "chrome/browser/profiles/profile_attributes_storage.h"
+#include "chrome/browser/profiles/profile_manager.h"
+#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/ui_features.h"
+#include "chrome/test/base/in_process_browser_test.h"
+#include "content/public/test/browser_test.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/skia/include/core/SkColor.h"
+
+class ProfileThemeUpdateServiceBrowserTest : public InProcessBrowserTest {
+ public:
+  ProfileThemeUpdateServiceBrowserTest() {
+    feature_list_.InitAndEnableFeature(features::kNewProfilePicker);
+  }
+
+  ProfileAttributesEntry* GetProfileAttributesEntry() {
+    ProfileAttributesEntry* entry;
+    CHECK(browser());
+    CHECK(browser()->profile());
+    CHECK(g_browser_process->profile_manager()
+              ->GetProfileAttributesStorage()
+              .GetProfileAttributesWithPath(browser()->profile()->GetPath(),
+                                            &entry));
+    return entry;
+  }
+
+  ThemeService* theme_service() {
+    return ThemeServiceFactory::GetForProfile(browser()->profile());
+  }
+
+ private:
+  base::test::ScopedFeatureList feature_list_;
+};
+
+// Tests that the profile theme colors are updated when an autogenerated theme
+// is set up.
+IN_PROC_BROWSER_TEST_F(ProfileThemeUpdateServiceBrowserTest,
+                       PRE_AutogeneratedTheme) {
+  EXPECT_FALSE(
+      GetProfileAttributesEntry()->GetProfileThemeColors().has_value());
+
+  theme_service()->BuildAutogeneratedThemeFromColor(SK_ColorDKGRAY);
+  base::Optional<ProfileThemeColors> theme_colors =
+      GetProfileAttributesEntry()->GetProfileThemeColors();
+  EXPECT_TRUE(theme_colors.has_value());
+
+  // Check that a switch to another autogenerated theme updates the colors.
+  theme_service()->BuildAutogeneratedThemeFromColor(SK_ColorMAGENTA);
+  base::Optional<ProfileThemeColors> theme_colors2 =
+      GetProfileAttributesEntry()->GetProfileThemeColors();
+  EXPECT_TRUE(theme_colors2.has_value());
+  EXPECT_NE(theme_colors, theme_colors2);
+
+  // Reset the cached colors to test that they're recreated on the next startup.
+  GetProfileAttributesEntry()->SetProfileThemeColors(base::nullopt);
+}
+
+// Tests that the profile theme colors are updated on startup.
+IN_PROC_BROWSER_TEST_F(ProfileThemeUpdateServiceBrowserTest,
+                       AutogeneratedTheme) {
+  EXPECT_TRUE(GetProfileAttributesEntry()->GetProfileThemeColors().has_value());
+}
+
+// Tests that switching to the default theme resets the colors.
+IN_PROC_BROWSER_TEST_F(ProfileThemeUpdateServiceBrowserTest, DefaultTheme) {
+  theme_service()->BuildAutogeneratedThemeFromColor(SK_ColorDKGRAY);
+  EXPECT_TRUE(GetProfileAttributesEntry()->GetProfileThemeColors().has_value());
+
+  theme_service()->UseDefaultTheme();
+  EXPECT_FALSE(
+      GetProfileAttributesEntry()->GetProfileThemeColors().has_value());
+}
diff --git a/chrome/browser/push_messaging/push_messaging_app_identifier.cc b/chrome/browser/push_messaging/push_messaging_app_identifier.cc
index b5d68923..a801aa85 100644
--- a/chrome/browser/push_messaging/push_messaging_app_identifier.cc
+++ b/chrome/browser/push_messaging/push_messaging_app_identifier.cc
@@ -34,16 +34,16 @@
 
 std::string FromTimeToString(base::Time time) {
   DCHECK(!time.is_null());
-  return base::NumberToString(time.ToDeltaSinceWindowsEpoch().InSeconds());
+  return base::NumberToString(time.ToDeltaSinceWindowsEpoch().InMilliseconds());
 }
 
 bool FromStringToTime(const std::string& time_string,
                       base::Optional<base::Time>* time) {
   DCHECK(!time_string.empty());
-  int64_t seconds;
-  if (base::StringToInt64(time_string, &seconds) && seconds > 0) {
+  int64_t milliseconds;
+  if (base::StringToInt64(time_string, &milliseconds) && milliseconds > 0) {
     *time = base::make_optional(base::Time::FromDeltaSinceWindowsEpoch(
-        base::TimeDelta::FromSeconds(seconds)));
+        base::TimeDelta::FromMilliseconds(milliseconds)));
     return true;
   }
   return false;
@@ -75,9 +75,8 @@
     return false;
 
   *origin = GURL(parts[0]);
-  if (!origin->is_valid()) {
+  if (!origin->is_valid())
     return false;
-  }
 
   if (parts.size() == 3)
     return FromStringToTime(parts[2], expiration_time);
diff --git a/chrome/browser/push_messaging/push_messaging_app_identifier.h b/chrome/browser/push_messaging/push_messaging_app_identifier.h
index 3e48380a3..d7e9dac 100644
--- a/chrome/browser/push_messaging/push_messaging_app_identifier.h
+++ b/chrome/browser/push_messaging/push_messaging_app_identifier.h
@@ -100,6 +100,10 @@
     return service_worker_registration_id_;
   }
 
+  void set_expiration_time(const base::Optional<base::Time>& expiration_time) {
+    expiration_time_ = expiration_time;
+  }
+
   base::Optional<base::Time> expiration_time() const {
     DCHECK(!is_null());
     return expiration_time_;
diff --git a/chrome/browser/push_messaging/push_messaging_browsertest.cc b/chrome/browser/push_messaging/push_messaging_browsertest.cc
index fa8645d..3e889aa 100644
--- a/chrome/browser/push_messaging/push_messaging_browsertest.cc
+++ b/chrome/browser/push_messaging/push_messaging_browsertest.cc
@@ -2627,7 +2627,8 @@
     : public PushMessagingBrowserTest {
  public:
   PushMessagingDisallowSenderIdsBrowserTest() {
-    scoped_feature_list_.InitAndEnableFeature(kPushMessagingDisallowSenderIDs);
+    scoped_feature_list_.InitAndEnableFeature(
+        features::kPushMessagingDisallowSenderIDs);
   }
 
   ~PushMessagingDisallowSenderIdsBrowserTest() override = default;
@@ -2660,3 +2661,109 @@
       "supported, please upgrade to VAPID authentication instead",
       script_result);
 }
+
+class PushSubscriptionWithExpirationTimeTest : public PushMessagingBrowserTest {
+ public:
+  PushSubscriptionWithExpirationTimeTest() {
+    scoped_feature_list_.InitAndEnableFeature(
+        features::kPushSubscriptionWithExpirationTime);
+  }
+
+  ~PushSubscriptionWithExpirationTimeTest() override = default;
+
+  // Checks whether |expiration_time| lies in the future and is in the
+  // valid format (seconds elapsed since Unix time)
+  bool IsExpirationTimeValid(const std::string& expiration_time);
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+bool PushSubscriptionWithExpirationTimeTest::IsExpirationTimeValid(
+    const std::string& expiration_time) {
+  int64_t output;
+  if (!base::StringToInt64(expiration_time, &output))
+    return false;
+  return base::Time::Now().ToJsTimeIgnoringNull() < output;
+}
+
+IN_PROC_BROWSER_TEST_F(PushSubscriptionWithExpirationTimeTest,
+                       SubscribeGetSubscriptionWithExpirationTime) {
+  std::string script_result;
+
+  ASSERT_TRUE(RunScript("registerServiceWorker()", &script_result));
+  ASSERT_EQ("ok - service worker registered", script_result);
+
+  ASSERT_NO_FATAL_FAILURE(RequestAndAcceptPermission());
+
+  LoadTestPage();  // Reload to become controlled.
+
+  ASSERT_TRUE(RunScript("isControlled()", &script_result));
+  ASSERT_EQ("true - is controlled", script_result);
+
+  // Subscribe with expiration time enabled, should get a subscription with
+  // expiration time in the future back
+  std::string subscription_expiration_time;
+  ASSERT_TRUE(RunScript("documentSubscribePushGetExpirationTime()",
+                        &subscription_expiration_time));
+  EXPECT_TRUE(IsExpirationTimeValid(subscription_expiration_time));
+
+  std::string get_subscription_expiration_time;
+  // Get subscription should also yield a subscription with expiration time
+  ASSERT_TRUE(RunScript("GetSubscriptionExpirationTime()",
+                        &get_subscription_expiration_time));
+  EXPECT_TRUE(IsExpirationTimeValid(get_subscription_expiration_time));
+  // Both methods should return the same expiration time
+  ASSERT_EQ(subscription_expiration_time, get_subscription_expiration_time);
+}
+
+IN_PROC_BROWSER_TEST_F(PushSubscriptionWithExpirationTimeTest,
+                       GetSubscriptionWithExpirationTime) {
+  std::string script_result;
+
+  ASSERT_NO_FATAL_FAILURE(SubscribeSuccessfully());
+
+  ASSERT_TRUE(RunScript("hasSubscription()", &script_result));
+  EXPECT_EQ("true - subscribed", script_result);
+
+  // Get subscription should also yield a subscription with expiration time
+  ASSERT_TRUE(RunScript("GetSubscriptionExpirationTime()", &script_result));
+  EXPECT_TRUE(IsExpirationTimeValid(script_result));
+}
+
+class PushSubscriptionWithoutExpirationTimeTest
+    : public PushMessagingBrowserTest {
+ public:
+  PushSubscriptionWithoutExpirationTimeTest() {
+    // Override current feature list to ensure having
+    // |kPushSubscriptionWithExpirationTime| disabled
+    scoped_feature_list_.InitAndDisableFeature(
+        features::kPushSubscriptionWithExpirationTime);
+  }
+
+  ~PushSubscriptionWithoutExpirationTimeTest() override = default;
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+IN_PROC_BROWSER_TEST_F(PushSubscriptionWithoutExpirationTimeTest,
+                       SubscribeDocumentExpirationTimeNull) {
+  std::string script_result;
+
+  ASSERT_TRUE(RunScript("registerServiceWorker()", &script_result));
+  ASSERT_EQ("ok - service worker registered", script_result);
+
+  ASSERT_NO_FATAL_FAILURE(RequestAndAcceptPermission());
+
+  LoadTestPage();  // Reload to become controlled.
+
+  ASSERT_TRUE(RunScript("isControlled()", &script_result));
+  ASSERT_EQ("true - is controlled", script_result);
+
+  // When |features::kPushSubscriptionWithExpirationTime| is disabled,
+  // expiration time should be null
+  ASSERT_TRUE(
+      RunScript("documentSubscribePushGetExpirationTime()", &script_result));
+  EXPECT_EQ("null", script_result);
+}
diff --git a/chrome/browser/push_messaging/push_messaging_constants.h b/chrome/browser/push_messaging/push_messaging_constants.h
index f6c9e4bf..41094fdc 100644
--- a/chrome/browser/push_messaging/push_messaging_constants.h
+++ b/chrome/browser/push_messaging/push_messaging_constants.h
@@ -5,10 +5,18 @@
 #ifndef CHROME_BROWSER_PUSH_MESSAGING_PUSH_MESSAGING_CONSTANTS_H_
 #define CHROME_BROWSER_PUSH_MESSAGING_PUSH_MESSAGING_CONSTANTS_H_
 
+#include "base/time/time.h"
+
 extern const char kPushMessagingGcmEndpoint[];
 
 // The tag of the notification that will be automatically shown if a webapp
 // receives a push message then fails to show a notification.
 extern const char kPushMessagingForcedNotificationTag[];
 
+// Chrome decided cadence on subscription refreshes. According to the standards:
+// https://w3c.github.io/push-api/#dfn-subscription-expiration-time it is
+// optional and set by the browser.
+constexpr base::TimeDelta kPushSubscriptionExpirationPeriodTimeDelta =
+    base::TimeDelta::FromDays(90);
+
 #endif  // CHROME_BROWSER_PUSH_MESSAGING_PUSH_MESSAGING_CONSTANTS_H_
diff --git a/chrome/browser/push_messaging/push_messaging_features.cc b/chrome/browser/push_messaging/push_messaging_features.cc
index 644b543c..11ad3a97 100644
--- a/chrome/browser/push_messaging/push_messaging_features.cc
+++ b/chrome/browser/push_messaging/push_messaging_features.cc
@@ -4,5 +4,12 @@
 
 #include "chrome/browser/push_messaging/push_messaging_features.h"
 
+namespace features {
+
 const base::Feature kPushMessagingDisallowSenderIDs{
     "PushMessagingDisallowSenderIDs", base::FEATURE_DISABLED_BY_DEFAULT};
+
+const base::Feature kPushSubscriptionWithExpirationTime{
+    "PushSubscriptionWithExpirationTime", base::FEATURE_DISABLED_BY_DEFAULT};
+
+}  // namespace features
diff --git a/chrome/browser/push_messaging/push_messaging_features.h b/chrome/browser/push_messaging/push_messaging_features.h
index 1dc1384..1bdc923 100644
--- a/chrome/browser/push_messaging/push_messaging_features.h
+++ b/chrome/browser/push_messaging/push_messaging_features.h
@@ -7,7 +7,15 @@
 
 #include "base/feature_list.h"
 
+namespace features {
+
 // Feature flag to disallow creation of push messages with GCM Sender IDs.
 extern const base::Feature kPushMessagingDisallowSenderIDs;
 
+// Feature flag to enable push subscription with expiration times specified in
+// /chrome/browser/push_messaging/push_messaging_constants.h
+extern const base::Feature kPushSubscriptionWithExpirationTime;
+
+}  // namespace features
+
 #endif  // CHROME_BROWSER_PUSH_MESSAGING_PUSH_MESSAGING_FEATURES_H_
diff --git a/chrome/browser/push_messaging/push_messaging_service_impl.cc b/chrome/browser/push_messaging/push_messaging_service_impl.cc
index 4ae450ac..a8cf117 100644
--- a/chrome/browser/push_messaging/push_messaging_service_impl.cc
+++ b/chrome/browser/push_messaging/push_messaging_service_impl.cc
@@ -55,6 +55,7 @@
 #include "content/public/browser/storage_partition.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/common/child_process_host.h"
+#include "content/public/common/content_features.h"
 #include "content/public/common/content_switches.h"
 #include "third_party/blink/public/mojom/permissions/permission_status.mojom.h"
 #include "third_party/blink/public/mojom/push_messaging/push_messaging_status.mojom.h"
@@ -589,7 +590,7 @@
       ContentSettingsType::NOTIFICATIONS, render_frame_host, requesting_origin,
       user_gesture,
       base::BindOnce(&PushMessagingServiceImpl::DoSubscribe,
-                     weak_factory_.GetWeakPtr(), app_identifier,
+                     weak_factory_.GetWeakPtr(), std::move(app_identifier),
                      std::move(options), std::move(callback), render_process_id,
                      render_frame_id));
 }
@@ -627,7 +628,8 @@
     return;
   }
 
-  DoSubscribe(app_identifier, std::move(options), std::move(register_callback),
+  DoSubscribe(std::move(app_identifier), std::move(options),
+              std::move(register_callback),
               /* render_process_id= */ -1, /* render_frame_id= */ -1,
               CONTENT_SETTING_ALLOW);
 }
@@ -653,7 +655,7 @@
 }
 
 void PushMessagingServiceImpl::DoSubscribe(
-    const PushMessagingAppIdentifier& app_identifier,
+    PushMessagingAppIdentifier app_identifier,
     blink::mojom::PushSubscriptionOptionsPtr options,
     RegisterCallback register_callback,
     int render_process_id,
@@ -679,7 +681,8 @@
     content::RenderFrameHost* main_frame =
         GetMainFrameForRenderFrameHost(render_frame_host);
 
-    if (base::FeatureList::IsEnabled(kPushMessagingDisallowSenderIDs)) {
+    if (base::FeatureList::IsEnabled(
+            features::kPushMessagingDisallowSenderIDs)) {
       if (main_frame) {
         main_frame->AddMessageToConsole(
             blink::mojom::ConsoleMessageLevel::kError,
@@ -698,11 +701,21 @@
 
   IncreasePushSubscriptionCount(1, true /* is_pending */);
 
+  // Set time to live for GCM registration
+  base::TimeDelta ttl = base::TimeDelta();
+
+  if (base::FeatureList::IsEnabled(
+          features::kPushSubscriptionWithExpirationTime)) {
+    app_identifier.set_expiration_time(
+        base::Time::Now() + kPushSubscriptionExpirationPeriodTimeDelta);
+    DCHECK(app_identifier.expiration_time());
+    ttl = kPushSubscriptionExpirationPeriodTimeDelta;
+  }
+
   GetInstanceIDDriver()
       ->GetInstanceID(app_identifier.app_id())
       ->GetToken(NormalizeSenderInfo(application_server_key_string), kGCMScope,
-                 base::TimeDelta() /* time_to_live */,
-                 std::map<std::string, std::string>() /* options */,
+                 ttl, std::map<std::string, std::string>() /* options */,
                  {} /* flags */,
                  base::BindOnce(&PushMessagingServiceImpl::DidSubscribe,
                                 weak_factory_.GetWeakPtr(), app_identifier,
@@ -714,12 +727,12 @@
     RegisterCallback callback,
     const std::string& subscription_id,
     const GURL& endpoint,
+    const base::Optional<base::Time>& expiration_time,
     const std::vector<uint8_t>& p256dh,
     const std::vector<uint8_t>& auth,
     blink::mojom::PushRegistrationStatus status) {
-  std::move(callback).Run(subscription_id, endpoint,
-                          base::nullopt /* expiration_time*/, p256dh, auth,
-                          status);
+  std::move(callback).Run(subscription_id, endpoint, expiration_time, p256dh,
+                          auth, status);
 }
 
 void PushMessagingServiceImpl::SubscribeEndWithError(
@@ -727,6 +740,7 @@
     blink::mojom::PushRegistrationStatus status) {
   SubscribeEnd(std::move(callback), std::string() /* subscription_id */,
                GURL::EmptyGURL() /* endpoint */,
+               base::nullopt /* expiration_time */,
                std::vector<uint8_t>() /* p256dh */,
                std::vector<uint8_t>() /* auth */, status);
 }
@@ -793,6 +807,7 @@
   IncreasePushSubscriptionCount(1, false /* is_pending */);
 
   SubscribeEnd(std::move(callback), subscription_id, endpoint,
+               app_identifier.expiration_time(),
                std::vector<uint8_t>(p256dh.begin(), p256dh.end()),
                std::vector<uint8_t>(auth_secret.begin(), auth_secret.end()),
                blink::mojom::PushRegistrationStatus::SUCCESS_FROM_PUSH_SERVICE);
@@ -800,8 +815,6 @@
 
 // GetSubscriptionInfo methods -------------------------------------------------
 
-// TODO(crbug.com/1104215): Get |expiration_time| from |app_identifier|, where
-// it is stored in profile preferences
 void PushMessagingServiceImpl::GetSubscriptionInfo(
     const GURL& origin,
     int64_t service_worker_registration_id,
@@ -822,10 +835,12 @@
 
   const GURL endpoint = CreateEndpoint(subscription_id);
   const std::string& app_id = app_identifier.app_id();
+  base::Optional<base::Time> expiration_time = app_identifier.expiration_time();
+
   base::OnceCallback<void(bool)> validate_cb =
       base::BindOnce(&PushMessagingServiceImpl::DidValidateSubscription,
                      weak_factory_.GetWeakPtr(), app_id, sender_id, endpoint,
-                     std::move(callback));
+                     expiration_time, std::move(callback));
 
   if (PushMessagingAppIdentifier::UseInstanceID(app_id)) {
     GetInstanceIDDriver()->GetInstanceID(app_id)->ValidateToken(
@@ -842,6 +857,7 @@
     const std::string& app_id,
     const std::string& sender_id,
     const GURL& endpoint,
+    const base::Optional<base::Time>& expiration_time,
     SubscriptionInfoCallback callback,
     bool is_valid) {
   if (!is_valid) {
@@ -855,19 +871,20 @@
   GetEncryptionInfoForAppId(
       app_id, sender_id,
       base::BindOnce(&PushMessagingServiceImpl::DidGetEncryptionInfo,
-                     weak_factory_.GetWeakPtr(), endpoint,
+                     weak_factory_.GetWeakPtr(), endpoint, expiration_time,
                      std::move(callback)));
 }
 
 void PushMessagingServiceImpl::DidGetEncryptionInfo(
     const GURL& endpoint,
+    const base::Optional<base::Time>& expiration_time,
     SubscriptionInfoCallback callback,
     std::string p256dh,
     std::string auth_secret) const {
   // I/O errors might prevent the GCM Driver from retrieving a key-pair.
   bool is_valid = !p256dh.empty();
   std::move(callback).Run(
-      is_valid, endpoint, base::nullopt /* expiration_time */,
+      is_valid, endpoint, expiration_time,
       std::vector<uint8_t>(p256dh.begin(), p256dh.end()),
       std::vector<uint8_t>(auth_secret.begin(), auth_secret.end()));
 }
diff --git a/chrome/browser/push_messaging/push_messaging_service_impl.h b/chrome/browser/push_messaging/push_messaging_service_impl.h
index 6572ceb..9056f929 100644
--- a/chrome/browser/push_messaging/push_messaging_service_impl.h
+++ b/chrome/browser/push_messaging/push_messaging_service_impl.h
@@ -16,6 +16,7 @@
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "base/optional.h"
+#include "base/time/time.h"
 #include "chrome/browser/push_messaging/push_messaging_notification_manager.h"
 #include "chrome/common/buildflags.h"
 #include "components/content_settings/core/browser/content_settings_observer.h"
@@ -165,7 +166,7 @@
 
   // Subscribe methods ---------------------------------------------------------
 
-  void DoSubscribe(const PushMessagingAppIdentifier& app_identifier,
+  void DoSubscribe(PushMessagingAppIdentifier app_identifier,
                    blink::mojom::PushSubscriptionOptionsPtr options,
                    RegisterCallback callback,
                    int render_process_id,
@@ -175,6 +176,7 @@
   void SubscribeEnd(RegisterCallback callback,
                     const std::string& subscription_id,
                     const GURL& endpoint,
+                    const base::Optional<base::Time>& expiration_time,
                     const std::vector<uint8_t>& p256dh,
                     const std::vector<uint8_t>& auth,
                     blink::mojom::PushRegistrationStatus status);
@@ -198,13 +200,16 @@
 
   // GetSubscriptionInfo methods -----------------------------------------------
 
-  void DidValidateSubscription(const std::string& app_id,
-                               const std::string& sender_id,
-                               const GURL& endpoint,
-                               SubscriptionInfoCallback callback,
-                               bool is_valid);
+  void DidValidateSubscription(
+      const std::string& app_id,
+      const std::string& sender_id,
+      const GURL& endpoint,
+      const base::Optional<base::Time>& expiration_time,
+      SubscriptionInfoCallback callback,
+      bool is_valid);
 
   void DidGetEncryptionInfo(const GURL& endpoint,
+                            const base::Optional<base::Time>& expiration_time,
                             SubscriptionInfoCallback callback,
                             std::string p256dh,
                             std::string auth_secret) const;
diff --git a/chrome/browser/renderer_context_menu/render_view_context_menu.cc b/chrome/browser/renderer_context_menu/render_view_context_menu.cc
index 34c88da..7b1ef11 100644
--- a/chrome/browser/renderer_context_menu/render_view_context_menu.cc
+++ b/chrome/browser/renderer_context_menu/render_view_context_menu.cc
@@ -1216,6 +1216,10 @@
 
     menu_model_.AddSeparator(ui::NORMAL_SEPARATOR);
 
+    // Place QR Generator close to send-tab-to-self feature for link images.
+    if (params_.has_image_contents)
+      AppendQRCodeGeneratorItem(/*for_image=*/true, /*draw_icon=*/true);
+
     if (browser && send_tab_to_self::ShouldOfferFeatureForLink(
                        active_web_contents, params_.link_url)) {
       if (send_tab_to_self::GetValidDeviceCount(GetBrowser()->profile()) == 1) {
@@ -1353,6 +1357,10 @@
                                   IDS_CONTENT_CONTEXT_COPYIMAGE);
   menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_COPYIMAGELOCATION,
                                   IDS_CONTENT_CONTEXT_COPYIMAGELOCATION);
+
+  // Don't double-add for linked images, which also add the item.
+  if (params_.link_url.is_empty())
+    AppendQRCodeGeneratorItem(/*for_image=*/true, /*draw_icon=*/false);
 }
 
 void RenderViewContextMenu::AppendSearchWebForImageItems() {
@@ -1497,15 +1505,7 @@
     if (!send_tab_to_self_menu_present)
       menu_model_.AddSeparator(ui::NORMAL_SEPARATOR);
 
-#if defined(OS_MAC)
-    menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_GENERATE_QR_CODE,
-                                    IDS_CONTEXT_MENU_GENERATE_QR_CODE_PAGE);
-#else
-    menu_model_.AddItemWithStringIdAndIcon(
-        IDC_CONTENT_CONTEXT_GENERATE_QR_CODE,
-        IDS_CONTEXT_MENU_GENERATE_QR_CODE_PAGE,
-        ui::ImageModel::FromVectorIcon(kQrcodeGeneratorIcon));
-#endif
+    AppendQRCodeGeneratorItem(/*for_image=*/false, /*draw_icon=*/true);
 
     menu_model_.AddSeparator(ui::NORMAL_SEPARATOR);
   } else if (send_tab_to_self_menu_present) {
@@ -2265,9 +2265,17 @@
           GetBrowser()->tab_strip_model()->GetActiveWebContents();
       auto* bubble_controller =
           qrcode_generator::QRCodeGeneratorBubbleController::Get(web_contents);
-      NavigationEntry* entry =
-          embedder_web_contents_->GetController().GetLastCommittedEntry();
-      bubble_controller->ShowBubble(entry->GetURL());
+      if (params_.media_type == ContextMenuDataMediaType::kImage) {
+        base::RecordAction(
+            UserMetricsAction("SharingQRCode.DialogLaunched.ContextMenuImage"));
+        bubble_controller->ShowBubble(params_.src_url);
+      } else {
+        base::RecordAction(
+            UserMetricsAction("SharingQRCode.DialogLaunched.ContextMenuPage"));
+        NavigationEntry* entry =
+            embedder_web_contents_->GetController().GetLastCommittedEntry();
+        bubble_controller->ShowBubble(entry->GetURL());
+      }
       break;
     }
 
@@ -2639,6 +2647,25 @@
       IsGeneratorAvailable(entry->GetURL(), incognito);
 }
 
+void RenderViewContextMenu::AppendQRCodeGeneratorItem(bool for_image,
+                                                      bool draw_icon) {
+  if (!IsQRCodeGeneratorEnabled())
+    return;
+  auto string_id = for_image ? IDS_CONTEXT_MENU_GENERATE_QR_CODE_IMAGE
+                             : IDS_CONTEXT_MENU_GENERATE_QR_CODE_PAGE;
+#if defined(OS_MAC)
+  draw_icon = false;
+#endif
+  if (draw_icon) {
+    menu_model_.AddItemWithStringIdAndIcon(
+        IDC_CONTENT_CONTEXT_GENERATE_QR_CODE, string_id,
+        ui::ImageModel::FromVectorIcon(kQrcodeGeneratorIcon));
+  } else {
+    menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_GENERATE_QR_CODE,
+                                    string_id);
+  }
+}
+
 bool RenderViewContextMenu::IsRouteMediaEnabled() const {
   if (!media_router::MediaRouterEnabled(browser_context_))
     return false;
diff --git a/chrome/browser/renderer_context_menu/render_view_context_menu.h b/chrome/browser/renderer_context_menu/render_view_context_menu.h
index 4915817..93b1df8 100644
--- a/chrome/browser/renderer_context_menu/render_view_context_menu.h
+++ b/chrome/browser/renderer_context_menu/render_view_context_menu.h
@@ -193,6 +193,7 @@
   void AppendSharingItems();
   void AppendClickToCallItem();
   void AppendSharedClipboardItem();
+  void AppendQRCodeGeneratorItem(bool for_image, bool draw_icon);
 
   // Command enabled query functions.
   bool IsReloadEnabled() const;
diff --git a/chrome/browser/renderer_host/chrome_navigation_ui_data.cc b/chrome/browser/renderer_host/chrome_navigation_ui_data.cc
index d58691a8..a71c780 100644
--- a/chrome/browser/renderer_host/chrome_navigation_ui_data.cc
+++ b/chrome/browser/renderer_host/chrome_navigation_ui_data.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/renderer_host/chrome_navigation_ui_data.h"
 
+#include "chrome/browser/prerender/chrome_prerender_contents_delegate.h"
 #include "chrome/browser/prerender/prerender_contents.h"
 #include "components/prerender/browser/prerender_histograms.h"
 #include "content/public/browser/navigation_handle.h"
@@ -35,7 +36,7 @@
 #endif
 
   auto* prerender_contents =
-      prerender::PrerenderContents::FromWebContents(web_contents);
+      prerender::ChromePrerenderContentsDelegate::FromWebContents(web_contents);
   if (prerender_contents) {
     prerender_mode_ = prerender_contents->prerender_mode();
     prerender_histogram_prefix_ =
diff --git a/chrome/browser/resource_coordinator/leveldb_site_characteristics_database.cc b/chrome/browser/resource_coordinator/leveldb_site_characteristics_database.cc
deleted file mode 100644
index 95c866a..0000000
--- a/chrome/browser/resource_coordinator/leveldb_site_characteristics_database.cc
+++ /dev/null
@@ -1,491 +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 "chrome/browser/resource_coordinator/leveldb_site_characteristics_database.h"
-
-#include <limits>
-#include <string>
-
-#include "base/bind.h"
-#include "base/files/file_util.h"
-#include "base/logging.h"
-#include "base/memory/ptr_util.h"
-#include "base/metrics/histogram_functions.h"
-#include "base/metrics/histogram_macros.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/task_runner_util.h"
-#include "base/threading/scoped_blocking_call.h"
-#include "build/build_config.h"
-#include "chrome/browser/resource_coordinator/utils.h"
-#include "third_party/leveldatabase/env_chromium.h"
-#include "third_party/leveldatabase/leveldb_chrome.h"
-#include "third_party/leveldatabase/src/include/leveldb/write_batch.h"
-
-namespace resource_coordinator {
-
-namespace {
-
-const char kInitStatusHistogramLabel[] =
-    "ResourceCoordinator.LocalDB.DatabaseInit";
-const char kInitStatusAfterRepairHistogramLabel[] =
-    "ResourceCoordinator.LocalDB.DatabaseInitAfterRepair";
-const char kInitStatusAfterDeleteHistogramLabel[] =
-    "ResourceCoordinator.LocalDB.DatabaseInitAfterDelete";
-
-enum class InitStatus {
-  kInitStatusOk,
-  kInitStatusCorruption,
-  kInitStatusIOError,
-  kInitStatusUnknownError,
-  kInitStatusMax
-};
-
-// Report the database's initialization status metrics.
-void ReportInitStatus(const char* histogram_name,
-                      const leveldb::Status& status) {
-  if (status.ok()) {
-    base::UmaHistogramEnumeration(histogram_name, InitStatus::kInitStatusOk,
-                                  InitStatus::kInitStatusMax);
-  } else if (status.IsCorruption()) {
-    base::UmaHistogramEnumeration(histogram_name,
-                                  InitStatus::kInitStatusCorruption,
-                                  InitStatus::kInitStatusMax);
-  } else if (status.IsIOError()) {
-    base::UmaHistogramEnumeration(histogram_name,
-                                  InitStatus::kInitStatusIOError,
-                                  InitStatus::kInitStatusMax);
-  } else {
-    base::UmaHistogramEnumeration(histogram_name,
-                                  InitStatus::kInitStatusUnknownError,
-                                  InitStatus::kInitStatusMax);
-  }
-}
-
-// Attempt to repair the database stored in |db_path|.
-bool RepairDatabase(const std::string& db_path) {
-  leveldb_env::Options options;
-  options.reuse_logs = false;
-  options.max_open_files = 0;
-  bool repair_succeeded = leveldb::RepairDB(db_path, options).ok();
-  UMA_HISTOGRAM_BOOLEAN("ResourceCoordinator.LocalDB.DatabaseRepair",
-                        repair_succeeded);
-  return repair_succeeded;
-}
-
-bool ShouldAttemptDbRepair(const leveldb::Status& status) {
-  // A corrupt database might be repaired (some data might be loss but it's
-  // better than losing everything).
-  if (status.IsCorruption())
-    return true;
-  // An I/O error might be caused by a missing manifest, it's sometime possible
-  // to repair this (some data might be loss).
-  if (status.IsIOError())
-    return true;
-
-  return false;
-}
-
-struct DatabaseSizeResult {
-  base::Optional<int64_t> num_rows;
-  base::Optional<int64_t> on_disk_size_kb;
-};
-
-}  // namespace
-
-// Version history:
-//
-// - {no version}:
-//     - Initial launch of the Database.
-// - 1:
-//     - Ignore the title/favicon events happening during the first fews seconds
-//       after a tab being loaded.
-//     - Ignore the audio events happening during the first fews seconds after a
-//       tab being backgrounded.
-// - 2:
-//     - Ignore events that happen shortly after a tab is backgrounded. This is
-//       because such events are likely a response to a recent user action
-//       rather than an attempt from the tab to communicate in background.
-//       See https://crbug.com/1865601.
-//
-// Transform logic:
-//     - From any version to v1: The database is erased entirely.
-//     - From any version to v2: The database is erased entirely.
-const size_t LevelDBSiteCharacteristicsDatabase::kDbVersion = 2U;
-
-const char LevelDBSiteCharacteristicsDatabase::kDbMetadataKey[] =
-    "database_metadata";
-
-// Helper class used to run all the blocking operations posted by
-// LocalSiteCharacteristicDatabase on a ThreadPool sequence with the
-// |MayBlock()| trait.
-//
-// Instances of this class should only be destructed once all the posted tasks
-// have been run, in practice it means that they should ideally be stored in a
-// std::unique_ptr<AsyncHelper, base::OnTaskRunnerDeleter>.
-class LevelDBSiteCharacteristicsDatabase::AsyncHelper {
- public:
-  explicit AsyncHelper(const base::FilePath& db_path) : db_path_(db_path) {
-    DETACH_FROM_SEQUENCE(sequence_checker_);
-    // Setting |sync| to false might cause some data loss if the system crashes
-    // but it'll make the write operations faster (no data will be lost if only
-    // the process crashes).
-    write_options_.sync = false;
-  }
-  ~AsyncHelper() = default;
-
-  // Open the database from |db_path_| after creating it if it didn't exist,
-  // this reset the database if it's not at the expected version.
-  void OpenOrCreateDatabase();
-
-  // Implementations of the DB manipulation functions of
-  // LevelDBSiteCharacteristicsDatabase that run on a blocking sequence.
-  base::Optional<SiteDataProto> ReadSiteCharacteristicsFromDB(
-      const url::Origin& origin);
-  void WriteSiteCharacteristicsIntoDB(
-      const url::Origin& origin,
-      const SiteDataProto& site_characteristic_proto);
-  void RemoveSiteCharacteristicsFromDB(
-      const std::vector<url::Origin>& site_origin);
-  void ClearDatabase();
-  // Returns a struct with unset fields on failure.
-  DatabaseSizeResult GetDatabaseSize();
-
-  bool DBIsInitialized() { return db_ != nullptr; }
-
-  leveldb::DB* GetDBForTesting() {
-    DCHECK(DBIsInitialized());
-    return db_.get();
-  }
-
- private:
-  enum class OpeningType {
-    // A new database has been created.
-    kNewDb,
-    // An existing database has been used.
-    kExistingDb,
-  };
-
-  // Implementation for the OpenOrCreateDatabase function.
-  OpeningType OpenOrCreateDatabaseImpl();
-
-  // The on disk location of the database.
-  const base::FilePath db_path_;
-  // The connection to the LevelDB database.
-  std::unique_ptr<leveldb::DB> db_;
-  // The options to be used for all database read operations.
-  leveldb::ReadOptions read_options_;
-  // The options to be used for all database write operations.
-  leveldb::WriteOptions write_options_;
-
-  SEQUENCE_CHECKER(sequence_checker_);
-  DISALLOW_COPY_AND_ASSIGN(AsyncHelper);
-};
-
-void LevelDBSiteCharacteristicsDatabase::AsyncHelper::OpenOrCreateDatabase() {
-  OpeningType opening_type = OpenOrCreateDatabaseImpl();
-  if (!db_)
-    return;
-  std::string db_metadata;
-  leveldb::Status s = db_->Get(
-      read_options_, LevelDBSiteCharacteristicsDatabase::kDbMetadataKey,
-      &db_metadata);
-  bool is_expected_version = false;
-  if (s.ok()) {
-    // The metadata only contains the version of the database as a size_t value
-    // for now.
-    size_t version = std::numeric_limits<size_t>::max();
-    CHECK(base::StringToSizeT(db_metadata, &version));
-    if (version == LevelDBSiteCharacteristicsDatabase::kDbVersion)
-      is_expected_version = true;
-  }
-  // TODO(sebmarchand): Add a migration engine rather than flushing the database
-  // for every version change, https://crbug.com/866540.
-  if ((opening_type == OpeningType::kExistingDb) && !is_expected_version) {
-    DLOG(ERROR) << "Invalid DB version, recreating it.";
-    ClearDatabase();
-    // The database might fail to open.
-    if (!db_)
-      return;
-    opening_type = OpeningType::kNewDb;
-  }
-  if (opening_type == OpeningType::kNewDb) {
-    std::string metadata =
-        base::NumberToString(LevelDBSiteCharacteristicsDatabase::kDbVersion);
-    s = db_->Put(write_options_,
-                  LevelDBSiteCharacteristicsDatabase::kDbMetadataKey,
-                  metadata);
-    if (!s.ok()) {
-      DLOG(ERROR) << "Error while inserting the metadata in the site "
-                  << "characteristics database: " << s.ToString();
-    }
-  }
-}
-
-base::Optional<SiteDataProto>
-LevelDBSiteCharacteristicsDatabase::AsyncHelper::ReadSiteCharacteristicsFromDB(
-    const url::Origin& origin) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-  if (!db_)
-    return base::nullopt;
-
-  leveldb::Status s;
-  std::string protobuf_value;
-  {
-    base::ScopedBlockingCall scoped_blocking_call(
-        FROM_HERE, base::BlockingType::MAY_BLOCK);
-    s = db_->Get(read_options_, SerializeOriginIntoDatabaseKey(origin),
-                 &protobuf_value);
-  }
-  base::Optional<SiteDataProto> site_characteristic_proto;
-  if (s.ok()) {
-    site_characteristic_proto = SiteDataProto();
-    if (!site_characteristic_proto->ParseFromString(protobuf_value)) {
-      site_characteristic_proto = base::nullopt;
-      DLOG(ERROR) << "Error while trying to parse a SiteDataProto "
-                  << "protobuf.";
-    }
-  }
-  return site_characteristic_proto;
-}
-
-void LevelDBSiteCharacteristicsDatabase::AsyncHelper::
-    WriteSiteCharacteristicsIntoDB(
-        const url::Origin& origin,
-        const SiteDataProto& site_characteristic_proto) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-  if (!db_)
-    return;
-
-  leveldb::Status s;
-  {
-    base::ScopedBlockingCall scoped_blocking_call(
-        FROM_HERE, base::BlockingType::MAY_BLOCK);
-    s = db_->Put(write_options_, SerializeOriginIntoDatabaseKey(origin),
-                 site_characteristic_proto.SerializeAsString());
-  }
-
-  if (!s.ok()) {
-    DLOG(ERROR)
-        << "Error while inserting an element in the site characteristics "
-        << "database: " << s.ToString();
-  }
-}
-
-void LevelDBSiteCharacteristicsDatabase::AsyncHelper::
-    RemoveSiteCharacteristicsFromDB(
-        const std::vector<url::Origin>& site_origins) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-  if (!db_)
-    return;
-
-  base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
-                                                base::BlockingType::MAY_BLOCK);
-  leveldb::WriteBatch batch;
-  for (const auto& iter : site_origins)
-    batch.Delete(SerializeOriginIntoDatabaseKey(iter));
-  leveldb::Status status = db_->Write(write_options_, &batch);
-  if (!status.ok()) {
-    LOG(WARNING) << "Failed to remove some entries from the site "
-                 << "characteristics database: " << status.ToString();
-  }
-}
-
-void LevelDBSiteCharacteristicsDatabase::AsyncHelper::ClearDatabase() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  if (!db_)
-    return;
-
-  base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
-                                                base::BlockingType::MAY_BLOCK);
-  db_.reset();
-  leveldb_env::Options options;
-  leveldb::Status status = leveldb::DestroyDB(db_path_.AsUTF8Unsafe(), options);
-  if (status.ok()) {
-    OpenOrCreateDatabaseImpl();
-  } else {
-    LOG(WARNING) << "Failed to destroy the site characteristics database: "
-                 << status.ToString();
-  }
-}
-
-DatabaseSizeResult
-LevelDBSiteCharacteristicsDatabase::AsyncHelper::GetDatabaseSize() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  if (!db_)
-    return DatabaseSizeResult();
-
-  base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
-                                                base::BlockingType::MAY_BLOCK);
-  DatabaseSizeResult ret;
-#if defined(OS_WIN)
-  // Windows has an annoying mis-feature that the size of an open file is not
-  // written to the parent directory until the file is closed. Since this is a
-  // diagnostic interface that should be rarely called, go to the trouble of
-  // closing and re-opening the database in order to get an up-to date size to
-  // report.
-  db_.reset();
-#endif
-  ret.on_disk_size_kb = base::ComputeDirectorySize(db_path_) / 1024;
-#if defined(OS_WIN)
-  OpenOrCreateDatabase();
-  if (!db_)
-    return DatabaseSizeResult();
-#endif
-
-  // Default read options will fill the cache as we go.
-  std::unique_ptr<leveldb::Iterator> iterator(
-      db_->NewIterator(leveldb::ReadOptions()));
-  int64_t num_rows = 0;
-  for (iterator->SeekToFirst(); iterator->Valid(); iterator->Next())
-    ++num_rows;
-
-  ret.num_rows = num_rows;
-  return ret;
-}
-
-LevelDBSiteCharacteristicsDatabase::AsyncHelper::OpeningType
-LevelDBSiteCharacteristicsDatabase::AsyncHelper::OpenOrCreateDatabaseImpl() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  DCHECK(!db_) << "Database already open";
-  base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
-                                                base::BlockingType::MAY_BLOCK);
-
-  OpeningType opening_type = OpeningType::kNewDb;
-
-  // Report the on disk size of the database if it already exists.
-  if (base::DirectoryExists(db_path_)) {
-    opening_type = OpeningType::kExistingDb;
-    int64_t db_ondisk_size_in_bytes = base::ComputeDirectorySize(db_path_);
-    UMA_HISTOGRAM_MEMORY_KB("ResourceCoordinator.LocalDB.OnDiskSize",
-                            db_ondisk_size_in_bytes / 1024);
-  }
-
-  leveldb_env::Options options;
-  options.create_if_missing = true;
-  leveldb::Status status =
-      leveldb_env::OpenDB(options, db_path_.AsUTF8Unsafe(), &db_);
-
-  ReportInitStatus(kInitStatusHistogramLabel, status);
-
-  if (status.ok())
-    return opening_type;
-
-  if (!ShouldAttemptDbRepair(status))
-    return opening_type;
-
-  if (RepairDatabase(db_path_.AsUTF8Unsafe())) {
-    status = leveldb_env::OpenDB(options, db_path_.AsUTF8Unsafe(), &db_);
-    ReportInitStatus(kInitStatusAfterRepairHistogramLabel, status);
-    if (status.ok())
-      return opening_type;
-  }
-
-  // Delete the database and try to open it one last time.
-  if (leveldb_chrome::DeleteDB(db_path_, options).ok()) {
-    status = leveldb_env::OpenDB(options, db_path_.AsUTF8Unsafe(), &db_);
-    ReportInitStatus(kInitStatusAfterDeleteHistogramLabel, status);
-    if (!status.ok())
-      db_.reset();
-  }
-
-  return opening_type;
-}
-
-LevelDBSiteCharacteristicsDatabase::LevelDBSiteCharacteristicsDatabase(
-    const base::FilePath& db_path)
-    : blocking_task_runner_(base::ThreadPool::CreateSequencedTaskRunner(
-          // The |BLOCK_SHUTDOWN| trait is required to ensure that a clearing of
-          // the database won't be skipped.
-          {base::MayBlock(), base::TaskShutdownBehavior::BLOCK_SHUTDOWN})),
-      async_helper_(new AsyncHelper(db_path),
-                    base::OnTaskRunnerDeleter(blocking_task_runner_)) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-  blocking_task_runner_->PostTask(
-      FROM_HERE, base::BindOnce(&LevelDBSiteCharacteristicsDatabase::
-                                    AsyncHelper::OpenOrCreateDatabase,
-                                base::Unretained(async_helper_.get())));
-}
-
-LevelDBSiteCharacteristicsDatabase::~LevelDBSiteCharacteristicsDatabase() =
-    default;
-
-void LevelDBSiteCharacteristicsDatabase::ReadSiteCharacteristicsFromDB(
-    const url::Origin& origin,
-    LocalSiteCharacteristicsDatabase::ReadSiteCharacteristicsFromDBCallback
-        callback) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-  // Trigger the asynchronous task and make it run the callback on this thread
-  // once it returns.
-  base::PostTaskAndReplyWithResult(
-      blocking_task_runner_.get(), FROM_HERE,
-      base::BindOnce(&LevelDBSiteCharacteristicsDatabase::AsyncHelper::
-                         ReadSiteCharacteristicsFromDB,
-                     base::Unretained(async_helper_.get()), origin),
-      base::BindOnce(std::move(callback)));
-}
-
-void LevelDBSiteCharacteristicsDatabase::WriteSiteCharacteristicsIntoDB(
-    const url::Origin& origin,
-    const SiteDataProto& site_characteristic_proto) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  blocking_task_runner_->PostTask(
-      FROM_HERE, base::BindOnce(&LevelDBSiteCharacteristicsDatabase::
-                                    AsyncHelper::WriteSiteCharacteristicsIntoDB,
-                                base::Unretained(async_helper_.get()), origin,
-                                std::move(site_characteristic_proto)));
-}
-
-void LevelDBSiteCharacteristicsDatabase::RemoveSiteCharacteristicsFromDB(
-    const std::vector<url::Origin>& site_origins) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  blocking_task_runner_->PostTask(
-      FROM_HERE,
-      base::BindOnce(&LevelDBSiteCharacteristicsDatabase::AsyncHelper::
-                         RemoveSiteCharacteristicsFromDB,
-                     base::Unretained(async_helper_.get()),
-                     std::move(site_origins)));
-}
-
-void LevelDBSiteCharacteristicsDatabase::ClearDatabase() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  blocking_task_runner_->PostTask(
-      FROM_HERE,
-      base::BindOnce(
-          &LevelDBSiteCharacteristicsDatabase::AsyncHelper::ClearDatabase,
-          base::Unretained(async_helper_.get())));
-}
-
-void LevelDBSiteCharacteristicsDatabase::GetDatabaseSize(
-    GetDatabaseSizeCallback callback) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-  // Adapt the callback with a lambda to allow using PostTaskAndReplyWithResult.
-  auto reply_callback = base::BindOnce(
-      [](GetDatabaseSizeCallback callback, const DatabaseSizeResult& result) {
-        std::move(callback).Run(result.num_rows, result.on_disk_size_kb);
-      },
-      std::move(callback));
-
-  base::PostTaskAndReplyWithResult(
-      blocking_task_runner_.get(), FROM_HERE,
-      base::BindOnce(
-          &LevelDBSiteCharacteristicsDatabase::AsyncHelper::GetDatabaseSize,
-          base::Unretained(async_helper_.get())),
-      std::move(reply_callback));
-}
-
-bool LevelDBSiteCharacteristicsDatabase::DatabaseIsInitializedForTesting() {
-  return async_helper_->DBIsInitialized();
-}
-
-leveldb::DB* LevelDBSiteCharacteristicsDatabase::GetDBForTesting() {
-  return async_helper_->GetDBForTesting();
-}
-
-}  // namespace resource_coordinator
diff --git a/chrome/browser/resource_coordinator/leveldb_site_characteristics_database.h b/chrome/browser/resource_coordinator/leveldb_site_characteristics_database.h
deleted file mode 100644
index 092c4ef..0000000
--- a/chrome/browser/resource_coordinator/leveldb_site_characteristics_database.h
+++ /dev/null
@@ -1,78 +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 CHROME_BROWSER_RESOURCE_COORDINATOR_LEVELDB_SITE_CHARACTERISTICS_DATABASE_H_
-#define CHROME_BROWSER_RESOURCE_COORDINATOR_LEVELDB_SITE_CHARACTERISTICS_DATABASE_H_
-
-#include "base/files/file_path.h"
-#include "base/macros.h"
-#include "base/sequence_checker.h"
-#include "base/sequenced_task_runner.h"
-#include "base/task/post_task.h"
-#include "chrome/browser/resource_coordinator/local_site_characteristics_database.h"
-#include "third_party/leveldatabase/src/include/leveldb/db.h"
-
-namespace resource_coordinator {
-
-// Manages a LevelDB database used by a site characteristic data store.
-// TODO(sebmarchand):
-//   - Constraint the size of the database: Use a background task to trim the
-//     database if it becomes too big and ensure that this fail nicely when the
-//     disk is full.
-//   - Batch the write operations to reduce the number of I/O events.
-//
-// All the DB operations are done asynchronously on a sequence allowed to do
-// I/O operations.
-class LevelDBSiteCharacteristicsDatabase
-    : public LocalSiteCharacteristicsDatabase {
- public:
-  explicit LevelDBSiteCharacteristicsDatabase(const base::FilePath& db_path);
-
-  ~LevelDBSiteCharacteristicsDatabase() override;
-
-  // LocalSiteCharacteristicDatabase:
-  void ReadSiteCharacteristicsFromDB(
-      const url::Origin& origin,
-      LocalSiteCharacteristicsDatabase::ReadSiteCharacteristicsFromDBCallback
-          callback) override;
-  void WriteSiteCharacteristicsIntoDB(
-      const url::Origin& origin,
-      const SiteDataProto& site_characteristic_proto) override;
-  void RemoveSiteCharacteristicsFromDB(
-      const std::vector<url::Origin>& site_origins) override;
-  void ClearDatabase() override;
-  void GetDatabaseSize(GetDatabaseSizeCallback callback) override;
-
-  bool DatabaseIsInitializedForTesting();
-
-  // Returns a raw pointer to the database for testing purposes. Note that as
-  // the DB operations are made on a separate sequence it's recommended to call
-  // TaskEnvironment::RunUntilIdle before calling this function to ensure
-  // that the database has been fully initialized. The LevelDB implementation is
-  // thread safe.
-  leveldb::DB* GetDBForTesting();
-
-  static const size_t kDbVersion;
-  static const char kDbMetadataKey[];
-
- private:
-  class AsyncHelper;
-
-  // The task runner used to run all the blocking operations.
-  const scoped_refptr<base::SequencedTaskRunner> blocking_task_runner_;
-
-  // Helper object that should be used to trigger all the operations that need
-  // to run on |blocking_task_runner_|, it is guaranteed that the AsyncHelper
-  // held by this object will only be destructed once all the tasks that have
-  // been posted to it have completed.
-  std::unique_ptr<AsyncHelper, base::OnTaskRunnerDeleter> async_helper_;
-
-  SEQUENCE_CHECKER(sequence_checker_);
-
-  DISALLOW_COPY_AND_ASSIGN(LevelDBSiteCharacteristicsDatabase);
-};
-
-}  // namespace resource_coordinator
-
-#endif  // CHROME_BROWSER_RESOURCE_COORDINATOR_LEVELDB_SITE_CHARACTERISTICS_DATABASE_H_
diff --git a/chrome/browser/resource_coordinator/leveldb_site_characteristics_database_unittest.cc b/chrome/browser/resource_coordinator/leveldb_site_characteristics_database_unittest.cc
deleted file mode 100644
index d0a39aad..0000000
--- a/chrome/browser/resource_coordinator/leveldb_site_characteristics_database_unittest.cc
+++ /dev/null
@@ -1,305 +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 "chrome/browser/resource_coordinator/leveldb_site_characteristics_database.h"
-
-#include <limits>
-
-#include "base/bind.h"
-#include "base/files/file_util.h"
-#include "base/files/scoped_temp_dir.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/stringprintf.h"
-#include "base/test/bind_test_util.h"
-#include "base/test/metrics/histogram_tester.h"
-#include "base/test/task_environment.h"
-#include "base/test/test_file_util.h"
-#include "build/build_config.h"
-#include "components/performance_manager/persistence/site_data/site_data.pb.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/leveldatabase/leveldb_chrome.h"
-#include "url/gurl.h"
-
-namespace resource_coordinator {
-
-namespace {
-
-class ScopedReadOnlyDirectory {
- public:
-  explicit ScopedReadOnlyDirectory(const base::FilePath& root_dir);
-  ~ScopedReadOnlyDirectory() {
-    permission_restorer_.reset();
-    EXPECT_TRUE(base::DeletePathRecursively(read_only_path_));
-  }
-
-  const base::FilePath& GetReadOnlyPath() { return read_only_path_; }
-
- private:
-  base::FilePath read_only_path_;
-  std::unique_ptr<base::FilePermissionRestorer> permission_restorer_;
-};
-
-ScopedReadOnlyDirectory::ScopedReadOnlyDirectory(
-    const base::FilePath& root_dir) {
-  EXPECT_TRUE(base::CreateTemporaryDirInDir(
-      root_dir, FILE_PATH_LITERAL("read_only_path"), &read_only_path_));
-  permission_restorer_ =
-      std::make_unique<base::FilePermissionRestorer>(read_only_path_);
-#if defined(OS_WIN)
-  base::DenyFilePermission(read_only_path_, GENERIC_WRITE);
-#else  // defined(OS_WIN)
-  EXPECT_TRUE(base::MakeFileUnwritable(read_only_path_));
-#endif
-  EXPECT_FALSE(base::PathIsWritable(read_only_path_));
-}
-
-// Initialize a SiteDataProto object with a test value (the same
-// value is used to initialize all fields).
-void InitSiteCharacteristicProto(SiteDataProto* proto,
-                                 ::google::protobuf::int64 test_value) {
-  proto->set_last_loaded(test_value);
-
-  SiteDataFeatureProto feature_proto;
-  feature_proto.set_observation_duration(test_value);
-  feature_proto.set_use_timestamp(test_value);
-
-  proto->mutable_updates_favicon_in_background()->CopyFrom(feature_proto);
-  proto->mutable_updates_title_in_background()->CopyFrom(feature_proto);
-  proto->mutable_uses_audio_in_background()->CopyFrom(feature_proto);
-}
-
-}  // namespace
-
-class LevelDBSiteCharacteristicsDatabaseTest : public ::testing::Test {
- public:
-  LevelDBSiteCharacteristicsDatabaseTest() {}
-
-  void SetUp() override {
-    EXPECT_TRUE(temp_dir_.CreateUniqueTempDir());
-    OpenDB();
-  }
-
-  void TearDown() override {
-    db_.reset();
-    WaitForAsyncOperationsToComplete();
-    EXPECT_TRUE(temp_dir_.Delete());
-  }
-
-  void OpenDB() {
-    OpenDB(temp_dir_.GetPath().Append(FILE_PATH_LITERAL("LocalDB")));
-  }
-
-  void OpenDB(base::FilePath path) {
-    db_ = std::make_unique<LevelDBSiteCharacteristicsDatabase>(path);
-    WaitForAsyncOperationsToComplete();
-    EXPECT_TRUE(db_);
-    db_path_ = path;
-  }
-
-  const base::FilePath& GetTempPath() { return temp_dir_.GetPath(); }
-  const base::FilePath& GetDBPath() { return db_path_; }
-
- protected:
-  // Try to read an entry from the database, returns true if the entry is
-  // present and false otherwise. |receiving_proto| will receive the protobuf
-  // corresponding to this entry on success.
-  bool ReadFromDB(const url::Origin& origin, SiteDataProto* receiving_proto) {
-    EXPECT_TRUE(receiving_proto);
-    bool success = false;
-    auto init_callback = base::BindOnce(
-        [](SiteDataProto* receiving_proto, bool* success,
-           base::Optional<SiteDataProto> proto_opt) {
-          *success = proto_opt.has_value();
-          if (proto_opt)
-            receiving_proto->CopyFrom(proto_opt.value());
-        },
-        base::Unretained(receiving_proto), base::Unretained(&success));
-    db_->ReadSiteCharacteristicsFromDB(origin, std::move(init_callback));
-    WaitForAsyncOperationsToComplete();
-    return success;
-  }
-
-  // Add some entries to the database and returns a vector with their origins.
-  std::vector<url::Origin> AddDummyEntriesToDB(size_t num_entries) {
-    std::vector<url::Origin> site_origins;
-    for (size_t i = 0; i < num_entries; ++i) {
-      SiteDataProto proto_temp;
-      std::string origin_str = base::StringPrintf("http://%zu.com", i);
-      InitSiteCharacteristicProto(&proto_temp,
-                                  static_cast<::google::protobuf::int64>(i));
-      EXPECT_TRUE(proto_temp.IsInitialized());
-      url::Origin origin = url::Origin::Create(GURL(origin_str));
-      db_->WriteSiteCharacteristicsIntoDB(origin, proto_temp);
-      site_origins.emplace_back(origin);
-    }
-    WaitForAsyncOperationsToComplete();
-    return site_origins;
-  }
-
-  void WaitForAsyncOperationsToComplete() { task_env_.RunUntilIdle(); }
-
-  const url::Origin kDummyOrigin = url::Origin::Create(GURL("http://foo.com"));
-
-  base::FilePath db_path_;
-  base::test::TaskEnvironment task_env_;
-  base::ScopedTempDir temp_dir_;
-  std::unique_ptr<LevelDBSiteCharacteristicsDatabase> db_;
-};
-
-TEST_F(LevelDBSiteCharacteristicsDatabaseTest, InitAndStoreSiteCharacteristic) {
-  // Initializing an entry that doesn't exist in the database should fail.
-  SiteDataProto early_read_proto;
-  EXPECT_FALSE(ReadFromDB(kDummyOrigin, &early_read_proto));
-
-  // Add an entry to the database and make sure that we can read it back.
-  ::google::protobuf::int64 test_value = 42;
-  SiteDataProto stored_proto;
-  InitSiteCharacteristicProto(&stored_proto, test_value);
-  db_->WriteSiteCharacteristicsIntoDB(kDummyOrigin, stored_proto);
-  SiteDataProto read_proto;
-  EXPECT_TRUE(ReadFromDB(kDummyOrigin, &read_proto));
-  EXPECT_TRUE(read_proto.IsInitialized());
-  EXPECT_EQ(stored_proto.SerializeAsString(), read_proto.SerializeAsString());
-}
-
-TEST_F(LevelDBSiteCharacteristicsDatabaseTest, RemoveEntries) {
-  std::vector<url::Origin> site_origins = AddDummyEntriesToDB(10);
-
-  // Remove half the origins from the database.
-  std::vector<url::Origin> site_origins_to_remove(
-      site_origins.begin(), site_origins.begin() + site_origins.size() / 2);
-  db_->RemoveSiteCharacteristicsFromDB(site_origins_to_remove);
-
-  WaitForAsyncOperationsToComplete();
-
-  // Verify that the origins were removed correctly.
-  SiteDataProto proto_temp;
-  for (const auto& iter : site_origins_to_remove)
-    EXPECT_FALSE(ReadFromDB(iter, &proto_temp));
-
-  for (auto iter = site_origins.begin() + site_origins.size() / 2;
-       iter != site_origins.end(); ++iter) {
-    EXPECT_TRUE(ReadFromDB(*iter, &proto_temp));
-  }
-
-  // Clear the database.
-  db_->ClearDatabase();
-
-  WaitForAsyncOperationsToComplete();
-
-  // Verify that no origin remains.
-  for (auto iter : site_origins)
-    EXPECT_FALSE(ReadFromDB(iter, &proto_temp));
-}
-
-TEST_F(LevelDBSiteCharacteristicsDatabaseTest, GetDatabaseSize) {
-  std::vector<url::Origin> site_origins = AddDummyEntriesToDB(200);
-
-  auto size_callback =
-      base::BindLambdaForTesting([&](base::Optional<int64_t> num_rows,
-                                     base::Optional<int64_t> on_disk_size_kb) {
-        EXPECT_TRUE(num_rows);
-        // The DB contains an extra row for metadata.
-        int64_t expected_rows = site_origins.size() + 1;
-        EXPECT_EQ(expected_rows, num_rows.value());
-
-        EXPECT_TRUE(on_disk_size_kb);
-        EXPECT_LT(0, on_disk_size_kb.value());
-      });
-
-  db_->GetDatabaseSize(std::move(size_callback));
-
-  WaitForAsyncOperationsToComplete();
-
-  // Verify that the DB is still operational (see implementation detail
-  // for Windows).
-  SiteDataProto read_proto;
-  EXPECT_TRUE(ReadFromDB(site_origins[0], &read_proto));
-}
-
-TEST_F(LevelDBSiteCharacteristicsDatabaseTest, DatabaseRecoveryTest) {
-  std::vector<url::Origin> site_origins = AddDummyEntriesToDB(10);
-
-  db_.reset();
-
-  EXPECT_TRUE(leveldb_chrome::CorruptClosedDBForTesting(GetDBPath()));
-
-  base::HistogramTester histogram_tester;
-  histogram_tester.ExpectTotalCount("ResourceCoordinator.LocalDB.DatabaseInit",
-                                    0);
-  // Open the corrupt DB and ensure that the appropriate histograms gets
-  // updated.
-  OpenDB();
-  EXPECT_TRUE(db_->DatabaseIsInitializedForTesting());
-  histogram_tester.ExpectUniqueSample(
-      "ResourceCoordinator.LocalDB.DatabaseInit", 1 /* kInitStatusCorruption */,
-      1);
-  histogram_tester.ExpectUniqueSample(
-      "ResourceCoordinator.LocalDB.DatabaseInitAfterRepair",
-      0 /* kInitStatusOk */, 1);
-
-  // TODO(sebmarchand): try to induce an I/O error by deleting one of the
-  // manifest files.
-}
-
-// Ensure that there's no fatal failures if we try using the database after
-// failing to open it (all the events will be ignored).
-TEST_F(LevelDBSiteCharacteristicsDatabaseTest, DatabaseOpeningFailure) {
-  db_.reset();
-  ScopedReadOnlyDirectory read_only_dir(GetTempPath());
-
-  OpenDB(read_only_dir.GetReadOnlyPath());
-  EXPECT_FALSE(db_->DatabaseIsInitializedForTesting());
-
-  SiteDataProto proto_temp;
-  EXPECT_FALSE(
-      ReadFromDB(url::Origin::Create(GURL("https://foo.com")), &proto_temp));
-  WaitForAsyncOperationsToComplete();
-  db_->WriteSiteCharacteristicsIntoDB(
-      url::Origin::Create(GURL("https://foo.com")), proto_temp);
-  WaitForAsyncOperationsToComplete();
-  db_->RemoveSiteCharacteristicsFromDB({});
-  WaitForAsyncOperationsToComplete();
-  db_->ClearDatabase();
-  WaitForAsyncOperationsToComplete();
-}
-
-TEST_F(LevelDBSiteCharacteristicsDatabaseTest, DBGetsClearedOnVersionUpgrade) {
-  leveldb::DB* raw_db = db_->GetDBForTesting();
-  EXPECT_TRUE(raw_db);
-
-  // Remove the entry containing the DB version number, this will cause the DB
-  // to be cleared the next time it gets opened.
-  leveldb::Status s =
-      raw_db->Delete(leveldb::WriteOptions(),
-                     LevelDBSiteCharacteristicsDatabase::kDbMetadataKey);
-  EXPECT_TRUE(s.ok());
-
-  // Add some dummy data to the database to ensure the database gets cleared
-  // when upgrading it to the new version.
-  ::google::protobuf::int64 test_value = 42;
-  SiteDataProto stored_proto;
-  InitSiteCharacteristicProto(&stored_proto, test_value);
-  db_->WriteSiteCharacteristicsIntoDB(kDummyOrigin, stored_proto);
-  WaitForAsyncOperationsToComplete();
-
-  db_.reset();
-
-  // Reopen the database and ensure that it has been cleared.
-  OpenDB();
-  raw_db = db_->GetDBForTesting();
-  std::string db_metadata;
-  s = raw_db->Get(leveldb::ReadOptions(),
-                  LevelDBSiteCharacteristicsDatabase::kDbMetadataKey,
-                  &db_metadata);
-  EXPECT_TRUE(s.ok());
-  size_t version = std::numeric_limits<size_t>::max();
-  EXPECT_TRUE(base::StringToSizeT(db_metadata, &version));
-  EXPECT_EQ(LevelDBSiteCharacteristicsDatabase::kDbVersion, version);
-
-  SiteDataProto proto_temp;
-  EXPECT_FALSE(ReadFromDB(kDummyOrigin, &proto_temp));
-}
-
-}  // namespace resource_coordinator
diff --git a/chrome/browser/resource_coordinator/local_site_characteristics_data_impl.cc b/chrome/browser/resource_coordinator/local_site_characteristics_data_impl.cc
deleted file mode 100644
index 0ed6699..0000000
--- a/chrome/browser/resource_coordinator/local_site_characteristics_data_impl.cc
+++ /dev/null
@@ -1,452 +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 "chrome/browser/resource_coordinator/local_site_characteristics_data_impl.h"
-
-#include <algorithm>
-#include <vector>
-
-#include "base/bind.h"
-#include "base/callback.h"
-#include "base/metrics/histogram_functions.h"
-#include "base/metrics/histogram_macros.h"
-#include "base/strings/stringprintf.h"
-#include "chrome/browser/resource_coordinator/local_site_characteristics_database.h"
-#include "chrome/browser/resource_coordinator/time.h"
-
-namespace resource_coordinator {
-namespace internal {
-
-namespace {
-
-// The sample weighing factor for the exponential moving averages for
-// performance measurements. A factor of 1/2 gives each sample an equal weight
-// to the entire previous history. As we don't know much noise there is to the
-// measurement, this is essentially a shot in the dark.
-// TODO(siggi): Consider adding UMA metrics to capture e.g. the fractional delta
-//      from the current average, or some such.
-constexpr float kSampleWeightFactor = 0.5;
-
-// Observations windows have a default value of 2 hours, 95% of backgrounded
-// tabs don't use any of these features in this time window.
-static constexpr base::TimeDelta kObservationWindowLength =
-    base::TimeDelta::FromHours(2);
-
-base::TimeDelta GetTickDeltaSinceEpoch() {
-  return NowTicks() - base::TimeTicks::UnixEpoch();
-}
-
-// Returns all the SiteDataFeatureProto elements contained in a
-// SiteDataProto protobuf object.
-std::vector<SiteDataFeatureProto*> GetAllFeaturesFromProto(
-    SiteDataProto* proto) {
-  std::vector<SiteDataFeatureProto*> ret(
-      {proto->mutable_updates_favicon_in_background(),
-       proto->mutable_updates_title_in_background(),
-       proto->mutable_uses_audio_in_background()});
-
-  return ret;
-}
-
-const char* FeatureTypeToFeatureName(
-    const LocalSiteCharacteristicsDataImpl::TrackedBackgroundFeatures feature) {
-  switch (feature) {
-    case LocalSiteCharacteristicsDataImpl::TrackedBackgroundFeatures::
-        kFaviconUpdate:
-      return "FaviconUpdateInBackground";
-    case LocalSiteCharacteristicsDataImpl::TrackedBackgroundFeatures::
-        kTitleUpdate:
-      return "TitleUpdateInBackground";
-    case LocalSiteCharacteristicsDataImpl::TrackedBackgroundFeatures::
-        kAudioUsage:
-      return "AudioUsageInBackground";
-  }
-}
-
-}  // namespace
-
-void LocalSiteCharacteristicsDataImpl::NotifySiteLoaded() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  // Update the last loaded time when this origin gets loaded for the first
-  // time.
-  if (loaded_tabs_count_ == 0) {
-    site_characteristics_.set_last_loaded(
-        TimeDeltaToInternalRepresentation(GetTickDeltaSinceEpoch()));
-
-    is_dirty_ = true;
-  }
-  loaded_tabs_count_++;
-}
-
-void LocalSiteCharacteristicsDataImpl::NotifySiteUnloaded(
-    performance_manager::TabVisibility tab_visibility) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-  if (tab_visibility == performance_manager::TabVisibility::kBackground)
-    DecrementNumLoadedBackgroundTabs();
-
-  loaded_tabs_count_--;
-  // Only update the last loaded time when there's no more loaded instance of
-  // this origin.
-  if (loaded_tabs_count_ > 0U)
-    return;
-
-  base::TimeDelta current_unix_time = GetTickDeltaSinceEpoch();
-
-  // Update the |last_loaded_time_| field, as the moment this site gets unloaded
-  // also corresponds to the last moment it was loaded.
-  site_characteristics_.set_last_loaded(
-      TimeDeltaToInternalRepresentation(current_unix_time));
-}
-
-void LocalSiteCharacteristicsDataImpl::NotifyLoadedSiteBackgrounded() {
-  if (loaded_tabs_in_background_count_ == 0)
-    background_session_begin_ = NowTicks();
-
-  loaded_tabs_in_background_count_++;
-
-  DCHECK_LE(loaded_tabs_in_background_count_, loaded_tabs_count_);
-}
-
-void LocalSiteCharacteristicsDataImpl::NotifyLoadedSiteForegrounded() {
-  DecrementNumLoadedBackgroundTabs();
-}
-
-performance_manager::SiteFeatureUsage
-LocalSiteCharacteristicsDataImpl::UpdatesFaviconInBackground() const {
-  return GetFeatureUsage(site_characteristics_.updates_favicon_in_background());
-}
-
-performance_manager::SiteFeatureUsage
-LocalSiteCharacteristicsDataImpl::UpdatesTitleInBackground() const {
-  return GetFeatureUsage(site_characteristics_.updates_title_in_background());
-}
-
-performance_manager::SiteFeatureUsage
-LocalSiteCharacteristicsDataImpl::UsesAudioInBackground() const {
-  return GetFeatureUsage(site_characteristics_.uses_audio_in_background());
-}
-
-bool LocalSiteCharacteristicsDataImpl::DataLoaded() const {
-  return fully_initialized_;
-}
-
-void LocalSiteCharacteristicsDataImpl::RegisterDataLoadedCallback(
-    base::OnceClosure&& callback) {
-  if (fully_initialized_) {
-    std::move(callback).Run();
-    return;
-  }
-  data_loaded_callbacks_.emplace_back(std::move(callback));
-}
-
-void LocalSiteCharacteristicsDataImpl::NotifyUpdatesFaviconInBackground() {
-  NotifyFeatureUsage(
-      site_characteristics_.mutable_updates_favicon_in_background(),
-      TrackedBackgroundFeatures::kFaviconUpdate);
-}
-
-void LocalSiteCharacteristicsDataImpl::NotifyUpdatesTitleInBackground() {
-  NotifyFeatureUsage(
-      site_characteristics_.mutable_updates_title_in_background(),
-      TrackedBackgroundFeatures::kTitleUpdate);
-}
-
-void LocalSiteCharacteristicsDataImpl::NotifyUsesAudioInBackground() {
-  NotifyFeatureUsage(site_characteristics_.mutable_uses_audio_in_background(),
-                     TrackedBackgroundFeatures::kAudioUsage);
-}
-
-void LocalSiteCharacteristicsDataImpl::NotifyLoadTimePerformanceMeasurement(
-    base::TimeDelta load_duration,
-    base::TimeDelta cpu_usage_estimate,
-    uint64_t private_footprint_kb_estimate) {
-  is_dirty_ = true;
-
-  load_duration_.AppendDatum(load_duration.InMicroseconds());
-  cpu_usage_estimate_.AppendDatum(cpu_usage_estimate.InMicroseconds());
-  private_footprint_kb_estimate_.AppendDatum(private_footprint_kb_estimate);
-}
-
-void LocalSiteCharacteristicsDataImpl::ExpireAllObservationWindowsForTesting() {
-  for (auto* iter : GetAllFeaturesFromProto(&site_characteristics_))
-    IncrementFeatureObservationDuration(iter, kObservationWindowLength);
-}
-
-void LocalSiteCharacteristicsDataImpl::RegisterFeatureUsageCallbackForTesting(
-    const TrackedBackgroundFeatures feature_type,
-    base::OnceClosure callback) {
-  DCHECK(
-      !feature_usage_callback_for_testing_[static_cast<size_t>(feature_type)]);
-  feature_usage_callback_for_testing_[static_cast<size_t>(feature_type)] =
-      std::move(callback);
-}
-
-LocalSiteCharacteristicsDataImpl::LocalSiteCharacteristicsDataImpl(
-    const url::Origin& origin,
-    OnDestroyDelegate* delegate,
-    LocalSiteCharacteristicsDatabase* database)
-    : load_duration_(kSampleWeightFactor),
-      cpu_usage_estimate_(kSampleWeightFactor),
-      private_footprint_kb_estimate_(kSampleWeightFactor),
-      origin_(origin),
-      loaded_tabs_count_(0U),
-      loaded_tabs_in_background_count_(0U),
-      database_(database),
-      delegate_(delegate),
-      fully_initialized_(false),
-      is_dirty_(false) {
-  DCHECK(database_);
-  DCHECK(delegate_);
-
-  database_->ReadSiteCharacteristicsFromDB(
-      origin_, base::BindOnce(&LocalSiteCharacteristicsDataImpl::OnInitCallback,
-                              weak_factory_.GetWeakPtr()));
-}
-
-LocalSiteCharacteristicsDataImpl::~LocalSiteCharacteristicsDataImpl() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  // All users of this object should make sure that they send the same number of
-  // NotifySiteLoaded and NotifySiteUnloaded events, in practice this mean
-  // tracking the loaded state and sending an unload event in their destructor
-  // if needed.
-  DCHECK(!IsLoaded());
-  DCHECK_EQ(0U, loaded_tabs_in_background_count_);
-
-  DCHECK(delegate_);
-  delegate_->OnLocalSiteCharacteristicsDataImplDestroyed(this);
-
-  // TODO(sebmarchand): Some data might be lost here if the read operation has
-  // not completed, add some metrics to measure if this is really an issue.
-  if (is_dirty_ && fully_initialized_)
-    database_->WriteSiteCharacteristicsIntoDB(origin_, FlushStateToProto());
-}
-
-base::TimeDelta LocalSiteCharacteristicsDataImpl::FeatureObservationDuration(
-    const SiteDataFeatureProto& feature_proto) const {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  // Get the current observation duration value if available.
-  base::TimeDelta observation_time_for_feature;
-  if (feature_proto.has_observation_duration()) {
-    observation_time_for_feature =
-        InternalRepresentationToTimeDelta(feature_proto.observation_duration());
-  }
-
-  // If this site is still in background and the feature isn't in use then the
-  // observation time since load needs to be added.
-  if (loaded_tabs_in_background_count_ > 0U &&
-      InternalRepresentationToTimeDelta(feature_proto.use_timestamp())
-          .is_zero()) {
-    base::TimeDelta observation_time_since_backgrounded =
-        NowTicks() - background_session_begin_;
-    observation_time_for_feature += observation_time_since_backgrounded;
-  }
-
-  return observation_time_for_feature;
-}
-
-// static:
-void LocalSiteCharacteristicsDataImpl::IncrementFeatureObservationDuration(
-    SiteDataFeatureProto* feature_proto,
-    base::TimeDelta extra_observation_duration) {
-  if (!feature_proto->has_use_timestamp() ||
-      InternalRepresentationToTimeDelta(feature_proto->use_timestamp())
-          .is_zero()) {
-    feature_proto->set_observation_duration(TimeDeltaToInternalRepresentation(
-        InternalRepresentationToTimeDelta(
-            feature_proto->observation_duration()) +
-        extra_observation_duration));
-  }
-}
-
-void LocalSiteCharacteristicsDataImpl::
-    ClearObservationsAndInvalidateReadOperation() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-  // Invalidate the weak pointer that have been served, this will ensure that
-  // this object doesn't get initialized from the database after being cleared.
-  weak_factory_.InvalidateWeakPtrs();
-
-  // Reset all the observations.
-  site_characteristics_.Clear();
-
-  // Clear the performance estimates, both the local state and the proto.
-  cpu_usage_estimate_.Clear();
-  private_footprint_kb_estimate_.Clear();
-  site_characteristics_.clear_load_time_estimates();
-
-  // Set the last loaded time to the current time if there's some loaded
-  // instances of this site.
-  if (IsLoaded()) {
-    site_characteristics_.set_last_loaded(
-        TimeDeltaToInternalRepresentation(GetTickDeltaSinceEpoch()));
-  }
-
-  // This object is now in a valid state and can be written in the database.
-  TransitionToFullyInitialized();
-}
-
-performance_manager::SiteFeatureUsage
-LocalSiteCharacteristicsDataImpl::GetFeatureUsage(
-    const SiteDataFeatureProto& feature_proto) const {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-  UMA_HISTOGRAM_BOOLEAN(
-      "ResourceCoordinator.LocalDB.ReadHasCompletedBeforeQuery",
-      fully_initialized_);
-
-  // Checks if this feature has already been observed.
-  // TODO(sebmarchand): Check the timestamp and reset features that haven't been
-  // observed in a long time, https://crbug.com/826446.
-  if (feature_proto.has_use_timestamp())
-    return performance_manager::SiteFeatureUsage::kSiteFeatureInUse;
-
-  if (FeatureObservationDuration(feature_proto) >= kObservationWindowLength)
-    return performance_manager::SiteFeatureUsage::kSiteFeatureNotInUse;
-
-  return performance_manager::SiteFeatureUsage::kSiteFeatureUsageUnknown;
-}
-
-void LocalSiteCharacteristicsDataImpl::NotifyFeatureUsage(
-    SiteDataFeatureProto* feature_proto,
-    const TrackedBackgroundFeatures feature_type) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  DCHECK(IsLoaded());
-  DCHECK_GT(loaded_tabs_in_background_count_, 0U);
-
-  // Report the observation time if this is the first time this feature is
-  // observed.
-  if (feature_proto->observation_duration() != 0) {
-    base::UmaHistogramCustomTimes(
-        base::StringPrintf(
-            "ResourceCoordinator.LocalDB.ObservationTimeBeforeFirstUse.%s",
-            FeatureTypeToFeatureName(feature_type)),
-        InternalRepresentationToTimeDelta(
-            feature_proto->observation_duration()),
-        base::TimeDelta::FromSeconds(1), base::TimeDelta::FromDays(1), 100);
-  }
-
-  feature_proto->Clear();
-  feature_proto->set_use_timestamp(
-      TimeDeltaToInternalRepresentation(GetTickDeltaSinceEpoch()));
-
-  if (feature_usage_callback_for_testing_[static_cast<size_t>(feature_type)]) {
-    std::move(
-        feature_usage_callback_for_testing_[static_cast<size_t>(feature_type)])
-        .Run();
-  }
-}
-
-void LocalSiteCharacteristicsDataImpl::OnInitCallback(
-    base::Optional<SiteDataProto> db_site_characteristics) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  // Check if the initialization has succeeded.
-  if (db_site_characteristics) {
-    // If so, iterates over all the features and initialize them.
-    auto this_features = GetAllFeaturesFromProto(&site_characteristics_);
-    auto db_features =
-        GetAllFeaturesFromProto(&db_site_characteristics.value());
-    auto this_features_iter = this_features.begin();
-    auto db_features_iter = db_features.begin();
-    for (; this_features_iter != this_features.end() &&
-           db_features_iter != db_features.end();
-         ++this_features_iter, ++db_features_iter) {
-      // If the |use_timestamp| field is set for the in-memory entry for this
-      // feature then there's nothing to do, otherwise update it with the values
-      // from the database.
-      if (!(*this_features_iter)->has_use_timestamp()) {
-        if ((*db_features_iter)->has_use_timestamp() &&
-            (*db_features_iter)->use_timestamp() != 0) {
-          (*this_features_iter)->Clear();
-          // Keep the use timestamp from the database, if any.
-          (*this_features_iter)
-              ->set_use_timestamp((*db_features_iter)->use_timestamp());
-        } else {
-          // Else, add the observation duration from the database to the
-          // in-memory observation duration.
-          IncrementFeatureObservationDuration(
-              (*this_features_iter),
-              InternalRepresentationToTimeDelta(
-                  (*db_features_iter)->observation_duration()));
-        }
-      }
-    }
-    // Only update the last loaded field if we haven't updated it since the
-    // creation of this object.
-    if (!site_characteristics_.has_last_loaded()) {
-      site_characteristics_.set_last_loaded(
-          db_site_characteristics->last_loaded());
-    }
-    // If there was on-disk data, update the in-memory performance averages.
-    if (db_site_characteristics->has_load_time_estimates()) {
-      const auto& estimates = db_site_characteristics->load_time_estimates();
-      if (estimates.has_avg_load_duration_us())
-        load_duration_.PrependDatum(estimates.avg_load_duration_us());
-      if (estimates.has_avg_cpu_usage_us())
-        cpu_usage_estimate_.PrependDatum(estimates.avg_cpu_usage_us());
-      if (estimates.has_avg_footprint_kb()) {
-        private_footprint_kb_estimate_.PrependDatum(
-            estimates.avg_footprint_kb());
-      }
-    }
-  }
-
-  TransitionToFullyInitialized();
-}
-
-void LocalSiteCharacteristicsDataImpl::DecrementNumLoadedBackgroundTabs() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  DCHECK_GT(loaded_tabs_in_background_count_, 0U);
-  loaded_tabs_in_background_count_--;
-  // Only update the observation durations if there's no more backgounded
-  // instance of this origin.
-  if (loaded_tabs_in_background_count_ == 0U)
-    FlushFeaturesObservationDurationToProto();
-}
-
-const SiteDataProto& LocalSiteCharacteristicsDataImpl::FlushStateToProto() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  // Update the proto with the most current performance measurement averages.
-  if (cpu_usage_estimate_.num_datums() ||
-      private_footprint_kb_estimate_.num_datums()) {
-    auto* estimates = site_characteristics_.mutable_load_time_estimates();
-    if (load_duration_.num_datums())
-      estimates->set_avg_load_duration_us(load_duration_.value());
-    if (cpu_usage_estimate_.num_datums())
-      estimates->set_avg_cpu_usage_us(cpu_usage_estimate_.value());
-    if (private_footprint_kb_estimate_.num_datums()) {
-      estimates->set_avg_footprint_kb(private_footprint_kb_estimate_.value());
-    }
-  }
-
-  if (loaded_tabs_in_background_count_ > 0U)
-    FlushFeaturesObservationDurationToProto();
-
-  return site_characteristics_;
-}
-
-void LocalSiteCharacteristicsDataImpl::
-    FlushFeaturesObservationDurationToProto() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  DCHECK(!background_session_begin_.is_null());
-
-  base::TimeTicks now = NowTicks();
-
-  base::TimeDelta extra_observation_duration = now - background_session_begin_;
-  background_session_begin_ = now;
-
-  // Update the observation duration fields.
-  for (auto* iter : GetAllFeaturesFromProto(&site_characteristics_))
-    IncrementFeatureObservationDuration(iter, extra_observation_duration);
-}
-
-void LocalSiteCharacteristicsDataImpl::TransitionToFullyInitialized() {
-  fully_initialized_ = true;
-  for (size_t i = 0; i < data_loaded_callbacks_.size(); ++i)
-    std::move(data_loaded_callbacks_[i]).Run();
-  data_loaded_callbacks_.clear();
-}
-
-}  // namespace internal
-}  // namespace resource_coordinator
diff --git a/chrome/browser/resource_coordinator/local_site_characteristics_data_impl.h b/chrome/browser/resource_coordinator/local_site_characteristics_data_impl.h
deleted file mode 100644
index d80f8d2..0000000
--- a/chrome/browser/resource_coordinator/local_site_characteristics_data_impl.h
+++ /dev/null
@@ -1,313 +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 CHROME_BROWSER_RESOURCE_COORDINATOR_LOCAL_SITE_CHARACTERISTICS_DATA_IMPL_H_
-#define CHROME_BROWSER_RESOURCE_COORDINATOR_LOCAL_SITE_CHARACTERISTICS_DATA_IMPL_H_
-
-#include "base/callback_forward.h"
-#include "base/gtest_prod_util.h"
-#include "base/macros.h"
-#include "base/memory/ref_counted.h"
-#include "base/memory/weak_ptr.h"
-#include "base/sequence_checker.h"
-#include "base/time/time.h"
-#include "chrome/browser/resource_coordinator/local_site_characteristics_database.h"
-#include "chrome/browser/resource_coordinator/tab_manager_features.h"
-#include "components/performance_manager/persistence/site_data/exponential_moving_average.h"
-#include "components/performance_manager/persistence/site_data/feature_usage.h"
-#include "components/performance_manager/persistence/site_data/site_data.pb.h"
-#include "components/performance_manager/persistence/site_data/tab_visibility.h"
-#include "url/origin.h"
-
-namespace resource_coordinator {
-
-class LocalSiteCharacteristicsDatabase;
-class LocalSiteCharacteristicsDataStore;
-class LocalSiteCharacteristicsDataStoreTest;
-class LocalSiteCharacteristicsDataReaderTest;
-class LocalSiteCharacteristicsDataWriterTest;
-
-FORWARD_DECLARE_TEST(LocalSiteCharacteristicsDataReaderTest,
-                     DestroyingReaderCancelsPendingCallbacks);
-FORWARD_DECLARE_TEST(LocalSiteCharacteristicsDataReaderTest,
-                     FreeingReaderDoesntCauseWriteOperation);
-FORWARD_DECLARE_TEST(LocalSiteCharacteristicsDataReaderTest,
-                     OnDataLoadedCallbackInvoked);
-
-namespace internal {
-
-FORWARD_DECLARE_TEST(LocalSiteCharacteristicsDataImplTest,
-                     LateAsyncReadDoesntBypassClearEvent);
-
-// Internal class used to read/write site characteristics. This is a wrapper
-// class around a SiteDataProto object and offers various to query
-// and/or modify it. This class shouldn't be used directly, instead it should be
-// created by a LocalSiteCharacteristicsDataStore that will serve reader and
-// writer objects.
-//
-// Reader and writers objects that are interested in reading/writing information
-// about the same origin will share a unique ref counted instance of this
-// object, because of this all the operations done on these objects should be
-// done on the same thread, this class isn't thread safe.
-//
-// By default tabs associated with instances of this class are assumed to be
-// running in foreground, |NotifyTabBackgrounded| should get called to indicate
-// that the tab is running in background.
-class LocalSiteCharacteristicsDataImpl
-    : public base::RefCounted<LocalSiteCharacteristicsDataImpl> {
- public:
-  // Interface that should be implemented in order to receive notifications when
-  // this object is about to get destroyed.
-  class OnDestroyDelegate {
-   public:
-    // Called when this object is about to get destroyed.
-    virtual void OnLocalSiteCharacteristicsDataImplDestroyed(
-        LocalSiteCharacteristicsDataImpl* impl) = 0;
-  };
-
-  enum class TrackedBackgroundFeatures {
-    kFaviconUpdate,
-    kTitleUpdate,
-    kAudioUsage,
-    kMaxValue = kAudioUsage,
-  };
-
-  // Must be called when a load event is received for this site, this can be
-  // invoked several times if instances of this class are shared between
-  // multiple tabs.
-  void NotifySiteLoaded();
-
-  // Must be called when an unload event is received for this site, this can be
-  // invoked several times if instances of this class are shared between
-  // multiple tabs.
-  void NotifySiteUnloaded(performance_manager::TabVisibility tab_visibility);
-
-  // Must be called when a loaded tab gets backgrounded.
-  void NotifyLoadedSiteBackgrounded();
-
-  // Must be called when a loaded tab gets foregrounded.
-  void NotifyLoadedSiteForegrounded();
-
-  // Returns the usage of a given feature for this origin.
-  performance_manager::SiteFeatureUsage UpdatesFaviconInBackground() const;
-  performance_manager::SiteFeatureUsage UpdatesTitleInBackground() const;
-  performance_manager::SiteFeatureUsage UsesAudioInBackground() const;
-
-  // Returns true if the most authoritative data has been loaded from the
-  // backing store.
-  bool DataLoaded() const;
-
-  // Registers a callback to be invoked when the data backing this object is
-  // loaded from disk, or otherwise authoritatively initialized.
-  void RegisterDataLoadedCallback(base::OnceClosure&& callback);
-
-  // Accessors for load-time performance measurement estimates.
-  // If |num_datum| is zero, there's no estimate available.
-  const performance_manager::ExponentialMovingAverage& load_duration() const {
-    return load_duration_;
-  }
-  const performance_manager::ExponentialMovingAverage& cpu_usage_estimate()
-      const {
-    return cpu_usage_estimate_;
-  }
-  const performance_manager::ExponentialMovingAverage&
-  private_footprint_kb_estimate() const {
-    return private_footprint_kb_estimate_;
-  }
-
-  // Must be called when a feature is used, calling this function updates the
-  // last observed timestamp for this feature.
-  void NotifyUpdatesFaviconInBackground();
-  void NotifyUpdatesTitleInBackground();
-  void NotifyUsesAudioInBackground();
-
-  // Call when a load-time performance measurement becomes available.
-  void NotifyLoadTimePerformanceMeasurement(
-      base::TimeDelta load_duration,
-      base::TimeDelta cpu_usage_estimate,
-      uint64_t private_footprint_kb_estimate);
-
-  base::TimeDelta last_loaded_time_for_testing() const {
-    return InternalRepresentationToTimeDelta(
-        site_characteristics_.last_loaded());
-  }
-
-  const SiteDataProto& site_characteristics_for_testing() const {
-    return site_characteristics_;
-  }
-
-  size_t loaded_tabs_count_for_testing() const { return loaded_tabs_count_; }
-
-  size_t loaded_tabs_in_background_count_for_testing() const {
-    return loaded_tabs_in_background_count_;
-  }
-
-  base::TimeTicks background_session_begin_for_testing() const {
-    return background_session_begin_;
-  }
-
-  const url::Origin& origin() const { return origin_; }
-  bool is_dirty() const { return is_dirty_; }
-
-  void ExpireAllObservationWindowsForTesting();
-
-  void ClearObservationsAndInvalidateReadOperationForTesting() {
-    ClearObservationsAndInvalidateReadOperation();
-  }
-
-  bool fully_initialized_for_testing() const { return fully_initialized_; }
-
-  void RegisterFeatureUsageCallbackForTesting(
-      const TrackedBackgroundFeatures feature_type,
-      base::OnceClosure callback);
-
- protected:
-  friend class base::RefCounted<LocalSiteCharacteristicsDataImpl>;
-  friend class resource_coordinator::LocalSiteCharacteristicsDataStore;
-
-  // Friend all the tests.
-  friend class LocalSiteCharacteristicsDataImplTest;
-  friend class resource_coordinator::LocalSiteCharacteristicsDataReaderTest;
-  friend class resource_coordinator::LocalSiteCharacteristicsDataStoreTest;
-  friend class resource_coordinator::LocalSiteCharacteristicsDataWriterTest;
-
-  LocalSiteCharacteristicsDataImpl(const url::Origin& origin,
-                                   OnDestroyDelegate* delegate,
-                                   LocalSiteCharacteristicsDatabase* database);
-
-  virtual ~LocalSiteCharacteristicsDataImpl();
-
-  // Helper functions to convert from/to the internal representation that is
-  // used to store TimeDelta values in the |SiteDataProto| protobuf.
-  static base::TimeDelta InternalRepresentationToTimeDelta(
-      ::google::protobuf::int64 value) {
-    return base::TimeDelta::FromSeconds(value);
-  }
-  static int64_t TimeDeltaToInternalRepresentation(base::TimeDelta delta) {
-    return delta.InSeconds();
-  }
-
-  // Returns for how long a given feature has been observed, this is the sum of
-  // the recorded observation duration and the current observation duration
-  // since this site has been loaded (if applicable). If a feature has been
-  // used then it returns 0.
-  base::TimeDelta FeatureObservationDuration(
-      const SiteDataFeatureProto& feature_proto) const;
-
- private:
-  FRIEND_TEST_ALL_PREFIXES(LocalSiteCharacteristicsDataImplTest,
-                           FlushingStateToProtoDoesntAffectData);
-  FRIEND_TEST_ALL_PREFIXES(LocalSiteCharacteristicsDataImplTest,
-                           LateAsyncReadDoesntBypassClearEvent);
-  FRIEND_TEST_ALL_PREFIXES(
-      resource_coordinator::LocalSiteCharacteristicsDataReaderTest,
-      DestroyingReaderCancelsPendingCallbacks);
-  FRIEND_TEST_ALL_PREFIXES(
-      resource_coordinator::LocalSiteCharacteristicsDataReaderTest,
-      FreeingReaderDoesntCauseWriteOperation);
-  FRIEND_TEST_ALL_PREFIXES(
-      resource_coordinator::LocalSiteCharacteristicsDataReaderTest,
-      OnDataLoadedCallbackInvoked);
-
-  // Add |extra_observation_duration| to the observation window of a given
-  // feature if it hasn't been used yet, do nothing otherwise.
-  static void IncrementFeatureObservationDuration(
-      SiteDataFeatureProto* feature_proto,
-      base::TimeDelta extra_observation_duration);
-
-  // Clear all the past observations about this site and invalidate the pending
-  // read observations from the database.
-  void ClearObservationsAndInvalidateReadOperation();
-
-  // Returns the usage of |site_feature| for this site.
-  performance_manager::SiteFeatureUsage GetFeatureUsage(
-      const SiteDataFeatureProto& feature_proto) const;
-
-  // Helper function to update a given |SiteDataFeatureProto| when a
-  // feature gets used.
-  void NotifyFeatureUsage(SiteDataFeatureProto* feature_proto,
-                          const TrackedBackgroundFeatures feature_type);
-
-  bool IsLoaded() const { return loaded_tabs_count_ > 0U; }
-
-  // Callback that needs to be called by the database once it has finished
-  // trying to read the protobuf.
-  void OnInitCallback(base::Optional<SiteDataProto> site_characteristic_proto);
-
-  // Decrement the |loaded_tabs_in_background_count_| counter and update the
-  // local feature observation durations if necessary.
-  void DecrementNumLoadedBackgroundTabs();
-
-  // Flush any state that's maintained in member variables to the proto.
-  const SiteDataProto& FlushStateToProto();
-
-  // Updates the proto with the current total observation duration and updates
-  // |background_session_begin_| to NowTicks().
-  void FlushFeaturesObservationDurationToProto();
-
-  void TransitionToFullyInitialized();
-
-  // This site's characteristics, contains the features and other values are
-  // measured.
-  SiteDataProto site_characteristics_;
-
-  // The in-memory storage for the moving performance averages.
-  performance_manager::ExponentialMovingAverage
-      load_duration_;  // microseconds.
-  performance_manager::ExponentialMovingAverage
-      cpu_usage_estimate_;  // microseconds.
-  performance_manager::ExponentialMovingAverage private_footprint_kb_estimate_;
-
-  // This site's origin.
-  const url::Origin origin_;
-
-  // The number of loaded tabs for this origin. Several tabs with the
-  // same origin might share the same instance of this object, this counter
-  // will allow to properly update the observation time (starts when the first
-  // tab gets loaded, stops when the last one gets unloaded).
-  size_t loaded_tabs_count_;
-
-  // Number of loaded tabs currently in background for this origin, the
-  // implementation doesn't need to track unloaded tabs running in background.
-  size_t loaded_tabs_in_background_count_;
-
-  // The time at which the |loaded_tabs_in_background_count_| counter changed
-  // from 0 to 1.
-  base::TimeTicks background_session_begin_;
-
-  // The database used to store the site characteristics, it should outlive
-  // this object.
-  LocalSiteCharacteristicsDatabase* const database_;
-
-  // The delegate that should get notified when this object is about to get
-  // destroyed, it should outlive this object.
-  OnDestroyDelegate* const delegate_;
-
-  // Indicates if this object has been fully initialized, either because the
-  // read operation from the database has completed or because it has been
-  // cleared.
-  bool fully_initialized_;
-
-  // Dirty bit, indicates if any of the fields in |site_characteristics_| has
-  // changed since it has been initialized.
-  bool is_dirty_;
-
-  // A collection of callbacks to be invoked when this object becomes fully
-  // initialized.
-  std::vector<base::OnceClosure> data_loaded_callbacks_;
-
-  base::OnceClosure feature_usage_callback_for_testing_[static_cast<size_t>(
-      TrackedBackgroundFeatures::kMaxValue) + 1];
-
-  SEQUENCE_CHECKER(sequence_checker_);
-
-  base::WeakPtrFactory<LocalSiteCharacteristicsDataImpl> weak_factory_{this};
-
-  DISALLOW_COPY_AND_ASSIGN(LocalSiteCharacteristicsDataImpl);
-};
-
-}  // namespace internal
-}  // namespace resource_coordinator
-
-#endif  // CHROME_BROWSER_RESOURCE_COORDINATOR_LOCAL_SITE_CHARACTERISTICS_DATA_IMPL_H_
diff --git a/chrome/browser/resource_coordinator/local_site_characteristics_data_impl_unittest.cc b/chrome/browser/resource_coordinator/local_site_characteristics_data_impl_unittest.cc
deleted file mode 100644
index 6da9851..0000000
--- a/chrome/browser/resource_coordinator/local_site_characteristics_data_impl_unittest.cc
+++ /dev/null
@@ -1,721 +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 "chrome/browser/resource_coordinator/local_site_characteristics_data_impl.h"
-
-#include "base/macros.h"
-#include "base/memory/ref_counted.h"
-#include "base/test/bind_test_util.h"
-#include "base/test/simple_test_tick_clock.h"
-#include "chrome/browser/resource_coordinator/local_site_characteristics_data_unittest_utils.h"
-#include "chrome/browser/resource_coordinator/time.h"
-#include "components/performance_manager/persistence/site_data/feature_usage.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "url/gurl.h"
-
-namespace resource_coordinator {
-namespace internal {
-
-namespace {
-
-constexpr base::TimeDelta kInitialTimeSinceEpoch =
-    base::TimeDelta::FromSeconds(1);
-
-constexpr base::TimeDelta kObservationWindowLength =
-    base::TimeDelta::FromHours(2);
-
-class TestLocalSiteCharacteristicsDataImpl
-    : public LocalSiteCharacteristicsDataImpl {
- public:
-  using LocalSiteCharacteristicsDataImpl::FeatureObservationDuration;
-  using LocalSiteCharacteristicsDataImpl::OnDestroyDelegate;
-  using LocalSiteCharacteristicsDataImpl::site_characteristics_for_testing;
-  using LocalSiteCharacteristicsDataImpl::TimeDeltaToInternalRepresentation;
-
-  explicit TestLocalSiteCharacteristicsDataImpl(
-      const url::Origin& origin,
-      LocalSiteCharacteristicsDataImpl::OnDestroyDelegate* delegate,
-      LocalSiteCharacteristicsDatabase* database)
-      : LocalSiteCharacteristicsDataImpl(origin, delegate, database) {}
-
-  base::TimeDelta FeatureObservationTimestamp(
-      const SiteDataFeatureProto& feature_proto) {
-    return InternalRepresentationToTimeDelta(feature_proto.use_timestamp());
-  }
-
- protected:
-  ~TestLocalSiteCharacteristicsDataImpl() override {}
-};
-
-class MockLocalSiteCharacteristicsDatabase
-    : public testing::NoopLocalSiteCharacteristicsDatabase {
- public:
-  MockLocalSiteCharacteristicsDatabase() = default;
-  ~MockLocalSiteCharacteristicsDatabase() = default;
-
-  // Note: As move-only parameters (e.g. OnceCallback) aren't supported by mock
-  // methods, add On... methods to pass a non-const reference to OnceCallback.
-  void ReadSiteCharacteristicsFromDB(
-      const url::Origin& origin,
-      LocalSiteCharacteristicsDatabase::ReadSiteCharacteristicsFromDBCallback
-          callback) override {
-    OnReadSiteCharacteristicsFromDB(origin, callback);
-  }
-  MOCK_METHOD2(OnReadSiteCharacteristicsFromDB,
-               void(const url::Origin&,
-                    LocalSiteCharacteristicsDatabase::
-                        ReadSiteCharacteristicsFromDBCallback&));
-
-  MOCK_METHOD2(WriteSiteCharacteristicsIntoDB,
-               void(const url::Origin&, const SiteDataProto&));
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(MockLocalSiteCharacteristicsDatabase);
-};
-
-// Returns a SiteDataFeatureProto that indicates that a feature
-// hasn't been used.
-SiteDataFeatureProto GetUnusedFeatureProto() {
-  SiteDataFeatureProto unused_feature_proto;
-  unused_feature_proto.set_observation_duration(1U);
-  unused_feature_proto.set_use_timestamp(0U);
-  return unused_feature_proto;
-}
-
-// Returns a SiteDataFeatureProto that indicates that a feature
-// has been used.
-SiteDataFeatureProto GetUsedFeatureProto() {
-  SiteDataFeatureProto used_feature_proto;
-  used_feature_proto.set_observation_duration(0U);
-  used_feature_proto.set_use_timestamp(1U);
-  return used_feature_proto;
-}
-
-}  // namespace
-
-class LocalSiteCharacteristicsDataImplTest : public ::testing::Test {
- public:
-  LocalSiteCharacteristicsDataImplTest()
-      : scoped_set_tick_clock_for_testing_(&test_clock_) {}
-
-  void SetUp() override {
-    test_clock_.SetNowTicks(base::TimeTicks::UnixEpoch());
-    // Advance the test clock by a small delay, as some tests will fail if the
-    // current time is equal to Epoch.
-    test_clock_.Advance(kInitialTimeSinceEpoch);
-  }
-
- protected:
-  scoped_refptr<TestLocalSiteCharacteristicsDataImpl> GetDataImpl(
-      const url::Origin& origin,
-      LocalSiteCharacteristicsDataImpl::OnDestroyDelegate* destroy_delegate,
-      LocalSiteCharacteristicsDatabase* database) {
-    return base::MakeRefCounted<TestLocalSiteCharacteristicsDataImpl>(
-        origin, destroy_delegate, database);
-  }
-
-  // Use a mock database to intercept the initialization callback and save it
-  // locally so it can be run later.
-  scoped_refptr<TestLocalSiteCharacteristicsDataImpl>
-  GetDataImplAndInterceptReadCallback(
-      const url::Origin& origin,
-      LocalSiteCharacteristicsDataImpl::OnDestroyDelegate* destroy_delegate,
-      MockLocalSiteCharacteristicsDatabase* mock_db,
-      LocalSiteCharacteristicsDatabase::ReadSiteCharacteristicsFromDBCallback*
-          read_cb) {
-    auto read_from_db_mock_impl =
-        [&](const url::Origin& origin,
-            LocalSiteCharacteristicsDatabase::
-                ReadSiteCharacteristicsFromDBCallback& callback) {
-          *read_cb = std::move(callback);
-        };
-
-    EXPECT_CALL(*mock_db,
-                OnReadSiteCharacteristicsFromDB(::testing::_, ::testing::_))
-        .WillOnce(::testing::Invoke(read_from_db_mock_impl));
-    auto local_site_data = GetDataImpl(origin, &destroy_delegate_, mock_db);
-    ::testing::Mock::VerifyAndClear(mock_db);
-    return local_site_data;
-  }
-
-  const url::Origin kDummyOrigin = url::Origin::Create(GURL("foo.com"));
-  const url::Origin kDummyOrigin2 = url::Origin::Create(GURL("bar.com"));
-
-  base::SimpleTestTickClock test_clock_;
-  ScopedSetTickClockForTesting scoped_set_tick_clock_for_testing_;
-  // Use a NiceMock as there's no need to add expectations in these tests,
-  // there's a dedicated test that ensure that the delegate works as expected.
-  ::testing::NiceMock<
-      testing::MockLocalSiteCharacteristicsDataImplOnDestroyDelegate>
-      destroy_delegate_;
-
-  testing::NoopLocalSiteCharacteristicsDatabase database_;
-};
-
-TEST_F(LocalSiteCharacteristicsDataImplTest, BasicTestEndToEnd) {
-  auto local_site_data =
-      GetDataImpl(kDummyOrigin, &destroy_delegate_, &database_);
-
-  local_site_data->NotifySiteLoaded();
-  local_site_data->NotifyLoadedSiteBackgrounded();
-
-  // Initially the feature usage should be reported as unknown.
-  EXPECT_EQ(performance_manager::SiteFeatureUsage::kSiteFeatureUsageUnknown,
-            local_site_data->UsesAudioInBackground());
-
-  // Advance the clock by a time lower than the miniumum observation time for
-  // the audio feature.
-  test_clock_.Advance(kObservationWindowLength -
-                      base::TimeDelta::FromSeconds(1));
-
-  // The audio feature usage is still unknown as the observation window hasn't
-  // expired.
-  EXPECT_EQ(performance_manager::SiteFeatureUsage::kSiteFeatureUsageUnknown,
-            local_site_data->UsesAudioInBackground());
-
-  // Report that the audio feature has been used.
-  local_site_data->NotifyUsesAudioInBackground();
-  EXPECT_EQ(performance_manager::SiteFeatureUsage::kSiteFeatureInUse,
-            local_site_data->UsesAudioInBackground());
-
-  // When a feature is in use it's expected that its recorded observation
-  // timestamp is equal to the time delta since Unix Epoch when the observation
-  // has been made.
-  EXPECT_EQ(local_site_data->FeatureObservationTimestamp(
-                local_site_data->site_characteristics_for_testing()
-                    .uses_audio_in_background()),
-            (test_clock_.NowTicks() - base::TimeTicks::UnixEpoch()));
-  EXPECT_EQ(local_site_data->FeatureObservationDuration(
-                local_site_data->site_characteristics_for_testing()
-                    .uses_audio_in_background()),
-            base::TimeDelta());
-
-  // Advance the clock and make sure that title update feature gets reported as
-  // unused.
-  test_clock_.Advance(kObservationWindowLength);
-  EXPECT_EQ(performance_manager::SiteFeatureUsage::kSiteFeatureNotInUse,
-            local_site_data->UpdatesTitleInBackground());
-
-  // Observing that a feature has been used after its observation window has
-  // expired should still be recorded, the feature should then be reported as
-  // used.
-  local_site_data->NotifyUpdatesTitleInBackground();
-  EXPECT_EQ(performance_manager::SiteFeatureUsage::kSiteFeatureInUse,
-            local_site_data->UpdatesTitleInBackground());
-
-  local_site_data->NotifySiteUnloaded(
-      performance_manager::TabVisibility::kBackground);
-}
-
-TEST_F(LocalSiteCharacteristicsDataImplTest, LastLoadedTime) {
-  auto local_site_data =
-      GetDataImpl(kDummyOrigin, &destroy_delegate_, &database_);
-
-  // Create a second instance of this object, simulates having several tab
-  // owning it.
-  auto local_site_data2(local_site_data);
-
-  local_site_data->NotifySiteLoaded();
-  base::TimeDelta last_loaded_time =
-      local_site_data->last_loaded_time_for_testing();
-
-  test_clock_.Advance(base::TimeDelta::FromSeconds(1));
-
-  // Loading the site a second time shouldn't change the last loaded time.
-  local_site_data2->NotifySiteLoaded();
-  EXPECT_EQ(last_loaded_time, local_site_data2->last_loaded_time_for_testing());
-
-  test_clock_.Advance(base::TimeDelta::FromSeconds(1));
-
-  // Unloading the site shouldn't update the last loaded time as there's still
-  // a loaded instance.
-  local_site_data2->NotifySiteUnloaded(
-      performance_manager::TabVisibility::kForeground);
-  EXPECT_EQ(last_loaded_time, local_site_data->last_loaded_time_for_testing());
-
-  test_clock_.Advance(base::TimeDelta::FromSeconds(1));
-
-  local_site_data->NotifySiteUnloaded(
-      performance_manager::TabVisibility::kForeground);
-  EXPECT_NE(last_loaded_time, local_site_data->last_loaded_time_for_testing());
-}
-
-TEST_F(LocalSiteCharacteristicsDataImplTest, GetFeatureUsageForUnloadedSite) {
-  auto local_site_data =
-      GetDataImpl(kDummyOrigin, &destroy_delegate_, &database_);
-
-  local_site_data->NotifySiteLoaded();
-  local_site_data->NotifyLoadedSiteBackgrounded();
-  local_site_data->NotifyUsesAudioInBackground();
-
-  test_clock_.Advance(kObservationWindowLength -
-                      base::TimeDelta::FromSeconds(1));
-  EXPECT_EQ(performance_manager::SiteFeatureUsage::kSiteFeatureInUse,
-            local_site_data->UsesAudioInBackground());
-  EXPECT_EQ(performance_manager::SiteFeatureUsage::kSiteFeatureUsageUnknown,
-            local_site_data->UpdatesTitleInBackground());
-
-  const base::TimeDelta observation_duration_before_unload =
-      local_site_data->FeatureObservationDuration(
-          local_site_data->site_characteristics_for_testing()
-              .updates_title_in_background());
-
-  local_site_data->NotifySiteUnloaded(
-      performance_manager::TabVisibility::kBackground);
-
-  // Once unloaded the feature observations should still be accessible.
-  EXPECT_EQ(performance_manager::SiteFeatureUsage::kSiteFeatureInUse,
-            local_site_data->UsesAudioInBackground());
-  EXPECT_EQ(performance_manager::SiteFeatureUsage::kSiteFeatureUsageUnknown,
-            local_site_data->UpdatesTitleInBackground());
-
-  // Advancing the clock shouldn't affect the observation duration for this
-  // feature.
-  test_clock_.Advance(base::TimeDelta::FromSeconds(1));
-  EXPECT_EQ(observation_duration_before_unload,
-            local_site_data->FeatureObservationDuration(
-                local_site_data->site_characteristics_for_testing()
-                    .updates_title_in_background()));
-  EXPECT_EQ(performance_manager::SiteFeatureUsage::kSiteFeatureUsageUnknown,
-            local_site_data->UpdatesTitleInBackground());
-
-  local_site_data->NotifySiteLoaded();
-  local_site_data->NotifyLoadedSiteBackgrounded();
-
-  test_clock_.Advance(base::TimeDelta::FromSeconds(1));
-
-  EXPECT_EQ(performance_manager::SiteFeatureUsage::kSiteFeatureInUse,
-            local_site_data->UsesAudioInBackground());
-  EXPECT_EQ(performance_manager::SiteFeatureUsage::kSiteFeatureNotInUse,
-            local_site_data->UpdatesTitleInBackground());
-
-  local_site_data->NotifySiteUnloaded(
-      performance_manager::TabVisibility::kBackground);
-}
-
-TEST_F(LocalSiteCharacteristicsDataImplTest, AllDurationGetSavedOnUnload) {
-  // This test helps making sure that the observation/timestamp fields get saved
-  // for all the features being tracked.
-  auto local_site_data =
-      GetDataImpl(kDummyOrigin, &destroy_delegate_, &database_);
-
-  const base::TimeDelta kInterval = base::TimeDelta::FromSeconds(1);
-  const auto kIntervalInternalRepresentation =
-      TestLocalSiteCharacteristicsDataImpl::TimeDeltaToInternalRepresentation(
-          kInterval);
-  const auto kZeroIntervalInternalRepresentation =
-      TestLocalSiteCharacteristicsDataImpl::TimeDeltaToInternalRepresentation(
-          base::TimeDelta());
-
-  // The internal representation of a zero interval is expected to be equal to
-  // zero as the protobuf use variable size integers and so storing zero values
-  // is really efficient (uses only one bit).
-  EXPECT_EQ(0U, kZeroIntervalInternalRepresentation);
-
-  local_site_data->NotifySiteLoaded();
-  local_site_data->NotifyLoadedSiteBackgrounded();
-  test_clock_.Advance(kInterval);
-  // Makes use of a feature to make sure that the observation timestamps get
-  // saved.
-  local_site_data->NotifyUsesAudioInBackground();
-  local_site_data->NotifySiteUnloaded(
-      performance_manager::TabVisibility::kBackground);
-
-  SiteDataProto expected_proto;
-
-  auto expected_last_loaded_time =
-      TestLocalSiteCharacteristicsDataImpl::TimeDeltaToInternalRepresentation(
-          kInterval + kInitialTimeSinceEpoch);
-
-  expected_proto.set_last_loaded(expected_last_loaded_time);
-
-  // Features that haven't been used should have an observation duration of
-  // |kIntervalInternalRepresentation| and an observation timestamp equal to
-  // zero.
-  SiteDataFeatureProto unused_feature_proto;
-  unused_feature_proto.set_observation_duration(
-      kIntervalInternalRepresentation);
-
-  expected_proto.mutable_updates_favicon_in_background()->CopyFrom(
-      unused_feature_proto);
-  expected_proto.mutable_updates_title_in_background()->CopyFrom(
-      unused_feature_proto);
-
-  // The audio feature has been used, so its observation duration value should
-  // be equal to zero, and its observation timestamp should be equal to the last
-  // loaded time in this case (as this feature has been used right before
-  // unloading).
-  SiteDataFeatureProto used_feature_proto;
-  used_feature_proto.set_use_timestamp(expected_last_loaded_time);
-  expected_proto.mutable_uses_audio_in_background()->CopyFrom(
-      used_feature_proto);
-
-  EXPECT_EQ(
-      expected_proto.SerializeAsString(),
-      local_site_data->site_characteristics_for_testing().SerializeAsString());
-}
-
-// Verify that the OnDestroyDelegate gets notified when a
-// LocalSiteCharacteristicsDataImpl object gets destroyed.
-TEST_F(LocalSiteCharacteristicsDataImplTest, DestroyNotifiesDelegate) {
-  ::testing::StrictMock<
-      testing::MockLocalSiteCharacteristicsDataImplOnDestroyDelegate>
-      strict_delegate;
-  {
-    auto local_site_data =
-        GetDataImpl(kDummyOrigin, &strict_delegate, &database_);
-    EXPECT_CALL(strict_delegate, OnLocalSiteCharacteristicsDataImplDestroyed(
-                                     local_site_data.get()));
-  }
-  ::testing::Mock::VerifyAndClear(&strict_delegate);
-}
-
-TEST_F(LocalSiteCharacteristicsDataImplTest,
-       OnInitCallbackMergePreviousObservations) {
-  // Use a mock database to intercept the initialization callback and save it
-  // locally so it can be run later. This simulates an asynchronous
-  // initialization of this object and is used to test that the observations
-  // made between the time this object has been created and the callback is
-  // called get properly merged.
-  ::testing::StrictMock<MockLocalSiteCharacteristicsDatabase> mock_db;
-  LocalSiteCharacteristicsDatabase::ReadSiteCharacteristicsFromDBCallback
-      read_cb;
-
-  auto local_site_data = GetDataImplAndInterceptReadCallback(
-      kDummyOrigin, &destroy_delegate_, &mock_db, &read_cb);
-
-  // Simulates audio in background usage before the callback gets called.
-  local_site_data->NotifySiteLoaded();
-  local_site_data->NotifyLoadedSiteBackgrounded();
-  local_site_data->NotifyUsesAudioInBackground();
-  EXPECT_EQ(performance_manager::SiteFeatureUsage::kSiteFeatureInUse,
-            local_site_data->UsesAudioInBackground());
-  EXPECT_EQ(performance_manager::SiteFeatureUsage::kSiteFeatureUsageUnknown,
-            local_site_data->UpdatesFaviconInBackground());
-  EXPECT_EQ(performance_manager::SiteFeatureUsage::kSiteFeatureUsageUnknown,
-            local_site_data->UpdatesTitleInBackground());
-
-  // Unload the site and save the last loaded time to make sure the
-  // initialization doesn't overwrite it.
-  test_clock_.Advance(base::TimeDelta::FromSeconds(1));
-  local_site_data->NotifySiteUnloaded(
-      performance_manager::TabVisibility::kBackground);
-  test_clock_.Advance(base::TimeDelta::FromSeconds(1));
-  auto last_loaded = local_site_data->last_loaded_time_for_testing();
-
-  // Add a couple of performance samples.
-  local_site_data->NotifyLoadTimePerformanceMeasurement(
-      base::TimeDelta::FromMicroseconds(100),
-      base::TimeDelta::FromMicroseconds(1000), 2000u);
-  local_site_data->NotifyLoadTimePerformanceMeasurement(
-      base::TimeDelta::FromMicroseconds(200),
-      base::TimeDelta::FromMicroseconds(500), 1000u);
-
-  // Make sure the local performance samples are averaged as expected.
-  EXPECT_EQ(2U, local_site_data->load_duration().num_datums());
-  EXPECT_EQ(150, local_site_data->load_duration().value());
-
-  EXPECT_EQ(2U, local_site_data->cpu_usage_estimate().num_datums());
-  EXPECT_EQ(750.0, local_site_data->cpu_usage_estimate().value());
-
-  EXPECT_EQ(2U, local_site_data->private_footprint_kb_estimate().num_datums());
-  EXPECT_EQ(1500.0, local_site_data->private_footprint_kb_estimate().value());
-
-  // This protobuf should have a valid |last_loaded| field and valid observation
-  // durations for each features, but the |use_timestamp| field shouldn't have
-  // been initialized for the features that haven't been used.
-  EXPECT_TRUE(
-      local_site_data->site_characteristics_for_testing().has_last_loaded());
-  EXPECT_TRUE(local_site_data->site_characteristics_for_testing()
-                  .uses_audio_in_background()
-                  .has_use_timestamp());
-  EXPECT_FALSE(local_site_data->site_characteristics_for_testing()
-                   .has_load_time_estimates());
-
-  // Initialize a fake protobuf that indicates that this site updates its title
-  // while in background and set a fake last loaded time (this should be
-  // overriden once the callback runs).
-  base::Optional<SiteDataProto> test_proto = SiteDataProto();
-  SiteDataFeatureProto unused_feature_proto = GetUnusedFeatureProto();
-  test_proto->mutable_updates_title_in_background()->CopyFrom(
-      GetUsedFeatureProto());
-  test_proto->mutable_updates_favicon_in_background()->CopyFrom(
-      unused_feature_proto);
-  test_proto->mutable_uses_audio_in_background()->CopyFrom(
-      unused_feature_proto);
-  test_proto->set_last_loaded(42);
-
-  // Set the previously saved performance averages.
-  auto* estimates = test_proto->mutable_load_time_estimates();
-  estimates->set_avg_load_duration_us(50);
-  estimates->set_avg_cpu_usage_us(250);
-  estimates->set_avg_footprint_kb(500);
-
-  // Run the callback to indicate that the initialization has completed.
-  std::move(read_cb).Run(test_proto);
-
-  EXPECT_EQ(performance_manager::SiteFeatureUsage::kSiteFeatureInUse,
-            local_site_data->UsesAudioInBackground());
-  EXPECT_EQ(performance_manager::SiteFeatureUsage::kSiteFeatureInUse,
-            local_site_data->UpdatesTitleInBackground());
-  EXPECT_EQ(performance_manager::SiteFeatureUsage::kSiteFeatureUsageUnknown,
-            local_site_data->UpdatesFaviconInBackground());
-  EXPECT_EQ(last_loaded, local_site_data->last_loaded_time_for_testing());
-
-  // Make sure the local performance samples have been updated with the previous
-  // averages.
-  EXPECT_EQ(3U, local_site_data->load_duration().num_datums());
-  EXPECT_EQ(137.5, local_site_data->load_duration().value());
-
-  EXPECT_EQ(3U, local_site_data->cpu_usage_estimate().num_datums());
-  EXPECT_EQ(562.5, local_site_data->cpu_usage_estimate().value());
-
-  EXPECT_EQ(3U, local_site_data->private_footprint_kb_estimate().num_datums());
-  EXPECT_EQ(1125, local_site_data->private_footprint_kb_estimate().value());
-
-
-  // Verify that the in-memory data is flushed to the protobuffer on write.
-  EXPECT_CALL(mock_db,
-              WriteSiteCharacteristicsIntoDB(::testing::_, ::testing::_))
-      .WillOnce(::testing::Invoke(
-          [](const url::Origin& origin, const SiteDataProto& proto) {
-            ASSERT_TRUE(proto.has_load_time_estimates());
-            const auto& estimates = proto.load_time_estimates();
-            ASSERT_TRUE(estimates.has_avg_load_duration_us());
-            EXPECT_EQ(137.5, estimates.avg_load_duration_us());
-            ASSERT_TRUE(estimates.has_avg_cpu_usage_us());
-            EXPECT_EQ(562.5, estimates.avg_cpu_usage_us());
-            ASSERT_TRUE(estimates.has_avg_footprint_kb());
-            EXPECT_EQ(1125, estimates.avg_footprint_kb());
-          }));
-
-  local_site_data = nullptr;
-  ::testing::Mock::VerifyAndClear(&mock_db);
-}
-
-TEST_F(LocalSiteCharacteristicsDataImplTest, LateAsyncReadDoesntEraseData) {
-  // Ensure that no historical data get lost if an asynchronous read from the
-  // database finishes after the last reference to a
-  // LocalSiteCharacteristicsDataImpl gets destroyed.
-
-  ::testing::StrictMock<MockLocalSiteCharacteristicsDatabase> mock_db;
-  LocalSiteCharacteristicsDatabase::ReadSiteCharacteristicsFromDBCallback
-      read_cb;
-
-  auto local_site_data_writer = GetDataImplAndInterceptReadCallback(
-      kDummyOrigin, &destroy_delegate_, &mock_db, &read_cb);
-
-  local_site_data_writer->NotifySiteLoaded();
-  local_site_data_writer->NotifyLoadedSiteBackgrounded();
-  local_site_data_writer->NotifyUsesAudioInBackground();
-  EXPECT_EQ(performance_manager::SiteFeatureUsage::kSiteFeatureInUse,
-            local_site_data_writer->UsesAudioInBackground());
-
-  local_site_data_writer->NotifySiteUnloaded(
-      performance_manager::TabVisibility::kBackground);
-
-  // Releasing |local_site_data_writer| should cause this object to get
-  // destroyed but there shouldn't be any write operation as the read hasn't
-  // completed.
-  EXPECT_CALL(destroy_delegate_,
-              OnLocalSiteCharacteristicsDataImplDestroyed(::testing::_));
-  EXPECT_CALL(mock_db,
-              WriteSiteCharacteristicsIntoDB(::testing::_, ::testing::_))
-      .Times(0);
-  local_site_data_writer = nullptr;
-  ::testing::Mock::VerifyAndClear(&destroy_delegate_);
-  ::testing::Mock::VerifyAndClear(&mock_db);
-
-  EXPECT_TRUE(read_cb.IsCancelled());
-}
-
-TEST_F(LocalSiteCharacteristicsDataImplTest,
-       LateAsyncReadDoesntBypassClearEvent) {
-  ::testing::NiceMock<MockLocalSiteCharacteristicsDatabase> mock_db;
-  LocalSiteCharacteristicsDatabase::ReadSiteCharacteristicsFromDBCallback
-      read_cb;
-
-  auto local_site_data = GetDataImplAndInterceptReadCallback(
-      kDummyOrigin, &destroy_delegate_, &mock_db, &read_cb);
-
-  local_site_data->NotifySiteLoaded();
-  local_site_data->NotifyLoadedSiteBackgrounded();
-  local_site_data->NotifyUsesAudioInBackground();
-  EXPECT_EQ(performance_manager::SiteFeatureUsage::kSiteFeatureInUse,
-            local_site_data->UsesAudioInBackground());
-  local_site_data->NotifySiteUnloaded(
-      performance_manager::TabVisibility::kBackground);
-
-  // TODO(sebmarchand): Test that data is cleared here.
-  local_site_data->ClearObservationsAndInvalidateReadOperation();
-
-  EXPECT_TRUE(read_cb.IsCancelled());
-}
-
-TEST_F(LocalSiteCharacteristicsDataImplTest, BackgroundedCountTests) {
-  auto local_site_data =
-      GetDataImpl(kDummyOrigin, &destroy_delegate_, &database_);
-
-  // By default the tabs are expected to be foregrounded.
-  EXPECT_EQ(0U, local_site_data->loaded_tabs_in_background_count_for_testing());
-
-  local_site_data->NotifySiteLoaded();
-  test_clock_.Advance(base::TimeDelta::FromSeconds(1));
-  local_site_data->NotifyLoadedSiteBackgrounded();
-
-  auto background_session_begin =
-      local_site_data->background_session_begin_for_testing();
-  EXPECT_EQ(test_clock_.NowTicks(), background_session_begin);
-
-  EXPECT_EQ(1U, local_site_data->loaded_tabs_in_background_count_for_testing());
-
-  test_clock_.Advance(base::TimeDelta::FromSeconds(1));
-
-  // Add a second instance of this object, this one pretending to be in
-  // foreground.
-  auto local_site_data_copy(local_site_data);
-  local_site_data_copy->NotifySiteLoaded();
-  EXPECT_EQ(1U, local_site_data->loaded_tabs_in_background_count_for_testing());
-
-  EXPECT_EQ(background_session_begin,
-            local_site_data->background_session_begin_for_testing());
-
-  test_clock_.Advance(base::TimeDelta::FromSeconds(1));
-
-  local_site_data->NotifyLoadedSiteForegrounded();
-  EXPECT_EQ(0U, local_site_data->loaded_tabs_in_background_count_for_testing());
-
-  auto expected_observation_duration =
-      test_clock_.NowTicks() - background_session_begin;
-
-  auto observed_observation_duration =
-      local_site_data->FeatureObservationDuration(
-          local_site_data->site_characteristics_for_testing()
-              .updates_title_in_background());
-
-  EXPECT_EQ(expected_observation_duration, observed_observation_duration);
-
-  test_clock_.Advance(base::TimeDelta::FromSeconds(1));
-
-  local_site_data->NotifyLoadedSiteBackgrounded();
-  EXPECT_EQ(1U, local_site_data->loaded_tabs_in_background_count_for_testing());
-  background_session_begin =
-      local_site_data->background_session_begin_for_testing();
-  EXPECT_EQ(test_clock_.NowTicks(), background_session_begin);
-
-  local_site_data->NotifySiteUnloaded(
-      performance_manager::TabVisibility::kBackground);
-  local_site_data_copy->NotifySiteUnloaded(
-      performance_manager::TabVisibility::kForeground);
-}
-
-TEST_F(LocalSiteCharacteristicsDataImplTest,
-       OptionalFieldsNotPopulatedWhenClean) {
-  ::testing::StrictMock<MockLocalSiteCharacteristicsDatabase> mock_db;
-  LocalSiteCharacteristicsDatabase::ReadSiteCharacteristicsFromDBCallback
-      read_cb;
-
-  auto local_site_data = GetDataImplAndInterceptReadCallback(
-      kDummyOrigin, &destroy_delegate_, &mock_db, &read_cb);
-
-  EXPECT_EQ(0u, local_site_data->cpu_usage_estimate().num_datums());
-  EXPECT_EQ(0u, local_site_data->private_footprint_kb_estimate().num_datums());
-
-  base::Optional<SiteDataProto> test_proto = SiteDataProto();
-
-  // Run the callback to indicate that the initialization has completed.
-  std::move(read_cb).Run(test_proto);
-
-  // There still should be no perf data.
-  EXPECT_EQ(0u, local_site_data->cpu_usage_estimate().num_datums());
-  EXPECT_EQ(0u, local_site_data->private_footprint_kb_estimate().num_datums());
-
-  // Dirty the record to force a write.
-  local_site_data->NotifySiteLoaded();
-  local_site_data->NotifyLoadedSiteBackgrounded();
-  local_site_data->NotifyUsesAudioInBackground();
-
-  // Verify that the saved protobuffer isn't populated with the perf fields.
-  EXPECT_CALL(mock_db,
-              WriteSiteCharacteristicsIntoDB(::testing::_, ::testing::_))
-      .WillOnce(::testing::Invoke(
-          [](const url::Origin& origin, const SiteDataProto& proto) {
-            ASSERT_FALSE(proto.has_load_time_estimates());
-          }));
-
-  local_site_data->NotifySiteUnloaded(
-      performance_manager::TabVisibility::kBackground);
-  local_site_data = nullptr;
-  ::testing::Mock::VerifyAndClear(&mock_db);
-}
-
-TEST_F(LocalSiteCharacteristicsDataImplTest,
-       FlushingStateToProtoDoesntAffectData) {
-  // Create 2 DataImpl object and do the same operations on them, ensures that
-  // calling FlushStateToProto doesn't affect the data that gets recorded.
-
-  auto local_site_data =
-      GetDataImpl(kDummyOrigin, &destroy_delegate_, &database_);
-  auto local_site_data_ref =
-      GetDataImpl(kDummyOrigin2, &destroy_delegate_, &database_);
-
-  local_site_data->NotifySiteLoaded();
-  local_site_data->NotifyLoadedSiteBackgrounded();
-  local_site_data_ref->NotifySiteLoaded();
-  local_site_data_ref->NotifyLoadedSiteBackgrounded();
-
-  test_clock_.Advance(base::TimeDelta::FromSeconds(15));
-  local_site_data->FlushStateToProto();
-  test_clock_.Advance(base::TimeDelta::FromSeconds(15));
-
-  local_site_data->NotifyUsesAudioInBackground();
-  local_site_data_ref->NotifyUsesAudioInBackground();
-
-  local_site_data->FlushStateToProto();
-
-  EXPECT_EQ(local_site_data->FeatureObservationTimestamp(
-                local_site_data->site_characteristics_for_testing()
-                    .uses_audio_in_background()),
-            local_site_data_ref->FeatureObservationTimestamp(
-                local_site_data_ref->site_characteristics_for_testing()
-                    .uses_audio_in_background()));
-
-  EXPECT_EQ(local_site_data->FeatureObservationDuration(
-                local_site_data->site_characteristics_for_testing()
-                    .updates_title_in_background()),
-            local_site_data_ref->FeatureObservationDuration(
-                local_site_data_ref->site_characteristics_for_testing()
-                    .updates_title_in_background()));
-
-  local_site_data->NotifySiteUnloaded(
-      performance_manager::TabVisibility::kBackground);
-  local_site_data_ref->NotifySiteUnloaded(
-      performance_manager::TabVisibility::kBackground);
-}
-
-TEST_F(LocalSiteCharacteristicsDataImplTest, DataLoadedCallbackInvoked) {
-  ::testing::StrictMock<MockLocalSiteCharacteristicsDatabase> mock_db;
-  LocalSiteCharacteristicsDatabase::ReadSiteCharacteristicsFromDBCallback
-      read_cb;
-
-  auto local_site_data = GetDataImplAndInterceptReadCallback(
-      kDummyOrigin, &destroy_delegate_, &mock_db, &read_cb);
-
-  EXPECT_FALSE(local_site_data->DataLoaded());
-
-  bool callback_invoked = false;
-  local_site_data->RegisterDataLoadedCallback(
-      base::BindLambdaForTesting([&]() { callback_invoked = true; }));
-
-  // Run the callback to indicate that the initialization has completed.
-  base::Optional<SiteDataProto> test_proto = SiteDataProto();
-  std::move(read_cb).Run(test_proto);
-
-  EXPECT_TRUE(callback_invoked);
-  EXPECT_TRUE(local_site_data->DataLoaded());
-}
-
-}  // namespace internal
-}  // namespace resource_coordinator
diff --git a/chrome/browser/resource_coordinator/local_site_characteristics_data_reader.cc b/chrome/browser/resource_coordinator/local_site_characteristics_data_reader.cc
deleted file mode 100644
index 5cdda44..0000000
--- a/chrome/browser/resource_coordinator/local_site_characteristics_data_reader.cc
+++ /dev/null
@@ -1,55 +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 "chrome/browser/resource_coordinator/local_site_characteristics_data_reader.h"
-
-#include <utility>
-
-#include "base/bind.h"
-#include "chrome/browser/resource_coordinator/local_site_characteristics_data_impl.h"
-
-namespace resource_coordinator {
-
-LocalSiteCharacteristicsDataReader::LocalSiteCharacteristicsDataReader(
-    scoped_refptr<internal::LocalSiteCharacteristicsDataImpl> impl)
-    : impl_(std::move(impl)) {}
-
-LocalSiteCharacteristicsDataReader::~LocalSiteCharacteristicsDataReader() {}
-
-performance_manager::SiteFeatureUsage
-LocalSiteCharacteristicsDataReader::UpdatesFaviconInBackground() const {
-  return impl_->UpdatesFaviconInBackground();
-}
-
-performance_manager::SiteFeatureUsage
-LocalSiteCharacteristicsDataReader::UpdatesTitleInBackground() const {
-  return impl_->UpdatesTitleInBackground();
-}
-
-performance_manager::SiteFeatureUsage
-LocalSiteCharacteristicsDataReader::UsesAudioInBackground() const {
-  return impl_->UsesAudioInBackground();
-}
-
-bool LocalSiteCharacteristicsDataReader::DataLoaded() const {
-  return impl_->DataLoaded();
-}
-
-void LocalSiteCharacteristicsDataReader::RegisterDataLoadedCallback(
-    base::OnceClosure&& callback) {
-  // Register a closure that is bound using a weak pointer to this instance.
-  // In that way it won't be invoked by the underlying |impl_| after this
-  // reader is destroyed.
-  base::OnceClosure closure(
-      base::BindOnce(&LocalSiteCharacteristicsDataReader::RunClosure,
-                     weak_factory_.GetWeakPtr(), std::move(callback)));
-  impl_->RegisterDataLoadedCallback(std::move(closure));
-}
-
-void LocalSiteCharacteristicsDataReader::RunClosure(
-    base::OnceClosure&& closure) {
-  std::move(closure).Run();
-}
-
-}  // namespace resource_coordinator
diff --git a/chrome/browser/resource_coordinator/local_site_characteristics_data_reader.h b/chrome/browser/resource_coordinator/local_site_characteristics_data_reader.h
deleted file mode 100644
index 9649582..0000000
--- a/chrome/browser/resource_coordinator/local_site_characteristics_data_reader.h
+++ /dev/null
@@ -1,75 +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 CHROME_BROWSER_RESOURCE_COORDINATOR_LOCAL_SITE_CHARACTERISTICS_DATA_READER_H_
-#define CHROME_BROWSER_RESOURCE_COORDINATOR_LOCAL_SITE_CHARACTERISTICS_DATA_READER_H_
-
-#include "base/gtest_prod_util.h"
-#include "base/macros.h"
-#include "base/memory/ref_counted.h"
-#include "base/memory/weak_ptr.h"
-#include "chrome/browser/resource_coordinator/site_characteristics_data_reader.h"
-
-namespace resource_coordinator {
-
-FORWARD_DECLARE_TEST(LocalSiteCharacteristicsDataReaderTest,
-                     FreeingReaderDoesntCauseWriteOperation);
-
-namespace internal {
-class LocalSiteCharacteristicsDataImpl;
-}  // namespace internal
-
-// Specialization of a SiteCharacteristicDataReader that delegates to a
-// LocalSiteCharacteristicsDataImpl.
-class LocalSiteCharacteristicsDataReader
-    : public SiteCharacteristicsDataReader {
- public:
-  ~LocalSiteCharacteristicsDataReader() override;
-
-  // SiteCharacteristicsDataReader:
-  performance_manager::SiteFeatureUsage UpdatesFaviconInBackground()
-      const override;
-  performance_manager::SiteFeatureUsage UpdatesTitleInBackground()
-      const override;
-  performance_manager::SiteFeatureUsage UsesAudioInBackground() const override;
-  bool DataLoaded() const override;
-  void RegisterDataLoadedCallback(base::OnceClosure&& callback) override;
-
-  internal::LocalSiteCharacteristicsDataImpl* impl_for_testing() const {
-    return impl_.get();
-  }
-
- private:
-  friend class LocalSiteCharacteristicsDataReaderTest;
-  friend class LocalSiteCharacteristicsDataStoreTest;
-  friend class LocalSiteCharacteristicsDataStore;
-  FRIEND_TEST_ALL_PREFIXES(LocalSiteCharacteristicsDataReaderTest,
-                           DestroyingReaderCancelsPendingCallbacks);
-  FRIEND_TEST_ALL_PREFIXES(LocalSiteCharacteristicsDataReaderTest,
-                           FreeingReaderDoesntCauseWriteOperation);
-  FRIEND_TEST_ALL_PREFIXES(LocalSiteCharacteristicsDataReaderTest,
-                           OnDataLoadedCallbackInvoked);
-
-  // Private constructor, these objects are meant to be created by a site
-  // characteristics data store.
-  explicit LocalSiteCharacteristicsDataReader(
-      scoped_refptr<internal::LocalSiteCharacteristicsDataImpl> impl);
-
-  // Runs the provided closure. This is used as a wrapper so that callbacks
-  // registered with the |impl_| by this reader are invalidated when the
-  // reader is destroyed.
-  void RunClosure(base::OnceClosure&& closure);
-
-  // The LocalSiteCharacteristicDataInternal object we delegate to.
-  const scoped_refptr<internal::LocalSiteCharacteristicsDataImpl> impl_;
-
-  // Used for invalidating callbacks.
-  base::WeakPtrFactory<LocalSiteCharacteristicsDataReader> weak_factory_{this};
-
-  DISALLOW_COPY_AND_ASSIGN(LocalSiteCharacteristicsDataReader);
-};
-
-}  // namespace resource_coordinator
-
-#endif  // CHROME_BROWSER_RESOURCE_COORDINATOR_LOCAL_SITE_CHARACTERISTICS_DATA_READER_H_
diff --git a/chrome/browser/resource_coordinator/local_site_characteristics_data_reader_unittest.cc b/chrome/browser/resource_coordinator/local_site_characteristics_data_reader_unittest.cc
deleted file mode 100644
index 441e909..0000000
--- a/chrome/browser/resource_coordinator/local_site_characteristics_data_reader_unittest.cc
+++ /dev/null
@@ -1,245 +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 "chrome/browser/resource_coordinator/local_site_characteristics_data_reader.h"
-
-#include <memory>
-
-#include "base/macros.h"
-#include "base/memory/ptr_util.h"
-#include "base/test/bind_test_util.h"
-#include "base/test/simple_test_tick_clock.h"
-#include "chrome/browser/resource_coordinator/local_site_characteristics_data_impl.h"
-#include "chrome/browser/resource_coordinator/local_site_characteristics_data_unittest_utils.h"
-#include "chrome/browser/resource_coordinator/time.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "url/gurl.h"
-
-namespace resource_coordinator {
-
-namespace {
-
-class MockLocalSiteCharacteristicsDatabase
-    : public testing::NoopLocalSiteCharacteristicsDatabase {
- public:
-  MockLocalSiteCharacteristicsDatabase() = default;
-  ~MockLocalSiteCharacteristicsDatabase() = default;
-
-  // Note: As move-only parameters (e.g. OnceCallback) aren't supported by mock
-  // methods, add On... methods to pass a non-const reference to OnceCallback.
-  void ReadSiteCharacteristicsFromDB(
-      const url::Origin& origin,
-      LocalSiteCharacteristicsDatabase::ReadSiteCharacteristicsFromDBCallback
-          callback) override {
-    OnReadSiteCharacteristicsFromDB(std::move(origin), callback);
-  }
-  MOCK_METHOD2(OnReadSiteCharacteristicsFromDB,
-               void(const url::Origin&,
-                    LocalSiteCharacteristicsDatabase::
-                        ReadSiteCharacteristicsFromDBCallback&));
-
-  MOCK_METHOD2(WriteSiteCharacteristicsIntoDB,
-               void(const url::Origin&, const SiteDataProto&));
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(MockLocalSiteCharacteristicsDatabase);
-};
-
-void InitializeSiteDataProto(SiteDataProto* site_characteristics) {
-  DCHECK(site_characteristics);
-  site_characteristics->set_last_loaded(42);
-
-  SiteDataFeatureProto used_feature_proto;
-  used_feature_proto.set_observation_duration(0U);
-  used_feature_proto.set_use_timestamp(1U);
-
-  site_characteristics->mutable_updates_favicon_in_background()->CopyFrom(
-      used_feature_proto);
-  site_characteristics->mutable_updates_title_in_background()->CopyFrom(
-      used_feature_proto);
-  site_characteristics->mutable_uses_audio_in_background()->CopyFrom(
-      used_feature_proto);
-
-  DCHECK(site_characteristics->IsInitialized());
-}
-
-}  // namespace
-
-class LocalSiteCharacteristicsDataReaderTest : public ::testing::Test {
- protected:
-  // The constructors needs to call 'new' directly rather than using the
-  // base::MakeRefCounted helper function because the constructor of
-  // LocalSiteCharacteristicsDataImpl is protected and not visible to
-  // base::MakeRefCounted.
-  LocalSiteCharacteristicsDataReaderTest()
-      : scoped_set_tick_clock_for_testing_(&test_clock_) {
-    test_clock_.Advance(base::TimeDelta::FromSeconds(1));
-    test_impl_ =
-        base::WrapRefCounted(new internal::LocalSiteCharacteristicsDataImpl(
-            url::Origin::Create(GURL("foo.com")), &delegate_, &database_));
-    test_impl_->NotifySiteLoaded();
-    test_impl_->NotifyLoadedSiteBackgrounded();
-    LocalSiteCharacteristicsDataReader* reader =
-        new LocalSiteCharacteristicsDataReader(test_impl_.get());
-    reader_ = base::WrapUnique(reader);
-  }
-
-  ~LocalSiteCharacteristicsDataReaderTest() override {
-    test_impl_->NotifySiteUnloaded(
-        performance_manager::TabVisibility::kBackground);
-  }
-
-  base::SimpleTestTickClock test_clock_;
-  ScopedSetTickClockForTesting scoped_set_tick_clock_for_testing_;
-
-  // The mock delegate used by the LocalSiteCharacteristicsDataImpl objects
-  // created by this class, NiceMock is used to avoid having to set expectations
-  // in test cases that don't care about this.
-  ::testing::NiceMock<
-      testing::MockLocalSiteCharacteristicsDataImplOnDestroyDelegate>
-      delegate_;
-
-  // The LocalSiteCharacteristicsDataImpl object used in these tests.
-  scoped_refptr<internal::LocalSiteCharacteristicsDataImpl> test_impl_;
-
-  // A LocalSiteCharacteristicsDataReader object associated with the origin used
-  // to create this object.
-  std::unique_ptr<LocalSiteCharacteristicsDataReader> reader_;
-
-  testing::NoopLocalSiteCharacteristicsDatabase database_;
-
-  DISALLOW_COPY_AND_ASSIGN(LocalSiteCharacteristicsDataReaderTest);
-};
-
-TEST_F(LocalSiteCharacteristicsDataReaderTest, TestAccessors) {
-  // Initially we have no information about any of the features.
-  EXPECT_EQ(performance_manager::SiteFeatureUsage::kSiteFeatureUsageUnknown,
-            reader_->UpdatesFaviconInBackground());
-  EXPECT_EQ(performance_manager::SiteFeatureUsage::kSiteFeatureUsageUnknown,
-            reader_->UpdatesTitleInBackground());
-  EXPECT_EQ(performance_manager::SiteFeatureUsage::kSiteFeatureUsageUnknown,
-            reader_->UsesAudioInBackground());
-
-  // Simulates a title update event, make sure it gets reported directly.
-  test_impl_->NotifyUpdatesTitleInBackground();
-
-  EXPECT_EQ(performance_manager::SiteFeatureUsage::kSiteFeatureInUse,
-            reader_->UpdatesTitleInBackground());
-
-  // Advance the clock by a large amount of time, enough for the unused features
-  // observation windows to expire.
-  test_clock_.Advance(base::TimeDelta::FromDays(31));
-
-  EXPECT_EQ(performance_manager::SiteFeatureUsage::kSiteFeatureNotInUse,
-            reader_->UpdatesFaviconInBackground());
-  EXPECT_EQ(performance_manager::SiteFeatureUsage::kSiteFeatureInUse,
-            reader_->UpdatesTitleInBackground());
-  EXPECT_EQ(performance_manager::SiteFeatureUsage::kSiteFeatureNotInUse,
-            reader_->UsesAudioInBackground());
-}
-
-TEST_F(LocalSiteCharacteristicsDataReaderTest,
-       FreeingReaderDoesntCauseWriteOperation) {
-  const url::Origin kOrigin = url::Origin::Create(GURL("foo.com"));
-  ::testing::StrictMock<MockLocalSiteCharacteristicsDatabase> database;
-
-  // Override the read callback to simulate a successful read from the
-  // database.
-  SiteDataProto proto = {};
-  InitializeSiteDataProto(&proto);
-  auto read_from_db_mock_impl =
-      [&](const url::Origin& origin,
-          LocalSiteCharacteristicsDatabase::
-              ReadSiteCharacteristicsFromDBCallback& callback) {
-        std::move(callback).Run(base::Optional<SiteDataProto>(proto));
-      };
-
-  EXPECT_CALL(database, OnReadSiteCharacteristicsFromDB(
-                            ::testing::Property(&url::Origin::Serialize,
-                                                kOrigin.Serialize()),
-                            ::testing::_))
-      .WillOnce(::testing::Invoke(read_from_db_mock_impl));
-
-  std::unique_ptr<LocalSiteCharacteristicsDataReader> reader =
-      base::WrapUnique(new LocalSiteCharacteristicsDataReader(
-          base::WrapRefCounted(new internal::LocalSiteCharacteristicsDataImpl(
-              kOrigin, &delegate_, &database))));
-  ::testing::Mock::VerifyAndClear(&database);
-
-  EXPECT_TRUE(reader->impl_for_testing()->fully_initialized_for_testing());
-
-  // Resetting the reader shouldn't cause any write operation to the database.
-  EXPECT_CALL(database,
-              WriteSiteCharacteristicsIntoDB(::testing::_, ::testing::_))
-      .Times(0);
-  reader.reset();
-  ::testing::Mock::VerifyAndClear(&database);
-}
-
-TEST_F(LocalSiteCharacteristicsDataReaderTest, OnDataLoadedCallbackInvoked) {
-  const url::Origin kOrigin = url::Origin::Create(GURL("foo.com"));
-  ::testing::StrictMock<MockLocalSiteCharacteristicsDatabase> database;
-
-  // Create the impl.
-  EXPECT_CALL(database, OnReadSiteCharacteristicsFromDB(
-                            ::testing::Property(&url::Origin::Serialize,
-                                                kOrigin.Serialize()),
-                            ::testing::_));
-  scoped_refptr<internal::LocalSiteCharacteristicsDataImpl> impl =
-      base::WrapRefCounted(new internal::LocalSiteCharacteristicsDataImpl(
-          kOrigin, &delegate_, &database));
-
-  // Create the reader.
-  std::unique_ptr<LocalSiteCharacteristicsDataReader> reader =
-      base::WrapUnique(new LocalSiteCharacteristicsDataReader(impl));
-  EXPECT_FALSE(reader->DataLoaded());
-
-  // Register a data ready closure.
-  bool on_data_loaded = false;
-  reader->RegisterDataLoadedCallback(base::BindLambdaForTesting(
-      [&on_data_loaded]() { on_data_loaded = true; }));
-
-  // Transition the impl to fully initialized, which should cause the callbacks
-  // to fire.
-  EXPECT_FALSE(impl->DataLoaded());
-  EXPECT_FALSE(on_data_loaded);
-  impl->TransitionToFullyInitialized();
-  EXPECT_TRUE(impl->DataLoaded());
-  EXPECT_TRUE(on_data_loaded);
-}
-
-TEST_F(LocalSiteCharacteristicsDataReaderTest,
-       DestroyingReaderCancelsPendingCallbacks) {
-  const url::Origin kOrigin = url::Origin::Create(GURL("foo.com"));
-  ::testing::StrictMock<MockLocalSiteCharacteristicsDatabase> database;
-
-  // Create the impl.
-  EXPECT_CALL(database, OnReadSiteCharacteristicsFromDB(
-                            ::testing::Property(&url::Origin::Serialize,
-                                                kOrigin.Serialize()),
-                            ::testing::_));
-  scoped_refptr<internal::LocalSiteCharacteristicsDataImpl> impl =
-      base::WrapRefCounted(new internal::LocalSiteCharacteristicsDataImpl(
-          kOrigin, &delegate_, &database));
-
-  // Create the reader.
-  std::unique_ptr<LocalSiteCharacteristicsDataReader> reader =
-      base::WrapUnique(new LocalSiteCharacteristicsDataReader(impl));
-  EXPECT_FALSE(reader->DataLoaded());
-
-  // Register a data ready closure.
-  reader->RegisterDataLoadedCallback(
-      base::MakeExpectedNotRunClosure(FROM_HERE));
-
-  // Reset the reader.
-  reader.reset();
-
-  // Transition the impl to fully initialized, which should cause the callbacks
-  // to fire. The reader's callback should *not* be invoked.
-  EXPECT_FALSE(impl->DataLoaded());
-  impl->TransitionToFullyInitialized();
-  EXPECT_TRUE(impl->DataLoaded());
-}
-
-}  // namespace resource_coordinator
diff --git a/chrome/browser/resource_coordinator/local_site_characteristics_data_store.cc b/chrome/browser/resource_coordinator/local_site_characteristics_data_store.cc
deleted file mode 100644
index df64491..0000000
--- a/chrome/browser/resource_coordinator/local_site_characteristics_data_store.cc
+++ /dev/null
@@ -1,193 +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 "chrome/browser/resource_coordinator/local_site_characteristics_data_store.h"
-
-#include "base/feature_list.h"
-#include "base/memory/ptr_util.h"
-#include "base/stl_util.h"
-#include "chrome/browser/history/history_service_factory.h"
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/resource_coordinator/leveldb_site_characteristics_database.h"
-#include "chrome/browser/resource_coordinator/local_site_characteristics_data_reader.h"
-#include "chrome/browser/resource_coordinator/local_site_characteristics_data_writer.h"
-#include "chrome/browser/resource_coordinator/tab_manager_features.h"
-#include "components/history/core/browser/url_row.h"
-#include "url/gurl.h"
-
-namespace resource_coordinator {
-
-namespace {
-
-constexpr char kSiteCharacteristicsDirectoryName[] =
-    "Site Characteristics Database";
-
-size_t CountOriginsInURLRows(const history::URLRows& rows) {
-  std::set<GURL> origins;
-  for (auto& row : rows)
-    origins.insert(row.url().GetOrigin());
-  return origins.size();
-}
-
-}  // namespace
-
-LocalSiteCharacteristicsDataStore::LocalSiteCharacteristicsDataStore(
-    Profile* profile)
-    : profile_(profile) {
-  database_ = std::make_unique<LevelDBSiteCharacteristicsDatabase>(
-      profile->GetPath().AppendASCII(kSiteCharacteristicsDirectoryName));
-
-  history::HistoryService* history =
-      HistoryServiceFactory::GetForProfileWithoutCreating(profile);
-  if (history)
-    history_observer_.Add(history);
-
-  // Register the debug interface against the profile.
-  LocalSiteCharacteristicsDataStoreInspector::SetForProfile(this, profile);
-}
-
-LocalSiteCharacteristicsDataStore::~LocalSiteCharacteristicsDataStore() {
-  LocalSiteCharacteristicsDataStoreInspector::SetForProfile(nullptr, profile_);
-}
-
-std::unique_ptr<SiteCharacteristicsDataReader>
-LocalSiteCharacteristicsDataStore::GetReaderForOrigin(
-    const url::Origin& origin) {
-  internal::LocalSiteCharacteristicsDataImpl* impl =
-      GetOrCreateFeatureImpl(origin);
-  DCHECK(impl);
-  SiteCharacteristicsDataReader* data_reader =
-      new LocalSiteCharacteristicsDataReader(impl);
-  return base::WrapUnique(data_reader);
-}
-
-std::unique_ptr<SiteCharacteristicsDataWriter>
-LocalSiteCharacteristicsDataStore::GetWriterForOrigin(
-    const url::Origin& origin,
-    performance_manager::TabVisibility tab_visibility) {
-  internal::LocalSiteCharacteristicsDataImpl* impl =
-      GetOrCreateFeatureImpl(origin);
-  DCHECK(impl);
-  LocalSiteCharacteristicsDataWriter* data_writer =
-      new LocalSiteCharacteristicsDataWriter(impl, tab_visibility);
-  return base::WrapUnique(data_writer);
-}
-
-bool LocalSiteCharacteristicsDataStore::IsRecordingForTesting() {
-  return true;
-}
-
-const char* LocalSiteCharacteristicsDataStore::GetDataStoreName() {
-  return "LocalSiteCharacteristicsDataStore";
-}
-
-std::vector<url::Origin>
-LocalSiteCharacteristicsDataStore::GetAllInMemoryOrigins() {
-  std::vector<url::Origin> ret;
-
-  ret.reserve(origin_data_map_.size());
-  for (const auto& entry : origin_data_map_)
-    ret.push_back(entry.first);
-
-  return ret;
-}
-
-void LocalSiteCharacteristicsDataStore::GetDatabaseSize(
-    DatabaseSizeCallback on_have_data) {
-  database_->GetDatabaseSize(std::move(on_have_data));
-}
-
-bool LocalSiteCharacteristicsDataStore::GetDataForOrigin(
-    const url::Origin& origin,
-    bool* is_dirty,
-    std::unique_ptr<SiteDataProto>* data) {
-  DCHECK_NE(nullptr, data);
-  const auto it = origin_data_map_.find(origin);
-  if (it == origin_data_map_.end())
-    return false;
-
-  std::unique_ptr<SiteDataProto> ret = std::make_unique<SiteDataProto>();
-  ret->CopyFrom(it->second->FlushStateToProto());
-  *is_dirty = it->second->is_dirty();
-  *data = std::move(ret);
-  return true;
-}
-
-LocalSiteCharacteristicsDataStore*
-LocalSiteCharacteristicsDataStore::GetDataStore() {
-  return this;
-}
-
-internal::LocalSiteCharacteristicsDataImpl*
-LocalSiteCharacteristicsDataStore::GetOrCreateFeatureImpl(
-    const url::Origin& origin) {
-  // Start by checking if there's already an entry for this origin.
-  auto iter = origin_data_map_.find(origin);
-  if (iter != origin_data_map_.end())
-    return iter->second;
-
-  // If not create a new one and add it to the map.
-  internal::LocalSiteCharacteristicsDataImpl* site_characteristic_data =
-      new internal::LocalSiteCharacteristicsDataImpl(origin, this,
-                                                     database_.get());
-
-  // internal::LocalSiteCharacteristicsDataImpl is a ref-counted object, it's
-  // safe to store a raw pointer to it here as this class will get notified when
-  // it's about to be destroyed and it'll be removed from the map.
-  origin_data_map_.insert(std::make_pair(origin, site_characteristic_data));
-  return site_characteristic_data;
-}
-
-void LocalSiteCharacteristicsDataStore::
-    OnLocalSiteCharacteristicsDataImplDestroyed(
-        internal::LocalSiteCharacteristicsDataImpl* impl) {
-  DCHECK(impl);
-  DCHECK(base::Contains(origin_data_map_, impl->origin()));
-  // Remove the entry for this origin as this is about to get destroyed.
-  auto num_erased = origin_data_map_.erase(impl->origin());
-  DCHECK_EQ(1U, num_erased);
-}
-
-void LocalSiteCharacteristicsDataStore::OnURLsDeleted(
-    history::HistoryService* history_service,
-    const history::DeletionInfo& deletion_info) {
-  // It's not necessary to invalidate the pending DB write operations as they
-  // run on a sequenced task and so it's guaranteed that the remove operations
-  // posted here will run after any other pending operation.
-  if (deletion_info.IsAllHistory()) {
-    for (auto& data : origin_data_map_)
-      data.second->ClearObservationsAndInvalidateReadOperation();
-    database_->ClearDatabase();
-  } else {
-    std::vector<url::Origin> origins_to_remove;
-
-    DCHECK_EQ(deletion_info.deleted_urls_origin_map().size(),
-              CountOriginsInURLRows(deletion_info.deleted_rows()));
-    for (const auto& it : deletion_info.deleted_urls_origin_map()) {
-      const url::Origin origin = url::Origin::Create(it.first);
-      const int remaining_visits_in_history = it.second.first;
-
-      // If the origin no longer exists in history, clear the site
-      // characteristics from memory and from the database.
-      DCHECK_GE(remaining_visits_in_history, 0);
-      if (remaining_visits_in_history == 0) {
-        auto map_iter = origin_data_map_.find(origin);
-        if (map_iter != origin_data_map_.end())
-          map_iter->second->ClearObservationsAndInvalidateReadOperation();
-
-        origins_to_remove.emplace_back(origin);
-      }
-    }
-
-    if (!origins_to_remove.empty())
-      database_->RemoveSiteCharacteristicsFromDB(origins_to_remove);
-  }
-}
-
-void LocalSiteCharacteristicsDataStore::HistoryServiceBeingDeleted(
-    history::HistoryService* history_service) {
-  history_observer_.Remove(history_service);
-}
-
-}  // namespace resource_coordinator
diff --git a/chrome/browser/resource_coordinator/local_site_characteristics_data_store.h b/chrome/browser/resource_coordinator/local_site_characteristics_data_store.h
deleted file mode 100644
index de68f331..0000000
--- a/chrome/browser/resource_coordinator/local_site_characteristics_data_store.h
+++ /dev/null
@@ -1,114 +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 CHROME_BROWSER_RESOURCE_COORDINATOR_LOCAL_SITE_CHARACTERISTICS_DATA_STORE_H_
-#define CHROME_BROWSER_RESOURCE_COORDINATOR_LOCAL_SITE_CHARACTERISTICS_DATA_STORE_H_
-
-#include "base/containers/flat_map.h"
-#include "base/gtest_prod_util.h"
-#include "base/macros.h"
-#include "base/scoped_observer.h"
-#include "base/sequence_checker.h"
-#include "chrome/browser/resource_coordinator/local_site_characteristics_data_impl.h"
-#include "chrome/browser/resource_coordinator/local_site_characteristics_data_store_inspector.h"
-#include "chrome/browser/resource_coordinator/site_characteristics_data_store.h"
-#include "chrome/browser/resource_coordinator/site_characteristics_data_writer.h"
-#include "components/history/core/browser/history_service.h"
-#include "components/history/core/browser/history_service_observer.h"
-
-class Profile;
-
-namespace resource_coordinator {
-
-class LocalSiteCharacteristicsDatabase;
-
-// Implementation of a SiteCharacteristicsDataStore that use the local site
-// characteristics database as a backend.
-//
-// This class should never be used for off the record profiles, the
-// LocalSiteCharacteristicsNonRecordingDataStore class should be used instead.
-class LocalSiteCharacteristicsDataStore
-    : public SiteCharacteristicsDataStore,
-      public LocalSiteCharacteristicsDataStoreInspector,
-      public internal::LocalSiteCharacteristicsDataImpl::OnDestroyDelegate,
-      public history::HistoryServiceObserver {
- public:
-  using LocalSiteCharacteristicsMap =
-      base::flat_map<url::Origin, internal::LocalSiteCharacteristicsDataImpl*>;
-
-  explicit LocalSiteCharacteristicsDataStore(Profile* profile);
-  ~LocalSiteCharacteristicsDataStore() override;
-
-  // SiteCharacteristicDataStore:
-  std::unique_ptr<SiteCharacteristicsDataReader> GetReaderForOrigin(
-      const url::Origin& origin) override;
-  std::unique_ptr<SiteCharacteristicsDataWriter> GetWriterForOrigin(
-      const url::Origin& origin,
-      performance_manager::TabVisibility tab_visibility) override;
-  bool IsRecordingForTesting() override;
-
-  const LocalSiteCharacteristicsMap& origin_data_map_for_testing() const {
-    return origin_data_map_;
-  }
-
-  // NOTE: This should be called before creating any
-  // LocalSiteCharacteristicsDataImpl object (this doesn't update the database
-  // used by these objects).
-  void SetDatabaseForTesting(
-      std::unique_ptr<LocalSiteCharacteristicsDatabase> database) {
-    database_ = std::move(database);
-  }
-
-  // LocalSiteCharacteristicsDataStoreInspector:
-  const char* GetDataStoreName() override;
-  std::vector<url::Origin> GetAllInMemoryOrigins() override;
-  void GetDatabaseSize(DatabaseSizeCallback on_have_data) override;
-  bool GetDataForOrigin(const url::Origin& origin,
-                        bool* is_dirty,
-                        std::unique_ptr<SiteDataProto>* data) override;
-  LocalSiteCharacteristicsDataStore* GetDataStore() override;
-
- private:
-  FRIEND_TEST_ALL_PREFIXES(LocalSiteCharacteristicsDataStoreTest, EndToEnd);
-  FRIEND_TEST_ALL_PREFIXES(LocalSiteCharacteristicsDataStoreTest,
-                           OnURLsDeleted_Partial_OriginNotReferenced);
-  FRIEND_TEST_ALL_PREFIXES(LocalSiteCharacteristicsDataStoreTest,
-                           OnURLsDeleted_Partial_OriginStillReferenced);
-  FRIEND_TEST_ALL_PREFIXES(LocalSiteCharacteristicsDataStoreTest,
-                           OnURLsDeleted_Full);
-
-  // Returns a pointer to the LocalSiteCharacteristicsDataImpl object
-  // associated with |origin|, create one and add it to |origin_data_map_|
-  // if it doesn't exist.
-  internal::LocalSiteCharacteristicsDataImpl* GetOrCreateFeatureImpl(
-      const url::Origin& origin);
-
-  // internal::LocalSiteCharacteristicsDataImpl::OnDestroyDelegate:
-  void OnLocalSiteCharacteristicsDataImplDestroyed(
-      internal::LocalSiteCharacteristicsDataImpl* impl) override;
-
-  // history::HistoryServiceObserver:
-  void OnURLsDeleted(history::HistoryService* history_service,
-                     const history::DeletionInfo& deletion_info) override;
-  void HistoryServiceBeingDeleted(
-      history::HistoryService* history_service) override;
-
-  // Map a serialized origin to a LocalSiteCharacteristicDataInternal
-  // pointer.
-  LocalSiteCharacteristicsMap origin_data_map_;
-
-  ScopedObserver<history::HistoryService, history::HistoryServiceObserver>
-      history_observer_{this};
-
-  std::unique_ptr<LocalSiteCharacteristicsDatabase> database_;
-
-  // The profile this data store is associated with.
-  Profile* profile_;
-
-  DISALLOW_COPY_AND_ASSIGN(LocalSiteCharacteristicsDataStore);
-};
-
-}  // namespace resource_coordinator
-
-#endif  // CHROME_BROWSER_RESOURCE_COORDINATOR_LOCAL_SITE_CHARACTERISTICS_DATA_STORE_H_
diff --git a/chrome/browser/resource_coordinator/local_site_characteristics_data_store_factory.cc b/chrome/browser/resource_coordinator/local_site_characteristics_data_store_factory.cc
deleted file mode 100644
index 0a87020..0000000
--- a/chrome/browser/resource_coordinator/local_site_characteristics_data_store_factory.cc
+++ /dev/null
@@ -1,121 +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 "chrome/browser/resource_coordinator/local_site_characteristics_data_store_factory.h"
-
-#include "base/feature_list.h"
-#include "chrome/browser/history/history_service_factory.h"
-#include "chrome/browser/profiles/incognito_helpers.h"
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/resource_coordinator/local_site_characteristics_data_store.h"
-#include "chrome/browser/resource_coordinator/local_site_characteristics_data_store_inspector.h"
-#include "chrome/browser/resource_coordinator/local_site_characteristics_non_recording_data_store.h"
-#include "chrome/browser/resource_coordinator/tab_manager_features.h"
-#include "components/keyed_service/content/browser_context_dependency_manager.h"
-#include "content/public/browser/browser_context.h"
-
-namespace resource_coordinator {
-
-namespace {
-
-bool g_enable_for_testing = false;
-
-}  // namespace
-
-// static
-SiteCharacteristicsDataStore*
-LocalSiteCharacteristicsDataStoreFactory::GetForProfile(Profile* profile) {
-  return static_cast<SiteCharacteristicsDataStore*>(
-      GetInstance()->GetServiceForBrowserContext(profile, true));
-}
-
-LocalSiteCharacteristicsDataStoreFactory*
-LocalSiteCharacteristicsDataStoreFactory::GetInstance() {
-  static base::NoDestructor<LocalSiteCharacteristicsDataStoreFactory> instance;
-  return instance.get();
-}
-
-void LocalSiteCharacteristicsDataStoreFactory::EnableForTesting() {
-  g_enable_for_testing = true;
-}
-
-LocalSiteCharacteristicsDataStoreFactory::
-    LocalSiteCharacteristicsDataStoreFactory()
-    : BrowserContextKeyedServiceFactory(
-          "LocalSiteCharacteristicsDataStore",
-          BrowserContextDependencyManager::GetInstance()) {
-  DependsOn(HistoryServiceFactory::GetInstance());
-}
-
-LocalSiteCharacteristicsDataStoreFactory::
-    ~LocalSiteCharacteristicsDataStoreFactory() = default;
-
-SiteCharacteristicsDataStore*
-LocalSiteCharacteristicsDataStoreFactory::GetExistingDataStoreForContext(
-    content::BrowserContext* context) const {
-  DCHECK(context);
-  // Remove the const attribute from |this| and calls
-  // |GetServiceForBrowserContext| with the |create| parameter set to false.
-  // This is basically a getter that returns the data store associated with a
-  // context without creating it if it doesn't exits.
-  SiteCharacteristicsDataStore* data_store =
-      static_cast<SiteCharacteristicsDataStore*>(
-          const_cast<LocalSiteCharacteristicsDataStoreFactory*>(this)
-              ->GetServiceForBrowserContext(context, false /* create */));
-  DCHECK(data_store);
-  return data_store;
-}
-
-KeyedService* LocalSiteCharacteristicsDataStoreFactory::BuildServiceInstanceFor(
-    content::BrowserContext* context) const {
-  SiteCharacteristicsDataStore* data_store = nullptr;
-  Profile* profile = Profile::FromBrowserContext(context);
-  DCHECK(profile);
-  if (context->IsOffTheRecord()) {
-    content::BrowserContext* parent_context =
-        chrome::GetBrowserContextRedirectedInIncognito(context);
-    DCHECK(parent_context);
-    // Off the record profiles correspond to incognito profile and are derived
-    // from a parent profile that is on record.
-    DCHECK(!parent_context->IsOffTheRecord());
-    LocalSiteCharacteristicsDataStoreInspector* parent_debug =
-        LocalSiteCharacteristicsDataStoreInspector::GetForProfile(
-            Profile::FromBrowserContext(parent_context));
-    SiteCharacteristicsDataStore* data_store_for_readers =
-        GetExistingDataStoreForContext(parent_context);
-    DCHECK(data_store_for_readers);
-    data_store = new LocalSiteCharacteristicsNonRecordingDataStore(
-        profile, parent_debug, data_store_for_readers);
-  } else {
-    data_store = new LocalSiteCharacteristicsDataStore(profile);
-  }
-  DCHECK(data_store);
-  return data_store;
-}
-
-content::BrowserContext*
-LocalSiteCharacteristicsDataStoreFactory::GetBrowserContextToUse(
-    content::BrowserContext* context) const {
-  return chrome::GetBrowserContextOwnInstanceInIncognito(context);
-}
-
-bool LocalSiteCharacteristicsDataStoreFactory::
-    ServiceIsCreatedWithBrowserContext() const {
-  // If this factory is enabled for tests then it's preferable to create this
-  // service on demand so tests can use custom factories (via the
-  // SetTestingFactory function).
-  if (g_enable_for_testing)
-    return false;
-
-  // Otherwise it's fine to initialize this service when the browser context
-  // gets created so the database will be ready when we need it.
-  return true;
-}
-
-bool LocalSiteCharacteristicsDataStoreFactory::ServiceIsNULLWhileTesting()
-    const {
-  return !g_enable_for_testing;
-}
-
-}  // namespace resource_coordinator
diff --git a/chrome/browser/resource_coordinator/local_site_characteristics_data_store_factory.h b/chrome/browser/resource_coordinator/local_site_characteristics_data_store_factory.h
deleted file mode 100644
index 926f739..0000000
--- a/chrome/browser/resource_coordinator/local_site_characteristics_data_store_factory.h
+++ /dev/null
@@ -1,54 +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 CHROME_BROWSER_RESOURCE_COORDINATOR_LOCAL_SITE_CHARACTERISTICS_DATA_STORE_FACTORY_H_
-#define CHROME_BROWSER_RESOURCE_COORDINATOR_LOCAL_SITE_CHARACTERISTICS_DATA_STORE_FACTORY_H_
-
-#include "base/containers/flat_map.h"
-#include "base/no_destructor.h"
-#include "chrome/browser/resource_coordinator/site_characteristics_data_store.h"
-#include "components/keyed_service/content/browser_context_keyed_service_factory.h"
-
-class Profile;
-
-namespace resource_coordinator {
-
-// Singleton that owns all the LocalSiteCharacteristicsDataStore instances and
-// associates them with Profiles.
-class LocalSiteCharacteristicsDataStoreFactory
-    : public BrowserContextKeyedServiceFactory {
- public:
-  static SiteCharacteristicsDataStore* GetForProfile(Profile* profile);
-  static LocalSiteCharacteristicsDataStoreFactory* GetInstance();
-
-  // In production, an instance is created with the profile. In unit tests, no
-  // instance is created by default. If this method is called, an instance will
-  // be created the first time GetInstance() is called. In most unit tests, a
-  // custom factory should be set before the first call to GetInstance().
-  static void EnableForTesting();
-
- private:
-  friend class base::NoDestructor<LocalSiteCharacteristicsDataStoreFactory>;
-
-  LocalSiteCharacteristicsDataStoreFactory();
-  ~LocalSiteCharacteristicsDataStoreFactory() override;
-
-  // Returns the |SiteCharacteristicsDataStore| instance associated with
-  // |context|. This is basically a wrapper around GetServiceForBrowserContext
-  // that allows calling it from a const function.
-  SiteCharacteristicsDataStore* GetExistingDataStoreForContext(
-      content::BrowserContext* context) const;
-
-  // BrowserContextKeyedServiceFactory:
-  KeyedService* BuildServiceInstanceFor(
-      content::BrowserContext* context) const override;
-  content::BrowserContext* GetBrowserContextToUse(
-      content::BrowserContext* context) const override;
-  bool ServiceIsCreatedWithBrowserContext() const override;
-  bool ServiceIsNULLWhileTesting() const override;
-};
-
-}  // namespace resource_coordinator
-
-#endif  // CHROME_BROWSER_RESOURCE_COORDINATOR_LOCAL_SITE_CHARACTERISTICS_DATA_STORE_FACTORY_H_
diff --git a/chrome/browser/resource_coordinator/local_site_characteristics_data_store_factory_browsertest.cc b/chrome/browser/resource_coordinator/local_site_characteristics_data_store_factory_browsertest.cc
deleted file mode 100644
index f916f9d..0000000
--- a/chrome/browser/resource_coordinator/local_site_characteristics_data_store_factory_browsertest.cc
+++ /dev/null
@@ -1,94 +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 "chrome/browser/resource_coordinator/local_site_characteristics_data_store_factory.h"
-
-#include "chrome/browser/browser_process.h"
-#include "chrome/browser/chrome_notification_types.h"
-#include "chrome/browser/profiles/profile_manager.h"
-#include "chrome/browser/profiles/profile_window.h"
-#include "chrome/browser/resource_coordinator/tab_manager_features.h"
-#include "chrome/test/base/in_process_browser_test.h"
-#include "chrome/test/base/ui_test_utils.h"
-#include "content/public/browser/notification_service.h"
-#include "content/public/test/browser_test.h"
-#include "content/public/test/test_utils.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-#if defined(OS_CHROMEOS)
-#include "chromeos/constants/chromeos_switches.h"
-#endif
-
-namespace resource_coordinator {
-
-namespace {
-
-// Ensures that a SiteCharacteristicsDataStore respect the |IsOffTheRecord| of
-// its corresponding profile.
-bool DataStoreRespectsOffTheRecordValue(
-    Profile* profile,
-    SiteCharacteristicsDataStore* data_store) {
-  return profile->IsOffTheRecord() == !data_store->IsRecordingForTesting();
-}
-
-}  // namespace
-
-class LocalSiteCharacteristicsDataStoreFactoryTest
-    : public InProcessBrowserTest {
- protected:
-  LocalSiteCharacteristicsDataStoreFactoryTest() = default;
-  ~LocalSiteCharacteristicsDataStoreFactoryTest() override = default;
-
-  void SetUp() override {
-    InProcessBrowserTest::SetUp();
-  }
-
-#if defined(OS_CHROMEOS)
-  void SetUpCommandLine(base::CommandLine* command_line) override {
-    command_line->AppendSwitch(
-        chromeos::switches::kIgnoreUserProfileMappingForTests);
-  }
-#endif
-
-  DISALLOW_COPY_AND_ASSIGN(LocalSiteCharacteristicsDataStoreFactoryTest);
-};
-
-IN_PROC_BROWSER_TEST_F(LocalSiteCharacteristicsDataStoreFactoryTest, EndToEnd) {
-  Profile* regular_profile = browser()->profile();
-  ASSERT_TRUE(regular_profile);
-  SiteCharacteristicsDataStore* recording_data_store =
-      LocalSiteCharacteristicsDataStoreFactory::GetForProfile(regular_profile);
-  ASSERT_TRUE(recording_data_store);
-  EXPECT_TRUE(DataStoreRespectsOffTheRecordValue(regular_profile,
-                                                 recording_data_store));
-
-  Profile* incognito_profile = regular_profile->GetPrimaryOTRProfile();
-  ASSERT_TRUE(incognito_profile);
-  SiteCharacteristicsDataStore* incognito_data_store =
-      static_cast<SiteCharacteristicsDataStore*>(
-          LocalSiteCharacteristicsDataStoreFactory::GetForProfile(
-              incognito_profile));
-  ASSERT_TRUE(incognito_data_store);
-  EXPECT_NE(recording_data_store, incognito_data_store);
-  EXPECT_TRUE(DataStoreRespectsOffTheRecordValue(incognito_profile,
-                                                 incognito_data_store));
-
-  profiles::SwitchToGuestProfile(ProfileManager::CreateCallback());
-  ui_test_utils::WaitForBrowserToOpen();
-
-  ProfileManager* profile_manager = g_browser_process->profile_manager();
-  Profile* guest_profile =
-      profile_manager->GetProfileByPath(ProfileManager::GetGuestProfilePath());
-  ASSERT_TRUE(guest_profile);
-  SiteCharacteristicsDataStore* guest_data_store =
-      static_cast<SiteCharacteristicsDataStore*>(
-          LocalSiteCharacteristicsDataStoreFactory::GetForProfile(
-              guest_profile));
-  ASSERT_TRUE(guest_data_store);
-  EXPECT_NE(recording_data_store, guest_data_store);
-  EXPECT_TRUE(
-      DataStoreRespectsOffTheRecordValue(guest_profile, guest_data_store));
-}
-
-}  // namespace resource_coordinator
diff --git a/chrome/browser/resource_coordinator/local_site_characteristics_data_store_inspector.cc b/chrome/browser/resource_coordinator/local_site_characteristics_data_store_inspector.cc
deleted file mode 100644
index 7732700..0000000
--- a/chrome/browser/resource_coordinator/local_site_characteristics_data_store_inspector.cc
+++ /dev/null
@@ -1,61 +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 "chrome/browser/resource_coordinator/local_site_characteristics_data_store_inspector.h"
-
-#include "base/macros.h"
-#include "chrome/browser/profiles/profile.h"
-
-namespace resource_coordinator {
-
-namespace {
-
-const void* const kSiteCharacteristicsDataStoreInspectorUserKey =
-    &kSiteCharacteristicsDataStoreInspectorUserKey;
-
-class SiteCharacteristicsUserData : public base::SupportsUserData::Data {
- public:
-  explicit SiteCharacteristicsUserData(
-      LocalSiteCharacteristicsDataStoreInspector* inspector)
-      : inspector_(inspector) {}
-
-  LocalSiteCharacteristicsDataStoreInspector* inspector() const {
-    return inspector_;
-  }
-
- private:
-  LocalSiteCharacteristicsDataStoreInspector* inspector_;
-};
-
-}  // namespace
-
-// static
-LocalSiteCharacteristicsDataStoreInspector*
-LocalSiteCharacteristicsDataStoreInspector::GetForProfile(Profile* profile) {
-  SiteCharacteristicsUserData* data = static_cast<SiteCharacteristicsUserData*>(
-      profile->GetUserData(kSiteCharacteristicsDataStoreInspectorUserKey));
-
-  if (!data)
-    return nullptr;
-
-  return data->inspector();
-}
-
-// static
-void LocalSiteCharacteristicsDataStoreInspector::SetForProfile(
-    LocalSiteCharacteristicsDataStoreInspector* inspector,
-    Profile* profile) {
-  if (inspector) {
-    DCHECK_EQ(nullptr, GetForProfile(profile));
-
-    profile->SetUserData(
-        kSiteCharacteristicsDataStoreInspectorUserKey,
-        std::make_unique<SiteCharacteristicsUserData>(inspector));
-  } else {
-    DCHECK_NE(nullptr, GetForProfile(profile));
-    profile->RemoveUserData(kSiteCharacteristicsDataStoreInspectorUserKey);
-  }
-}
-
-}  // namespace resource_coordinator
diff --git a/chrome/browser/resource_coordinator/local_site_characteristics_data_store_inspector.h b/chrome/browser/resource_coordinator/local_site_characteristics_data_store_inspector.h
deleted file mode 100644
index 812e815f..0000000
--- a/chrome/browser/resource_coordinator/local_site_characteristics_data_store_inspector.h
+++ /dev/null
@@ -1,77 +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 CHROME_BROWSER_RESOURCE_COORDINATOR_LOCAL_SITE_CHARACTERISTICS_DATA_STORE_INSPECTOR_H_
-#define CHROME_BROWSER_RESOURCE_COORDINATOR_LOCAL_SITE_CHARACTERISTICS_DATA_STORE_INSPECTOR_H_
-
-#include <cstdint>
-#include <string>
-#include <vector>
-
-#include "base/callback.h"
-#include "base/optional.h"
-#include "base/supports_user_data.h"
-#include "components/performance_manager/persistence/site_data/site_data.pb.h"
-#include "url/origin.h"
-
-class Profile;
-
-namespace resource_coordinator {
-
-class SiteCharacteristicsDataStore;
-
-// An interface that allows LocalSite data stores to expose diagnostic
-// information for the associated web UI.
-class LocalSiteCharacteristicsDataStoreInspector {
- public:
-  // Retrieves the instance associated with a given profile, or nullptr
-  // if none is associated with that profile.
-  static LocalSiteCharacteristicsDataStoreInspector* GetForProfile(
-      Profile* profile);
-
-  // Returns the name of the data store, which should uniquely identify the kind
-  // of storage it implements.
-  virtual const char* GetDataStoreName() = 0;
-
-  // Retrieves the origins that are current represented by in-memory data
-  // at the present time.
-  virtual std::vector<url::Origin> GetAllInMemoryOrigins() = 0;
-
-  // Retrieves the number of rows and the on-disk size of the DB. Invokes
-  // the |on_have_data| callback once the data has been collected, or once it's
-  // determined that the data can't be retrieved.
-  // On callback |num_rows| is the number of rows in the database, or -1 if
-  // the number can't be determined. |on_disk_size_kb| is the on-disk size of
-  // the database, or -1 if the on-disk size can't be determined.
-  using DatabaseSizeCallback =
-      base::OnceCallback<void(base::Optional<int64_t> num_rows,
-                              base::Optional<int64_t> on_disk_size_kb)>;
-  virtual void GetDatabaseSize(DatabaseSizeCallback on_have_data) = 0;
-
-  // Retrieves the in-memory data for a given origin.
-  // On return |data| contains the available data for |origin| if available,
-  // and |is_dirty| is true if the entry needs flushing to disk.
-  // Returns true if an entry exists for |origin|.
-  virtual bool GetDataForOrigin(const url::Origin& origin,
-                                bool* is_dirty,
-                                std::unique_ptr<SiteDataProto>* data) = 0;
-
-  // Retrieves the data store this inspector is associated with.
-  virtual SiteCharacteristicsDataStore* GetDataStore() = 0;
-
- protected:
-  // Sets the inspector instance associated with a given profile.
-  // If |inspector| is nullptr the association is cleared.
-  // The caller must ensure that |inspector|'s registration is cleared before
-  // |inspector| or |profile| are deleted.
-  // The intent is for this to be called from implementation class' constructors
-  // and destructors.
-  static void SetForProfile(
-      LocalSiteCharacteristicsDataStoreInspector* inspector,
-      Profile* profile);
-};
-
-}  // namespace resource_coordinator
-
-#endif  // CHROME_BROWSER_RESOURCE_COORDINATOR_LOCAL_SITE_CHARACTERISTICS_DATA_STORE_INSPECTOR_H_
diff --git a/chrome/browser/resource_coordinator/local_site_characteristics_data_store_unittest.cc b/chrome/browser/resource_coordinator/local_site_characteristics_data_store_unittest.cc
deleted file mode 100644
index da1af9c2..0000000
--- a/chrome/browser/resource_coordinator/local_site_characteristics_data_store_unittest.cc
+++ /dev/null
@@ -1,327 +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 "chrome/browser/resource_coordinator/local_site_characteristics_data_store.h"
-
-#include "base/macros.h"
-#include "base/memory/ptr_util.h"
-#include "base/test/scoped_feature_list.h"
-#include "base/test/simple_test_tick_clock.h"
-#include "chrome/browser/resource_coordinator/local_site_characteristics_data_impl.h"
-#include "chrome/browser/resource_coordinator/local_site_characteristics_data_store_inspector.h"
-#include "chrome/browser/resource_coordinator/local_site_characteristics_data_unittest_utils.h"
-#include "chrome/browser/resource_coordinator/time.h"
-#include "chrome/test/base/testing_profile.h"
-#include "components/history/core/browser/history_types.h"
-#include "components/history/core/browser/url_row.h"
-#include "content/public/test/browser_task_environment.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "url/gurl.h"
-
-namespace resource_coordinator {
-
-namespace {
-
-constexpr base::TimeDelta kDelay = base::TimeDelta::FromMinutes(1);
-
-// TODO(https://crbug.com/1042727): Fix test GURL scoping and remove this getter
-// function.
-url::Origin Origin1() {
-  return url::Origin::Create(GURL("http://www.foo.com"));
-}
-url::Origin Origin2() {
-  return url::Origin::Create(GURL("http://www.bar.com"));
-}
-
-class MockLocalSiteCharacteristicsDatabase
-    : public testing::NoopLocalSiteCharacteristicsDatabase {
- public:
-  MockLocalSiteCharacteristicsDatabase() = default;
-  ~MockLocalSiteCharacteristicsDatabase() = default;
-
-  MOCK_METHOD1(RemoveSiteCharacteristicsFromDB,
-               void(const std::vector<url::Origin>&));
-  MOCK_METHOD0(ClearDatabase, void());
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(MockLocalSiteCharacteristicsDatabase);
-};
-
-}  // namespace
-
-class LocalSiteCharacteristicsDataStoreTest : public ::testing::Test {
- protected:
-  LocalSiteCharacteristicsDataStoreTest()
-      : scoped_set_tick_clock_for_testing_(&test_clock_) {
-    data_store_ =
-        std::make_unique<LocalSiteCharacteristicsDataStore>(&profile_);
-    mock_db_ =
-        new ::testing::StrictMock<MockLocalSiteCharacteristicsDatabase>();
-    data_store_->SetDatabaseForTesting(base::WrapUnique(mock_db_));
-    test_clock_.SetNowTicks(base::TimeTicks::UnixEpoch());
-    test_clock_.Advance(base::TimeDelta::FromHours(1));
-    WaitForAsyncOperationsToComplete();
-  }
-
-  void TearDown() override { WaitForAsyncOperationsToComplete(); }
-
-  void WaitForAsyncOperationsToComplete() { task_environment_.RunUntilIdle(); }
-
-  // Populates |writer_|, |reader_| and |data_| to refer to a tab navigated to
-  // |Origin1()| that updated its title in background. Populates |writer2_|,
-  // |reader2_| and |data2_| to refer to a tab navigated to |Origin2()| that
-  // updates its favicon in background.
-  void SetupTwoSitesUsingFeaturesInBackground() {
-    // Load a first origin, and then make use of a feature on it.
-    ASSERT_FALSE(reader_);
-    reader_ = data_store_->GetReaderForOrigin(Origin1());
-    EXPECT_TRUE(reader_);
-
-    ASSERT_FALSE(writer_);
-    writer_ = data_store_->GetWriterForOrigin(
-        Origin1(), performance_manager::TabVisibility::kBackground);
-    EXPECT_TRUE(writer_);
-
-    ASSERT_FALSE(data_);
-    data_ = data_store_->origin_data_map_for_testing().find(Origin1())->second;
-    EXPECT_TRUE(data_);
-
-    EXPECT_EQ(performance_manager::SiteFeatureUsage::kSiteFeatureUsageUnknown,
-              reader_->UpdatesTitleInBackground());
-    writer_->NotifySiteLoaded();
-    writer_->NotifyUpdatesTitleInBackground();
-    EXPECT_EQ(performance_manager::SiteFeatureUsage::kSiteFeatureInUse,
-              reader_->UpdatesTitleInBackground());
-    test_clock_.Advance(kDelay);
-
-    // Load a second origin, make use of a feature on it too.
-    ASSERT_FALSE(reader2_);
-    reader2_ = data_store_->GetReaderForOrigin(Origin2());
-    EXPECT_TRUE(reader2_);
-
-    ASSERT_FALSE(writer2_);
-    writer2_ = data_store_->GetWriterForOrigin(
-        Origin2(), performance_manager::TabVisibility::kBackground);
-    EXPECT_TRUE(writer2_);
-
-    ASSERT_FALSE(data2_);
-    data2_ = data_store_->origin_data_map_for_testing().find(Origin2())->second;
-    EXPECT_TRUE(data2_);
-
-    EXPECT_EQ(performance_manager::SiteFeatureUsage::kSiteFeatureUsageUnknown,
-              reader2_->UpdatesFaviconInBackground());
-    writer2_->NotifySiteLoaded();
-    writer2_->NotifyUpdatesFaviconInBackground();
-    EXPECT_EQ(performance_manager::SiteFeatureUsage::kSiteFeatureInUse,
-              reader2_->UpdatesFaviconInBackground());
-    test_clock_.Advance(kDelay);
-  }
-
-  base::SimpleTestTickClock test_clock_;
-  ScopedSetTickClockForTesting scoped_set_tick_clock_for_testing_;
-  content::BrowserTaskEnvironment task_environment_;
-  TestingProfile profile_;
-
-  // Owned by |data_store_|.
-  ::testing::StrictMock<MockLocalSiteCharacteristicsDatabase>* mock_db_ =
-      nullptr;
-  std::unique_ptr<LocalSiteCharacteristicsDataStore> data_store_;
-
-  std::unique_ptr<SiteCharacteristicsDataReader> reader_;
-  std::unique_ptr<SiteCharacteristicsDataWriter> writer_;
-  internal::LocalSiteCharacteristicsDataImpl* data_ = nullptr;
-
-  std::unique_ptr<SiteCharacteristicsDataReader> reader2_;
-  std::unique_ptr<SiteCharacteristicsDataWriter> writer2_;
-  internal::LocalSiteCharacteristicsDataImpl* data2_ = nullptr;
-};
-
-TEST_F(LocalSiteCharacteristicsDataStoreTest, EndToEnd) {
-  auto reader = data_store_->GetReaderForOrigin(Origin1());
-  EXPECT_TRUE(reader);
-  auto writer = data_store_->GetWriterForOrigin(
-      Origin1(), performance_manager::TabVisibility::kBackground);
-  EXPECT_TRUE(writer);
-
-  EXPECT_EQ(1U, data_store_->origin_data_map_for_testing().size());
-
-  EXPECT_EQ(performance_manager::SiteFeatureUsage::kSiteFeatureUsageUnknown,
-            reader->UpdatesTitleInBackground());
-  writer->NotifySiteLoaded();
-  writer->NotifyUpdatesTitleInBackground();
-  EXPECT_EQ(performance_manager::SiteFeatureUsage::kSiteFeatureInUse,
-            reader->UpdatesTitleInBackground());
-  writer->NotifySiteUnloaded();
-  EXPECT_EQ(performance_manager::SiteFeatureUsage::kSiteFeatureInUse,
-            reader->UpdatesTitleInBackground());
-
-  auto reader_copy = data_store_->GetReaderForOrigin(Origin1());
-  EXPECT_EQ(1U, data_store_->origin_data_map_for_testing().size());
-  auto reader2 = data_store_->GetReaderForOrigin(Origin2());
-  EXPECT_EQ(2U, data_store_->origin_data_map_for_testing().size());
-  reader2.reset();
-
-  WaitForAsyncOperationsToComplete();
-  EXPECT_EQ(1U, data_store_->origin_data_map_for_testing().size());
-  reader_copy.reset();
-
-  reader.reset();
-  writer.reset();
-  EXPECT_TRUE(data_store_->origin_data_map_for_testing().empty());
-
-  EXPECT_CALL(*mock_db_, ClearDatabase());
-  data_store_->OnURLsDeleted(nullptr, history::DeletionInfo::ForAllHistory());
-}
-
-// Verify that an origin is removed from the data store (in memory and on disk)
-// when there are no more references to it in the history, after the history is
-// partially cleared.
-TEST_F(LocalSiteCharacteristicsDataStoreTest,
-       OnURLsDeleted_Partial_OriginNotReferenced) {
-  SetupTwoSitesUsingFeaturesInBackground();
-
-  const base::TimeDelta last_loaded_time2_before_urls_deleted =
-      data2_->last_loaded_time_for_testing();
-
-  // Make sure that all data passed to |OnURLsDeleted| get passed to the
-  // database, even if they're not in the internal map used by the data store.
-  const url::Origin kOriginNotInMap =
-      url::Origin::Create(GURL("http://www.url-not-in-map.com"));
-  history::URLRows urls_to_delete = {history::URLRow(Origin1().GetURL()),
-                                     history::URLRow(kOriginNotInMap.GetURL())};
-  history::DeletionInfo deletion_info =
-      history::DeletionInfo::ForUrls(urls_to_delete, std::set<GURL>());
-  deletion_info.set_deleted_urls_origin_map({
-      {Origin1().GetURL(), {0, base::Time::Now()}},
-      {kOriginNotInMap.GetURL(), {0, base::Time::Now()}},
-  });
-  EXPECT_CALL(*mock_db_,
-              RemoveSiteCharacteristicsFromDB(::testing::WhenSorted(
-                  ::testing::ElementsAre(Origin1(), kOriginNotInMap))));
-  data_store_->OnURLsDeleted(nullptr, deletion_info);
-  ::testing::Mock::VerifyAndClear(mock_db_);
-
-  // The information for the first site should have been cleared. The last
-  // loaded time should be equal to the current time.
-  EXPECT_EQ(data_->last_loaded_time_for_testing(),
-            test_clock_.NowTicks() - base::TimeTicks::UnixEpoch());
-  EXPECT_EQ(performance_manager::SiteFeatureUsage::kSiteFeatureUsageUnknown,
-            reader_->UpdatesTitleInBackground());
-  // The second site shouldn't have been cleared.
-  EXPECT_EQ(data2_->last_loaded_time_for_testing(),
-            last_loaded_time2_before_urls_deleted);
-  EXPECT_EQ(performance_manager::SiteFeatureUsage::kSiteFeatureInUse,
-            reader2_->UpdatesFaviconInBackground());
-
-  writer_->NotifySiteUnloaded();
-  writer2_->NotifySiteUnloaded();
-}
-
-// Verify that an origin is *not* removed from the data store (in memory and on
-// disk) when there remain references to it in the history, after the history is
-// partially cleared.
-TEST_F(LocalSiteCharacteristicsDataStoreTest,
-       OnURLsDeleted_Partial_OriginStillReferenced) {
-  SetupTwoSitesUsingFeaturesInBackground();
-
-  const base::TimeDelta last_loaded_time_before_urls_deleted =
-      data_->last_loaded_time_for_testing();
-  const base::TimeDelta last_loaded_time2_before_urls_deleted =
-      data2_->last_loaded_time_for_testing();
-
-  // Make sure that all data passed to |OnURLsDeleted| get passed to the
-  // database, even if they're not in the internal map used by the data store.
-  const url::Origin kOriginNotInMap =
-      url::Origin::Create(GURL("http://www.url-not-in-map.com"));
-  history::URLRows urls_to_delete = {history::URLRow(Origin1().GetURL()),
-                                     history::URLRow(kOriginNotInMap.GetURL())};
-  history::DeletionInfo deletion_info =
-      history::DeletionInfo::ForUrls(urls_to_delete, std::set<GURL>());
-  deletion_info.set_deleted_urls_origin_map({
-      {Origin1().GetURL(), {4, base::Time::Now()}},
-      {kOriginNotInMap.GetURL(), {3, base::Time::Now()}},
-  });
-  data_store_->OnURLsDeleted(nullptr, deletion_info);
-  ::testing::Mock::VerifyAndClear(mock_db_);
-
-  // Sites shouldn't have been cleared.
-  EXPECT_EQ(data_->last_loaded_time_for_testing(),
-            last_loaded_time_before_urls_deleted);
-  EXPECT_EQ(performance_manager::SiteFeatureUsage::kSiteFeatureInUse,
-            reader_->UpdatesTitleInBackground());
-  EXPECT_EQ(data2_->last_loaded_time_for_testing(),
-            last_loaded_time2_before_urls_deleted);
-  EXPECT_EQ(performance_manager::SiteFeatureUsage::kSiteFeatureInUse,
-            reader2_->UpdatesFaviconInBackground());
-
-  writer_->NotifySiteUnloaded();
-  writer2_->NotifySiteUnloaded();
-}
-
-// Verify that origins are removed from the data store (in memory and on disk)
-// when the history is completely cleared.
-TEST_F(LocalSiteCharacteristicsDataStoreTest, OnURLsDeleted_Full) {
-  SetupTwoSitesUsingFeaturesInBackground();
-
-  // Delete all the information stored in the data store.
-  EXPECT_CALL(*mock_db_, ClearDatabase());
-  data_store_->OnURLsDeleted(nullptr, history::DeletionInfo::ForAllHistory());
-  ::testing::Mock::VerifyAndClear(mock_db_);
-
-  // The information for both sites should have been cleared.
-  EXPECT_EQ(data_->last_loaded_time_for_testing(),
-            test_clock_.NowTicks() - base::TimeTicks::UnixEpoch());
-  EXPECT_EQ(performance_manager::SiteFeatureUsage::kSiteFeatureUsageUnknown,
-            reader_->UpdatesTitleInBackground());
-  EXPECT_EQ(data2_->last_loaded_time_for_testing(),
-            test_clock_.NowTicks() - base::TimeTicks::UnixEpoch());
-  EXPECT_EQ(performance_manager::SiteFeatureUsage::kSiteFeatureUsageUnknown,
-            reader2_->UpdatesFaviconInBackground());
-
-  writer_->NotifySiteUnloaded();
-  writer2_->NotifySiteUnloaded();
-}
-
-TEST_F(LocalSiteCharacteristicsDataStoreTest, InspectorWorks) {
-  // Make sure the inspector interface was registered at construction.
-  LocalSiteCharacteristicsDataStoreInspector* inspector =
-      LocalSiteCharacteristicsDataStoreInspector::GetForProfile(&profile_);
-  EXPECT_NE(nullptr, inspector);
-  EXPECT_EQ(data_store_.get(), inspector);
-
-  EXPECT_STREQ("LocalSiteCharacteristicsDataStore",
-               inspector->GetDataStoreName());
-
-  // We expect an empty data store at the outset.
-  EXPECT_EQ(0U, inspector->GetAllInMemoryOrigins().size());
-  std::unique_ptr<SiteDataProto> data;
-  bool is_dirty = false;
-  EXPECT_FALSE(inspector->GetDataForOrigin(Origin1(), &is_dirty, &data));
-  EXPECT_FALSE(is_dirty);
-  EXPECT_EQ(nullptr, data.get());
-
-  {
-    // Add an entry, see that it's reflected in the inspector interface.
-    auto writer = data_store_->GetWriterForOrigin(
-        Origin1(), performance_manager::TabVisibility::kBackground);
-
-    EXPECT_EQ(1U, inspector->GetAllInMemoryOrigins().size());
-    EXPECT_TRUE(inspector->GetDataForOrigin(Origin1(), &is_dirty, &data));
-    EXPECT_FALSE(is_dirty);
-    ASSERT_NE(nullptr, data.get());
-
-    // Touch the underlying data, see that the dirty bit updates.
-    writer->NotifySiteLoaded();
-    EXPECT_TRUE(inspector->GetDataForOrigin(Origin1(), &is_dirty, &data));
-    EXPECT_TRUE(is_dirty);
-  }
-
-  // Make sure the interface is unregistered from the profile on destruction.
-  data_store_.reset();
-  EXPECT_EQ(nullptr, LocalSiteCharacteristicsDataStoreInspector::GetForProfile(
-                         &profile_));
-}
-
-}  // namespace resource_coordinator
diff --git a/chrome/browser/resource_coordinator/local_site_characteristics_data_unittest_utils.cc b/chrome/browser/resource_coordinator/local_site_characteristics_data_unittest_utils.cc
deleted file mode 100644
index 3812f80..0000000
--- a/chrome/browser/resource_coordinator/local_site_characteristics_data_unittest_utils.cc
+++ /dev/null
@@ -1,127 +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 "chrome/browser/resource_coordinator/local_site_characteristics_data_unittest_utils.h"
-
-#include <utility>
-
-#include "base/bind_helpers.h"
-#include "base/task/post_task.h"
-#include "chrome/browser/resource_coordinator/local_site_characteristics_data_store_factory.h"
-#include "chrome/browser/resource_coordinator/local_site_characteristics_webcontents_observer.h"
-#include "chrome/browser/resource_coordinator/tab_helper.h"
-#include "chrome/browser/tab_contents/form_interaction_tab_helper.h"
-#include "content/public/browser/browser_task_traits.h"
-#include "content/public/browser/browser_thread.h"
-#include "content/public/browser/system_connector.h"
-#include "content/public/browser/web_contents.h"
-#include "mojo/public/cpp/bindings/pending_receiver.h"
-
-namespace resource_coordinator {
-namespace testing {
-
-internal::LocalSiteCharacteristicsDataImpl*
-GetLocalSiteCharacteristicsDataImplForWC(content::WebContents* web_contents) {
-  ResourceCoordinatorTabHelper* tab_helper =
-      ResourceCoordinatorTabHelper::FromWebContents(web_contents);
-  DCHECK(tab_helper);
-  auto* wc_observer = tab_helper->local_site_characteristics_wc_observer();
-  DCHECK(wc_observer);
-
-  auto* writer = static_cast<LocalSiteCharacteristicsDataWriter*>(
-      wc_observer->GetWriterForTesting());
-
-  if (!writer)
-    return nullptr;
-
-  return writer->impl_for_testing();
-}
-
-void WaitForLocalDBEntryToBeInitialized(
-    content::WebContents* web_contents,
-    base::RepeatingClosure run_pending_tasks) {
-  internal::LocalSiteCharacteristicsDataImpl* impl =
-      GetLocalSiteCharacteristicsDataImplForWC(web_contents);
-  DCHECK(impl);
-  while (!impl->fully_initialized_for_testing())
-    run_pending_tasks.Run();
-}
-
-void ExpireLocalDBObservationWindows(content::WebContents* web_contents) {
-  internal::LocalSiteCharacteristicsDataImpl* impl =
-      GetLocalSiteCharacteristicsDataImplForWC(web_contents);
-  DCHECK(impl);
-  impl->ExpireAllObservationWindowsForTesting();
-}
-
-void MarkWebContentsAsLoadedInBackground(content::WebContents* web_contents) {
-  internal::LocalSiteCharacteristicsDataImpl* impl =
-      GetLocalSiteCharacteristicsDataImplForWC(web_contents);
-  DCHECK(impl);
-  impl->NotifySiteLoaded();
-  impl->NotifyLoadedSiteBackgrounded();
-}
-
-MockLocalSiteCharacteristicsDataImplOnDestroyDelegate::
-    MockLocalSiteCharacteristicsDataImplOnDestroyDelegate() = default;
-MockLocalSiteCharacteristicsDataImplOnDestroyDelegate::
-    ~MockLocalSiteCharacteristicsDataImplOnDestroyDelegate() = default;
-
-NoopLocalSiteCharacteristicsDatabase::NoopLocalSiteCharacteristicsDatabase() =
-    default;
-NoopLocalSiteCharacteristicsDatabase::~NoopLocalSiteCharacteristicsDatabase() =
-    default;
-
-void NoopLocalSiteCharacteristicsDatabase::ReadSiteCharacteristicsFromDB(
-    const url::Origin& origin,
-    ReadSiteCharacteristicsFromDBCallback callback) {
-  std::move(callback).Run(base::nullopt);
-}
-
-void NoopLocalSiteCharacteristicsDatabase::WriteSiteCharacteristicsIntoDB(
-    const url::Origin& origin,
-    const SiteDataProto& site_characteristic_proto) {}
-
-void NoopLocalSiteCharacteristicsDatabase::RemoveSiteCharacteristicsFromDB(
-    const std::vector<url::Origin>& site_origins) {}
-
-void NoopLocalSiteCharacteristicsDatabase::ClearDatabase() {}
-
-void NoopLocalSiteCharacteristicsDatabase::GetDatabaseSize(
-    GetDatabaseSizeCallback callback) {
-  std::move(callback).Run(base::nullopt, base::nullopt);
-}
-
-ChromeTestHarnessWithLocalDB::~ChromeTestHarnessWithLocalDB() = default;
-
-void ChromeTestHarnessWithLocalDB::SetUp() {
-  // TODO(siggi): Can this die now?
-  mojo::PendingReceiver<service_manager::mojom::Connector> connector_receiver;
-  content::SetSystemConnectorForTesting(
-      service_manager::Connector::Create(&connector_receiver));
-
-  // Enable the LocalSiteCharacteristicsDataStoreFactory before calling
-  // ChromeRenderViewHostTestHarness::SetUp(), this will prevent the creation
-  // of a non-mock version of a data store when browser_context() gets
-  // initialized.
-  pm_harness_.SetUp();
-
-  performance_manager::PerformanceManagerImpl::CallOnGraph(
-      FROM_HERE, base::BindOnce([](performance_manager::Graph* graph) {
-        graph->PassToGraph(FormInteractionTabHelper::CreateGraphObserver());
-      }));
-
-  LocalSiteCharacteristicsDataStoreFactory::EnableForTesting();
-
-  ChromeRenderViewHostTestHarness::SetUp();
-}
-
-void ChromeTestHarnessWithLocalDB::TearDown() {
-  pm_harness_.TearDown();
-  content::SetSystemConnectorForTesting(nullptr);
-  ChromeRenderViewHostTestHarness::TearDown();
-}
-
-}  // namespace testing
-}  // namespace resource_coordinator
diff --git a/chrome/browser/resource_coordinator/local_site_characteristics_data_unittest_utils.h b/chrome/browser/resource_coordinator/local_site_characteristics_data_unittest_utils.h
deleted file mode 100644
index 9416e085..0000000
--- a/chrome/browser/resource_coordinator/local_site_characteristics_data_unittest_utils.h
+++ /dev/null
@@ -1,111 +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 CHROME_BROWSER_RESOURCE_COORDINATOR_LOCAL_SITE_CHARACTERISTICS_DATA_UNITTEST_UTILS_H_
-#define CHROME_BROWSER_RESOURCE_COORDINATOR_LOCAL_SITE_CHARACTERISTICS_DATA_UNITTEST_UTILS_H_
-
-#include <memory>
-
-#include "base/callback_forward.h"
-#include "base/macros.h"
-#include "base/test/scoped_feature_list.h"
-#include "chrome/browser/resource_coordinator/local_site_characteristics_data_impl.h"
-#include "chrome/browser/resource_coordinator/local_site_characteristics_data_store_factory.h"
-#include "chrome/browser/resource_coordinator/local_site_characteristics_database.h"
-#include "chrome/test/base/chrome_render_view_host_test_harness.h"
-#include "components/performance_manager/performance_manager_impl.h"
-#include "components/performance_manager/test_support/test_harness_helper.h"
-#include "testing/gmock/include/gmock/gmock.h"
-
-namespace content {
-class WebContents;
-}
-
-namespace resource_coordinator {
-
-namespace testing {
-
-// Return the LocalSiteCharacteristicsDataImpl instance backing a WebContents,
-// this might be null if this WebContents isn't loaded with a valid URL.
-internal::LocalSiteCharacteristicsDataImpl*
-GetLocalSiteCharacteristicsDataImplForWC(content::WebContents* web_contents);
-
-// Wait for the Local Site Characteristics Database entry for a given
-// WebContents to be initialized, run |run_pending_tasks| repeatedly while
-// waiting.
-void WaitForLocalDBEntryToBeInitialized(
-    content::WebContents* web_contents,
-    base::RepeatingClosure run_pending_tasks);
-
-// Expire all the Local Site Characteristics Database observation windows
-// for a given WebContents.
-void ExpireLocalDBObservationWindows(content::WebContents* web_contents);
-
-// Pretend that this WebContents has been loaded in background.
-void MarkWebContentsAsLoadedInBackground(content::WebContents* web_contents);
-
-class MockLocalSiteCharacteristicsDataImplOnDestroyDelegate
-    : public internal::LocalSiteCharacteristicsDataImpl::OnDestroyDelegate {
- public:
-  MockLocalSiteCharacteristicsDataImplOnDestroyDelegate();
-  ~MockLocalSiteCharacteristicsDataImplOnDestroyDelegate();
-
-  MOCK_METHOD1(OnLocalSiteCharacteristicsDataImplDestroyed,
-               void(internal::LocalSiteCharacteristicsDataImpl*));
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(
-      MockLocalSiteCharacteristicsDataImplOnDestroyDelegate);
-};
-
-// An implementation of a LocalSiteCharacteristicsDatabase that doesn't record
-// anything.
-class NoopLocalSiteCharacteristicsDatabase
-    : public LocalSiteCharacteristicsDatabase {
- public:
-  NoopLocalSiteCharacteristicsDatabase();
-  ~NoopLocalSiteCharacteristicsDatabase() override;
-
-  // LocalSiteCharacteristicsDatabase:
-  void ReadSiteCharacteristicsFromDB(
-      const url::Origin& origin,
-      ReadSiteCharacteristicsFromDBCallback callback) override;
-  void WriteSiteCharacteristicsIntoDB(
-      const url::Origin& origin,
-      const SiteDataProto& site_characteristic_proto) override;
-  void RemoveSiteCharacteristicsFromDB(
-      const std::vector<url::Origin>& site_origins) override;
-  void ClearDatabase() override;
-  void GetDatabaseSize(GetDatabaseSizeCallback callback) override;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(NoopLocalSiteCharacteristicsDatabase);
-};
-
-// A wrapper around ChromeRenderViewHostTestHarness that ensures that the Local
-// Site Characteristics Database is initialized.
-class ChromeTestHarnessWithLocalDB : public ChromeRenderViewHostTestHarness {
- public:
-  // Construct a ChromeTestHarnessWithLocalDB with zero or more arguments
-  // passed to ChromeRenderViewHostTestHarness.
-  template <typename... TaskEnvironmentTraits>
-  explicit ChromeTestHarnessWithLocalDB(TaskEnvironmentTraits&&... traits)
-      : ChromeRenderViewHostTestHarness(
-            std::forward<TaskEnvironmentTraits>(traits)...) {
-  }
-
-  ~ChromeTestHarnessWithLocalDB() override;
-
- protected:
-  void SetUp() override;
-  void TearDown() override;
-
- private:
-  performance_manager::PerformanceManagerTestHarnessHelper pm_harness_;
-};
-
-}  // namespace testing
-}  // namespace resource_coordinator
-
-#endif  // CHROME_BROWSER_RESOURCE_COORDINATOR_LOCAL_SITE_CHARACTERISTICS_DATA_UNITTEST_UTILS_H_
diff --git a/chrome/browser/resource_coordinator/local_site_characteristics_data_writer.cc b/chrome/browser/resource_coordinator/local_site_characteristics_data_writer.cc
deleted file mode 100644
index a1c65e6..0000000
--- a/chrome/browser/resource_coordinator/local_site_characteristics_data_writer.cc
+++ /dev/null
@@ -1,92 +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 "chrome/browser/resource_coordinator/local_site_characteristics_data_writer.h"
-
-namespace resource_coordinator {
-
-LocalSiteCharacteristicsDataWriter::~LocalSiteCharacteristicsDataWriter() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  if (is_loaded_)
-    NotifySiteUnloaded();
-}
-
-void LocalSiteCharacteristicsDataWriter::NotifySiteLoaded() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-  DCHECK(!is_loaded_);
-  is_loaded_ = true;
-  impl_->NotifySiteLoaded();
-
-  if (tab_visibility_ == performance_manager::TabVisibility::kBackground)
-    impl_->NotifyLoadedSiteBackgrounded();
-}
-
-void LocalSiteCharacteristicsDataWriter::NotifySiteUnloaded() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  DCHECK(is_loaded_);
-
-  is_loaded_ = false;
-
-  impl_->NotifySiteUnloaded(tab_visibility_);
-}
-
-void LocalSiteCharacteristicsDataWriter::NotifySiteVisibilityChanged(
-    performance_manager::TabVisibility visibility) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-  // Ignore this if we receive the same event multiple times.
-  if (tab_visibility_ == visibility)
-    return;
-
-  tab_visibility_ = visibility;
-
-  if (is_loaded_) {
-    if (visibility == performance_manager::TabVisibility::kBackground) {
-      impl_->NotifyLoadedSiteBackgrounded();
-    } else {
-      impl_->NotifyLoadedSiteForegrounded();
-    }
-  }
-}
-
-void LocalSiteCharacteristicsDataWriter::NotifyUpdatesFaviconInBackground() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  DCHECK_EQ(performance_manager::TabVisibility::kBackground, tab_visibility_);
-  impl_->NotifyUpdatesFaviconInBackground();
-}
-
-void LocalSiteCharacteristicsDataWriter::NotifyUpdatesTitleInBackground() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  DCHECK_EQ(performance_manager::TabVisibility::kBackground, tab_visibility_);
-  impl_->NotifyUpdatesTitleInBackground();
-}
-
-void LocalSiteCharacteristicsDataWriter::NotifyUsesAudioInBackground() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  DCHECK_EQ(performance_manager::TabVisibility::kBackground, tab_visibility_);
-  // TODO(sebmarchand): Do not advance the background audio observation time
-  // when the WebContents has never played audio.
-  impl_->NotifyUsesAudioInBackground();
-}
-
-void LocalSiteCharacteristicsDataWriter::NotifyLoadTimePerformanceMeasurement(
-    base::TimeDelta load_duration,
-    base::TimeDelta cpu_usage_estimate,
-    uint64_t private_footprint_kb_estimate) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  impl_->NotifyLoadTimePerformanceMeasurement(load_duration, cpu_usage_estimate,
-                                              private_footprint_kb_estimate);
-}
-
-LocalSiteCharacteristicsDataWriter::LocalSiteCharacteristicsDataWriter(
-    scoped_refptr<internal::LocalSiteCharacteristicsDataImpl> impl,
-    performance_manager::TabVisibility tab_visibility)
-    : impl_(std::move(impl)),
-      tab_visibility_(tab_visibility),
-      is_loaded_(false) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-}
-
-}  // namespace resource_coordinator
diff --git a/chrome/browser/resource_coordinator/local_site_characteristics_data_writer.h b/chrome/browser/resource_coordinator/local_site_characteristics_data_writer.h
deleted file mode 100644
index d06f5fc6..0000000
--- a/chrome/browser/resource_coordinator/local_site_characteristics_data_writer.h
+++ /dev/null
@@ -1,69 +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 CHROME_BROWSER_RESOURCE_COORDINATOR_LOCAL_SITE_CHARACTERISTICS_DATA_WRITER_H_
-#define CHROME_BROWSER_RESOURCE_COORDINATOR_LOCAL_SITE_CHARACTERISTICS_DATA_WRITER_H_
-
-#include "base/macros.h"
-#include "base/memory/ref_counted.h"
-#include "chrome/browser/resource_coordinator/local_site_characteristics_data_impl.h"
-#include "chrome/browser/resource_coordinator/site_characteristics_data_writer.h"
-
-namespace resource_coordinator {
-
-// Specialization of a SiteCharacteristicsDataWriter that delegates to a
-// LocalSiteCharacteristicsDataImpl.
-//
-// This writer is initially in an unloaded state, a |NotifySiteLoaded| event
-// should be sent if/when the tab using it gets loaded.
-class LocalSiteCharacteristicsDataWriter
-    : public SiteCharacteristicsDataWriter {
- public:
-  ~LocalSiteCharacteristicsDataWriter() override;
-
-  // SiteCharacteristicsDataWriter:
-  void NotifySiteLoaded() override;
-  void NotifySiteUnloaded() override;
-  void NotifySiteVisibilityChanged(
-      performance_manager::TabVisibility visibility) override;
-  void NotifyUpdatesFaviconInBackground() override;
-  void NotifyUpdatesTitleInBackground() override;
-  void NotifyUsesAudioInBackground() override;
-  void NotifyLoadTimePerformanceMeasurement(
-      base::TimeDelta load_duration,
-      base::TimeDelta cpu_usage_estimate,
-      uint64_t private_footprint_kb_estimate) override;
-
-  internal::LocalSiteCharacteristicsDataImpl* impl_for_testing() const {
-    return impl_.get();
-  }
-
- private:
-  friend class LocalSiteCharacteristicsDataWriterTest;
-  friend class LocalSiteCharacteristicsDataStoreTest;
-  friend class LocalSiteCharacteristicsDataStore;
-
-  // Private constructor, these objects are meant to be created by a site
-  // characteristics data store.
-  LocalSiteCharacteristicsDataWriter(
-      scoped_refptr<internal::LocalSiteCharacteristicsDataImpl> impl,
-      performance_manager::TabVisibility tab_visibility);
-
-  // The LocalSiteCharacteristicDataInternal object we delegate to.
-  const scoped_refptr<internal::LocalSiteCharacteristicsDataImpl> impl_;
-
-  // The visibility of the tab using this writer.
-  performance_manager::TabVisibility tab_visibility_;
-
-  // Indicates if the tab using this writer is loaded.
-  bool is_loaded_;
-
-  SEQUENCE_CHECKER(sequence_checker_);
-
-  DISALLOW_COPY_AND_ASSIGN(LocalSiteCharacteristicsDataWriter);
-};
-
-}  // namespace resource_coordinator
-
-#endif  // CHROME_BROWSER_RESOURCE_COORDINATOR_LOCAL_SITE_CHARACTERISTICS_DATA_WRITER_H_
diff --git a/chrome/browser/resource_coordinator/local_site_characteristics_data_writer_unittest.cc b/chrome/browser/resource_coordinator/local_site_characteristics_data_writer_unittest.cc
deleted file mode 100644
index aabae6e8..0000000
--- a/chrome/browser/resource_coordinator/local_site_characteristics_data_writer_unittest.cc
+++ /dev/null
@@ -1,202 +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 "chrome/browser/resource_coordinator/local_site_characteristics_data_writer.h"
-
-#include "base/macros.h"
-#include "base/memory/ptr_util.h"
-#include "base/time/time.h"
-#include "chrome/browser/resource_coordinator/local_site_characteristics_data_impl.h"
-#include "chrome/browser/resource_coordinator/local_site_characteristics_data_unittest_utils.h"
-#include "components/performance_manager/persistence/site_data/feature_usage.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "url/gurl.h"
-
-namespace resource_coordinator {
-
-class LocalSiteCharacteristicsDataWriterTest : public ::testing::Test {
- protected:
-  // The constructors needs to call 'new' directly rather than using the
-  // base::MakeRefCounted helper function because the constructor of
-  // LocalSiteCharacteristicsDataImpl is protected and not visible to
-  // base::MakeRefCounted.
-  LocalSiteCharacteristicsDataWriterTest()
-      : test_impl_(
-            base::WrapRefCounted(new internal::LocalSiteCharacteristicsDataImpl(
-                url::Origin::Create(GURL("foo.com")),
-                &delegate_,
-                &database_))) {
-    LocalSiteCharacteristicsDataWriter* writer =
-        new LocalSiteCharacteristicsDataWriter(
-            test_impl_.get(), performance_manager::TabVisibility::kBackground);
-    writer_ = base::WrapUnique(writer);
-  }
-
-  ~LocalSiteCharacteristicsDataWriterTest() override = default;
-
-  bool TabIsLoaded() { return test_impl_->IsLoaded(); }
-
-  bool TabIsLoadedAndInBackground() {
-    return test_impl_->loaded_tabs_in_background_count_for_testing() != 0U;
-  }
-
-  // The mock delegate used by the LocalSiteCharacteristicsDataImpl objects
-  // created by this class, NiceMock is used to avoid having to set
-  // expectations in test cases that don't care about this.
-  ::testing::NiceMock<
-      testing::MockLocalSiteCharacteristicsDataImplOnDestroyDelegate>
-      delegate_;
-
-  testing::NoopLocalSiteCharacteristicsDatabase database_;
-
-  // The LocalSiteCharacteristicsDataImpl object used in these tests.
-  scoped_refptr<internal::LocalSiteCharacteristicsDataImpl> test_impl_;
-
-  // A LocalSiteCharacteristicsDataWriter object associated with the origin used
-  // to create this object.
-  std::unique_ptr<LocalSiteCharacteristicsDataWriter> writer_;
-
-  DISALLOW_COPY_AND_ASSIGN(LocalSiteCharacteristicsDataWriterTest);
-};
-
-TEST_F(LocalSiteCharacteristicsDataWriterTest, TestModifiers) {
-  // Make sure that we initially have no information about any of the features
-  // and that the site is in an unloaded state.
-  EXPECT_EQ(performance_manager::SiteFeatureUsage::kSiteFeatureUsageUnknown,
-            test_impl_->UpdatesFaviconInBackground());
-  EXPECT_EQ(performance_manager::SiteFeatureUsage::kSiteFeatureUsageUnknown,
-            test_impl_->UpdatesTitleInBackground());
-  EXPECT_EQ(performance_manager::SiteFeatureUsage::kSiteFeatureUsageUnknown,
-            test_impl_->UsesAudioInBackground());
-
-  // Test the OnTabLoaded function.
-  EXPECT_FALSE(TabIsLoaded());
-  writer_->NotifySiteLoaded();
-  EXPECT_TRUE(TabIsLoaded());
-
-  // Test all the modifiers.
-
-  writer_->NotifyUpdatesFaviconInBackground();
-  EXPECT_EQ(performance_manager::SiteFeatureUsage::kSiteFeatureInUse,
-            test_impl_->UpdatesFaviconInBackground());
-  EXPECT_EQ(performance_manager::SiteFeatureUsage::kSiteFeatureUsageUnknown,
-            test_impl_->UpdatesTitleInBackground());
-  EXPECT_EQ(performance_manager::SiteFeatureUsage::kSiteFeatureUsageUnknown,
-            test_impl_->UsesAudioInBackground());
-
-  writer_->NotifyUpdatesTitleInBackground();
-  EXPECT_EQ(performance_manager::SiteFeatureUsage::kSiteFeatureInUse,
-            test_impl_->UpdatesFaviconInBackground());
-  EXPECT_EQ(performance_manager::SiteFeatureUsage::kSiteFeatureInUse,
-            test_impl_->UpdatesTitleInBackground());
-  EXPECT_EQ(performance_manager::SiteFeatureUsage::kSiteFeatureUsageUnknown,
-            test_impl_->UsesAudioInBackground());
-
-  writer_->NotifyUsesAudioInBackground();
-  EXPECT_EQ(performance_manager::SiteFeatureUsage::kSiteFeatureInUse,
-            test_impl_->UpdatesFaviconInBackground());
-  EXPECT_EQ(performance_manager::SiteFeatureUsage::kSiteFeatureInUse,
-            test_impl_->UpdatesTitleInBackground());
-  EXPECT_EQ(performance_manager::SiteFeatureUsage::kSiteFeatureInUse,
-            test_impl_->UsesAudioInBackground());
-
-  writer_->NotifyLoadTimePerformanceMeasurement(
-      base::TimeDelta::FromMicroseconds(202),
-      base::TimeDelta::FromMicroseconds(101), 1005);
-  EXPECT_EQ(1u, test_impl_->load_duration().num_datums());
-  EXPECT_EQ(202.0, test_impl_->load_duration().value());
-  EXPECT_EQ(1u, test_impl_->cpu_usage_estimate().num_datums());
-  EXPECT_EQ(101.0, test_impl_->cpu_usage_estimate().value());
-
-  EXPECT_EQ(1u, test_impl_->private_footprint_kb_estimate().num_datums());
-  EXPECT_EQ(1005.0, test_impl_->private_footprint_kb_estimate().value());
-
-  writer_->NotifySiteUnloaded();
-}
-
-TEST_F(LocalSiteCharacteristicsDataWriterTest,
-       LoadAndBackgroundStateTransitions) {
-  // There's 4 different states a tab can be in:
-  //   - Unloaded + Background
-  //   - Unloaded + Foreground (might not be possible in practice but this
-  //     will depend on the order of the events when an unloaded background tab
-  //     gets foregrounded, so it's safer to consider this state).
-  //   - Loaded + Background
-  //   - Loaded + Foreground
-  //
-  // Only one of these parameter can change at a time, so you have the following
-  // possible transitions:
-  //
-  //   +-------------+           +-------------+
-  //   |Unloaded + Bg|<--------->|Unloaded + Fg|
-  //   +-------------+ 1       2 +-------------+
-  //         /|\ 3                     /|\ 5
-  //          |                         |
-  //         \|/ 4                     \|/ 6
-  //   +-------------+           +-------------+
-  //   | Loaded + Bg |<--------->| Loaded + Fg |
-  //   +-------------+ 7       8 +-------------+
-  //
-  //   - 1,2: There's nothing to do, the tab is already unloaded so |impl_|
-  //       shouldn't count it as a background tab anyway.
-  //   - 3: The tab gets unloaded while in background, |impl_| should be
-  //       notified so it can *decrement* the counter of loaded AND backgrounded
-  //       tabs.
-  //   - 4: The tab gets loaded while in background, |impl_| should be notified
-  //       so it can *increment* the counter of loaded AND backgrounded tabs.
-  //   - 5: The tab gets unloaded while in foreground, this should theorically
-  //       not happen, but if it does then |impl_| should just be notified about
-  //       the unload event so it can update its last loaded timestamp.
-  //   - 6: The tab gets loaded while in foreground, |impl_| should only be
-  //       notified about the load event, the background state hasn't changed.
-  //   - 7: A loaded foreground tab gets backgrounded, |impl_| should be
-  //       notified that the tab has been background so it can *increment* the
-  //       counter of loaded AND backgrounded tabs.
-  //   - 8: A loaded background tab gets foregrounded, |impl_| should be
-  //       notified that the tab has been background so it can *decrement* the
-  //       counter of loaded AND backgrounded tabs.
-  EXPECT_FALSE(TabIsLoaded());
-
-  // Transition #4: Unloaded + Bg -> Loaded + Bg.
-  writer_->NotifySiteLoaded();
-  EXPECT_TRUE(TabIsLoadedAndInBackground());
-
-  // Transition #8: Loaded + Bg -> Loaded + Fg.
-  writer_->NotifySiteVisibilityChanged(
-      performance_manager::TabVisibility::kForeground);
-  EXPECT_TRUE(TabIsLoaded());
-  EXPECT_FALSE(TabIsLoadedAndInBackground());
-
-  // Transition #5: Loaded + Fg -> Unloaded + Fg.
-  writer_->NotifySiteUnloaded();
-  EXPECT_FALSE(TabIsLoaded());
-
-  // Transition #1: Unloaded + Fg -> Unloaded + Bg.
-  writer_->NotifySiteVisibilityChanged(
-      performance_manager::TabVisibility::kBackground);
-  EXPECT_FALSE(TabIsLoaded());
-
-  // Transition #2: Unloaded + Bg -> Unloaded + Fg.
-  writer_->NotifySiteVisibilityChanged(
-      performance_manager::TabVisibility::kForeground);
-  EXPECT_FALSE(TabIsLoaded());
-
-  // Transition #6: Unloaded + Fg -> Loaded + Fg.
-  writer_->NotifySiteLoaded();
-  EXPECT_TRUE(TabIsLoaded());
-  EXPECT_FALSE(TabIsLoadedAndInBackground());
-
-  // Transition #7: Loaded + Fg -> Loaded + Bg.
-  writer_->NotifySiteVisibilityChanged(
-      performance_manager::TabVisibility::kBackground);
-  EXPECT_TRUE(TabIsLoaded());
-  EXPECT_TRUE(TabIsLoadedAndInBackground());
-
-  // Transition #3: Loaded + Bg -> Unloaded + Bg.
-  writer_->NotifySiteUnloaded();
-  EXPECT_FALSE(TabIsLoaded());
-  EXPECT_FALSE(TabIsLoadedAndInBackground());
-}
-
-}  // namespace resource_coordinator
diff --git a/chrome/browser/resource_coordinator/local_site_characteristics_database.h b/chrome/browser/resource_coordinator/local_site_characteristics_database.h
deleted file mode 100644
index 2d8cd25..0000000
--- a/chrome/browser/resource_coordinator/local_site_characteristics_database.h
+++ /dev/null
@@ -1,59 +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 CHROME_BROWSER_RESOURCE_COORDINATOR_LOCAL_SITE_CHARACTERISTICS_DATABASE_H_
-#define CHROME_BROWSER_RESOURCE_COORDINATOR_LOCAL_SITE_CHARACTERISTICS_DATABASE_H_
-
-#include <vector>
-
-#include "base/callback.h"
-#include "base/macros.h"
-#include "base/optional.h"
-#include "components/performance_manager/persistence/site_data/site_data.pb.h"
-#include "url/origin.h"
-
-namespace resource_coordinator {
-
-// Interface for a local site characteristic database.
-class LocalSiteCharacteristicsDatabase {
- public:
-  // Callback to call once the initialization from the database has completed,
-  // |site_characteristic_proto| should be equal to base::nullopt if the
-  // initialization has failed.
-  using ReadSiteCharacteristicsFromDBCallback = base::OnceCallback<void(
-      base::Optional<SiteDataProto> site_characteristic_proto)>;
-  using GetDatabaseSizeCallback =
-      base::OnceCallback<void(base::Optional<int64_t> num_rows,
-                              base::Optional<int64_t> on_disk_size_kb)>;
-
-  LocalSiteCharacteristicsDatabase() = default;
-  virtual ~LocalSiteCharacteristicsDatabase() {}
-
-  // Checks the if there's an entry with the key |site_origin| and if so use it
-  // to initialize |site_characteristic_proto|. Calls |callback| to indicate
-  // whether or not the initialization has been successful.
-  virtual void ReadSiteCharacteristicsFromDB(
-      const url::Origin& origin,
-      ReadSiteCharacteristicsFromDBCallback callback) = 0;
-
-  // Store an entry in the database, create it if it doesn't exist and update it
-  // if it does.
-  virtual void WriteSiteCharacteristicsIntoDB(
-      const url::Origin& origin,
-      const SiteDataProto& site_characteristic_proto) = 0;
-
-  // Removes some entries from the database.
-  virtual void RemoveSiteCharacteristicsFromDB(
-      const std::vector<url::Origin>& site_origins) = 0;
-
-  // Clear the database, removes every entries that it contains.
-  virtual void ClearDatabase() = 0;
-
-  // Retrieve the size of the database.
-  virtual void GetDatabaseSize(GetDatabaseSizeCallback callback) = 0;
-};
-
-}  // namespace resource_coordinator
-
-#endif  // CHROME_BROWSER_RESOURCE_COORDINATOR_LOCAL_SITE_CHARACTERISTICS_DATABASE_H_
diff --git a/chrome/browser/resource_coordinator/local_site_characteristics_database_browsertest.cc b/chrome/browser/resource_coordinator/local_site_characteristics_database_browsertest.cc
deleted file mode 100644
index 1907fca3c..0000000
--- a/chrome/browser/resource_coordinator/local_site_characteristics_database_browsertest.cc
+++ /dev/null
@@ -1,630 +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 <algorithm>
-
-#include "base/bind.h"
-#include "base/bind_helpers.h"
-#include "base/callback.h"
-#include "base/path_service.h"
-#include "base/run_loop.h"
-#include "base/test/simple_test_tick_clock.h"
-#include "build/build_config.h"
-#include "chrome/browser/chrome_notification_types.h"
-#include "chrome/browser/content_settings/host_content_settings_map_factory.h"
-#include "chrome/browser/history/history_service_factory.h"
-#include "chrome/browser/profiles/profile_manager.h"
-#include "chrome/browser/resource_coordinator/local_site_characteristics_data_impl.h"
-#include "chrome/browser/resource_coordinator/local_site_characteristics_data_reader.h"
-#include "chrome/browser/resource_coordinator/local_site_characteristics_data_store_factory.h"
-#include "chrome/browser/resource_coordinator/site_characteristics_data_reader.h"
-#include "chrome/browser/resource_coordinator/tab_load_tracker.h"
-#include "chrome/browser/resource_coordinator/tab_load_tracker_test_support.h"
-#include "chrome/browser/resource_coordinator/tab_manager_features.h"
-#include "chrome/browser/resource_coordinator/time.h"
-#include "chrome/browser/ui/browser.h"
-#include "chrome/test/base/in_process_browser_test.h"
-#include "chrome/test/base/ui_test_utils.h"
-#include "components/content_settings/core/browser/host_content_settings_map.h"
-#include "components/network_session_configurator/common/network_switches.h"
-#include "content/public/test/browser_test.h"
-#include "content/public/test/browser_test_utils.h"
-#include "content/public/test/test_utils.h"
-#include "content/public/test/web_contents_tester.h"
-#include "media/base/media_switches.h"
-#include "net/dns/mock_host_resolver.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "url/origin.h"
-
-#if defined(OS_CHROMEOS)
-#include "chromeos/constants/chromeos_switches.h"
-#endif
-
-namespace resource_coordinator {
-
-namespace {
-
-using WebContents = content::WebContents;
-using WebContentsTester = content::WebContentsTester;
-
-constexpr char kTestPage[] =
-    "/resource_coordinator/site_characteristics_test_page.html";
-
-constexpr base::TimeDelta kObservationWindowLength =
-    base::TimeDelta::FromHours(2);
-constexpr base::TimeDelta kLongestGracePeriod =
-    base::TimeDelta::FromSeconds(20);
-
-// Returns the LocalSiteCharacteristicsDataImpl that backs |reader|.
-internal::LocalSiteCharacteristicsDataImpl* GetImplFromReader(
-    SiteCharacteristicsDataReader* reader) {
-  return static_cast<LocalSiteCharacteristicsDataReader*>(reader)
-      ->impl_for_testing();
-}
-
-// Returns the SiteDataProto that backs |reader|.
-const SiteDataProto* GetSiteDataProtoFromReader(
-    SiteCharacteristicsDataReader* reader) {
-  return &GetImplFromReader(reader)->site_characteristics_for_testing();
-}
-
-}  // namespace
-
-class LocalSiteCharacteristicsDatabaseTest : public InProcessBrowserTest {
- public:
-  LocalSiteCharacteristicsDatabaseTest()
-      : scoped_set_tick_clock_for_testing_(&test_clock_),
-        test_server_(net::test_server::EmbeddedTestServer::TYPE_HTTPS) {}
-  ~LocalSiteCharacteristicsDatabaseTest() override = default;
-
-  void SetUp() override {
-    test_clock_.Advance(base::TimeDelta::FromSeconds(1));
-    InProcessBrowserTest::SetUp();
-  }
-
-  void SetUpOnMainThread() override {
-    InProcessBrowserTest::SetUpOnMainThread();
-
-    // Setup the test server.
-    base::FilePath test_data_dir;
-    ASSERT_TRUE(base::PathService::Get(base::DIR_SOURCE_ROOT, &test_data_dir));
-    test_server_.SetSSLConfig(net::EmbeddedTestServer::CERT_OK);
-    test_server_.ServeFilesFromDirectory(
-        test_data_dir.AppendASCII("chrome/test/data/"));
-    ASSERT_TRUE(test_server_.InitializeAndListen());
-    test_server_.StartAcceptingConnections();
-    const std::string real_host = test_server_.host_port_pair().host();
-    host_resolver()->AddRule("*", real_host);
-  }
-
-  void SetUpCommandLine(base::CommandLine* command_line) override {
-    command_line->AppendSwitchASCII(
-        switches::kAutoplayPolicy,
-        switches::autoplay::kNoUserGestureRequiredPolicy);
-
-    // HTTPS server only serves a valid cert for localhost, so this is needed
-    // to load pages from other origins without an interstitial.
-    command_line->AppendSwitch(switches::kIgnoreCertificateErrors);
-
-#if defined(OS_CHROMEOS)
-    command_line->AppendSwitch(
-        chromeos::switches::kIgnoreUserProfileMappingForTests);
-#endif
-    InProcessBrowserTest::SetUpCommandLine(command_line);
-  }
-
-  WebContents* GetActiveWebContents() {
-    return browser()->tab_strip_model()->GetActiveWebContents();
-  }
-
-  std::unique_ptr<SiteCharacteristicsDataReader> GetReaderForOrigin(
-      Profile* profile,
-      const url::Origin& origin) {
-    SiteCharacteristicsDataStore* data_store =
-        LocalSiteCharacteristicsDataStoreFactory::GetForProfile(profile);
-    EXPECT_TRUE(data_store);
-    std::unique_ptr<SiteCharacteristicsDataReader> reader =
-        data_store->GetReaderForOrigin(origin);
-    base::RunLoop run_loop;
-    reader->RegisterDataLoadedCallback(run_loop.QuitClosure());
-    run_loop.Run();
-
-    const internal::LocalSiteCharacteristicsDataImpl* impl =
-        GetImplFromReader(reader.get());
-    EXPECT_TRUE(impl->fully_initialized_for_testing());
-    return reader;
-  }
-
-  // Test that feature usage is tracked correctly:
-  //   - kSiteFeatureUsageUnknown if never observed and observation window
-  //     hasn't expired.
-  //   - kSiteFeatureNotInUse if never observed and observation window has
-  //     expired.
-  //   - kSiteFeatureInUse if observed.
-  // |feature_detection_method| is the SiteCharacteristicsDataReader method that
-  // will be called to query the status of this feature. |triggering_closure| is
-  // the closure to run to cause this feature to be used (this will get called
-  // while the tab is in background) and |allowing_closure| is an optional
-  // closure that should run before testing the feature usage (to allow it to
-  // be used).
-  void TestFeatureUsageDetection(
-      performance_manager::SiteFeatureUsage (
-          SiteCharacteristicsDataReader::*feature_detection_method)() const,
-      internal::LocalSiteCharacteristicsDataImpl::TrackedBackgroundFeatures
-          feature_type,
-      base::RepeatingClosure triggering_closure,
-      base::RepeatingClosure allowing_closure = base::DoNothing::Repeatedly()) {
-    // Test that feature usage is tracked correctly before the expiration of its
-    // observation window.
-    TestFeatureUsageDetectionImpl(feature_detection_method, feature_type,
-                                  allowing_closure, triggering_closure, false);
-    // Test that feature usage is tracked correctly after the expiration of its
-    // observation window.
-    TestFeatureUsageDetectionImpl(feature_detection_method, feature_type,
-                                  std::move(allowing_closure),
-                                  std::move(triggering_closure), true);
-  }
-
-  void ExecuteScriptInMainFrame(const char* script) {
-    content::RenderFrameHost* main_frame =
-        GetActiveWebContents()->GetMainFrame();
-    EXPECT_TRUE(content::ExecuteScript(main_frame, script));
-  }
-
-  void PlayAudioInActiveWebContents() {
-    ExecuteScriptInMainFrame("PlayAudio();");
-  }
-
-  void ChangeTitleOfActiveWebContents() {
-    ExecuteScriptInMainFrame("ChangeTitle('new_title')");
-  }
-
-  void ChangeFaviconOfActiveWebContents() {
-    ExecuteScriptInMainFrame("ChangeFavicon()");
-  }
-
-  // By default a tab has to play audio while being visible if it wants to be
-  // able to play audio in background (see
-  // ChromeContentRendererClient::DeferMediaLoad). This makes the current active
-  // WebContents visible, play some audio and background it. After calling
-  // this the background tab is allowed play audio.
-  void AllowBackgroundAudioInActiveTab() {
-    content::WebContents* active_webcontents = GetActiveWebContents();
-    active_webcontents->WasShown();
-    PlayAudioInActiveWebContents();
-    // Wait for the audio to start playing.
-    while (!active_webcontents->WasEverAudible())
-      base::RunLoop().RunUntilIdle();
-
-    active_webcontents->GetController().Reload(content::ReloadType::NORMAL,
-                                               false);
-    EXPECT_TRUE(content::WaitForLoadStop(GetActiveWebContents()));
-    // Background the tab and reload it so the audio will stop playing if it's
-    // still playing.
-    GetActiveWebContents()->WasHidden();
-    test_clock_.Advance(kLongestGracePeriod);
-  }
-
-  void ExpireTitleOrFaviconGracePeriod() {
-    test_clock_.Advance(kLongestGracePeriod);
-  }
-
-  base::SimpleTestTickClock& test_clock() { return test_clock_; }
-  net::test_server::EmbeddedTestServer& test_server() { return test_server_; }
-
- private:
-  void TestFeatureUsageDetectionImpl(
-      performance_manager::SiteFeatureUsage (
-          SiteCharacteristicsDataReader::*feature_detection_method)() const,
-      const internal::LocalSiteCharacteristicsDataImpl::
-          TrackedBackgroundFeatures feature_type,
-      base::OnceClosure allowing_closure,
-      base::RepeatingClosure triggering_closure,
-      bool wait_for_observation_window_to_expire);
-
-  base::SimpleTestTickClock test_clock_;
-  ScopedSetTickClockForTesting scoped_set_tick_clock_for_testing_;
-  base::test::ScopedFeatureList scoped_feature_list_;
-  net::test_server::EmbeddedTestServer test_server_;
-
-  DISALLOW_COPY_AND_ASSIGN(LocalSiteCharacteristicsDatabaseTest);
-};
-
-void LocalSiteCharacteristicsDatabaseTest::TestFeatureUsageDetectionImpl(
-    performance_manager::SiteFeatureUsage (
-        SiteCharacteristicsDataReader::*feature_detection_method)() const,
-    const internal::LocalSiteCharacteristicsDataImpl::TrackedBackgroundFeatures
-        feature_type,
-    base::OnceClosure allowing_closure,
-    base::RepeatingClosure triggering_closure,
-    bool wait_for_observation_window_to_expire) {
-  // Use a different origin depending on the type of test to make sure that
-  // previous observations don't get re-used.
-  const char* kOrigin =
-      wait_for_observation_window_to_expire ? "foo.com" : "bar.com";
-  GURL test_url(test_server_.GetURL(kOrigin, kTestPage));
-
-  // Get the reader for this origin.
-  auto reader =
-      GetReaderForOrigin(browser()->profile(), url::Origin::Create(test_url));
-  EXPECT_EQ(performance_manager::SiteFeatureUsage::kSiteFeatureUsageUnknown,
-            (reader.get()->*feature_detection_method)());
-
-  // Navigate to the test url and background it.
-  ui_test_utils::NavigateToURLWithDisposition(
-      browser(), test_url, WindowOpenDisposition::CURRENT_TAB,
-      ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP);
-  GetActiveWebContents()->WasHidden();
-  WaitForTransitionToLoaded(GetActiveWebContents());
-
-  // If needed, wait for all feature observation windows to expire.
-  if (wait_for_observation_window_to_expire) {
-    test_clock_.Advance(kObservationWindowLength);
-    EXPECT_EQ(performance_manager::SiteFeatureUsage::kSiteFeatureNotInUse,
-              (reader.get()->*feature_detection_method)());
-  }
-
-  // Call the allowing closure.
-  std::move(allowing_closure).Run();
-
-  // Ensure that the closure hasn't caused the feature usage status to
-  // change.
-  if (wait_for_observation_window_to_expire) {
-    EXPECT_EQ(performance_manager::SiteFeatureUsage::kSiteFeatureNotInUse,
-              (reader.get()->*feature_detection_method)());
-  } else {
-    EXPECT_EQ(performance_manager::SiteFeatureUsage::kSiteFeatureUsageUnknown,
-              (reader.get()->*feature_detection_method)());
-  }
-
-  base::RunLoop run_loop;
-  GetImplFromReader(reader.get())
-      ->RegisterFeatureUsageCallbackForTesting(feature_type,
-                                               run_loop.QuitClosure());
-
-  // Cause the feature to be used.
-  triggering_closure.Run();
-
-  run_loop.Run();
-
-  EXPECT_EQ(performance_manager::SiteFeatureUsage::kSiteFeatureInUse,
-            (reader.get()->*feature_detection_method)());
-
-  // Advance the clock, make sure that the feature usage status doesn't
-  // change.
-  test_clock_.Advance(kObservationWindowLength);
-
-  EXPECT_EQ(performance_manager::SiteFeatureUsage::kSiteFeatureInUse,
-            (reader.get()->*feature_detection_method)());
-}
-
-// Test that doesn't use any feature.
-IN_PROC_BROWSER_TEST_F(LocalSiteCharacteristicsDatabaseTest, NoFeatureUsed) {
-  GURL test_url(test_server().GetURL("foo.com", kTestPage));
-
-  ui_test_utils::NavigateToURLWithDisposition(
-      browser(), test_url, WindowOpenDisposition::NEW_BACKGROUND_TAB,
-      ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP);
-
-  content::WebContents* contents =
-      browser()->tab_strip_model()->GetWebContentsAt(1);
-  EXPECT_EQ(test_url, contents->GetLastCommittedURL());
-  WaitForTransitionToLoaded(contents);
-
-  auto reader =
-      GetReaderForOrigin(browser()->profile(), url::Origin::Create(test_url));
-  EXPECT_EQ(performance_manager::SiteFeatureUsage::kSiteFeatureUsageUnknown,
-            reader->UpdatesFaviconInBackground());
-  EXPECT_EQ(performance_manager::SiteFeatureUsage::kSiteFeatureUsageUnknown,
-            reader->UpdatesTitleInBackground());
-  EXPECT_EQ(performance_manager::SiteFeatureUsage::kSiteFeatureUsageUnknown,
-            reader->UsesAudioInBackground());
-
-  test_clock().Advance(kObservationWindowLength);
-
-  EXPECT_EQ(performance_manager::SiteFeatureUsage::kSiteFeatureNotInUse,
-            reader->UpdatesFaviconInBackground());
-  EXPECT_EQ(performance_manager::SiteFeatureUsage::kSiteFeatureNotInUse,
-            reader->UpdatesTitleInBackground());
-  EXPECT_EQ(performance_manager::SiteFeatureUsage::kSiteFeatureNotInUse,
-            reader->UsesAudioInBackground());
-}
-
-// Test that use features while in foreground, this shouldn't be recorded.
-IN_PROC_BROWSER_TEST_F(LocalSiteCharacteristicsDatabaseTest,
-                       FeatureUsedInForegroundOnly) {
-  GURL test_url(test_server().GetURL("foo.com", kTestPage));
-
-  ui_test_utils::NavigateToURLWithDisposition(
-      browser(), test_url, WindowOpenDisposition::CURRENT_TAB,
-      ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP);
-
-  GetActiveWebContents()->WasShown();
-
-  ChangeTitleOfActiveWebContents();
-  ChangeFaviconOfActiveWebContents();
-  PlayAudioInActiveWebContents();
-
-  auto reader =
-      GetReaderForOrigin(browser()->profile(), url::Origin::Create(test_url));
-  EXPECT_EQ(performance_manager::SiteFeatureUsage::kSiteFeatureUsageUnknown,
-            reader->UpdatesFaviconInBackground());
-  EXPECT_EQ(performance_manager::SiteFeatureUsage::kSiteFeatureUsageUnknown,
-            reader->UpdatesTitleInBackground());
-
-  // Advance the clock while the tab is still in foreground and make sure that
-  // the state hasn't changed.
-  test_clock().Advance(kObservationWindowLength);
-  EXPECT_EQ(performance_manager::SiteFeatureUsage::kSiteFeatureUsageUnknown,
-            reader->UpdatesFaviconInBackground());
-  EXPECT_EQ(performance_manager::SiteFeatureUsage::kSiteFeatureUsageUnknown,
-            reader->UpdatesTitleInBackground());
-  EXPECT_EQ(performance_manager::SiteFeatureUsage::kSiteFeatureUsageUnknown,
-            reader->UsesAudioInBackground());
-}
-
-// Test that the audio feature usage in background gets detected properly.
-IN_PROC_BROWSER_TEST_F(LocalSiteCharacteristicsDatabaseTest,
-                       AudioFeatureUsage) {
-  TestFeatureUsageDetection(
-      &SiteCharacteristicsDataReader::UsesAudioInBackground,
-      internal::LocalSiteCharacteristicsDataImpl::TrackedBackgroundFeatures::
-          kAudioUsage,
-      base::BindRepeating(
-          &LocalSiteCharacteristicsDatabaseTest::PlayAudioInActiveWebContents,
-          base::Unretained(this)),
-      base::BindRepeating(&LocalSiteCharacteristicsDatabaseTest::
-                              AllowBackgroundAudioInActiveTab,
-                          base::Unretained(this)));
-}
-
-IN_PROC_BROWSER_TEST_F(LocalSiteCharacteristicsDatabaseTest,
-                       TitleUpdateFeatureUsage) {
-  TestFeatureUsageDetection(
-      &SiteCharacteristicsDataReader::UpdatesTitleInBackground,
-      internal::LocalSiteCharacteristicsDataImpl::TrackedBackgroundFeatures::
-          kTitleUpdate,
-      base::BindRepeating(
-          &LocalSiteCharacteristicsDatabaseTest::ChangeTitleOfActiveWebContents,
-          base::Unretained(this)),
-      base::BindRepeating(&LocalSiteCharacteristicsDatabaseTest::
-                              ExpireTitleOrFaviconGracePeriod,
-                          base::Unretained(this)));
-}
-
-// Test that the favicon update feature usage in background gets detected
-// properly.
-// TODO(crbug.com/1004641): Investigate and reenable.
-#if defined(OS_WIN)
-#define MAYBE_FaviconUpdateFeatureUsage DISABLED_FaviconUpdateFeatureUsage
-#else
-#define MAYBE_FaviconUpdateFeatureUsage FaviconUpdateFeatureUsage
-#endif
-IN_PROC_BROWSER_TEST_F(LocalSiteCharacteristicsDatabaseTest,
-                       MAYBE_FaviconUpdateFeatureUsage) {
-  TestFeatureUsageDetection(
-      &SiteCharacteristicsDataReader::UpdatesFaviconInBackground,
-      internal::LocalSiteCharacteristicsDataImpl::TrackedBackgroundFeatures::
-          kFaviconUpdate,
-      base::BindRepeating(&LocalSiteCharacteristicsDatabaseTest::
-                              ChangeFaviconOfActiveWebContents,
-                          base::Unretained(this)),
-      base::BindRepeating(&LocalSiteCharacteristicsDatabaseTest::
-                              ExpireTitleOrFaviconGracePeriod,
-                          base::Unretained(this)));
-}
-
-// Test that loads the same origin into multiple tabs and ensure that they get
-// tracked properly.
-IN_PROC_BROWSER_TEST_F(LocalSiteCharacteristicsDatabaseTest,
-                       LoadedStateGetsTrackedProperly) {
-  GURL test_url(test_server().GetURL("foo.com", kTestPage));
-  auto test_reader =
-      GetReaderForOrigin(browser()->profile(), url::Origin::Create(test_url));
-
-  const size_t kTabCount = 3;
-
-  // Load all the tabs and background them.
-  for (size_t i = 0; i < kTabCount; ++i) {
-    ui_test_utils::NavigateToURLWithDisposition(
-        browser(), test_url,
-        i == 0 ? WindowOpenDisposition::CURRENT_TAB
-               : WindowOpenDisposition::NEW_FOREGROUND_TAB,
-        ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP);
-    content::WebContents* contents =
-        browser()->tab_strip_model()->GetWebContentsAt(i);
-    WaitForTransitionToLoaded(contents);
-    EXPECT_EQ(TabLoadTracker::LoadingState::LOADED,
-              TabLoadTracker::Get()->GetLoadingState(contents));
-    contents->WasHidden();
-  }
-
-  const internal::LocalSiteCharacteristicsDataImpl* impl =
-      GetImplFromReader(test_reader.get());
-  EXPECT_TRUE(impl);
-  EXPECT_EQ(3U, impl->loaded_tabs_count_for_testing());
-  EXPECT_EQ(3U, impl->loaded_tabs_in_background_count_for_testing());
-
-  // Change the visibility of the tabs.
-  for (size_t i = 0; i < kTabCount; ++i) {
-    browser()->tab_strip_model()->GetWebContentsAt(i)->WasShown();
-
-    EXPECT_EQ(kTabCount, impl->loaded_tabs_count_for_testing());
-    EXPECT_EQ(kTabCount - (i + 1),
-              impl->loaded_tabs_in_background_count_for_testing());
-  }
-
-  for (size_t i = 0; i < kTabCount; ++i)
-    browser()->tab_strip_model()->GetWebContentsAt(i)->WasHidden();
-
-  EXPECT_EQ(3U, impl->loaded_tabs_in_background_count_for_testing());
-
-  // Close the tabs.
-  for (size_t i = 0; i < kTabCount; ++i) {
-    EXPECT_TRUE(browser()->tab_strip_model()->CloseWebContentsAt(0, 0));
-    EXPECT_EQ(kTabCount - (i + 1), impl->loaded_tabs_count_for_testing());
-    EXPECT_EQ(kTabCount - (i + 1),
-              impl->loaded_tabs_in_background_count_for_testing());
-  }
-}
-
-// Ensure that the observations gets persisted on disk.
-IN_PROC_BROWSER_TEST_F(LocalSiteCharacteristicsDatabaseTest,
-                       PRE_DatabaseGetsPersisted) {
-  GURL test_url(test_server().GetURL("foo.com", kTestPage));
-
-  // Get the reader for this origin.
-  auto reader =
-      GetReaderForOrigin(browser()->profile(), url::Origin::Create(test_url));
-  EXPECT_EQ(performance_manager::SiteFeatureUsage::kSiteFeatureUsageUnknown,
-            reader->UpdatesTitleInBackground());
-
-  // Navigate to the test url and background it.
-  ui_test_utils::NavigateToURLWithDisposition(
-      browser(), test_url, WindowOpenDisposition::CURRENT_TAB,
-      ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP);
-  WaitForTransitionToLoaded(GetActiveWebContents());
-  GetActiveWebContents()->WasHidden();
-
-  test_clock().Advance(kLongestGracePeriod);
-
-  base::RunLoop run_loop;
-  GetImplFromReader(reader.get())
-      ->RegisterFeatureUsageCallbackForTesting(
-          internal::LocalSiteCharacteristicsDataImpl::
-              TrackedBackgroundFeatures::kTitleUpdate,
-          run_loop.QuitClosure());
-
-  // Cause the "title update in background" feature to be used.
-  ChangeTitleOfActiveWebContents();
-
-  run_loop.Run();
-  EXPECT_EQ(performance_manager::SiteFeatureUsage::kSiteFeatureInUse,
-            reader->UpdatesTitleInBackground());
-}
-
-IN_PROC_BROWSER_TEST_F(LocalSiteCharacteristicsDatabaseTest,
-                       DatabaseGetsPersisted) {
-  GURL test_url(test_server().GetURL("foo.com", kTestPage));
-
-  // Get the reader for this origin.
-  auto reader =
-      GetReaderForOrigin(browser()->profile(), url::Origin::Create(test_url));
-
-  // We should remember the observation made previously.
-  EXPECT_EQ(performance_manager::SiteFeatureUsage::kSiteFeatureInUse,
-            reader->UpdatesTitleInBackground());
-}
-
-// Ensure that clearing the history removes the observations from disk.
-IN_PROC_BROWSER_TEST_F(LocalSiteCharacteristicsDatabaseTest, PRE_ClearHistory) {
-  GURL test_url(test_server().GetURL("foo.com", kTestPage));
-
-  // Get the reader for this origin.
-  auto reader =
-      GetReaderForOrigin(browser()->profile(), url::Origin::Create(test_url));
-  EXPECT_EQ(performance_manager::SiteFeatureUsage::kSiteFeatureUsageUnknown,
-            reader->UpdatesTitleInBackground());
-
-  // Navigate to the test url and background it.
-  ui_test_utils::NavigateToURLWithDisposition(
-      browser(), test_url, WindowOpenDisposition::CURRENT_TAB,
-      ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP);
-  WaitForTransitionToLoaded(GetActiveWebContents());
-  GetActiveWebContents()->WasHidden();
-
-  test_clock().Advance(kLongestGracePeriod);
-
-  base::RunLoop run_loop;
-  GetImplFromReader(reader.get())
-      ->RegisterFeatureUsageCallbackForTesting(
-          internal::LocalSiteCharacteristicsDataImpl::
-              TrackedBackgroundFeatures::kTitleUpdate,
-          run_loop.QuitClosure());
-
-  // Cause the "title update in background" feature to be used.
-  ChangeTitleOfActiveWebContents();
-
-  run_loop.Run();
-  EXPECT_EQ(performance_manager::SiteFeatureUsage::kSiteFeatureInUse,
-            reader->UpdatesTitleInBackground());
-
-  HistoryServiceFactory::GetForProfile(browser()->profile(),
-                                       ServiceAccessType::IMPLICIT_ACCESS)
-      ->DeleteURLs({test_url});
-  // The history gets cleared asynchronously.
-  while (reader->UpdatesTitleInBackground() !=
-         performance_manager::SiteFeatureUsage::kSiteFeatureUsageUnknown) {
-    base::RunLoop().RunUntilIdle();
-  }
-}
-
-IN_PROC_BROWSER_TEST_F(LocalSiteCharacteristicsDatabaseTest, ClearHistory) {
-  GURL test_url(test_server().GetURL("foo.com", kTestPage));
-
-  // Get the reader for this origin.
-  auto reader =
-      GetReaderForOrigin(browser()->profile(), url::Origin::Create(test_url));
-
-  // The history has been cleared, we shouldn't know if this feature is used.
-  EXPECT_EQ(performance_manager::SiteFeatureUsage::kSiteFeatureUsageUnknown,
-            reader->UpdatesTitleInBackground());
-}
-
-// Ensure that the observation time gets tracked across sessions.
-IN_PROC_BROWSER_TEST_F(LocalSiteCharacteristicsDatabaseTest,
-                       PRE_DatabaseObservationTimeTrackedAcrossSessions) {
-  GURL test_url(test_server().GetURL("foo.com", kTestPage));
-
-  auto reader =
-      GetReaderForOrigin(browser()->profile(), url::Origin::Create(test_url));
-  const SiteDataProto* site_characteristics =
-      GetSiteDataProtoFromReader(reader.get());
-
-  EXPECT_EQ(0, site_characteristics->updates_title_in_background()
-                   .observation_duration());
-
-  // Navigate to the test url and background it.
-  ui_test_utils::NavigateToURLWithDisposition(
-      browser(), test_url, WindowOpenDisposition::CURRENT_TAB,
-      ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP);
-  WaitForTransitionToLoaded(GetActiveWebContents());
-  GetActiveWebContents()->WasHidden();
-
-  test_clock().Advance(base::TimeDelta::FromSeconds(10));
-  EXPECT_GE(10, site_characteristics->updates_title_in_background()
-                    .observation_duration());
-}
-
-IN_PROC_BROWSER_TEST_F(LocalSiteCharacteristicsDatabaseTest,
-                       DatabaseObservationTimeTrackedAcrossSessions) {
-  GURL test_url(test_server().GetURL("foo.com", kTestPage));
-
-  auto reader =
-      GetReaderForOrigin(browser()->profile(), url::Origin::Create(test_url));
-  const SiteDataProto* site_characteristics =
-      GetSiteDataProtoFromReader(reader.get());
-
-  auto observation_duration =
-      site_characteristics->updates_title_in_background()
-          .observation_duration();
-  // The observation duration shouldn't be null as this has been augmented in
-  // the 'PRE' part of this test.
-  EXPECT_NE(0, observation_duration);
-
-  // Advance the clock and reset this reader. This should increase the
-  // observation duration that we have for this origin in the database.
-  test_clock().Advance(base::TimeDelta::FromSeconds(10));
-  reader.reset();
-
-  reader =
-      GetReaderForOrigin(browser()->profile(), url::Origin::Create(test_url));
-  site_characteristics = GetSiteDataProtoFromReader(reader.get());
-
-  EXPECT_GE(observation_duration,
-            site_characteristics->updates_title_in_background()
-                .observation_duration());
-}
-
-}  // namespace resource_coordinator
diff --git a/chrome/browser/resource_coordinator/local_site_characteristics_non_recording_data_store.cc b/chrome/browser/resource_coordinator/local_site_characteristics_non_recording_data_store.cc
deleted file mode 100644
index ef7cada7..0000000
--- a/chrome/browser/resource_coordinator/local_site_characteristics_non_recording_data_store.cc
+++ /dev/null
@@ -1,88 +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 "chrome/browser/resource_coordinator/local_site_characteristics_non_recording_data_store.h"
-
-#include "base/check.h"
-#include "base/memory/ptr_util.h"
-#include "chrome/browser/resource_coordinator/local_site_characteristics_noop_data_writer.h"
-
-namespace resource_coordinator {
-
-LocalSiteCharacteristicsNonRecordingDataStore::
-    LocalSiteCharacteristicsNonRecordingDataStore(
-        Profile* profile,
-        LocalSiteCharacteristicsDataStoreInspector* data_store_inspector,
-        SiteCharacteristicsDataStore* data_store_for_readers)
-    : data_store_for_readers_(data_store_for_readers),
-      data_store_inspector_(data_store_inspector),
-      profile_(profile) {
-  DCHECK(data_store_for_readers_);
-  // Register the debug interface against the profile.
-  LocalSiteCharacteristicsDataStoreInspector::SetForProfile(this, profile);
-}
-
-LocalSiteCharacteristicsNonRecordingDataStore::
-    ~LocalSiteCharacteristicsNonRecordingDataStore() {
-  LocalSiteCharacteristicsDataStoreInspector::SetForProfile(nullptr, profile_);
-}
-
-std::unique_ptr<SiteCharacteristicsDataReader>
-LocalSiteCharacteristicsNonRecordingDataStore::GetReaderForOrigin(
-    const url::Origin& origin) {
-  return data_store_for_readers_->GetReaderForOrigin(origin);
-}
-
-std::unique_ptr<SiteCharacteristicsDataWriter>
-LocalSiteCharacteristicsNonRecordingDataStore::GetWriterForOrigin(
-    const url::Origin& origin,
-    performance_manager::TabVisibility tab_visibility) {
-  // Return a fake data writer.
-  SiteCharacteristicsDataWriter* writer =
-      new LocalSiteCharacteristicsNoopDataWriter();
-  return base::WrapUnique(writer);
-}
-
-bool LocalSiteCharacteristicsNonRecordingDataStore::IsRecordingForTesting() {
-  return false;
-}
-
-const char* LocalSiteCharacteristicsNonRecordingDataStore::GetDataStoreName() {
-  return "LocalSiteCharacteristicsNonRecordingDataStore";
-}
-
-std::vector<url::Origin>
-LocalSiteCharacteristicsNonRecordingDataStore::GetAllInMemoryOrigins() {
-  if (!data_store_inspector_)
-    return std::vector<url::Origin>();
-
-  return data_store_inspector_->GetAllInMemoryOrigins();
-}
-
-void LocalSiteCharacteristicsNonRecordingDataStore::GetDatabaseSize(
-    DatabaseSizeCallback on_have_data) {
-  if (!data_store_inspector_) {
-    std::move(on_have_data).Run(base::nullopt, base::nullopt);
-    return;
-  }
-
-  data_store_inspector_->GetDatabaseSize(std::move(on_have_data));
-}
-
-bool LocalSiteCharacteristicsNonRecordingDataStore::GetDataForOrigin(
-    const url::Origin& origin,
-    bool* is_dirty,
-    std::unique_ptr<SiteDataProto>* data) {
-  if (!data_store_inspector_)
-    return false;
-
-  return data_store_inspector_->GetDataForOrigin(origin, is_dirty, data);
-}
-
-SiteCharacteristicsDataStore*
-LocalSiteCharacteristicsNonRecordingDataStore::GetDataStore() {
-  return this;
-}
-
-}  // namespace resource_coordinator
diff --git a/chrome/browser/resource_coordinator/local_site_characteristics_non_recording_data_store.h b/chrome/browser/resource_coordinator/local_site_characteristics_non_recording_data_store.h
deleted file mode 100644
index 4bed7e4..0000000
--- a/chrome/browser/resource_coordinator/local_site_characteristics_non_recording_data_store.h
+++ /dev/null
@@ -1,69 +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 CHROME_BROWSER_RESOURCE_COORDINATOR_LOCAL_SITE_CHARACTERISTICS_NON_RECORDING_DATA_STORE_H_
-#define CHROME_BROWSER_RESOURCE_COORDINATOR_LOCAL_SITE_CHARACTERISTICS_NON_RECORDING_DATA_STORE_H_
-
-#include "base/macros.h"
-#include "chrome/browser/resource_coordinator/local_site_characteristics_data_store_inspector.h"
-#include "chrome/browser/resource_coordinator/site_characteristics_data_store.h"
-
-class Profile;
-
-namespace resource_coordinator {
-
-// Specialization of a SiteCharacteristicsDataStore whose
-// SiteCharacteristicsDataWriters don't persist observations and whose
-// SiteCharacteristicsDataReader are obtained from another
-// SiteCharacteristicsDataStore.
-class LocalSiteCharacteristicsNonRecordingDataStore
-    : public SiteCharacteristicsDataStore,
-      public LocalSiteCharacteristicsDataStoreInspector {
- public:
-  // |profile| is the profile this data store is associated with.
-  // |data_store_inspector| is the inspector instance this instance will
-  // delegate to, may be null, but this is typically the inspector instance
-  // associated with |data_store_for_readers|. |data_store_for_readers| should
-  // outlive this object.
-  LocalSiteCharacteristicsNonRecordingDataStore(
-      Profile* profile,
-      LocalSiteCharacteristicsDataStoreInspector* data_store_inspector,
-      SiteCharacteristicsDataStore* data_store_for_readers);
-  ~LocalSiteCharacteristicsNonRecordingDataStore() override;
-
-  // SiteCharacteristicDataStore:
-  std::unique_ptr<SiteCharacteristicsDataReader> GetReaderForOrigin(
-      const url::Origin& origin) override;
-  std::unique_ptr<SiteCharacteristicsDataWriter> GetWriterForOrigin(
-      const url::Origin& origin,
-      performance_manager::TabVisibility tab_visibility) override;
-  bool IsRecordingForTesting() override;
-
-  // LocalSiteCharacteristicsDataStoreInspector:
-  const char* GetDataStoreName() override;
-  std::vector<url::Origin> GetAllInMemoryOrigins() override;
-  void GetDatabaseSize(DatabaseSizeCallback on_have_data) override;
-  bool GetDataForOrigin(const url::Origin& origin,
-                        bool* is_dirty,
-                        std::unique_ptr<SiteDataProto>* data) override;
-  SiteCharacteristicsDataStore* GetDataStore() override;
-
- private:
-  // The data store to use to create the readers served by this data store. E.g.
-  // during an incognito session it should point to the data store used by the
-  // parent session.
-  SiteCharacteristicsDataStore* data_store_for_readers_;
-
-  // The inspector implementation this instance delegates to.
-  LocalSiteCharacteristicsDataStoreInspector* data_store_inspector_;
-
-  // The profile this data store is associated with.
-  Profile* profile_;
-
-  DISALLOW_COPY_AND_ASSIGN(LocalSiteCharacteristicsNonRecordingDataStore);
-};
-
-}  // namespace resource_coordinator
-
-#endif  // CHROME_BROWSER_RESOURCE_COORDINATOR_LOCAL_SITE_CHARACTERISTICS_NON_RECORDING_DATA_STORE_H_
diff --git a/chrome/browser/resource_coordinator/local_site_characteristics_non_recording_data_store_unittest.cc b/chrome/browser/resource_coordinator/local_site_characteristics_non_recording_data_store_unittest.cc
deleted file mode 100644
index c955f3b8..0000000
--- a/chrome/browser/resource_coordinator/local_site_characteristics_non_recording_data_store_unittest.cc
+++ /dev/null
@@ -1,135 +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 "chrome/browser/resource_coordinator/local_site_characteristics_non_recording_data_store.h"
-
-#include "chrome/browser/resource_coordinator/local_site_characteristics_data_store.h"
-#include "chrome/browser/resource_coordinator/local_site_characteristics_data_store_inspector.h"
-#include "chrome/test/base/testing_profile.h"
-#include "content/public/test/browser_task_environment.h"
-#include "content/public/test/test_utils.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "url/gurl.h"
-
-namespace resource_coordinator {
-
-namespace {
-// TODO(https://crbug.com/1042727): Fix test GURL scoping and remove this getter
-// function.
-url::Origin TestOrigin() {
-  return url::Origin::Create(GURL("http://www.foo.com"));
-}
-
-class LocalSiteCharacteristicsNonRecordingDataStoreTest : public testing::Test {
- public:
-  void SetUp() override {
-    recording_data_store_ =
-        std::make_unique<LocalSiteCharacteristicsDataStore>(&parent_profile_);
-    non_recording_data_store_ =
-        std::make_unique<LocalSiteCharacteristicsNonRecordingDataStore>(
-            &profile_, recording_data_store_.get(),
-            recording_data_store_.get());
-
-    WaitForAsyncOperationsToComplete();
-  }
-
-  void WaitForAsyncOperationsToComplete() { task_environment_.RunUntilIdle(); }
-
- protected:
-  content::BrowserTaskEnvironment task_environment_;
-  TestingProfile parent_profile_;
-  TestingProfile profile_;
-  std::unique_ptr<LocalSiteCharacteristicsDataStore> recording_data_store_;
-  std::unique_ptr<LocalSiteCharacteristicsNonRecordingDataStore>
-      non_recording_data_store_;
-};
-
-}  // namespace
-
-TEST_F(LocalSiteCharacteristicsNonRecordingDataStoreTest, EndToEnd) {
-  // Ensures that the observation made via a writer created by the non
-  // recording data store aren't recorded.
-  auto reader = non_recording_data_store_->GetReaderForOrigin(TestOrigin());
-  EXPECT_TRUE(reader);
-  auto fake_writer = non_recording_data_store_->GetWriterForOrigin(
-      TestOrigin(), performance_manager::TabVisibility::kBackground);
-  EXPECT_TRUE(fake_writer);
-  auto real_writer = recording_data_store_->GetWriterForOrigin(
-      TestOrigin(), performance_manager::TabVisibility::kBackground);
-  EXPECT_TRUE(real_writer);
-
-  EXPECT_EQ(performance_manager::SiteFeatureUsage::kSiteFeatureUsageUnknown,
-            reader->UpdatesTitleInBackground());
-  fake_writer->NotifySiteLoaded();
-  fake_writer->NotifyUpdatesTitleInBackground();
-  EXPECT_EQ(performance_manager::SiteFeatureUsage::kSiteFeatureUsageUnknown,
-            reader->UpdatesTitleInBackground());
-
-  real_writer->NotifySiteLoaded();
-  real_writer->NotifyUpdatesTitleInBackground();
-  EXPECT_EQ(performance_manager::SiteFeatureUsage::kSiteFeatureInUse,
-            reader->UpdatesTitleInBackground());
-
-  // These unload events shouldn't be registered, make sure that they aren't by
-  // unloading the site more time than it has been loaded.
-  fake_writer->NotifySiteUnloaded();
-  fake_writer->NotifySiteUnloaded();
-
-  real_writer->NotifySiteUnloaded();
-
-  real_writer.reset();
-  reader.reset();
-
-  WaitForAsyncOperationsToComplete();
-
-  recording_data_store_.reset();
-
-  // Wait for the database of the recording store to be flushed to disk before
-  // terminating this test, otherwise the profile might get deleted while the
-  // database is still being flushed to disk and this could prevent from
-  // deleting its temporary directory.
-  WaitForAsyncOperationsToComplete();
-}
-
-TEST_F(LocalSiteCharacteristicsNonRecordingDataStoreTest, InspectorWorks) {
-  // Make sure the inspector interface was registered at construction.
-  LocalSiteCharacteristicsDataStoreInspector* inspector =
-      LocalSiteCharacteristicsDataStoreInspector::GetForProfile(&profile_);
-  EXPECT_NE(nullptr, inspector);
-  EXPECT_EQ(non_recording_data_store_.get(), inspector);
-
-  EXPECT_STREQ("LocalSiteCharacteristicsNonRecordingDataStore",
-               inspector->GetDataStoreName());
-
-  // We expect an empty data store at the outset.
-  EXPECT_EQ(0U, inspector->GetAllInMemoryOrigins().size());
-  std::unique_ptr<SiteDataProto> data;
-  bool is_dirty = false;
-  EXPECT_FALSE(inspector->GetDataForOrigin(TestOrigin(), &is_dirty, &data));
-  EXPECT_FALSE(is_dirty);
-  EXPECT_EQ(nullptr, data.get());
-
-  {
-    // Add an entry through the writing data store, see that it's reflected in
-    // the inspector interface.
-    auto writer = recording_data_store_->GetWriterForOrigin(
-        TestOrigin(), performance_manager::TabVisibility::kBackground);
-
-    EXPECT_EQ(1U, inspector->GetAllInMemoryOrigins().size());
-    EXPECT_TRUE(inspector->GetDataForOrigin(TestOrigin(), &is_dirty, &data));
-    EXPECT_FALSE(is_dirty);
-    ASSERT_NE(nullptr, data.get());
-
-    // Touch the underlying data, see that the dirty bit updates.
-    writer->NotifySiteLoaded();
-    EXPECT_TRUE(inspector->GetDataForOrigin(TestOrigin(), &is_dirty, &data));
-  }
-
-  // Make sure the interface is unregistered from the profile on destruction.
-  non_recording_data_store_.reset();
-  EXPECT_EQ(nullptr, LocalSiteCharacteristicsDataStoreInspector::GetForProfile(
-                         &profile_));
-}
-
-}  // namespace resource_coordinator
diff --git a/chrome/browser/resource_coordinator/local_site_characteristics_noop_data_writer.cc b/chrome/browser/resource_coordinator/local_site_characteristics_noop_data_writer.cc
deleted file mode 100644
index 8ba7a0b0..0000000
--- a/chrome/browser/resource_coordinator/local_site_characteristics_noop_data_writer.cc
+++ /dev/null
@@ -1,34 +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 "chrome/browser/resource_coordinator/local_site_characteristics_noop_data_writer.h"
-
-namespace resource_coordinator {
-
-LocalSiteCharacteristicsNoopDataWriter::
-    LocalSiteCharacteristicsNoopDataWriter() = default;
-LocalSiteCharacteristicsNoopDataWriter::
-    ~LocalSiteCharacteristicsNoopDataWriter() = default;
-
-void LocalSiteCharacteristicsNoopDataWriter::NotifySiteLoaded() {}
-
-void LocalSiteCharacteristicsNoopDataWriter::NotifySiteUnloaded() {}
-
-void LocalSiteCharacteristicsNoopDataWriter::NotifySiteVisibilityChanged(
-    performance_manager::TabVisibility visibility) {}
-
-void LocalSiteCharacteristicsNoopDataWriter::
-    NotifyUpdatesFaviconInBackground() {}
-
-void LocalSiteCharacteristicsNoopDataWriter::NotifyUpdatesTitleInBackground() {}
-
-void LocalSiteCharacteristicsNoopDataWriter::NotifyUsesAudioInBackground() {}
-
-void LocalSiteCharacteristicsNoopDataWriter::
-    NotifyLoadTimePerformanceMeasurement(
-        base::TimeDelta load_duration,
-        base::TimeDelta cpu_usage_estimate,
-        uint64_t private_footprint_kb_estimate) {}
-
-}  // namespace resource_coordinator
diff --git a/chrome/browser/resource_coordinator/local_site_characteristics_noop_data_writer.h b/chrome/browser/resource_coordinator/local_site_characteristics_noop_data_writer.h
deleted file mode 100644
index ca6d0cc49..0000000
--- a/chrome/browser/resource_coordinator/local_site_characteristics_noop_data_writer.h
+++ /dev/null
@@ -1,40 +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 CHROME_BROWSER_RESOURCE_COORDINATOR_LOCAL_SITE_CHARACTERISTICS_NOOP_DATA_WRITER_H_
-#define CHROME_BROWSER_RESOURCE_COORDINATOR_LOCAL_SITE_CHARACTERISTICS_NOOP_DATA_WRITER_H_
-
-#include "base/macros.h"
-#include "chrome/browser/resource_coordinator/site_characteristics_data_writer.h"
-
-namespace resource_coordinator {
-
-// Specialization of a SiteCharacteristicsDataWriter that doesn't record
-// anyting.
-class LocalSiteCharacteristicsNoopDataWriter
-    : public SiteCharacteristicsDataWriter {
- public:
-  LocalSiteCharacteristicsNoopDataWriter();
-  ~LocalSiteCharacteristicsNoopDataWriter() override;
-
-  // SiteCharacteristicsDataWriter:
-  void NotifySiteLoaded() override;
-  void NotifySiteUnloaded() override;
-  void NotifySiteVisibilityChanged(
-      performance_manager::TabVisibility visibility) override;
-  void NotifyUpdatesFaviconInBackground() override;
-  void NotifyUpdatesTitleInBackground() override;
-  void NotifyUsesAudioInBackground() override;
-  void NotifyLoadTimePerformanceMeasurement(
-      base::TimeDelta load_duration,
-      base::TimeDelta cpu_usage_estimate,
-      uint64_t private_footprint_kb_estimate) override;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(LocalSiteCharacteristicsNoopDataWriter);
-};
-
-}  // namespace resource_coordinator
-
-#endif  // CHROME_BROWSER_RESOURCE_COORDINATOR_LOCAL_SITE_CHARACTERISTICS_NOOP_DATA_WRITER_H_
diff --git a/chrome/browser/resource_coordinator/local_site_characteristics_webcontents_observer.cc b/chrome/browser/resource_coordinator/local_site_characteristics_webcontents_observer.cc
deleted file mode 100644
index 3a4a243..0000000
--- a/chrome/browser/resource_coordinator/local_site_characteristics_webcontents_observer.cc
+++ /dev/null
@@ -1,272 +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 "chrome/browser/resource_coordinator/local_site_characteristics_webcontents_observer.h"
-
-#include "base/bind.h"
-#include "base/metrics/histogram_macros.h"
-#include "base/threading/sequenced_task_runner_handle.h"
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/resource_coordinator/local_site_characteristics_data_store_factory.h"
-#include "chrome/browser/resource_coordinator/tab_helper.h"
-#include "chrome/browser/resource_coordinator/time.h"
-#include "chrome/browser/resource_coordinator/utils.h"
-#include "components/performance_manager/performance_manager_impl.h"
-#include "components/performance_manager/public/graph/frame_node.h"
-#include "components/performance_manager/public/graph/graph.h"
-#include "components/performance_manager/public/graph/page_node.h"
-#include "components/performance_manager/public/web_contents_proxy.h"
-#include "content/public/browser/navigation_handle.h"
-#include "content/public/browser/web_contents.h"
-
-namespace resource_coordinator {
-
-namespace {
-
-using LoadingState = TabLoadTracker::LoadingState;
-
-// The period of time after loading during which we ignore title/favicon
-// change events. It's possible for some site that are loaded in background to
-// use some of these features without this being an attempt to communicate
-// with the user (e.g. the tab is just really finishing to load).
-constexpr base::TimeDelta kTitleOrFaviconChangePostLoadGracePeriod =
-    base::TimeDelta::FromSeconds(20);
-
-// The period of time during which we ignore events after a tab gets
-// backgrounded. It's necessary because some events might happen shortly after
-// backgrounding a tab without this being an attempt to communicate with the
-// user:
-//    - There might be a delay between a media request gets initiated and the
-//      time the audio actually starts.
-//    - Same-document navigation can cause the title or favicon to change, if
-//      the user switch tab before this completes this will be recorded as a
-//      background communication event while in reality it's just a navigation
-//      event.
-constexpr base::TimeDelta kFeatureUsagePostBackgroundGracePeriod =
-    base::TimeDelta::FromSeconds(10);
-
-performance_manager::TabVisibility ContentVisibilityToRCVisibility(
-    content::Visibility visibility) {
-  if (visibility == content::Visibility::VISIBLE)
-    return performance_manager::TabVisibility::kForeground;
-  return performance_manager::TabVisibility::kBackground;
-}
-
-}  // namespace
-
-LocalSiteCharacteristicsWebContentsObserver::
-    LocalSiteCharacteristicsWebContentsObserver(
-        content::WebContents* web_contents)
-    : content::WebContentsObserver(web_contents) {
-  // May not be present in some tests.
-  if (performance_manager::PerformanceManagerImpl::IsAvailable()) {
-    TabLoadTracker::Get()->AddObserver(this);
-  }
-}
-
-LocalSiteCharacteristicsWebContentsObserver::
-    ~LocalSiteCharacteristicsWebContentsObserver() {
-  DCHECK(!writer_);
-}
-
-void LocalSiteCharacteristicsWebContentsObserver::OnVisibilityChanged(
-    content::Visibility visibility) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  if (!writer_)
-    return;
-
-  auto rc_visibility = ContentVisibilityToRCVisibility(visibility);
-  UpdateBackgroundedTime(rc_visibility);
-  writer_->NotifySiteVisibilityChanged(rc_visibility);
-}
-
-void LocalSiteCharacteristicsWebContentsObserver::WebContentsDestroyed() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  if (performance_manager::PerformanceManagerImpl::IsAvailable())
-    TabLoadTracker::Get()->RemoveObserver(this);
-  writer_.reset();
-  writer_origin_ = url::Origin();
-}
-
-void LocalSiteCharacteristicsWebContentsObserver::DidFinishNavigation(
-    content::NavigationHandle* navigation_handle) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  DCHECK(navigation_handle);
-
-  // Ignore the navigation events happening in a subframe of in the same
-  // document.
-  if (!navigation_handle->IsInMainFrame() ||
-      navigation_handle->IsSameDocument()) {
-    return;
-  }
-
-  first_time_title_set_ = false;
-  first_time_favicon_set_ = false;
-
-  if (!navigation_handle->HasCommitted())
-    return;
-
-  const url::Origin new_origin =
-      url::Origin::Create(navigation_handle->GetURL());
-
-  if (writer_ && new_origin == writer_origin_)
-    return;
-
-  writer_.reset();
-  writer_origin_ = url::Origin();
-
-  if (!URLShouldBeStoredInLocalDatabase(navigation_handle->GetURL()))
-    return;
-
-  Profile* profile =
-      Profile::FromBrowserContext(web_contents()->GetBrowserContext());
-  DCHECK(profile);
-  SiteCharacteristicsDataStore* data_store =
-      LocalSiteCharacteristicsDataStoreFactory::GetForProfile(profile);
-
-  // A data store might not be available in some unit tests.
-  if (data_store) {
-    auto rc_visibility =
-        ContentVisibilityToRCVisibility(web_contents()->GetVisibility());
-    writer_ = data_store->GetWriterForOrigin(new_origin, rc_visibility);
-    UpdateBackgroundedTime(rc_visibility);
-  }
-
-  // The writer is initially in an unloaded state, load it if necessary.
-  if (TabLoadTracker::Get()->GetLoadingState(web_contents()) ==
-      LoadingState::LOADED) {
-    OnSiteLoaded();
-  }
-
-  writer_origin_ = new_origin;
-}
-
-void LocalSiteCharacteristicsWebContentsObserver::TitleWasSet(
-    content::NavigationEntry* entry) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-  // TODO(sebmarchand): Check if the title is always set at least once before
-  // loading completes, in which case this check could be removed.
-  if (!first_time_title_set_) {
-    first_time_title_set_ = true;
-    return;
-  }
-
-  MaybeNotifyBackgroundFeatureUsage(
-      &SiteCharacteristicsDataWriter::NotifyUpdatesTitleInBackground,
-      FeatureType::kTitleChange);
-}
-
-void LocalSiteCharacteristicsWebContentsObserver::DidUpdateFaviconURL(
-    content::RenderFrameHost* rfh,
-    const std::vector<blink::mojom::FaviconURLPtr>& candidates) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-  if (!first_time_favicon_set_) {
-    first_time_favicon_set_ = true;
-    return;
-  }
-
-  MaybeNotifyBackgroundFeatureUsage(
-      &SiteCharacteristicsDataWriter::NotifyUpdatesFaviconInBackground,
-      FeatureType::kFaviconChange);
-}
-
-void LocalSiteCharacteristicsWebContentsObserver::OnAudioStateChanged(
-    bool audible) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-  if (!audible)
-    return;
-
-  MaybeNotifyBackgroundFeatureUsage(
-      &SiteCharacteristicsDataWriter::NotifyUsesAudioInBackground,
-      FeatureType::kAudioUsage);
-}
-
-void LocalSiteCharacteristicsWebContentsObserver::OnLoadingStateChange(
-    content::WebContents* contents,
-    LoadingState old_loading_state,
-    LoadingState new_loading_state) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  if (web_contents() != contents)
-    return;
-
-  if (!writer_)
-    return;
-
-  // Ignore the transitions from/to an UNLOADED state.
-  if (new_loading_state == LoadingState::LOADED) {
-    OnSiteLoaded();
-  } else if (old_loading_state == LoadingState::LOADED) {
-    writer_->NotifySiteUnloaded();
-    loaded_time_ = base::TimeTicks();
-  }
-}
-
-bool LocalSiteCharacteristicsWebContentsObserver::ShouldIgnoreFeatureUsageEvent(
-    FeatureType feature_type) {
-  // The feature usage should be ignored if there's no writer for this tab.
-  if (!writer_)
-    return true;
-
-  // Ignore all features happening before the website gets fully loaded.
-  if (TabLoadTracker::Get()->GetLoadingState(web_contents()) !=
-      LoadingState::LOADED) {
-    return true;
-  }
-
-  // Ignore events if the tab is not in background.
-  if (ContentVisibilityToRCVisibility(web_contents()->GetVisibility()) !=
-      performance_manager::TabVisibility::kBackground) {
-    return true;
-  }
-
-  if (feature_type == FeatureType::kTitleChange ||
-      feature_type == FeatureType::kFaviconChange) {
-    DCHECK(!loaded_time_.is_null());
-    if (NowTicks() - loaded_time_ < kTitleOrFaviconChangePostLoadGracePeriod) {
-      return true;
-    }
-  }
-
-  // Ignore events happening shortly after the tab being backgrounded, they're
-  // usually false positives.
-  DCHECK(!backgrounded_time_.is_null());
-  if ((NowTicks() - backgrounded_time_ <
-       kFeatureUsagePostBackgroundGracePeriod)) {
-    return true;
-  }
-
-  return false;
-}
-
-void LocalSiteCharacteristicsWebContentsObserver::
-    MaybeNotifyBackgroundFeatureUsage(
-        void (SiteCharacteristicsDataWriter::*method)(),
-        FeatureType feature_type) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-  if (ShouldIgnoreFeatureUsageEvent(feature_type))
-    return;
-
-  (writer_.get()->*method)();
-}
-
-void LocalSiteCharacteristicsWebContentsObserver::OnSiteLoaded() {
-  DCHECK(writer_);
-  writer_->NotifySiteLoaded();
-  loaded_time_ = NowTicks();
-}
-
-void LocalSiteCharacteristicsWebContentsObserver::UpdateBackgroundedTime(
-    performance_manager::TabVisibility visibility) {
-  if (visibility == performance_manager::TabVisibility::kBackground) {
-    backgrounded_time_ = NowTicks();
-  } else {
-    backgrounded_time_ = base::TimeTicks();
-  }
-}
-
-}  // namespace resource_coordinator
diff --git a/chrome/browser/resource_coordinator/local_site_characteristics_webcontents_observer.h b/chrome/browser/resource_coordinator/local_site_characteristics_webcontents_observer.h
deleted file mode 100644
index 16b6a78..0000000
--- a/chrome/browser/resource_coordinator/local_site_characteristics_webcontents_observer.h
+++ /dev/null
@@ -1,108 +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 CHROME_BROWSER_RESOURCE_COORDINATOR_LOCAL_SITE_CHARACTERISTICS_WEBCONTENTS_OBSERVER_H_
-#define CHROME_BROWSER_RESOURCE_COORDINATOR_LOCAL_SITE_CHARACTERISTICS_WEBCONTENTS_OBSERVER_H_
-
-#include "base/macros.h"
-#include "base/sequence_checker.h"
-#include "base/time/time.h"
-#include "chrome/browser/resource_coordinator/local_site_characteristics_data_writer.h"
-#include "chrome/browser/resource_coordinator/tab_load_tracker.h"
-#include "content/public/browser/visibility.h"
-#include "content/public/browser/web_contents_observer.h"
-#include "third_party/blink/public/mojom/favicon/favicon_url.mojom-forward.h"
-#include "url/origin.h"
-
-namespace content {
-class NavigationHandle;
-}
-
-namespace resource_coordinator {
-
-// WebContents observer that manages a SiteCharacteristicsDataWriter associated
-// with a WebContents and forwards the appropriate events to it.
-class LocalSiteCharacteristicsWebContentsObserver
-    : public content::WebContentsObserver,
-      public TabLoadTracker::Observer {
- public:
-  explicit LocalSiteCharacteristicsWebContentsObserver(
-      content::WebContents* contents);
-  ~LocalSiteCharacteristicsWebContentsObserver() override;
-
-  // WebContentsObserver implementation.
-  void OnVisibilityChanged(content::Visibility visibility) override;
-  void WebContentsDestroyed() override;
-  void DidFinishNavigation(
-      content::NavigationHandle* navigation_handle) override;
-  void TitleWasSet(content::NavigationEntry* entry) override;
-  void DidUpdateFaviconURL(
-      content::RenderFrameHost* rfh,
-      const std::vector<blink::mojom::FaviconURLPtr>& candidates) override;
-  void OnAudioStateChanged(bool audible) override;
-
-  // TabLoadTracker::Observer:
-  void OnLoadingStateChange(content::WebContents* web_contents,
-                            LoadingState old_loading_state,
-                            LoadingState new_loading_state) override;
-
-  const url::Origin& writer_origin() const { return writer_origin_; }
-
-  SiteCharacteristicsDataWriter* GetWriterForTesting() const {
-    return writer_.get();
-  }
-  void ResetWriterForTesting() { writer_.reset(); }
-
- private:
-  enum class FeatureType {
-    kTitleChange,
-    kFaviconChange,
-    kAudioUsage,
-  };
-
-  // Indicates if the feature usage event just received should be ignored.
-  bool ShouldIgnoreFeatureUsageEvent(FeatureType feature_type);
-
-  // Helper function to maybe notify |writer_| that a feature event has been
-  // received while in background. Doesn't do anything if
-  // ShouldIgnoreFeatureUsageEvent returns true.
-  void MaybeNotifyBackgroundFeatureUsage(
-      void (SiteCharacteristicsDataWriter::*method)(),
-      FeatureType feature_type);
-
-  // Function to call when the site switch to the loaded state.
-  void OnSiteLoaded();
-
-  // Updates |backgrounded_time_| based on |visibility|.
-  void UpdateBackgroundedTime(performance_manager::TabVisibility visibility);
-
-  // The writer that processes the event received by this class.
-  std::unique_ptr<SiteCharacteristicsDataWriter> writer_;
-
-  // The Origin tracked by the writer.
-  url::Origin writer_origin_;
-
-  // Favicon and title are set when a page is loaded, we only want to send
-  // signals to the database about title and favicon update from the previous
-  // title and favicon, thus we want to ignore the very first update since it is
-  // always supposed to happen.
-  bool first_time_favicon_set_ = false;
-  bool first_time_title_set_ = false;
-
-  // The time at which this tab switched to the loaded state, null if this tab
-  // is not currently loaded.
-  base::TimeTicks loaded_time_;
-
-  // The time at which this tab has been backgrounded, null if this tab is
-  // currently visible.
-  base::TimeTicks backgrounded_time_;
-
-  SEQUENCE_CHECKER(sequence_checker_);
-
-  DISALLOW_COPY_AND_ASSIGN(LocalSiteCharacteristicsWebContentsObserver);
-};
-
-}  // namespace resource_coordinator
-
-#endif  // CHROME_BROWSER_RESOURCE_COORDINATOR_LOCAL_SITE_CHARACTERISTICS_WEBCONTENTS_OBSERVER_H_
diff --git a/chrome/browser/resource_coordinator/local_site_characteristics_webcontents_observer_unittest.cc b/chrome/browser/resource_coordinator/local_site_characteristics_webcontents_observer_unittest.cc
deleted file mode 100644
index a8ddd6b..0000000
--- a/chrome/browser/resource_coordinator/local_site_characteristics_webcontents_observer_unittest.cc
+++ /dev/null
@@ -1,339 +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 "chrome/browser/resource_coordinator/local_site_characteristics_webcontents_observer.h"
-
-#include "base/bind.h"
-#include "base/macros.h"
-#include "base/test/simple_test_tick_clock.h"
-#include "chrome/browser/resource_coordinator/local_site_characteristics_data_store_factory.h"
-#include "chrome/browser/resource_coordinator/local_site_characteristics_data_unittest_utils.h"
-#include "chrome/browser/resource_coordinator/site_characteristics_data_store.h"
-#include "chrome/browser/resource_coordinator/time.h"
-#include "content/public/browser/web_contents.h"
-#include "content/public/test/mock_navigation_handle.h"
-#include "content/public/test/web_contents_tester.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/blink/public/mojom/favicon/favicon_url.mojom.h"
-
-namespace resource_coordinator {
-
-using LoadingState = TabLoadTracker::LoadingState;
-
-constexpr base::TimeDelta kTitleOrFaviconChangePostLoadGracePeriod =
-    base::TimeDelta::FromSeconds(20);
-constexpr base::TimeDelta kFeatureUsagePostBackgroundGracePeriod =
-    base::TimeDelta::FromSeconds(10);
-
-// A mock implementation of a SiteCharacteristicsDataWriter.
-class LenientMockDataWriter : public SiteCharacteristicsDataWriter {
- public:
-  explicit LenientMockDataWriter(const url::Origin& origin) : origin_(origin) {}
-  ~LenientMockDataWriter() override { OnDestroy(); }
-
-  // Mock function to be notified when this object gets destroyed.
-  MOCK_METHOD0(OnDestroy, void());
-
-  MOCK_METHOD0(NotifySiteLoaded, void());
-  MOCK_METHOD0(NotifySiteUnloaded, void());
-  MOCK_METHOD1(NotifySiteVisibilityChanged,
-               void(performance_manager::TabVisibility));
-  MOCK_METHOD0(NotifyUpdatesFaviconInBackground, void());
-  MOCK_METHOD0(NotifyUpdatesTitleInBackground, void());
-  MOCK_METHOD0(NotifyUsesAudioInBackground, void());
-  MOCK_METHOD3(NotifyLoadTimePerformanceMeasurement,
-               void(base::TimeDelta, base::TimeDelta, uint64_t));
-
-  const url::Origin& Origin() const { return origin_; }
-
- private:
-  url::Origin origin_;
-
-  DISALLOW_COPY_AND_ASSIGN(LenientMockDataWriter);
-};
-using MockDataWriter = ::testing::StrictMock<LenientMockDataWriter>;
-
-// A data store that serves MockDataWriter objects.
-class MockDataStore : public SiteCharacteristicsDataStore {
- public:
-  MockDataStore() = default;
-  ~MockDataStore() override {}
-
-  // SiteCharacteristicsDataStore:
-  std::unique_ptr<SiteCharacteristicsDataReader> GetReaderForOrigin(
-      const url::Origin& origin) override {
-    return nullptr;
-  }
-  std::unique_ptr<SiteCharacteristicsDataWriter> GetWriterForOrigin(
-      const url::Origin& origin,
-      performance_manager::TabVisibility tab_visibility) override {
-    return std::make_unique<MockDataWriter>(origin);
-  }
-  bool IsRecordingForTesting() override { return true; }
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(MockDataStore);
-};
-
-std::unique_ptr<KeyedService> BuildMockDataStoreForContext(
-    content::BrowserContext* browser_context) {
-  return std::make_unique<MockDataStore>();
-}
-
-class LocalSiteCharacteristicsWebContentsObserverTest
-    : public testing::ChromeTestHarnessWithLocalDB {
- protected:
-  LocalSiteCharacteristicsWebContentsObserverTest()
-      : scoped_set_tick_clock_for_testing_(&test_clock_) {}
-  ~LocalSiteCharacteristicsWebContentsObserverTest() override = default;
-
-  void SetUp() override {
-    testing::ChromeTestHarnessWithLocalDB::SetUp();
-
-    test_clock().Advance(base::TimeDelta::FromSeconds(1));
-    // Set the testing factory for the test browser context.
-    LocalSiteCharacteristicsDataStoreFactory::GetInstance()->SetTestingFactory(
-        browser_context(), base::BindRepeating(&BuildMockDataStoreForContext));
-
-    TabLoadTracker::Get()->StartTracking(web_contents());
-    observer_ = std::make_unique<LocalSiteCharacteristicsWebContentsObserver>(
-        web_contents());
-  }
-
-  void TearDown() override {
-    TabLoadTracker::Get()->StopTracking(web_contents());
-    DeleteContents();
-    observer_.reset();
-    testing::ChromeTestHarnessWithLocalDB::TearDown();
-  }
-
-  MockDataWriter* NavigateAndReturnMockWriter(const GURL& url) {
-    content::WebContentsTester* web_contents_tester =
-        content::WebContentsTester::For(web_contents());
-    EXPECT_TRUE(web_contents_tester);
-    web_contents_tester->NavigateAndCommit(url);
-    return static_cast<MockDataWriter*>(observer_->GetWriterForTesting());
-  }
-
-  const GURL kTestUrl1 = GURL("http://foo.com");
-  const GURL kTestUrl2 = GURL("http://bar.com");
-
-  LocalSiteCharacteristicsWebContentsObserver* observer() {
-    return observer_.get();
-  }
-
-  base::SimpleTestTickClock& test_clock() { return test_clock_; }
-
- private:
-  std::unique_ptr<LocalSiteCharacteristicsWebContentsObserver> observer_;
-  base::SimpleTestTickClock test_clock_;
-  ScopedSetTickClockForTesting scoped_set_tick_clock_for_testing_;
-
-  DISALLOW_COPY_AND_ASSIGN(LocalSiteCharacteristicsWebContentsObserverTest);
-};
-
-TEST_F(LocalSiteCharacteristicsWebContentsObserverTest,
-       NavigationEventsBasicTests) {
-  // Send a navigation event with the |committed| bit set and make sure that a
-  // writer has been created for this origin.
-
-  EXPECT_FALSE(observer()->GetWriterForTesting());
-  MockDataWriter* mock_writer = NavigateAndReturnMockWriter(kTestUrl1);
-  EXPECT_TRUE(mock_writer);
-
-  auto writer_origin = observer()->writer_origin();
-
-  EXPECT_EQ(url::Origin::Create(kTestUrl1), writer_origin);
-
-  // A navigation to the same origin shouldn't cause caused this writer to get
-  // destroyed.
-  mock_writer = NavigateAndReturnMockWriter(kTestUrl1);
-  ::testing::Mock::VerifyAndClear(mock_writer);
-
-  // Navigate to a different origin but don't set the |committed| bit, this
-  // shouldn't affect the writer.
-  content::MockNavigationHandle navigation_handle(
-      kTestUrl2, web_contents()->GetMainFrame());
-  observer()->DidFinishNavigation(&navigation_handle);
-  ::testing::Mock::VerifyAndClear(mock_writer);
-
-  // Set the |committed| bit and ensure that the navigation event cause the
-  // destruction of the writer.
-  EXPECT_CALL(*mock_writer, OnDestroy());
-  mock_writer = NavigateAndReturnMockWriter(kTestUrl2);
-  ::testing::Mock::VerifyAndClear(mock_writer);
-
-  EXPECT_FALSE(writer_origin == observer()->writer_origin());
-  writer_origin = observer()->writer_origin();
-
-  EXPECT_EQ(url::Origin::Create(kTestUrl2), mock_writer->Origin());
-
-  EXPECT_CALL(*mock_writer, OnDestroy());
-}
-
-// Test that the feature usage events get forwarded to the writer when the tab
-// is in background.
-TEST_F(LocalSiteCharacteristicsWebContentsObserverTest,
-       FeatureEventsGetForwardedWhenInBackground) {
-  MockDataWriter* mock_writer = NavigateAndReturnMockWriter(kTestUrl1);
-
-  // Send dummy events to simulate the initial title/favicon update (as these
-  // are ignored).
-  observer()->DidUpdateFaviconURL(web_contents()->GetMainFrame(),
-                                  std::vector<blink::mojom::FaviconURLPtr>());
-  observer()->TitleWasSet(nullptr);
-
-  EXPECT_CALL(*mock_writer, NotifySiteLoaded());
-  TabLoadTracker::Get()->TransitionStateForTesting(web_contents(),
-                                                   LoadingState::LOADED);
-  EXPECT_CALL(*mock_writer,
-              NotifySiteVisibilityChanged(
-                  performance_manager::TabVisibility::kForeground));
-  EXPECT_CALL(*mock_writer, NotifySiteLoaded());
-  web_contents()->WasShown();
-  observer()->OnLoadingStateChange(web_contents(),
-                                   TabLoadTracker::LoadingState::LOADING,
-                                   TabLoadTracker::LoadingState::LOADED);
-  ::testing::Mock::VerifyAndClear(mock_writer);
-
-  // Ensure that no event gets forwarded if the tab is not in background.
-  observer()->DidUpdateFaviconURL(web_contents()->GetMainFrame(), {});
-  ::testing::Mock::VerifyAndClear(mock_writer);
-  observer()->TitleWasSet(nullptr);
-  ::testing::Mock::VerifyAndClear(mock_writer);
-  observer()->OnAudioStateChanged(true);
-  ::testing::Mock::VerifyAndClear(mock_writer);
-
-  EXPECT_CALL(*mock_writer,
-              NotifySiteVisibilityChanged(
-                  performance_manager::TabVisibility::kBackground));
-  web_contents()->WasHidden();
-  ::testing::Mock::VerifyAndClear(mock_writer);
-
-  // Title and Favicon should be ignored during the post-loading grace period.
-  observer()->DidUpdateFaviconURL(web_contents()->GetMainFrame(), {});
-  observer()->TitleWasSet(nullptr);
-  ::testing::Mock::VerifyAndClear(mock_writer);
-
-  test_clock().Advance(kTitleOrFaviconChangePostLoadGracePeriod);
-
-  EXPECT_CALL(*mock_writer, NotifyUpdatesFaviconInBackground());
-  observer()->DidUpdateFaviconURL(web_contents()->GetMainFrame(), {});
-  ::testing::Mock::VerifyAndClear(mock_writer);
-  EXPECT_CALL(*mock_writer, NotifyUpdatesTitleInBackground());
-  observer()->TitleWasSet(nullptr);
-  ::testing::Mock::VerifyAndClear(mock_writer);
-
-  // Brievly switch the tab to foreground to reset the last backgrounded time.
-  EXPECT_CALL(*mock_writer,
-              NotifySiteVisibilityChanged(
-                  performance_manager::TabVisibility::kForeground));
-  EXPECT_CALL(*mock_writer,
-              NotifySiteVisibilityChanged(
-                  performance_manager::TabVisibility::kBackground));
-  web_contents()->WasShown();
-  web_contents()->WasHidden();
-  ::testing::Mock::VerifyAndClear(mock_writer);
-
-  // These events should be ignored during the post-background grace period.
-  observer()->OnAudioStateChanged(true);
-  observer()->DidUpdateFaviconURL(web_contents()->GetMainFrame(), {});
-  observer()->TitleWasSet(nullptr);
-  ::testing::Mock::VerifyAndClear(mock_writer);
-
-  test_clock().Advance(kFeatureUsagePostBackgroundGracePeriod);
-  EXPECT_CALL(*mock_writer, NotifyUsesAudioInBackground());
-  EXPECT_CALL(*mock_writer, NotifyUpdatesFaviconInBackground());
-  EXPECT_CALL(*mock_writer, NotifyUpdatesTitleInBackground());
-  observer()->OnAudioStateChanged(true);
-  observer()->DidUpdateFaviconURL(web_contents()->GetMainFrame(), {});
-  observer()->TitleWasSet(nullptr);
-  ::testing::Mock::VerifyAndClear(mock_writer);
-
-  EXPECT_CALL(*mock_writer, OnDestroy());
-}
-
-TEST_F(LocalSiteCharacteristicsWebContentsObserverTest,
-       FeatureEventsIgnoredWhenLoadingInBackground) {
-  MockDataWriter* mock_writer = NavigateAndReturnMockWriter(kTestUrl1);
-
-  // Send dummy events to simulate the initial title/favicon update (as these
-  // are ignored).
-  observer()->DidUpdateFaviconURL(web_contents()->GetMainFrame(), {});
-  observer()->TitleWasSet(nullptr);
-
-  TabLoadTracker::Get()->TransitionStateForTesting(web_contents(),
-                                                   LoadingState::LOADING);
-
-  EXPECT_CALL(*mock_writer,
-              NotifySiteVisibilityChanged(
-                  performance_manager::TabVisibility::kBackground));
-  web_contents()->WasHidden();
-  ::testing::Mock::VerifyAndClear(mock_writer);
-
-  observer()->DidUpdateFaviconURL(web_contents()->GetMainFrame(), {});
-  ::testing::Mock::VerifyAndClear(mock_writer);
-  observer()->TitleWasSet(nullptr);
-  ::testing::Mock::VerifyAndClear(mock_writer);
-  observer()->OnAudioStateChanged(true);
-  ::testing::Mock::VerifyAndClear(mock_writer);
-
-  EXPECT_CALL(*mock_writer, OnDestroy());
-}
-
-TEST_F(LocalSiteCharacteristicsWebContentsObserverTest, VisibilityEvent) {
-  MockDataWriter* mock_writer = NavigateAndReturnMockWriter(kTestUrl1);
-
-  // Test that the visibility events get forwarded to the writer.
-
-  EXPECT_CALL(*mock_writer,
-              NotifySiteVisibilityChanged(
-                  performance_manager::TabVisibility::kBackground))
-      .Times(2);
-  observer()->OnVisibilityChanged(content::Visibility::OCCLUDED);
-  observer()->OnVisibilityChanged(content::Visibility::HIDDEN);
-  ::testing::Mock::VerifyAndClear(mock_writer);
-
-  EXPECT_CALL(*mock_writer,
-              NotifySiteVisibilityChanged(
-                  performance_manager::TabVisibility::kForeground));
-  observer()->OnVisibilityChanged(content::Visibility::VISIBLE);
-  ::testing::Mock::VerifyAndClear(mock_writer);
-
-  EXPECT_CALL(*mock_writer, OnDestroy());
-}
-
-TEST_F(LocalSiteCharacteristicsWebContentsObserverTest, LoadEvent) {
-  MockDataWriter* mock_writer = NavigateAndReturnMockWriter(kTestUrl1);
-
-  // Test that the load/unload events get forwarded to the writer.
-
-  EXPECT_CALL(*mock_writer, NotifySiteLoaded());
-  observer()->OnLoadingStateChange(web_contents(),
-                                   TabLoadTracker::LoadingState::LOADING,
-                                   TabLoadTracker::LoadingState::LOADED);
-  ::testing::Mock::VerifyAndClear(mock_writer);
-
-  EXPECT_CALL(*mock_writer, NotifySiteUnloaded());
-  observer()->OnLoadingStateChange(web_contents(),
-                                   TabLoadTracker::LoadingState::LOADED,
-                                   TabLoadTracker::LoadingState::LOADING);
-  ::testing::Mock::VerifyAndClear(mock_writer);
-
-  observer()->OnLoadingStateChange(web_contents(),
-                                   TabLoadTracker::LoadingState::LOADING,
-                                   TabLoadTracker::LoadingState::UNLOADED);
-  ::testing::Mock::VerifyAndClear(mock_writer);
-
-  // Ensure that a transition from UNLOADED to LOADING doesn't cause any call to
-  // NotifySiteUnloaded.
-  observer()->OnLoadingStateChange(web_contents(),
-                                   TabLoadTracker::LoadingState::LOADING,
-                                   TabLoadTracker::LoadingState::UNLOADED);
-
-  EXPECT_CALL(*mock_writer, OnDestroy());
-}
-
-}  // namespace resource_coordinator
diff --git a/chrome/browser/resource_coordinator/session_restore_policy.cc b/chrome/browser/resource_coordinator/session_restore_policy.cc
index da732c4..fca3755 100644
--- a/chrome/browser/resource_coordinator/session_restore_policy.cc
+++ b/chrome/browser/resource_coordinator/session_restore_policy.cc
@@ -10,9 +10,11 @@
 #include <utility>
 
 #include "base/bind.h"
+#include "base/callback_forward.h"
 #include "base/feature_list.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/no_destructor.h"
+#include "base/sequence_checker.h"
 #include "base/stl_util.h"
 #include "base/system/sys_info.h"
 #include "base/threading/sequenced_task_runner_handle.h"
@@ -20,14 +22,18 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/resource_coordinator/tab_manager_features.h"
 #include "chrome/common/url_constants.h"
+#include "components/performance_manager/public/decorators/site_data_recorder.h"
+#include "components/performance_manager/public/graph/graph.h"
+#include "components/performance_manager/public/performance_manager.h"
+#include "components/performance_manager/public/persistence/site_data/site_data_reader.h"
+#include "content/public/browser/browser_task_traits.h"
+#include "content/public/browser/browser_thread.h"
 #include "content/public/browser/navigation_controller.h"
 #include "content/public/browser/navigation_entry.h"
 #include "content/public/browser/web_contents.h"
 
 #if !defined(OS_ANDROID)
 #include "chrome/browser/permissions/permission_manager_factory.h"
-#include "chrome/browser/resource_coordinator/local_site_characteristics_data_store_factory.h"
-#include "chrome/browser/resource_coordinator/site_characteristics_data_reader.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_list.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
@@ -108,96 +114,98 @@
   TabDataAccess(const TabDataAccess&) = delete;
   TabDataAccess& operator=(const TabDataAccess&) = delete;
 
-  // Schedule the task that will initialize |TabData::used_in_bg| from the local
-  // site characteristics database. This will either call
-  // |SetUsedInBgFromSiteCharacteristicsDataReader| if the data is available
-  // immediately, or will schedule a call to |OnSiteDataLoaded| once the data is
-  // available.
-  static void SetUsedInBgFromSiteCharacteristicsDB(
+  // Schedule the task that will initialize |TabData::used_in_bg| from the site
+  // data database. This will schedule a call to |OnSiteDataLoaded| once the
+  // data is available.
+  static void SetUsedInBgFromSiteDataDB(
       base::WeakPtr<SessionRestorePolicy> policy,
       TabData* tab_data,
       content::WebContents* contents);
 
-  // Set the |TabData::used_in_bg| bit based on the data provided by |reader|.
-  static void SetUsedInBgFromSiteCharacteristicsDataReader(
-      TabData* tab_data,
-      content::WebContents* contents,
-      std::unique_ptr<SiteCharacteristicsDataReader> reader);
+  // Set the |TabData::used_in_bg| bit based on the data returned from the site
+  // data database.
+  static void SetUsedInBgFromSiteData(TabData* tab_data,
+                                      content::WebContents* contents,
+                                      TabData::SiteDataReaderData reader_data);
 
-  // Callback that is invoked when the SiteCharacteristicsDataReader associated
-  // with a WebContents is ready to use. This will initialize
-  // |tab_data->used_in_bg| to the proper value and call
-  // |DispatchNotifyAllTabsScoredIfNeeded|.
-  static void OnSiteDataLoaded(
-      base::WeakPtr<SessionRestorePolicy> policy,
-      content::WebContents* contents,
-      std::unique_ptr<SiteCharacteristicsDataReader> reader);
+  // Callback that is invoked when the SiteData associated with a WebContents is
+  // ready to use. This will initialize |tab_data->used_in_bg| to the proper
+  // value and call |DispatchNotifyAllTabsScoredIfNeeded|.
+  static void OnSiteDataAvailable(base::WeakPtr<SessionRestorePolicy> policy,
+                                  content::WebContents* contents,
+                                  TabData::SiteDataReaderData reader_data);
 };
 
-void TabDataAccess::SetUsedInBgFromSiteCharacteristicsDB(
+void TabDataAccess::SetUsedInBgFromSiteDataDB(
     base::WeakPtr<SessionRestorePolicy> policy,
     TabData* tab_data,
     content::WebContents* contents) {
-  // Get a reader for the local site characteristics data corresponding to this
-  // tab's origin. This makes it possible to determine if the tab has been
-  // observed communicating with the user while in the background. This can be
-  // ready immediately in which case a final score is emitted immediately.
-  // Otherwise, it will be loaded asynchronously and the score will potentially
-  // be updated.
-  Profile* profile = Profile::FromBrowserContext(contents->GetBrowserContext());
-  DCHECK(profile);
-  auto* factory = LocalSiteCharacteristicsDataStoreFactory::GetInstance();
-  auto* store = factory->GetForProfile(profile);
+  tab_data->used_in_bg_setter_cancel_callback.Reset(base::BindOnce(
+      &TabDataAccess::OnSiteDataAvailable, std::move(policy), contents));
 
-  // The local database isn't always available.
-  // TODO(chrisha): Note that the reader can only not exist in tests. Once we
-  // have a single point to check for the availability of the performance
-  // manager, gate the following logic behind that.
-  if (!store) {
-    tab_data->used_in_bg = false;
-    return;
-  }
+  auto call_on_graph_cb = base::BindOnce(
+      [](base::WeakPtr<performance_manager::PageNode> page_node,
+         base::OnceCallback<void(TabData::SiteDataReaderData)> reply_cb,
+         scoped_refptr<base::SequencedTaskRunner> reply_task_runner) {
+        if (!page_node) {
+          reply_task_runner->PostTask(
+              FROM_HERE, base::BindOnce(std::move(reply_cb),
+                                        TabData::SiteDataReaderData()));
+          return;
+        }
+        auto* reader =
+            performance_manager::SiteDataRecorder::Data::FromPageNode(
+                page_node.get())
+                ->reader();
+        // The tab won't have a reader if it doesn't have an URL tracked in the
+        // site data database.
+        if (!reader) {
+          reply_task_runner->PostTask(
+              FROM_HERE, base::BindOnce(std::move(reply_cb),
+                                        TabData::SiteDataReaderData()));
+          return;
+        }
 
-  auto reader = store->GetReaderForOrigin(
-      url::Origin::Create(contents->GetLastCommittedURL()));
+        // The reader will call |reply_cb| once the data is available.
+        reader->RegisterDataLoadedCallback(base::BindOnce(
+            [](const performance_manager::SiteDataReader* reader,
+               base::OnceCallback<void(TabData::SiteDataReaderData)> reply_cb,
+               scoped_refptr<base::SequencedTaskRunner> reply_task_runner) {
+              static const performance_manager::SiteFeatureUsage kNotUsed =
+                  performance_manager::SiteFeatureUsage::kSiteFeatureNotInUse;
+              TabData::SiteDataReaderData reader_data = {};
+              reader_data.updates_favicon_in_bg =
+                  reader->UpdatesFaviconInBackground() != kNotUsed;
+              reader_data.updates_title_in_bg =
+                  reader->UpdatesTitleInBackground() != kNotUsed;
+              reply_task_runner->PostTask(
+                  FROM_HERE, base::BindOnce(std::move(reply_cb), reader_data));
+            },
+            base::Unretained(reader), std::move(reply_cb), reply_task_runner));
+      },
+      performance_manager::PerformanceManager::GetPageNodeForWebContents(
+          contents),
+      tab_data->used_in_bg_setter_cancel_callback.callback(),
+      base::SequencedTaskRunnerHandle::Get());
 
-  if (reader->DataLoaded()) {
-    SetUsedInBgFromSiteCharacteristicsDataReader(tab_data, contents,
-                                                 std::move(reader));
-    DCHECK(tab_data->used_in_bg.has_value());
-    if (tab_data->used_in_bg)
-      ++policy->tabs_used_in_bg_;
-  } else {
-    auto* reader_raw = reader.get();
-    tab_data->used_in_bg_setter_cancel_closure.Reset(
-        base::BindOnce(&TabDataAccess::OnSiteDataLoaded, std::move(policy),
-                       contents, std::move(reader)));
-    reader_raw->RegisterDataLoadedCallback(
-        tab_data->used_in_bg_setter_cancel_closure.callback());
-  }
+  performance_manager::PerformanceManager::CallOnGraph(
+      FROM_HERE, std::move(call_on_graph_cb));
 }
 
-void TabDataAccess::SetUsedInBgFromSiteCharacteristicsDataReader(
+void TabDataAccess::SetUsedInBgFromSiteData(
     TabData* tab_data,
     content::WebContents* contents,
-    std::unique_ptr<SiteCharacteristicsDataReader> reader) {
-  static const performance_manager::SiteFeatureUsage kNotUsed =
-      performance_manager::SiteFeatureUsage::kSiteFeatureNotInUse;
-  DCHECK(reader->DataLoaded());
-
+    TabData::SiteDataReaderData reader_data) {
   // Determine if background communication with the user is used. A pinned tab
   // has no visible tab title, so tab title updates can be ignored in that case.
   // The audio bit is ignored as tab can't play audio until they have been
   // visible at least once. We err on the side of caution, if unsure about a
   // feature (usually because of a lack of observation) then the feature is
   // considered as used.
-  bool used_in_bg = reader->UpdatesFaviconInBackground() != kNotUsed;
-  if (!tab_data->is_pinned && (reader->UpdatesTitleInBackground() != kNotUsed))
+  bool used_in_bg = reader_data.updates_favicon_in_bg;
+  if (!tab_data->is_pinned && reader_data.updates_title_in_bg)
     used_in_bg = true;
 
-  // TODO(sebmarchand): Consider that the tabs that are still under observation
-  // could be used in background.
-
   auto notif_permission =
       PermissionManagerFactory::GetForProfile(
           Profile::FromBrowserContext(contents->GetBrowserContext()))
@@ -207,20 +215,13 @@
   if (notif_permission.content_setting == CONTENT_SETTING_ALLOW)
     used_in_bg = true;
 
-  // Persist this data and detach from the reader. We need to detach from the
-  // reader in a separate task because this callback is actually being invoked
-  // by the reader itself; as we're the sole owner, we'll cause it to be deleted
-  // while several stack frames deep in its code, causing an immediate use
-  // after free.
   tab_data->used_in_bg = used_in_bg;
-  base::SequencedTaskRunnerHandle::Get()->DeleteSoon(FROM_HERE,
-                                                     std::move(reader));
 }
 
-void TabDataAccess::OnSiteDataLoaded(
+void TabDataAccess::OnSiteDataAvailable(
     base::WeakPtr<SessionRestorePolicy> policy,
     content::WebContents* contents,
-    std::unique_ptr<SiteCharacteristicsDataReader> reader) {
+    TabData::SiteDataReaderData reader_data) {
   if (!policy)
     return;
 
@@ -228,8 +229,7 @@
   DCHECK(it != policy->tab_data_.end());
   auto* tab_data = it->second.get();
 
-  SetUsedInBgFromSiteCharacteristicsDataReader(tab_data, contents,
-                                               std::move(reader));
+  SetUsedInBgFromSiteData(tab_data, contents, reader_data);
 
   // Score the tab and notify observers if the score has changed.
   if (policy->RescoreTabAfterDataLoaded(contents, tab_data))
@@ -300,8 +300,8 @@
 
   // The local database doesn't exist on Android at all.
 #if !defined(OS_ANDROID)
-  TabDataAccess::SetUsedInBgFromSiteCharacteristicsDB(
-      weak_factory_.GetWeakPtr(), tab_data, contents);
+  TabDataAccess::SetUsedInBgFromSiteDataDB(weak_factory_.GetWeakPtr(), tab_data,
+                                           contents);
 #endif  // !defined(OS_ANDROID)
 
   // Another tab has been added, so an existing all tabs scored notification may
@@ -557,7 +557,7 @@
 SessionRestorePolicy::TabData::TabData() = default;
 
 SessionRestorePolicy::TabData::~TabData() {
-  used_in_bg_setter_cancel_closure.Cancel();
+  used_in_bg_setter_cancel_callback.Cancel();
 }
 
 bool SessionRestorePolicy::TabData::UsedInBg() const {
diff --git a/chrome/browser/resource_coordinator/session_restore_policy.h b/chrome/browser/resource_coordinator/session_restore_policy.h
index 3b71654..c97f51e 100644
--- a/chrome/browser/resource_coordinator/session_restore_policy.h
+++ b/chrome/browser/resource_coordinator/session_restore_policy.h
@@ -184,9 +184,15 @@
     // change as new data becomes available.
     float score = 0.0f;
 
-    // Cancelable closure used to cancel the async initialization of the
-    // |used_in_bg| if it comes too late.
-    base::CancelableOnceClosure used_in_bg_setter_cancel_closure;
+    struct SiteDataReaderData {
+      bool updates_favicon_in_bg = false;
+      bool updates_title_in_bg = false;
+    };
+
+    // Cancelable callback used to cancel the async initialization of the
+    // |used_in_bg| bit.
+    base::CancelableOnceCallback<void(SiteDataReaderData)>
+        used_in_bg_setter_cancel_callback;
   };
 
   // This is safe to call from the constructor if |delegate_| is already
diff --git a/chrome/browser/resource_coordinator/session_restore_policy_unittest.cc b/chrome/browser/resource_coordinator/session_restore_policy_unittest.cc
index df850e0c..2c04494 100644
--- a/chrome/browser/resource_coordinator/session_restore_policy_unittest.cc
+++ b/chrome/browser/resource_coordinator/session_restore_policy_unittest.cc
@@ -8,6 +8,7 @@
 #include <vector>
 
 #include "base/bind.h"
+#include "base/memory/weak_ptr.h"
 #include "base/rand_util.h"
 #include "base/run_loop.h"
 #include "base/test/bind_test_util.h"
@@ -15,10 +16,16 @@
 #include "base/test/simple_test_tick_clock.h"
 #include "build/build_config.h"
 #include "chrome/browser/notifications/notification_permission_context.h"
-#include "chrome/browser/resource_coordinator/local_site_characteristics_data_unittest_utils.h"
+#if !defined(OS_ANDROID)
+#include "chrome/browser/performance_manager/test_support/site_data_utils.h"
+#endif
 #include "chrome/browser/resource_coordinator/tab_helper.h"
 #include "chrome/browser/resource_coordinator/tab_manager_features.h"
 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
+#include "components/performance_manager/persistence/site_data/site_data_impl.h"
+#include "components/performance_manager/persistence/site_data/site_data_writer.h"
+#include "components/performance_manager/public/decorators/site_data_recorder.h"
+#include "components/performance_manager/public/performance_manager.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/test/web_contents_tester.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -132,14 +139,19 @@
 
 }  // namespace
 
-class SessionRestorePolicyTest : public testing::ChromeTestHarnessWithLocalDB {
+class SessionRestorePolicyTest : public ChromeRenderViewHostTestHarness {
  public:
   SessionRestorePolicyTest() : delegate_(&clock_) {}
 
   ~SessionRestorePolicyTest() override {}
 
   void SetUp() override {
-    testing::ChromeTestHarnessWithLocalDB::SetUp();
+    ChromeRenderViewHostTestHarness::SetUp();
+
+#if !defined(OS_ANDROID)
+    // Some tests requires the SiteData database to be initialized.
+    site_data_harness_.SetUp();
+#endif
 
     // Set some reasonable delegate constants.
     delegate_.SetNumberOfCores(4);
@@ -154,15 +166,12 @@
 
   void TearDown() override {
 #if !defined(OS_ANDROID)
-    resource_coordinator::testing::GetLocalSiteCharacteristicsDataImplForWC(
-        contents1_.get())
-        ->NotifySiteUnloaded(performance_manager::TabVisibility::kBackground);
-    resource_coordinator::testing::GetLocalSiteCharacteristicsDataImplForWC(
-        contents2_.get())
-        ->NotifySiteUnloaded(performance_manager::TabVisibility::kBackground);
-    resource_coordinator::testing::GetLocalSiteCharacteristicsDataImplForWC(
-        contents3_.get())
-        ->NotifySiteUnloaded(performance_manager::TabVisibility::kBackground);
+    performance_manager::MarkWebContentsAsUnloadedInBackgroundInSiteDataDb(
+        contents1_.get());
+    performance_manager::MarkWebContentsAsUnloadedInBackgroundInSiteDataDb(
+        contents2_.get());
+    performance_manager::MarkWebContentsAsUnloadedInBackgroundInSiteDataDb(
+        contents3_.get());
 #endif
     if (policy_)
       policy_.reset();
@@ -175,7 +184,10 @@
 
     tab_for_scoring_.clear();
 
-    testing::ChromeTestHarnessWithLocalDB::TearDown();
+#if !defined(OS_ANDROID)
+    site_data_harness_.TearDown(profile());
+#endif
+    ChromeRenderViewHostTestHarness::TearDown();
   }
 
   void CreateTestContents() {
@@ -200,11 +212,10 @@
     tester->SetLastActiveTime(last_active);
 
 #if !defined(OS_ANDROID)
-    ResourceCoordinatorTabHelper::CreateForWebContents(contents.get());
     tester->NavigateAndCommit(url);
-    resource_coordinator::testing::MarkWebContentsAsLoadedInBackground(
+    performance_manager::MarkWebContentsAsLoadedInBackgroundInSiteDataDb(
         contents.get());
-    resource_coordinator::testing::ExpireLocalDBObservationWindows(
+    performance_manager::ExpireSiteDataObservationWindowsForWebContents(
         contents.get());
 #endif
     return contents;
@@ -252,6 +263,10 @@
   base::SimpleTestTickClock clock_;
   TestDelegate delegate_;
 
+#if !defined(OS_ANDROID)
+  performance_manager::SiteDataTestHarness site_data_harness_;
+#endif
+
   TabScoreChangeMock mock_;
   std::unique_ptr<TestSessionRestorePolicy> policy_;
 
@@ -486,13 +501,10 @@
   policy_->AddTabForScoring(contents5.get());
   WaitForFinalTabScores();
 
-  resource_coordinator::testing::GetLocalSiteCharacteristicsDataImplForWC(
-      contents4.get())
-      ->NotifySiteUnloaded(performance_manager::TabVisibility::kBackground);
-
-  resource_coordinator::testing::GetLocalSiteCharacteristicsDataImplForWC(
-      contents5.get())
-      ->NotifySiteUnloaded(performance_manager::TabVisibility::kBackground);
+  performance_manager::MarkWebContentsAsUnloadedInBackgroundInSiteDataDb(
+      contents4.get());
+  performance_manager::MarkWebContentsAsUnloadedInBackgroundInSiteDataDb(
+      contents5.get());
 }
 
 TEST_F(SessionRestorePolicyTest, CalculateAgeScore) {
@@ -622,9 +634,23 @@
   // Indicates that |contents1_| might update its title while in background,
   // this should set the |used_in_bg_| bit.
 
-  resource_coordinator::testing::GetLocalSiteCharacteristicsDataImplForWC(
-      contents1_.get())
-      ->NotifyUpdatesTitleInBackground();
+  base::RunLoop run_loop;
+  performance_manager::PerformanceManager::CallOnGraph(
+      FROM_HERE,
+      base::BindOnce(
+          [](base::WeakPtr<performance_manager::PageNode> page_node,
+             base::OnceClosure closure) {
+            EXPECT_TRUE(page_node);
+            auto* impl = performance_manager::GetSiteDataImplForPageNode(
+                page_node.get());
+            EXPECT_TRUE(impl);
+            impl->NotifyUpdatesTitleInBackground();
+            std::move(closure).Run();
+          },
+          performance_manager::PerformanceManager::GetPageNodeForWebContents(
+              contents1_.get()),
+          run_loop.QuitClosure()));
+  run_loop.Run();
 
   // Adding/Removing the tab for scoring will cause the callback to be called a
   // few times, ignore this.
@@ -655,12 +681,27 @@
   CreatePolicy(true);
   WaitForFinalTabScores();
 
-  performance_manager::SiteFeatureUsage title_feature_usage =
-      resource_coordinator::testing::GetLocalSiteCharacteristicsDataImplForWC(
-          contents.get())
-          ->UpdatesTitleInBackground();
-  EXPECT_EQ(performance_manager::SiteFeatureUsage::kSiteFeatureUsageUnknown,
-            title_feature_usage);
+  base::RunLoop run_loop;
+  performance_manager::PerformanceManager::CallOnGraph(
+      FROM_HERE,
+      base::BindOnce(
+          [](base::WeakPtr<performance_manager::PageNode> page_node,
+             base::OnceClosure closure) {
+            EXPECT_TRUE(page_node);
+            auto* impl = performance_manager::GetSiteDataImplForPageNode(
+                page_node.get());
+            EXPECT_TRUE(impl);
+            performance_manager::SiteFeatureUsage title_feature_usage =
+                impl->UpdatesTitleInBackground();
+            EXPECT_EQ(
+                performance_manager::SiteFeatureUsage::kSiteFeatureUsageUnknown,
+                title_feature_usage);
+            std::move(closure).Run();
+          },
+          performance_manager::PerformanceManager::GetPageNodeForWebContents(
+              contents.get()),
+          run_loop.QuitClosure()));
+  run_loop.Run();
 
   auto iter = policy_->tab_data_.find(contents.get());
   EXPECT_TRUE(iter != policy_->tab_data_.end());
diff --git a/chrome/browser/resource_coordinator/site_characteristics_data_reader.h b/chrome/browser/resource_coordinator/site_characteristics_data_reader.h
deleted file mode 100644
index 6f0c4868..0000000
--- a/chrome/browser/resource_coordinator/site_characteristics_data_reader.h
+++ /dev/null
@@ -1,43 +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 CHROME_BROWSER_RESOURCE_COORDINATOR_SITE_CHARACTERISTICS_DATA_READER_H_
-#define CHROME_BROWSER_RESOURCE_COORDINATOR_SITE_CHARACTERISTICS_DATA_READER_H_
-
-#include "components/performance_manager/persistence/site_data/feature_usage.h"
-
-#include "base/callback_forward.h"
-
-namespace resource_coordinator {
-
-// Pure virtual interface to read the characteristics of an origin. This is a
-// usable abstraction for both the local and global database.
-class SiteCharacteristicsDataReader {
- public:
-  SiteCharacteristicsDataReader() = default;
-  virtual ~SiteCharacteristicsDataReader() {}
-
-  // Accessors for the site characteristics usage.
-  virtual performance_manager::SiteFeatureUsage UpdatesFaviconInBackground()
-      const = 0;
-  virtual performance_manager::SiteFeatureUsage UpdatesTitleInBackground()
-      const = 0;
-  virtual performance_manager::SiteFeatureUsage UsesAudioInBackground()
-      const = 0;
-
-  // Returns true if this reader is fully initialized and serving the most
-  // authoritative data. This can initially return false as the backing store is
-  // loaded asynchronously.
-  virtual bool DataLoaded() const = 0;
-
-  // Registers a callback that will be invoked when the data backing this object
-  // has been loaded. Note that if "DataLoaded" is true at the time this is
-  // called it may immediately invoke the callback. The callback will not be
-  // invoked after this object has been destroyed.
-  virtual void RegisterDataLoadedCallback(base::OnceClosure&& callback) = 0;
-};
-
-}  // namespace resource_coordinator
-
-#endif  // CHROME_BROWSER_RESOURCE_COORDINATOR_SITE_CHARACTERISTICS_DATA_READER_H_
diff --git a/chrome/browser/resource_coordinator/site_characteristics_data_store.h b/chrome/browser/resource_coordinator/site_characteristics_data_store.h
deleted file mode 100644
index 59037e23c..0000000
--- a/chrome/browser/resource_coordinator/site_characteristics_data_store.h
+++ /dev/null
@@ -1,48 +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 CHROME_BROWSER_RESOURCE_COORDINATOR_SITE_CHARACTERISTICS_DATA_STORE_H_
-#define CHROME_BROWSER_RESOURCE_COORDINATOR_SITE_CHARACTERISTICS_DATA_STORE_H_
-
-#include <memory>
-
-#include "base/macros.h"
-#include "chrome/browser/resource_coordinator/site_characteristics_data_reader.h"
-#include "chrome/browser/resource_coordinator/site_characteristics_data_writer.h"
-#include "components/keyed_service/core/keyed_service.h"
-#include "components/performance_manager/persistence/site_data/tab_visibility.h"
-#include "url/origin.h"
-
-namespace resource_coordinator {
-
-// Pure virtual interface for a site characteristics data store.
-class SiteCharacteristicsDataStore : public KeyedService {
- public:
-  SiteCharacteristicsDataStore() = default;
-  ~SiteCharacteristicsDataStore() override {}
-
-  // Returns a SiteCharacteristicsDataReader for the given origin.
-  virtual std::unique_ptr<SiteCharacteristicsDataReader> GetReaderForOrigin(
-      const url::Origin& origin) = 0;
-
-  // Returns a SiteCharacteristicsDataWriter for the given origin.
-  //
-  // |tab_visibility| indicates the current visibility of the tab. The writer
-  // starts in an unloaded state, NotifyTabLoaded() must be called explicitly
-  // afterwards if the site is loaded.
-  virtual std::unique_ptr<SiteCharacteristicsDataWriter> GetWriterForOrigin(
-      const url::Origin& origin,
-      performance_manager::TabVisibility tab_visibility) = 0;
-
-  // Indicate if the SiteCharacteristicsDataWriter served by this data store
-  // actually persist informations.
-  virtual bool IsRecordingForTesting() = 0;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(SiteCharacteristicsDataStore);
-};
-
-}  // namespace resource_coordinator
-
-#endif  // CHROME_BROWSER_RESOURCE_COORDINATOR_SITE_CHARACTERISTICS_DATA_STORE_H_
diff --git a/chrome/browser/resource_coordinator/site_characteristics_data_writer.h b/chrome/browser/resource_coordinator/site_characteristics_data_writer.h
deleted file mode 100644
index cbfff1c..0000000
--- a/chrome/browser/resource_coordinator/site_characteristics_data_writer.h
+++ /dev/null
@@ -1,41 +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 CHROME_BROWSER_RESOURCE_COORDINATOR_SITE_CHARACTERISTICS_DATA_WRITER_H_
-#define CHROME_BROWSER_RESOURCE_COORDINATOR_SITE_CHARACTERISTICS_DATA_WRITER_H_
-
-#include "base/time/time.h"
-#include "components/performance_manager/persistence/site_data/tab_visibility.h"
-
-namespace resource_coordinator {
-
-// Pure virtual interface to record the observations made for an origin.
-class SiteCharacteristicsDataWriter {
- public:
-  SiteCharacteristicsDataWriter() = default;
-  virtual ~SiteCharacteristicsDataWriter() {}
-
-  // Records tab load/unload events.
-  virtual void NotifySiteLoaded() = 0;
-  virtual void NotifySiteUnloaded() = 0;
-
-  // Records visibility change events.
-  virtual void NotifySiteVisibilityChanged(
-      performance_manager::TabVisibility visibility) = 0;
-
-  // Records feature usage.
-  virtual void NotifyUpdatesFaviconInBackground() = 0;
-  virtual void NotifyUpdatesTitleInBackground() = 0;
-  virtual void NotifyUsesAudioInBackground() = 0;
-
-  // Records performance measurements.
-  virtual void NotifyLoadTimePerformanceMeasurement(
-      base::TimeDelta load_duration,
-      base::TimeDelta cpu_usage_estimate,
-      uint64_t private_footprint_kb_estimate) = 0;
-};
-
-}  // namespace resource_coordinator
-
-#endif  // CHROME_BROWSER_RESOURCE_COORDINATOR_SITE_CHARACTERISTICS_DATA_WRITER_H_
diff --git a/chrome/browser/resource_coordinator/tab_helper.cc b/chrome/browser/resource_coordinator/tab_helper.cc
index bf8ff8db..ccf38154 100644
--- a/chrome/browser/resource_coordinator/tab_helper.cc
+++ b/chrome/browser/resource_coordinator/tab_helper.cc
@@ -26,7 +26,6 @@
 #include "services/resource_coordinator/public/cpp/memory_instrumentation/memory_instrumentation.h"
 
 #if !defined(OS_ANDROID)
-#include "chrome/browser/resource_coordinator/local_site_characteristics_webcontents_observer.h"
 #include "chrome/browser/resource_coordinator/tab_manager.h"
 #endif
 
@@ -42,12 +41,6 @@
     rc_parts->tab_memory_metrics_reporter()->StartReporting(
         TabLoadTracker::Get());
   }
-
-#if !defined(OS_ANDROID)
-  local_site_characteristics_wc_observer_ =
-      std::make_unique<LocalSiteCharacteristicsWebContentsObserver>(
-          web_contents);
-#endif
 }
 
 ResourceCoordinatorTabHelper::~ResourceCoordinatorTabHelper() = default;
diff --git a/chrome/browser/resource_coordinator/tab_helper.h b/chrome/browser/resource_coordinator/tab_helper.h
index 744e552..d7619e4b 100644
--- a/chrome/browser/resource_coordinator/tab_helper.h
+++ b/chrome/browser/resource_coordinator/tab_helper.h
@@ -21,8 +21,6 @@
 
 namespace resource_coordinator {
 
-class LocalSiteCharacteristicsWebContentsObserver;
-
 class ResourceCoordinatorTabHelper
     : public content::WebContentsObserver,
       public content::WebContentsUserData<ResourceCoordinatorTabHelper> {
@@ -45,13 +43,6 @@
   ukm::SourceId ukm_source_id() const { return ukm_source_id_; }
   void SetUkmSourceIdForTest(ukm::SourceId id) { ukm_source_id_ = id; }
 
-#if !defined(OS_ANDROID)
-  LocalSiteCharacteristicsWebContentsObserver*
-  local_site_characteristics_wc_observer() {
-    return local_site_characteristics_wc_observer_.get();
-  }
-#endif
-
  private:
   explicit ResourceCoordinatorTabHelper(content::WebContents* web_contents);
 
@@ -60,12 +51,6 @@
 
   friend class content::WebContentsUserData<ResourceCoordinatorTabHelper>;
 
-#if !defined(OS_ANDROID)
-  std::unique_ptr<LocalSiteCharacteristicsWebContentsObserver>
-      local_site_characteristics_wc_observer_;
-#endif
-
-
   WEB_CONTENTS_USER_DATA_KEY_DECL();
 
   DISALLOW_COPY_AND_ASSIGN(ResourceCoordinatorTabHelper);
diff --git a/chrome/browser/resource_coordinator/tab_lifecycle_unit.cc b/chrome/browser/resource_coordinator/tab_lifecycle_unit.cc
index 927e4af..94e36de 100644
--- a/chrome/browser/resource_coordinator/tab_lifecycle_unit.cc
+++ b/chrome/browser/resource_coordinator/tab_lifecycle_unit.cc
@@ -20,7 +20,6 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/resource_coordinator/intervention_policy_database.h"
 #include "chrome/browser/resource_coordinator/lifecycle_unit_state.mojom.h"
-#include "chrome/browser/resource_coordinator/local_site_characteristics_data_store_factory.h"
 #include "chrome/browser/resource_coordinator/tab_activity_watcher.h"
 #include "chrome/browser/resource_coordinator/tab_helper.h"
 #include "chrome/browser/resource_coordinator/tab_lifecycle_observer.h"
diff --git a/chrome/browser/resource_coordinator/tab_lifecycle_unit_source_unittest.cc b/chrome/browser/resource_coordinator/tab_lifecycle_unit_source_unittest.cc
index aeb10384..628a7643 100644
--- a/chrome/browser/resource_coordinator/tab_lifecycle_unit_source_unittest.cc
+++ b/chrome/browser/resource_coordinator/tab_lifecycle_unit_source_unittest.cc
@@ -17,8 +17,6 @@
 #include "chrome/browser/resource_coordinator/lifecycle_unit.h"
 #include "chrome/browser/resource_coordinator/lifecycle_unit_observer.h"
 #include "chrome/browser/resource_coordinator/lifecycle_unit_source_observer.h"
-#include "chrome/browser/resource_coordinator/local_site_characteristics_data_unittest_utils.h"
-#include "chrome/browser/resource_coordinator/local_site_characteristics_webcontents_observer.h"
 #include "chrome/browser/resource_coordinator/tab_helper.h"
 #include "chrome/browser/resource_coordinator/tab_lifecycle_observer.h"
 #include "chrome/browser/resource_coordinator/tab_lifecycle_unit.h"
@@ -30,6 +28,7 @@
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/browser/ui/tabs/test_tab_strip_model_delegate.h"
 #include "chrome/common/pref_names.h"
+#include "chrome/test/base/chrome_render_view_host_test_harness.h"
 #include "chrome/test/base/testing_profile.h"
 #include "components/performance_manager/public/performance_manager.h"
 #include "components/performance_manager/test_support/graph_impl.h"
@@ -93,17 +92,16 @@
   return lifecycle_unit->GetLastFocusedTime() == base::TimeTicks::Max();
 }
 
-class TabLifecycleUnitSourceTest
-    : public testing::ChromeTestHarnessWithLocalDB {
+class TabLifecycleUnitSourceTest : public ChromeRenderViewHostTestHarness {
  protected:
   TabLifecycleUnitSourceTest()
-      : testing::ChromeTestHarnessWithLocalDB(
+      : ChromeRenderViewHostTestHarness(
             base::test::SingleThreadTaskEnvironment::TimeSource::MOCK_TIME) {
     task_runner_ = task_environment()->GetMainThreadTaskRunner();
   }
 
   void SetUp() override {
-    ChromeTestHarnessWithLocalDB::SetUp();
+    ChromeRenderViewHostTestHarness::SetUp();
 
     // Force TabManager/TabLifecycleUnitSource creation.
     g_browser_process->GetTabManager();
@@ -122,7 +120,7 @@
     tab_strip_model_.reset();
 
     task_environment()->RunUntilIdle();
-    ChromeTestHarnessWithLocalDB::TearDown();
+    ChromeRenderViewHostTestHarness::TearDown();
   }
 
   // If |focus_tab_strip| is true, focuses the tab strip. Then, appends 2 tabs
@@ -158,9 +156,6 @@
     base::RepeatingClosure run_loop_cb = base::BindRepeating(
         &base::test::SingleThreadTaskEnvironment::RunUntilIdle,
         base::Unretained(task_environment()));
-    testing::WaitForLocalDBEntryToBeInitialized(raw_first_web_contents,
-                                                run_loop_cb);
-    testing::ExpireLocalDBObservationWindows(raw_first_web_contents);
 
     // Add another foreground tab to the focused tab strip.
     task_environment()->FastForwardBy(kShortDelay);
@@ -186,9 +181,6 @@
     tab_strip_model_->AppendWebContents(std::move(second_web_contents), true);
     ::testing::Mock::VerifyAndClear(&source_observer_);
     EXPECT_TRUE(source_->GetTabLifecycleUnitExternal(raw_second_web_contents));
-    testing::WaitForLocalDBEntryToBeInitialized(raw_second_web_contents,
-                                                run_loop_cb);
-    testing::ExpireLocalDBObservationWindows(raw_second_web_contents);
 
     // TabStripModel doesn't update the visibility of its WebContents by itself.
     raw_first_web_contents->WasHidden();
diff --git a/chrome/browser/resource_coordinator/tab_lifecycle_unit_unittest.cc b/chrome/browser/resource_coordinator/tab_lifecycle_unit_unittest.cc
index 19708380..c0cc5805 100644
--- a/chrome/browser/resource_coordinator/tab_lifecycle_unit_unittest.cc
+++ b/chrome/browser/resource_coordinator/tab_lifecycle_unit_unittest.cc
@@ -21,8 +21,6 @@
 #include "chrome/browser/notifications/notification_permission_context.h"
 #include "chrome/browser/resource_coordinator/intervention_policy_database.h"
 #include "chrome/browser/resource_coordinator/lifecycle_unit_observer.h"
-#include "chrome/browser/resource_coordinator/local_site_characteristics_data_unittest_utils.h"
-#include "chrome/browser/resource_coordinator/local_site_characteristics_webcontents_observer.h"
 #include "chrome/browser/resource_coordinator/tab_helper.h"
 #include "chrome/browser/resource_coordinator/tab_lifecycle_observer.h"
 #include "chrome/browser/resource_coordinator/tab_lifecycle_unit_external.h"
@@ -40,6 +38,7 @@
 #include "chrome/browser/usb/usb_chooser_context.h"
 #include "chrome/browser/usb/usb_chooser_context_factory.h"
 #include "chrome/browser/usb/usb_tab_helper.h"
+#include "chrome/test/base/chrome_render_view_host_test_harness.h"
 #include "chrome/test/base/testing_profile.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/common/referrer.h"
@@ -93,7 +92,7 @@
   DISALLOW_COPY_AND_ASSIGN(MockLifecycleUnitObserver);
 };
 
-class TabLifecycleUnitTest : public testing::ChromeTestHarnessWithLocalDB {
+class TabLifecycleUnitTest : public ChromeRenderViewHostTestHarness {
  protected:
   using TabLifecycleUnit = TabLifecycleUnitSource::TabLifecycleUnit;
 
@@ -108,7 +107,7 @@
   }
 
   void SetUp() override {
-    ChromeTestHarnessWithLocalDB::SetUp();
+    ChromeRenderViewHostTestHarness::SetUp();
 
     metrics::DesktopSessionDurationTracker::Initialize();
     usage_clock_ = std::make_unique<UsageClock>();
@@ -139,12 +138,6 @@
     tab_strip_model_->AppendWebContents(std::move(second_web_contents),
                                         /*foreground=*/true);
     raw_second_web_contents->WasHidden();
-
-    testing::WaitForLocalDBEntryToBeInitialized(
-        web_contents_,
-        base::BindRepeating([]() { base::RunLoop().RunUntilIdle(); }));
-
-    testing::ExpireLocalDBObservationWindows(web_contents_);
   }
 
   void TearDown() override {
@@ -153,13 +146,9 @@
     tab_strip_model_.reset();
     usage_clock_.reset();
     metrics::DesktopSessionDurationTracker::CleanupForTesting();
-    ChromeTestHarnessWithLocalDB::TearDown();
+    ChromeRenderViewHostTestHarness::TearDown();
   }
 
-  void TestCannotDiscardBasedOnHeuristicUsage(
-      DecisionFailureReason failure_reason,
-      void (SiteCharacteristicsDataWriter::*notify_feature_usage_method)());
-
   // Create a new test WebContents and append it to the tab strip to allow
   // testing discarding operations on it. The returned WebContents is in the
   // hidden state.
diff --git a/chrome/browser/resource_coordinator/tab_load_tracker.cc b/chrome/browser/resource_coordinator/tab_load_tracker.cc
index f61f803c..d3baf1a 100644
--- a/chrome/browser/resource_coordinator/tab_load_tracker.cc
+++ b/chrome/browser/resource_coordinator/tab_load_tracker.cc
@@ -9,6 +9,7 @@
 #include "base/check_op.h"
 #include "base/stl_util.h"
 #include "chrome/browser/browser_process.h"
+#include "chrome/browser/prerender/chrome_prerender_contents_delegate.h"
 #include "chrome/browser/prerender/prerender_contents.h"
 #include "chrome/browser/resource_coordinator/resource_coordinator_parts.h"
 #include "content/public/browser/navigation_controller.h"
@@ -272,7 +273,8 @@
   // tabstrip UI or use a platform-independent tabstrip observer interface to
   // learn about |web_contents| associated with the tabstrip, rather than
   // checking for specific cases where |web_contents| is not a ui tab.
-  if (prerender::PrerenderContents::FromWebContents(web_contents) != nullptr)
+  if (prerender::ChromePrerenderContentsDelegate::FromWebContents(
+          web_contents) != nullptr)
     return false;
   if (web_contents->GetOuterWebContents())
     return false;
diff --git a/chrome/browser/resource_coordinator/tab_manager.cc b/chrome/browser/resource_coordinator/tab_manager.cc
index c43f26d..a85f262 100644
--- a/chrome/browser/resource_coordinator/tab_manager.cc
+++ b/chrome/browser/resource_coordinator/tab_manager.cc
@@ -35,7 +35,6 @@
 #include "chrome/browser/performance_manager/policies/policy_features.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/resource_coordinator/background_tab_navigation_throttle.h"
-#include "chrome/browser/resource_coordinator/local_site_characteristics_webcontents_observer.h"
 #include "chrome/browser/resource_coordinator/resource_coordinator_parts.h"
 #include "chrome/browser/resource_coordinator/tab_lifecycle_unit_external.h"
 #include "chrome/browser/resource_coordinator/tab_manager.h"
diff --git a/chrome/browser/resource_coordinator/tab_manager_browsertest.cc b/chrome/browser/resource_coordinator/tab_manager_browsertest.cc
index 9ecceefa2..d63cf659d 100644
--- a/chrome/browser/resource_coordinator/tab_manager_browsertest.cc
+++ b/chrome/browser/resource_coordinator/tab_manager_browsertest.cc
@@ -19,7 +19,6 @@
 #include "chrome/browser/media/webrtc/media_stream_capture_indicator.h"
 #include "chrome/browser/performance_manager/policies/policy_features.h"
 #include "chrome/browser/resource_coordinator/lifecycle_unit_observer.h"
-#include "chrome/browser/resource_coordinator/local_site_characteristics_data_unittest_utils.h"
 #include "chrome/browser/resource_coordinator/tab_lifecycle_observer.h"
 #include "chrome/browser/resource_coordinator/tab_lifecycle_unit.h"
 #include "chrome/browser/resource_coordinator/tab_lifecycle_unit_external.h"
@@ -167,8 +166,6 @@
                         ui::PAGE_TRANSITION_TYPED, false);
     content::WebContents* web_contents = browser()->OpenURL(open1);
     load1.Wait();
-    if (URLShouldBeStoredInLocalDatabase(first_url))
-      testing::ExpireLocalDBObservationWindows(web_contents);
 
     content::WindowedNotificationObserver load2(
         content::NOTIFICATION_LOAD_COMPLETED_MAIN_FRAME,
@@ -178,10 +175,6 @@
                         ui::PAGE_TRANSITION_TYPED, false);
     web_contents = browser()->OpenURL(open2);
     load2.Wait();
-    // Expire all the observation windows to prevent the discarding intervention
-    // to fail because of a lack of observations.
-    if (URLShouldBeStoredInLocalDatabase(second_url))
-      testing::ExpireLocalDBObservationWindows(web_contents);
 
     ASSERT_EQ(2, tsm()->count());
   }
diff --git a/chrome/browser/resource_coordinator/tab_manager_stats_collector_unittest.cc b/chrome/browser/resource_coordinator/tab_manager_stats_collector_unittest.cc
index 87d60fa..38413f3 100644
--- a/chrome/browser/resource_coordinator/tab_manager_stats_collector_unittest.cc
+++ b/chrome/browser/resource_coordinator/tab_manager_stats_collector_unittest.cc
@@ -18,7 +18,6 @@
 #include "base/time/tick_clock.h"
 #include "base/time/time.h"
 #include "chrome/browser/browser_process.h"
-#include "chrome/browser/resource_coordinator/local_site_characteristics_data_unittest_utils.h"
 #include "chrome/browser/resource_coordinator/tab_helper.h"
 #include "chrome/browser/resource_coordinator/tab_load_tracker.h"
 #include "chrome/browser/resource_coordinator/tab_manager_web_contents_data.h"
@@ -47,8 +46,7 @@
 constexpr TabLoadTracker::LoadingState LOADING = LoadingState::LOADING;
 constexpr TabLoadTracker::LoadingState LOADED = LoadingState::LOADED;
 
-class TabManagerStatsCollectorTest
-    : public testing::ChromeTestHarnessWithLocalDB {
+class TabManagerStatsCollectorTest : public ChromeRenderViewHostTestHarness {
  protected:
   TabManagerStatsCollectorTest()
       : scoped_context_(
@@ -89,12 +87,8 @@
     return tab_manager()->GetWebContentsData(contents);
   }
 
-  void RestoreTab(WebContents* contents) {
-    tab_manager()->OnWillRestoreTab(contents);
-  }
-
   void SetUp() override {
-    ChromeTestHarnessWithLocalDB::SetUp();
+    ChromeRenderViewHostTestHarness::SetUp();
 
     // Call the tab manager so that it is created right away.
     tab_manager();
@@ -103,33 +97,7 @@
   void TearDown() override {
     task_runner_->RunUntilIdle();
     scoped_context_.reset();
-    ChromeTestHarnessWithLocalDB::TearDown();
-  }
-
-  std::unique_ptr<WebContents> CreateWebContentsForUKM(ukm::SourceId id) {
-    std::unique_ptr<WebContents> contents(CreateTestWebContents());
-    ResourceCoordinatorTabHelper::CreateForWebContents(contents.get());
-    ResourceCoordinatorTabHelper::FromWebContents(contents.get())
-        ->SetUkmSourceIdForTest(id);
-    return contents;
-  }
-
-  std::unique_ptr<WebContents> CreateDiscardableWebContents(ukm::SourceId id) {
-    std::unique_ptr<WebContents> web_contents = CreateWebContentsForUKM(id);
-
-    // Commit an URL and mark the tab as "loaded" to allow discarding.
-    content::WebContentsTester::For(web_contents.get())
-        ->NavigateAndCommit(GURL("https://www.example.com"));
-    TabLoadTracker::Get()->TransitionStateForTesting(web_contents.get(),
-                                                     LoadingState::LOADED);
-
-    base::RepeatingClosure run_loop_cb = base::BindRepeating(
-        &base::TestMockTimeTaskRunner::RunUntilIdle, task_runner_);
-
-    testing::WaitForLocalDBEntryToBeInitialized(web_contents.get(),
-                                                run_loop_cb);
-    testing::ExpireLocalDBObservationWindows(web_contents.get());
-    return web_contents;
+    ChromeRenderViewHostTestHarness::TearDown();
   }
 
   scoped_refptr<base::TestMockTimeTaskRunner> task_runner_ =
diff --git a/chrome/browser/resource_coordinator/tab_manager_unittest.cc b/chrome/browser/resource_coordinator/tab_manager_unittest.cc
index 9ea1982..59ef477 100644
--- a/chrome/browser/resource_coordinator/tab_manager_unittest.cc
+++ b/chrome/browser/resource_coordinator/tab_manager_unittest.cc
@@ -29,8 +29,6 @@
 #include "chrome/browser/metrics/desktop_session_duration/desktop_session_duration_tracker.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/resource_coordinator/background_tab_navigation_throttle.h"
-#include "chrome/browser/resource_coordinator/local_site_characteristics_data_unittest_utils.h"
-#include "chrome/browser/resource_coordinator/local_site_characteristics_webcontents_observer.h"
 #include "chrome/browser/resource_coordinator/tab_helper.h"
 #include "chrome/browser/resource_coordinator/tab_lifecycle_unit.h"
 #include "chrome/browser/resource_coordinator/tab_lifecycle_unit_external.h"
@@ -46,6 +44,7 @@
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/browser/ui/tabs/test_tab_strip_model_delegate.h"
 #include "chrome/common/url_constants.h"
+#include "chrome/test/base/chrome_render_view_host_test_harness.h"
 #include "chrome/test/base/test_browser_window.h"
 #include "chrome/test/base/testing_profile.h"
 #include "components/variations/variations_associated_data.h"
@@ -110,7 +109,7 @@
 
 }  // namespace
 
-class TabManagerTest : public testing::ChromeTestHarnessWithLocalDB {
+class TabManagerTest : public ChromeRenderViewHostTestHarness {
  public:
   TabManagerTest()
       : scoped_context_(
@@ -133,14 +132,11 @@
     base::RepeatingClosure run_loop_cb = base::BindRepeating(
         &base::TestMockTimeTaskRunner::RunUntilIdle, task_runner_);
 
-    testing::WaitForLocalDBEntryToBeInitialized(web_contents.get(),
-                                                run_loop_cb);
-    testing::ExpireLocalDBObservationWindows(web_contents.get());
     return web_contents;
   }
 
   void SetUp() override {
-    ChromeTestHarnessWithLocalDB::SetUp();
+    ChromeRenderViewHostTestHarness::SetUp();
     tab_manager_ = g_browser_process->GetTabManager();
   }
 
@@ -164,7 +160,7 @@
     nav_handle3_.reset();
 
     // WebContents must be deleted before
-    // ChromeTestHarnessWithLocalDB::TearDown() deletes the
+    // ChromeRenderViewHostTestHarness::TearDown() deletes the
     // RenderProcessHost.
     contents1_.reset();
     contents2_.reset();
@@ -176,7 +172,7 @@
 
     task_runner_->RunUntilIdle();
     scoped_context_.reset();
-    ChromeTestHarnessWithLocalDB::TearDown();
+    ChromeRenderViewHostTestHarness::TearDown();
   }
 
   void PrepareTabs(const char* url1 = kTestUrl,
diff --git a/chrome/browser/resources/component_extension_resources.grd b/chrome/browser/resources/component_extension_resources.grd
index 3be4790..b4a38d65 100644
--- a/chrome/browser/resources/component_extension_resources.grd
+++ b/chrome/browser/resources/component_extension_resources.grd
@@ -118,6 +118,7 @@
         <include name="IDR_PDF_GESTURE_DETECTOR_JS" file="pdf/gesture_detector.js" type="BINDATA" />
         <include name="IDR_PDF_BROWSER_API_JS" file="pdf/browser_api.js" type="BINDATA" />
         <include name="IDR_PDF_METRICS_JS" file="pdf/metrics.js" type="BINDATA" />
+        <include name="IDR_PDF_LOCAL_STORAGE_PROXY_JS" file="pdf/local_storage_proxy.js" type="BINDATA" />
 
         <include name="IDR_PDF_ELEMENTS_SHARED_CSS_JS" file="${root_gen_dir}/chrome/browser/resources/pdf/elements/shared-css.js" use_base_dir="false" type="BINDATA" />
         <include name="IDR_PDF_SHARED_VARS_JS" file="${root_gen_dir}/chrome/browser/resources/pdf/elements/shared-vars.js" use_base_dir="false" type="BINDATA" />
@@ -136,6 +137,7 @@
         </if>
         <include name="IDR_PDF_VIEWER_PAGE_SELECTOR_JS" file="${root_gen_dir}/chrome/browser/resources/pdf/elements/viewer-page-selector.js" use_base_dir="false" type="BINDATA" />
         <include name="IDR_PDF_VIEWER_PASSWORD_SCREEN_JS" file="${root_gen_dir}/chrome/browser/resources/pdf/elements/viewer-password-screen.js" use_base_dir="false" type="BINDATA" />
+        <include name="IDR_PDF_VIEWER_PDF_SIDENAV_JS" file="${root_gen_dir}/chrome/browser/resources/pdf/elements/viewer-pdf-sidenav.js" use_base_dir="false" type="BINDATA"/>
         <include name="IDR_PDF_VIEWER_PDF_TOOLBAR_JS" file="${root_gen_dir}/chrome/browser/resources/pdf/elements/viewer-pdf-toolbar.js" use_base_dir="false" type="BINDATA" preprocess="true"/>
         <include name="IDR_PDF_VIEWER_PDF_TOOLBAR_NEW_JS" file="${root_gen_dir}/chrome/browser/resources/pdf/elements/viewer-pdf-toolbar-new.js" use_base_dir="false" type="BINDATA" preprocess="true"/>
         <include name="IDR_PDF_VIEWER_TOOLBAR_DROPDOWN_JS" file="${root_gen_dir}/chrome/browser/resources/pdf/elements/viewer-toolbar-dropdown.js" use_base_dir="false" type="BINDATA" />
diff --git a/chrome/browser/resources/discards/database_tab.js b/chrome/browser/resources/discards/database_tab.js
index c93db1e4..893b0710 100644
--- a/chrome/browser/resources/discards/database_tab.js
+++ b/chrome/browser/resources/discards/database_tab.js
@@ -15,10 +15,8 @@
 
 /**
  * Compares two db rows by their origin.
- * @param {discards.mojom.SiteCharacteristicsDatabaseEntry} a The first value
- *     being compared.
- * @param {discards.mojom.SiteCharacteristicsDatabaseEntry} b The second value
- *     being compared.
+ * @param {discards.mojom.SiteDataEntry} a The first value being compared.
+ * @param {discards.mojom.SiteDataEntry} b The second value being compared.
  * @return {number} A negative number if a < b, 0 if a === b, and a positive
  *     number if a > b.
  */
@@ -28,10 +26,8 @@
 
 /**
  * Compares two db rows by their dirty bit.
- * @param {discards.mojom.SiteCharacteristicsDatabaseEntry} a The first value
- *     being compared.
- * @param {discards.mojom.SiteCharacteristicsDatabaseEntry} b The second value
- *     being compared.
+ * @param {discards.mojom.SiteDataEntry} a The first value being compared.
+ * @param {discards.mojom.SiteDataEntry} b The second value being compared.
  * @return {number} A negative number if a < b, 0 if a === b, and a positive
  *     number if a > b.
  */
@@ -41,10 +37,8 @@
 
 /**
  * Compares two db rows by their last load time.
- * @param {discards.mojom.SiteCharacteristicsDatabaseEntry} a The first value
- *     being compared.
- * @param {discards.mojom.SiteCharacteristicsDatabaseEntry} b The second value
- *     being compared.
+ * @param {discards.mojom.SiteDataEntry} a The first value being compared.
+ * @param {discards.mojom.SiteDataEntry} b The second value being compared.
  * @return {number} A negative number if a < b, 0 if a === b, and a positive
  *     number if a > b.
  */
@@ -54,10 +48,8 @@
 
 /**
  * Compares two db rows by their CPU usage.
- * @param {discards.mojom.SiteCharacteristicsDatabaseEntry} a The first value
- *     being compared.
- * @param {discards.mojom.SiteCharacteristicsDatabaseEntry} b The second value
- *     being compared.
+ * @param {discards.mojom.SiteDataEntry} a The first value being compared.
+ * @param {discards.mojom.SiteDataEntry} b The second value being compared.
  * @return {number} A negative number if a < b, 0 if a === b, and a positive
  *     number if a > b.
  */
@@ -71,10 +63,8 @@
 
 /**
  * Compares two db rows by their memory usage.
- * @param {discards.mojom.SiteCharacteristicsDatabaseEntry} a The first value
- *     being compared.
- * @param {discards.mojom.SiteCharacteristicsDatabaseEntry} b The second value
- *     being compared.
+ * @param {discards.mojom.SiteDataEntry} a The first value being compared.
+ * @param {discards.mojom.SiteDataEntry} b The second value being compared.
  * @return {number} A negative number if a < b, 0 if a === b, and a positive
  *     number if a > b.
  */
@@ -88,10 +78,8 @@
 
 /**
  * Compares two db rows by their load duration.
- * @param {discards.mojom.SiteCharacteristicsDatabaseEntry} a The first value
- *     being compared.
- * @param {discards.mojom.SiteCharacteristicsDatabaseEntry} b The second value
- *     being compared.
+ * @param {discards.mojom.SiteDataEntry} a The first value being compared.
+ * @param {discards.mojom.SiteDataEntry} b The second value being compared.
  * @return {number} A negative number if a < b, 0 if a === b, and a positive
  *     number if a > b.
  */
@@ -107,8 +95,8 @@
 
 /**
  * @param {string} sortKey The sort key to get a function for.
- * @return {function(discards.mojom.SiteCharacteristicsDatabaseEntry,
-                     discards.mojom.SiteCharacteristicsDatabaseEntry): number}
+ * @return {function(discards.mojom.SiteDataEntry,
+                     discards.mojom.SiteDataEntry): number}
  *     A comparison function that compares two tab infos, returns
  *     negative number if a < b, 0 if a === b, and a positive
  *     number if a > b.
@@ -196,7 +184,7 @@
   properties: {
     /**
      * List of database rows.
-     * @private {?Array<!discards.mojom.SiteCharacteristicsDatabaseEntry>}
+     * @private {?Array<!discards.mojom.SiteDataEntry>}
      */
     rows_: {
       type: Array,
@@ -204,7 +192,7 @@
 
     /**
      * The database size response.
-     * @private {!discards.mojom.SiteCharacteristicsDatabaseSize}
+     * @private {!discards.mojom.SiteDataDatabaseSize}
      */
     size_: {
       type: Object,
@@ -269,10 +257,9 @@
    * @private
    */
   updateDbRows_() {
-    this.siteDataProvider_
-        .getSiteCharacteristicsDatabase(Object.keys(this.requestedOrigins_))
+    this.siteDataProvider_.getSiteDataArray(Object.keys(this.requestedOrigins_))
         .then(response => {
-          // Bail if the SiteCharacteristicsDatabase is turned off.
+          // Bail if the SiteData database is turned off.
           if (!response.result) {
             return;
           }
@@ -325,14 +312,13 @@
    * @private
    */
   updateDbSizes_() {
-    this.siteDataProvider_.getSiteCharacteristicsDatabaseSize().then(
-        response => {
-          // Bail if the SiteCharacteristicsDatabase is turned off.
-          if (!response.dbSize) {
-            return;
-          }
-          this.size_ = response.dbSize;
-        });
+    this.siteDataProvider_.getSiteDataDatabaseSize().then(response => {
+      // Bail if the SiteData database is turned off.
+      if (!response.dbSize) {
+        return;
+      }
+      this.size_ = response.dbSize;
+    });
   },
 
   /**
@@ -402,8 +388,7 @@
   },
 
   /**
-   * @param {?discards.mojom.SiteCharacteristicsFeature} feature The feature
-   *     in question.
+   * @param {?discards.mojom.SiteDataFeature} feature The feature in question.
    * @return {string} A human-readable string representing the feature.
    * @private
    */
diff --git a/chrome/browser/resources/nearby_share/BUILD.gn b/chrome/browser/resources/nearby_share/BUILD.gn
index eb7fb47..5a5d1fd9 100644
--- a/chrome/browser/resources/nearby_share/BUILD.gn
+++ b/chrome/browser/resources/nearby_share/BUILD.gn
@@ -42,6 +42,7 @@
     "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
     "//ui/webui/resources/cr_elements/cr_button:cr_button.m",
     "//ui/webui/resources/cr_elements/cr_checkbox:cr_checkbox.m",
+    "//ui/webui/resources/js:i18n_behavior.m",
   ]
 }
 
diff --git a/chrome/browser/resources/nearby_share/app.js b/chrome/browser/resources/nearby_share/app.js
index 30c3354..c295756 100644
--- a/chrome/browser/resources/nearby_share/app.js
+++ b/chrome/browser/resources/nearby_share/app.js
@@ -12,7 +12,6 @@
 import './shared/nearby_onboarding_page.m.js';
 import './nearby_confirmation_page.js';
 import './nearby_discovery_page.js';
-import './strings.m.js';
 
 import {html, Polymer} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
diff --git a/chrome/browser/resources/nearby_share/discovery_manager.js b/chrome/browser/resources/nearby_share/discovery_manager.js
index cf357f8..ab68433 100644
--- a/chrome/browser/resources/nearby_share/discovery_manager.js
+++ b/chrome/browser/resources/nearby_share/discovery_manager.js
@@ -9,6 +9,7 @@
 
 import 'chrome://resources/mojo/mojo/public/js/mojo_bindings_lite.js';
 import 'chrome://resources/mojo/mojo/public/mojom/base/unguessable_token.mojom-lite.js';
+import './nearby_share_target_types.mojom-lite.js';
 import './nearby_share.mojom-lite.js';
 
 /** @type {?nearbyShare.mojom.DiscoveryManagerInterface} */
diff --git a/chrome/browser/resources/nearby_share/nearby_confirmation_page.html b/chrome/browser/resources/nearby_share/nearby_confirmation_page.html
index e2e2bd0..6ff74f3 100644
--- a/chrome/browser/resources/nearby_share/nearby_confirmation_page.html
+++ b/chrome/browser/resources/nearby_share/nearby_confirmation_page.html
@@ -78,7 +78,7 @@
   <div id="process-row">
     <nearby-preview title="Doggo.jpg"></nearby-preview>
     <div id="confirmation-token">
-      Secure connection ID: [[confirmationToken]]
+      [[i18n('secureConnectionId', confirmationToken)]]
     </div>
     <nearby-progress share-target="[[shareTarget]]"></nearby-progress>
   </div>
diff --git a/chrome/browser/resources/nearby_share/nearby_confirmation_page.js b/chrome/browser/resources/nearby_share/nearby_confirmation_page.js
index 2aa4feaa..de2d74c7 100644
--- a/chrome/browser/resources/nearby_share/nearby_confirmation_page.js
+++ b/chrome/browser/resources/nearby_share/nearby_confirmation_page.js
@@ -14,13 +14,18 @@
 import 'chrome://resources/mojo/mojo/public/mojom/base/unguessable_token.mojom-lite.js';
 import './nearby_preview.js';
 import './nearby_progress.js';
+import './nearby_share_target_types.mojom-lite.js';
 import './nearby_share.mojom-lite.js';
+import './strings.m.js';
 
+import {I18nBehavior} from 'chrome://resources/js/i18n_behavior.m.js';
 import {html, Polymer} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 Polymer({
   is: 'nearby-confirmation-page',
 
+  behaviors: [I18nBehavior],
+
   _template: html`{__html_template__}`,
 
   properties: {
diff --git a/chrome/browser/resources/nearby_share/nearby_device.js b/chrome/browser/resources/nearby_share/nearby_device.js
index 3a0110e..82706d9 100644
--- a/chrome/browser/resources/nearby_share/nearby_device.js
+++ b/chrome/browser/resources/nearby_share/nearby_device.js
@@ -9,6 +9,7 @@
 import 'chrome://resources/mojo/mojo/public/js/mojo_bindings_lite.js';
 import 'chrome://resources/mojo/mojo/public/mojom/base/unguessable_token.mojom-lite.js';
 import './nearby_device_icon.js';
+import './nearby_share_target_types.mojom-lite.js';
 import './nearby_share.mojom-lite.js';
 
 import {html, Polymer} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
diff --git a/chrome/browser/resources/nearby_share/nearby_device_icon.js b/chrome/browser/resources/nearby_share/nearby_device_icon.js
index b03adff..13486460 100644
--- a/chrome/browser/resources/nearby_share/nearby_device_icon.js
+++ b/chrome/browser/resources/nearby_share/nearby_device_icon.js
@@ -12,6 +12,7 @@
 import 'chrome://resources/mojo/mojo/public/mojom/base/unguessable_token.mojom-lite.js';
 import 'chrome://resources/polymer/v3_0/iron-icon/iron-icon.js';
 import './icons.js';
+import './nearby_share_target_types.mojom-lite.js';
 import './nearby_share.mojom-lite.js';
 
 import {html, Polymer} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
diff --git a/chrome/browser/resources/nearby_share/nearby_discovery_page.js b/chrome/browser/resources/nearby_share/nearby_discovery_page.js
index 0b09848..70bfbd5 100644
--- a/chrome/browser/resources/nearby_share/nearby_discovery_page.js
+++ b/chrome/browser/resources/nearby_share/nearby_discovery_page.js
@@ -12,6 +12,7 @@
 import 'chrome://resources/mojo/mojo/public/mojom/base/unguessable_token.mojom-lite.js';
 import './nearby_device.js';
 import './nearby_preview.js';
+import './nearby_share_target_types.mojom-lite.js';
 import './nearby_share.mojom-lite.js';
 
 import {assert} from 'chrome://resources/js/assert.m.js';
diff --git a/chrome/browser/resources/nearby_share/nearby_progress.js b/chrome/browser/resources/nearby_share/nearby_progress.js
index b0a5021b..ad2bf23 100644
--- a/chrome/browser/resources/nearby_share/nearby_progress.js
+++ b/chrome/browser/resources/nearby_share/nearby_progress.js
@@ -12,6 +12,7 @@
 import 'chrome://resources/mojo/mojo/public/js/mojo_bindings_lite.js';
 import 'chrome://resources/mojo/mojo/public/mojom/base/unguessable_token.mojom-lite.js';
 import './nearby_device_icon.js';
+import './nearby_share_target_types.mojom-lite.js';
 import './nearby_share.mojom-lite.js';
 
 import {html, Polymer} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
diff --git a/chrome/browser/resources/nearby_share/nearby_share_dialog_resources.grd b/chrome/browser/resources/nearby_share/nearby_share_dialog_resources.grd
index dcd22cb..cd4ae84 100644
--- a/chrome/browser/resources/nearby_share/nearby_share_dialog_resources.grd
+++ b/chrome/browser/resources/nearby_share/nearby_share_dialog_resources.grd
@@ -19,6 +19,9 @@
       <include name="IDR_NEARBY_SHARE_MOJO_JS"
                file="${root_gen_dir}/chrome/browser/ui/webui/nearby_share/nearby_share.mojom-lite.js"
                use_base_dir="false" type="BINDATA"/>
+      <include name="IDR_NEARBY_SHARE_TARGET_TYPES_MOJO_JS"
+               file="${root_gen_dir}/chrome/browser/ui/webui/nearby_share/nearby_share_target_types.mojom-lite.js"
+               use_base_dir="false" type="BINDATA"/>
       <include name="IDR_NEARBY_SHARE_SETTINGS_MOJOM_LITE_JS"
                file="${root_gen_dir}/chrome/browser/ui/webui/nearby_share/public/mojom/nearby_share_settings.mojom-lite.js"
                use_base_dir="false" type="BINDATA"/>
diff --git a/chrome/browser/resources/pdf/BUILD.gn b/chrome/browser/resources/pdf/BUILD.gn
index aad74b7..1cbb07a1 100644
--- a/chrome/browser/resources/pdf/BUILD.gn
+++ b/chrome/browser/resources/pdf/BUILD.gn
@@ -113,6 +113,10 @@
   ]
 }
 
+js_library("local_storage_proxy") {
+  deps = [ "//ui/webui/resources/js:cr.m" ]
+}
+
 js_library("controller") {
   deps = [
     ":viewport",
@@ -150,6 +154,7 @@
     ":constants",
     ":controller",
     ":ink_controller",
+    ":local_storage_proxy",
     ":metrics",
     ":navigator",
     ":pdf_scripting_api",
@@ -214,6 +219,7 @@
     ":controller",
     ":gesture_detector",
     ":ink_controller",
+    ":local_storage_proxy",
     ":main",
     ":metrics",
     ":navigator",
diff --git a/chrome/browser/resources/pdf/elements/BUILD.gn b/chrome/browser/resources/pdf/elements/BUILD.gn
index d4dc2ec..8d05784 100644
--- a/chrome/browser/resources/pdf/elements/BUILD.gn
+++ b/chrome/browser/resources/pdf/elements/BUILD.gn
@@ -14,6 +14,7 @@
     ":viewer-page-indicator",
     ":viewer-page-selector",
     ":viewer-password-screen",
+    ":viewer-pdf-sidenav",
     ":viewer-pdf-toolbar",
     ":viewer-pdf-toolbar-new",
     ":viewer-toolbar-dropdown",
@@ -94,6 +95,13 @@
   ]
 }
 
+js_library("viewer-pdf-sidenav") {
+  deps = [
+    "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
+    "//ui/webui/resources/cr_elements/cr_icon_button:cr_icon_button.m",
+  ]
+}
+
 js_library("viewer-pdf-toolbar") {
   deps = [
     ":viewer-annotations-bar",
@@ -148,6 +156,7 @@
     "viewer-page-indicator.js",
     "viewer-page-selector.js",
     "viewer-password-screen.js",
+    "viewer-pdf-sidenav.js",
     "viewer-pdf-toolbar.js",
     "viewer-pdf-toolbar-new.js",
     "viewer-toolbar-dropdown.js",
diff --git a/chrome/browser/resources/pdf/elements/viewer-pdf-sidenav.html b/chrome/browser/resources/pdf/elements/viewer-pdf-sidenav.html
new file mode 100644
index 0000000..747024b
--- /dev/null
+++ b/chrome/browser/resources/pdf/elements/viewer-pdf-sidenav.html
@@ -0,0 +1,58 @@
+<style include="pdf-shared">
+  :host {
+    background-color: var(--viewer-pdf-toolbar-background-color);
+    display: flex;
+    min-width: 300px;
+  }
+
+  #icons {
+    display: flex;
+    flex-direction: column;
+    min-width: 48px;
+  }
+
+  /* These are dummy styles currently - replace with real content. */
+  #content {
+    color: white;
+    font-weight: 500;
+    line-height: 36px;
+    text-align: center;
+    width: 100%;
+  }
+
+  .selected cr-icon-button {
+    --cr-icon-button-fill-color: var(--google-blue-refresh-700);
+  }
+
+  .button-wrapper {
+    --highlight-border-width: 6px;
+    align-items: center;
+    border-inline-start: var(--highlight-border-width) solid transparent;
+    display: flex;
+    height: var(--cr-icon-button-size);
+    justify-content: center;
+    margin: 6px 0;
+    width: calc(100% - var(--highlight-border-width));
+  }
+
+  .button-wrapper.selected {
+    border-color: var(--google-blue-refresh-700);
+  }
+</style>
+<div id="icons">
+  <!-- TODO(rbpotter): Localize titles once the strings are finalized. -->
+  <div class$="button-wrapper [[thumbnailButtonClass_(thumbnailView_)]]">
+    <cr-icon-button iron-icon="pdf:thumbnails" role="tab"
+        title="Thumbnail view" on-click="onThumbnailClick_">
+    </cr-icon-button>
+  </div>
+  <div class$="button-wrapper [[outlineButtonClass_(thumbnailView_)]]">
+    <cr-icon-button iron-icon="pdf:doc-outline" role="tab"
+        title="Outline view" on-click="onOutlineClick_">
+    </cr-icon-button>
+  </div>
+</div>
+<div id="content">
+  <div id="thumbnails" hidden="[[!thumbnailView_]]">Thumbnails</div>
+  <div id="outline" hidden="[[thumbnailView_]]">Outline</div>
+</div>
diff --git a/chrome/browser/resources/pdf/elements/viewer-pdf-sidenav.js b/chrome/browser/resources/pdf/elements/viewer-pdf-sidenav.js
new file mode 100644
index 0000000..5fabf858
--- /dev/null
+++ b/chrome/browser/resources/pdf/elements/viewer-pdf-sidenav.js
@@ -0,0 +1,58 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import './shared-vars.js';
+import '../pdf_viewer_shared_style.js';
+import './icons.js';
+import 'chrome://resources/cr_elements/cr_icon_button/cr_icon_button.m.js';
+
+import {html, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+
+export class ViewerPdfSidenavElement extends PolymerElement {
+  static get is() {
+    return 'viewer-pdf-sidenav';
+  }
+
+  static get template() {
+    return html`{__html_template__}`;
+  }
+
+  static get properties() {
+    return {
+      /** @private */
+      thumbnailView_: {
+        type: Boolean,
+        value: true,
+      },
+    };
+  }
+
+  /** @private */
+  onThumbnailClick_() {
+    this.thumbnailView_ = true;
+  }
+
+  /** @private */
+  onOutlineClick_() {
+    this.thumbnailView_ = false;
+  }
+
+  /**
+   * @return {string}
+   * @private
+   */
+  outlineButtonClass_() {
+    return this.thumbnailView_ ? '' : 'selected';
+  }
+
+  /**
+   * @return {string}
+   * @private
+   */
+  thumbnailButtonClass_() {
+    return this.thumbnailView_ ? 'selected' : '';
+  }
+}
+
+customElements.define(ViewerPdfSidenavElement.is, ViewerPdfSidenavElement);
diff --git a/chrome/browser/resources/pdf/elements/viewer-pdf-toolbar-new.html b/chrome/browser/resources/pdf/elements/viewer-pdf-toolbar-new.html
index 182b20d7..c5efbd60 100644
--- a/chrome/browser/resources/pdf/elements/viewer-pdf-toolbar-new.html
+++ b/chrome/browser/resources/pdf/elements/viewer-pdf-toolbar-new.html
@@ -1,6 +1,5 @@
 <style include="pdf-shared">
   :host {
-    --pdf-toolbar-text-color: rgb(241, 241, 241);
     box-shadow:
         0 -2px 8px rgba(0, 0, 0, 0.09),
         0 4px 8px rgba(0, 0, 0, 0.06),
diff --git a/chrome/browser/resources/pdf/local_storage_proxy.js b/chrome/browser/resources/pdf/local_storage_proxy.js
new file mode 100644
index 0000000..92b36c31
--- /dev/null
+++ b/chrome/browser/resources/pdf/local_storage_proxy.js
@@ -0,0 +1,35 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import {addSingletonGetter} from 'chrome://resources/js/cr.m.js';
+
+/** @interface */
+class LocalStorageProxy {
+  /**
+   * @param {string} key
+   * @return {?string}
+   */
+  getItem(key) {}
+
+  /**
+   * @param {string} key
+   * @param {string} value
+   */
+  setItem(key, value) {}
+}
+
+/** @implements {LocalStorageProxy} */
+export class LocalStorageProxyImpl {
+  /** @override */
+  getItem(key) {
+    return window.localStorage.getItem(key);
+  }
+
+  /** @override */
+  setItem(key, value) {
+    window.localStorage.setItem(key, value);
+  }
+}
+
+addSingletonGetter(LocalStorageProxyImpl);
diff --git a/chrome/browser/resources/pdf/pdf_viewer.html b/chrome/browser/resources/pdf/pdf_viewer.html
index cb3c5891..799e1d44 100644
--- a/chrome/browser/resources/pdf/pdf_viewer.html
+++ b/chrome/browser/resources/pdf/pdf_viewer.html
@@ -1,4 +1,9 @@
-<style include="pdf-viewer-shared-style">
+<style include="pdf-viewer-shared-style cr-hidden-style">
+  viewer-pdf-sidenav,
+  viewer-pdf-toolbar-new {
+    --pdf-toolbar-text-color: rgb(241, 241, 241);
+  }
+
   #content-focus-rectangle {
     border: 2px solid white;
     border-radius: 2px;
@@ -48,11 +53,6 @@
     overflow: hidden;
   }
 
-  #sidenav {
-    background-color: var(--viewer-pdf-toolbar-background-color);
-    min-width: 260px;
-  }
-
   :host-context([pdf-viewer-update-enabled]) #plugin {
     position: initial;
   }
@@ -134,7 +134,8 @@
 
 <template is="dom-if" if="[[pdfViewerUpdateEnabled_]]">
   <div id="container">
-    <div id="sidenav" hidden="[[sidenavCollapsed_]]"></div>
+    <viewer-pdf-sidenav id="sidenav" hidden="[[sidenavCollapsed_]]">
+    </viewer-pdf-sidenav>
     <div id="main" on-scroll="onScroll_">
       <div id="sizer"></div>
       <div id="content"></div>
@@ -167,4 +168,4 @@
   <div id="content"></div>
 </template>
 
-<div id="content-focus-rectangle" hidden$="[[!documentHasFocus_]]"></div>
\ No newline at end of file
+<div id="content-focus-rectangle" hidden$="[[!documentHasFocus_]]"></div>
diff --git a/chrome/browser/resources/pdf/pdf_viewer.js b/chrome/browser/resources/pdf/pdf_viewer.js
index 2f2be36b..60d607e8 100644
--- a/chrome/browser/resources/pdf/pdf_viewer.js
+++ b/chrome/browser/resources/pdf/pdf_viewer.js
@@ -4,6 +4,7 @@
 
 import './elements/viewer-error-screen.js';
 import './elements/viewer-password-screen.js';
+import './elements/viewer-pdf-sidenav.js';
 import './elements/viewer-pdf-toolbar.js';
 import './elements/viewer-zoom-toolbar.js';
 import './elements/shared-vars.js';
@@ -12,6 +13,7 @@
 import './elements/viewer-form-warning.js';
 // </if>
 import './pdf_viewer_shared_style.js';
+import 'chrome://resources/cr_elements/hidden_style_css.m.js';
 
 import {assert, assertNotReached} from 'chrome://resources/js/assert.m.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
@@ -25,6 +27,7 @@
 // <if expr="chromeos">
 import {InkController} from './ink_controller.js';
 //</if>
+import {LocalStorageProxyImpl} from './local_storage_proxy.js';
 import {PDFMetrics} from './metrics.js';
 import {NavigatorDelegate, PdfNavigator} from './navigator.js';
 import {OpenPdfParamsParser} from './open_pdf_params_parser.js';
@@ -83,6 +86,9 @@
   }
 }
 
+/** @type {string} */
+const LOCAL_STORAGE_SIDENAV_COLLAPSED_KEY = 'sidenavCollapsed';
+
 export class PDFViewerElement extends PDFViewerBaseElement {
   static get is() {
     return 'pdf-viewer';
@@ -132,14 +138,7 @@
 
       isFormFieldFocused_: Boolean,
 
-      /** @private */
-      pdfViewerUpdateEnabled_: {
-        type: Boolean,
-        value: function() {
-          return document.documentElement.hasAttribute(
-              'pdf-viewer-update-enabled');
-        },
-      },
+      pdfViewerUpdateEnabled_: Boolean,
 
       docLength_: Number,
       // <if expr="chromeos">
@@ -217,9 +216,6 @@
     this.hadPassword_ = false;
 
     /** @private {boolean} */
-    this.sidenavCollapsed_ = false;
-
-    /** @private {boolean} */
     this.toolbarEnabled_ = false;
 
     /** @private {?ToolbarManager} */
@@ -250,7 +246,19 @@
     this.loadProgress_;
 
     /** @private {boolean} */
-    this.pdfViewerUpdateEnabled_;
+    this.pdfViewerUpdateEnabled_ =
+        document.documentElement.hasAttribute('pdf-viewer-update-enabled');
+
+    /** @private {boolean} */
+    this.sidenavCollapsed_ = false;
+
+    if (this.pdfViewerUpdateEnabled_) {
+      // TODO(dpapad): Add tests after crbug.com/1111459 is fixed.
+      this.sidenavCollapsed_ = Boolean(Number.parseInt(
+          LocalStorageProxyImpl.getInstance().getItem(
+              LOCAL_STORAGE_SIDENAV_COLLAPSED_KEY),
+          10));
+    }
   }
 
   /** @override */
@@ -880,7 +888,10 @@
 
   /** @private */
   onSidenavToggleClick_() {
+    assert(this.pdfViewerUpdateEnabled_);
     this.sidenavCollapsed_ = !this.sidenavCollapsed_;
+    LocalStorageProxyImpl.getInstance().setItem(
+        LOCAL_STORAGE_SIDENAV_COLLAPSED_KEY, this.sidenavCollapsed_ ? 1 : 0);
   }
 
   /**
diff --git a/chrome/browser/resources/settings/autofill_page/password_edit_dialog.html b/chrome/browser/resources/settings/autofill_page/password_edit_dialog.html
index 5f2af5132..5e7041bb 100644
--- a/chrome/browser/resources/settings/autofill_page/password_edit_dialog.html
+++ b/chrome/browser/resources/settings/autofill_page/password_edit_dialog.html
@@ -31,7 +31,7 @@
       }
     </style>
     <cr-dialog id="dialog" close-text="$i18n{close}">
-      <div slot="title">$i18n{passwordDetailsTitle}</div>
+      <div slot="title" id="title">[[getTitle_(isEditDialog_)]]</div>
       <div slot="body">
         <div hidden="[[!shouldShowStorageDetails]]" id="storageDetails">
           [[getStorageDetailsMessage_()]]
@@ -45,7 +45,9 @@
         <cr-input id="passwordInput" label="$i18n{editPasswordPasswordLabel}"
             type="[[getPasswordInputType_(password)]]"
             value="[[getPassword_(password)]]" on-blur="onInputBlur_"
-            class="password-input" readonly>
+            class="password-input" readonly="[[!isEditDialog_]]"
+            required="[[isEditDialog_]]" auto-validate="[[isEditDialog_]]"
+            invalid="{{inputInvalid_}}">
           <cr-icon-button id="showPasswordButton"
               class$="[[getIconClass_(password)]]" slot="suffix"
               hidden$="[[entry.federationText]]"
@@ -55,10 +57,18 @@
                   '$i18nPolymer{showPassword}')]]">
           </cr-icon-button>
         </cr-input>
+        <div id="footnote" hidden="[[!isEditDialog_]]">
+          [[getFootnote_()]]
+        </div>
       </div>
       <div slot="button-container">
-        <cr-button class="action-button" on-click="onActionButtonTap_">
-          $i18n{done}
+        <cr-button id="cancel" class="cancel-button" on-click="onCancel_"
+            hidden="[[!isEditDialog_]]">
+          $i18n{cancel}
+        </cr-button>
+        <cr-button id="actionButton" class="action-button"
+            on-click="onActionButtonTap_" disabled="[[inputInvalid_]]">
+          [[getActionButtonName_(isEditDialog_)]]
         </cr-button>
       </div>
     </cr-dialog>
diff --git a/chrome/browser/resources/settings/autofill_page/password_edit_dialog.js b/chrome/browser/resources/settings/autofill_page/password_edit_dialog.js
index ab68498..c63cac4e 100644
--- a/chrome/browser/resources/settings/autofill_page/password_edit_dialog.js
+++ b/chrome/browser/resources/settings/autofill_page/password_edit_dialog.js
@@ -17,12 +17,14 @@
 import '../settings_shared_css.m.js';
 import '../settings_vars_css.m.js';
 import './passwords_shared_css.js';
-import {I18nBehavior} from 'chrome://resources/js/i18n_behavior.m.js';
 
+import {I18nBehavior} from 'chrome://resources/js/i18n_behavior.m.js';
 import {html, Polymer} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
-import {ShowPasswordBehavior} from './show_password_behavior.js';
+import {loadTimeData} from '../i18n_setup.js';
 
+import {PasswordManagerImpl, PasswordManagerProxy} from './password_manager_proxy.js';
+import {ShowPasswordBehavior} from './show_password_behavior.js';
 
 Polymer({
   is: 'password-edit-dialog',
@@ -33,24 +35,89 @@
 
   properties: {
     shouldShowStorageDetails: {type: Boolean, value: false},
+
+    /** @private */
+    editPasswordsInSettings_: {
+      type: Boolean,
+      value() {
+        return loadTimeData.getBoolean('editPasswordsInSettings');
+      }
+    },
+
+    /**
+     * Check if editPasswordsInSettings flag is true and entry isn't federation
+     * credential.
+     * @private
+     * */
+    isEditDialog_: {
+      type: Boolean,
+      computed: 'computeIsEditDialog_(editPasswordsInSettings_, entry)'
+    },
+
+    /**
+     * Whether the input is invalid.
+     * @private
+     */
+    inputInvalid_: Boolean,
   },
 
+  /** @private {?PasswordManagerProxy} */
+  passwordManager_: null,
+
   /** @override */
   attached() {
+    this.passwordManager_ = PasswordManagerImpl.getInstance();
     this.$.dialog.showModal();
   },
 
+  /**
+   * Helper function that checks if editPasswordsInSettings flag is true and
+   * entry isn't federation credential.
+   * @return {boolean}
+   * @private
+   * */
+  computeIsEditDialog_() {
+    return this.editPasswordsInSettings_ && !this.entry.federationText;
+  },
+
   /** Closes the dialog. */
   close() {
     this.$.dialog.close();
   },
 
   /**
-   * Handler for tapping the 'done' button. Should just dismiss the dialog.
+   * Handler for tapping the 'cancel' button. Should just dismiss the dialog.
+   * @private
+   */
+  onCancel_() {
+    this.close();
+  },
+
+  /**
+   * Handler for tapping the 'done' or 'save' button depending on isEditDialog_.
+   * For 'save' button it should save new password. After pressing action button
+   * button the edit dialog should be closed.
    * @private
    */
   onActionButtonTap_() {
-    this.close();
+    if (this.isEditDialog_) {
+      this.passwordManager_
+          .changeSavedPassword(
+              this.entry.getAnyId(), this.$.passwordInput.value)
+          .finally(() => {
+            this.close();
+          });
+    } else {
+      this.close();
+    }
+  },
+
+  /**
+   * @return {string}
+   * @private
+   */
+  getActionButtonName_() {
+    return this.isEditDialog_ ? this.i18n('save') : this.i18n('done');
   },
 
   /** Manually de-select texts for readonly inputs. */
@@ -69,5 +136,24 @@
     return this.entry.isPresentInAccount() ?
         this.i18n('passwordStoredInAccount') :
         this.i18n('passwordStoredOnDevice');
+  },
+
+  /**
+   * @return {string}
+   * @private
+   */
+  getTitle_() {
+    // TODO(crbug.com/377410): Change strings like
+    // 'editCompromisedPasswordTitle' to 'editPasswordTitle'.
+    return this.isEditDialog_ ? this.i18n('editCompromisedPasswordTitle') :
+                                this.i18n('passwordDetailsTitle');
+  },
+
+  /**
+   * @return {string} The text to be displayed as the dialog's footnote.
+   * @private
+   */
+  getFootnote_() {
+    return this.i18n('editCompromisedPasswordFootnote', this.entry.urls.shown);
   }
 });
diff --git a/chrome/browser/resources/settings/autofill_page/passwords_list_handler.html b/chrome/browser/resources/settings/autofill_page/passwords_list_handler.html
index fa957236..2401a5d 100644
--- a/chrome/browser/resources/settings/autofill_page/passwords_list_handler.html
+++ b/chrome/browser/resources/settings/autofill_page/passwords_list_handler.html
@@ -18,7 +18,9 @@
             on-click="onMenuCopyPasswordButtonTap_">$i18n{copyPassword}</button>
       </template>
       <button id="menuEditPassword" class="dropdown-item"
-          on-click="onMenuEditPasswordTap_">$i18n{passwordViewDetails}</button>
+          on-click="onMenuEditPasswordTap_">
+        [[getMenuEditPasswordName_(isEditDialog_)]]
+      </button>
       <button id="menuRemovePassword" class="dropdown-item"
           on-click="onMenuRemovePasswordTap_">$i18n{removePassword}</button>
       <template is="dom-if" if="[[shouldShowMoveToAccountOption]]">
diff --git a/chrome/browser/resources/settings/autofill_page/passwords_list_handler.js b/chrome/browser/resources/settings/autofill_page/passwords_list_handler.js
index 6daf339..65a13dc4 100644
--- a/chrome/browser/resources/settings/autofill_page/passwords_list_handler.js
+++ b/chrome/browser/resources/settings/autofill_page/passwords_list_handler.js
@@ -87,6 +87,16 @@
       }
     },
 
+    /**
+     * Check if editPasswordsInSettings flag is true and entry isn't federation
+     * credential.
+     * @private
+     * */
+    isEditDialog_: {
+      type: Boolean,
+      computed: 'computeIsEditDialog_(editPasswordsInSettings_, activePassword)'
+    },
+
     /** @private */
     showPasswordEditDialog_: {type: Boolean, value: false},
 
@@ -103,7 +113,6 @@
      */
     activeDialogAnchor_: {type: Object, value: null},
 
-
     /**
      * The message displayed in the toast following a password removal.
      */
@@ -131,6 +140,17 @@
   },
 
   /**
+   * Helper function that checks if editPasswordsInSettings flag is true and
+   * entry isn't federation credential.
+   * @return {boolean}
+   * @private
+   * */
+  computeIsEditDialog_() {
+    return this.editPasswordsInSettings_ &&
+        (!this.activePassword || !this.activePassword.entry.federationText);
+  },
+
+  /**
    * Closes the toast manager.
    */
   onSavedPasswordOrExceptionRemoved() {
@@ -151,16 +171,25 @@
   },
 
   /**
-   * Shows the edit password dialog.
    * @param {!Event} e
    * @private
    */
   onMenuEditPasswordTap_(e) {
+    // TODO(crbug.com/377410): Add authentication if isEditDialog_ is true.
     e.preventDefault();
     this.$.menu.close();
     this.showPasswordEditDialog_ = true;
   },
 
+  /**
+   * @return {string}
+   * @private
+   */
+  getMenuEditPasswordName_() {
+    return this.isEditDialog_ ? this.i18n('editCompromisedPassword') :
+                                this.i18n('passwordViewDetails');
+  },
+
   /** @private */
   onPasswordEditDialogClosed_() {
     this.showPasswordEditDialog_ = false;
diff --git a/chrome/browser/resources/settings/chromeos/os_settings_page/os_settings_page.html b/chrome/browser/resources/settings/chromeos/os_settings_page/os_settings_page.html
index 00c722c..3f91f3c 100644
--- a/chrome/browser/resources/settings/chromeos/os_settings_page/os_settings_page.html
+++ b/chrome/browser/resources/settings/chromeos/os_settings_page/os_settings_page.html
@@ -2,6 +2,7 @@
 
 <link rel="import" href="chrome://resources/cr_elements/cr_button/cr_button.html">
 <link rel="import" href="chrome://resources/cr_elements/hidden_style_css.html">
+<link rel="import" href="chrome://resources/cr_elements/icons.html">
 <link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html">
 <link rel="import" href="chrome://resources/html/web_ui_listener_behavior.html">
 <link rel="import" href="main_page_behavior.html">
@@ -25,6 +26,10 @@
 <dom-module id="os-settings-page">
   <template>
     <style include="settings-page-styles cr-hidden-style settings-shared">
+      :host {
+        --google-yellow-50: #fef7e0;
+      }
+
       :host([is-subpage-animating]) {
         /* Prevent an unwanted horizontal scrollbar when transitioning back from
          * a sub-page. */
@@ -41,6 +46,21 @@
         margin-top: var(--cr-section-vertical-margin);
       }
 
+      .eol-warning-icon {
+        align-items: center;
+        background: var(--google-yellow-50);
+        border-radius: 50%;
+        display: flex;
+        height: 40px;
+        justify-content: center;
+        margin-inline-end: var(--cr-section-padding);
+        width: 40px;
+      }
+
+      .eol-warning-icon iron-icon {
+        margin: 0;
+      }
+
       #advancedToggle {
         --ink-color: currentColor;
         align-items: center;
@@ -97,6 +117,22 @@
     <template is="dom-if" if="[[showBasicPage_(
         currentRoute_, inSearchMode, hasExpandedSection_)]]">
       <div id="basicPage">
+        <template is="dom-if" if="[[computeShowUpdateRequiredEolBanner_(
+            hasExpandedSection_, showUpdateRequiredEolBanner_)]]">
+          <div id="updateRequiredEolBanner"
+              class="settings-box two-line banner">
+            <div class="eol-warning-icon">
+              <iron-icon icon="cr20:banner-warning"></iron-icon>
+            </div>
+            <settings-localized-link id="bannerText" class="start"
+                localized-string="$i18n{updateRequiredEolBannerText}">
+            </settings-localized-link>
+            <cr-icon-button title="$i18n{close}" id="closeUpdateRequiredEol"
+                class="icon-clear" on-click="onCloseEolBannerClicked_"
+                aria-describedby="bannerText">
+            </cr-icon-button>
+          </div>
+        </template>
         <div id="secondaryUserBanner" class="banner"
             hidden="[[!showSecondaryUserBanner_]]">
           <div id="secondaryUserIcon"></div>
diff --git a/chrome/browser/resources/settings/chromeos/os_settings_page/os_settings_page.js b/chrome/browser/resources/settings/chromeos/os_settings_page/os_settings_page.js
index b88f4c4d..2ff2bfa 100644
--- a/chrome/browser/resources/settings/chromeos/os_settings_page/os_settings_page.js
+++ b/chrome/browser/resources/settings/chromeos/os_settings_page/os_settings_page.js
@@ -87,6 +87,16 @@
       computed: 'computeShowSecondaryUserBanner_(hasExpandedSection_)',
     },
 
+    /**
+     * Whether to show banner indicating the user to return this device as an
+     * update is required as per policy but the device has reached end of life.
+     * @private
+     */
+    showUpdateRequiredEolBanner_: {
+      type: Boolean,
+      value: !!loadTimeData.getString('updateRequiredEolBannerText'),
+    },
+
     /** @private {!settings.Route|undefined} */
     currentRoute_: Object,
   },
@@ -201,6 +211,14 @@
   },
 
   /**
+   * @return {boolean}
+   * @private
+   */
+  computeShowUpdateRequiredEolBanner_() {
+    return !this.hasExpandedSection_ && this.showUpdateRequiredEolBanner_;
+  },
+
+  /**
    * @param {!AndroidAppsInfo} info
    * @private
    */
@@ -209,6 +227,15 @@
   },
 
   /**
+   * Hides the update required EOL banner. It is shown again when Settings is
+   * re-opened.
+   * @private
+   */
+  onCloseEolBannerClicked_() {
+    this.showUpdateRequiredEolBanner_ = false;
+  },
+
+  /**
    * Hides everything but the newly expanded subpage.
    * @private
    */
diff --git a/chrome/browser/resources/signin/profile_picker/navigation_behavior.js b/chrome/browser/resources/signin/profile_picker/navigation_behavior.js
index c004c44..0c3fbda 100644
--- a/chrome/browser/resources/signin/profile_picker/navigation_behavior.js
+++ b/chrome/browser/resources/signin/profile_picker/navigation_behavior.js
@@ -48,12 +48,17 @@
   switch (path) {
     case `/${Routes.NEW_PROFILE}`:
       history.replaceState(
-          {route: Routes.NEW_PROFILE, step: computeStep(Routes.NEW_PROFILE)},
+          {
+            route: Routes.NEW_PROFILE,
+            step: computeStep(Routes.NEW_PROFILE),
+            isFirst: true,
+          },
           '', path);
       break;
     default:
       history.replaceState(
-          {route: Routes.MAIN, step: computeStep(Routes.MAIN)}, '', '/');
+          {route: Routes.MAIN, step: computeStep(Routes.MAIN), isFirst: true},
+          '', '/');
   }
 }
 
@@ -83,16 +88,24 @@
       {
         route: route,
         step: computeStep(route),
+        isFirst: false,
       },
       '', route === Routes.MAIN ? '/' : `/${route}`);
   notifyObservers();
 }
 
 /**
- * Navigates to the previous route.
+ * Navigates to the previous route if it belongs to the profile picker
+ * otherwise to the main route.
  */
 export function navigateToPreviousRoute() {
-  // TODO(crbug.com/1063856): Add implementation.
+  // This can happen if the profile creation flow is opened directly from the
+  // profile menu.
+  if (history.state.isFirst) {
+    navigateTo(Routes.MAIN);
+  } else {
+    window.history.back();
+  }
 }
 
 /** @polymerBehavior */
@@ -118,4 +131,4 @@
    * @param {string} step
    */
   onRouteChange: function(route, step) {},
-};
\ No newline at end of file
+};
diff --git a/chrome/browser/resources/signin/profile_picker/profile_picker_app.html b/chrome/browser/resources/signin/profile_picker/profile_picker_app.html
index d068857..23a2704 100644
--- a/chrome/browser/resources/signin/profile_picker/profile_picker_app.html
+++ b/chrome/browser/resources/signin/profile_picker/profile_picker_app.html
@@ -6,7 +6,7 @@
   }
 </style>
 <cr-view-manager id="viewManager">
-  <profile-picker-main-view id="mainView" slot="view" class="active">
+  <profile-picker-main-view id="mainView" slot="view">
   </profile-picker-main-view>
 
   <cr-lazy-render id="profileTypeChoice">
diff --git a/chrome/browser/safe_browsing/delayed_warning_navigation_throttle.cc b/chrome/browser/safe_browsing/delayed_warning_navigation_throttle.cc
index 322de5c..c5b6cf5 100644
--- a/chrome/browser/safe_browsing/delayed_warning_navigation_throttle.cc
+++ b/chrome/browser/safe_browsing/delayed_warning_navigation_throttle.cc
@@ -5,6 +5,7 @@
 #include "chrome/browser/safe_browsing/delayed_warning_navigation_throttle.h"
 
 #include "base/feature_list.h"
+#include "chrome/browser/prerender/chrome_prerender_contents_delegate.h"
 #include "chrome/browser/prerender/prerender_contents.h"
 #include "chrome/browser/safe_browsing/user_interaction_observer.h"
 #include "components/safe_browsing/core/features.h"
@@ -29,7 +30,8 @@
     content::NavigationHandle* navigation_handle) {
   // If the tab is being prerendered, stop here before it breaks metrics.
   content::WebContents* web_contents = navigation_handle->GetWebContents();
-  if (prerender::PrerenderContents::FromWebContents(web_contents)) {
+  if (prerender::ChromePrerenderContentsDelegate::FromWebContents(
+          web_contents)) {
     return nullptr;
   }
 
diff --git a/chrome/browser/safe_browsing/ui_manager.cc b/chrome/browser/safe_browsing/ui_manager.cc
index 8e7f709a..210d7af 100644
--- a/chrome/browser/safe_browsing/ui_manager.cc
+++ b/chrome/browser/safe_browsing/ui_manager.cc
@@ -14,6 +14,7 @@
 #include "chrome/browser/history/history_service_factory.h"
 #include "chrome/browser/interstitials/enterprise_util.h"
 #include "chrome/browser/metrics/chrome_metrics_service_accessor.h"
+#include "chrome/browser/prerender/chrome_prerender_contents_delegate.h"
 #include "chrome/browser/prerender/prerender_contents.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/safe_browsing/safe_browsing_blocking_page.h"
@@ -110,8 +111,10 @@
     const security_interstitials::UnsafeResource& resource) {
   content::WebContents* web_contents = resource.web_contents_getter.Run();
   prerender::PrerenderContents* prerender_contents =
-      web_contents ? prerender::PrerenderContents::FromWebContents(web_contents)
-                   : nullptr;
+      web_contents
+          ? prerender::ChromePrerenderContentsDelegate::FromWebContents(
+                web_contents)
+          : nullptr;
   if (!web_contents || prerender_contents) {
     if (prerender_contents) {
       prerender_contents->Destroy(prerender::FINAL_STATUS_SAFE_BROWSING);
diff --git a/chrome/browser/safe_browsing/url_checker_delegate_impl.cc b/chrome/browser/safe_browsing/url_checker_delegate_impl.cc
index 9e2f1f45..9642d29 100644
--- a/chrome/browser/safe_browsing/url_checker_delegate_impl.cc
+++ b/chrome/browser/safe_browsing/url_checker_delegate_impl.cc
@@ -7,6 +7,7 @@
 #include "base/bind.h"
 #include "base/feature_list.h"
 #include "chrome/browser/browser_process.h"
+#include "chrome/browser/prerender/chrome_prerender_contents_delegate.h"
 #include "chrome/browser/prerender/prerender_contents.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_io_data.h"
@@ -34,7 +35,8 @@
   content::WebContents* web_contents = std::move(web_contents_getter).Run();
   if (web_contents) {
     prerender::PrerenderContents* prerender_contents =
-        prerender::PrerenderContents::FromWebContents(web_contents);
+        prerender::ChromePrerenderContentsDelegate::FromWebContents(
+            web_contents);
     if (prerender_contents)
       prerender_contents->Destroy(prerender::FINAL_STATUS_SAFE_BROWSING);
   }
@@ -48,7 +50,8 @@
   content::WebContents* web_contents = web_contents_getter.Run();
   // Don't delay the interstitial for prerender pages.
   if (!web_contents ||
-      prerender::PrerenderContents::FromWebContents(web_contents)) {
+      prerender::ChromePrerenderContentsDelegate::FromWebContents(
+          web_contents)) {
     SafeBrowsingUIManager::StartDisplayingBlockingPage(ui_manager, resource);
     return;
   }
diff --git a/chrome/browser/safety_check/android/java/src/org/chromium/chrome/browser/safety_check/SafetyCheckViewBinder.java b/chrome/browser/safety_check/android/java/src/org/chromium/chrome/browser/safety_check/SafetyCheckViewBinder.java
index b71c7085..ecd3983 100644
--- a/chrome/browser/safety_check/android/java/src/org/chromium/chrome/browser/safety_check/SafetyCheckViewBinder.java
+++ b/chrome/browser/safety_check/android/java/src/org/chromium/chrome/browser/safety_check/SafetyCheckViewBinder.java
@@ -100,9 +100,10 @@
             case SafeBrowsingState.ENABLED_ENHANCED:
                 return R.drawable.ic_done_blue;
             case SafeBrowsingState.DISABLED:
-            case SafeBrowsingState.DISABLED_BY_ADMIN:
             case SafeBrowsingState.ERROR:
                 return R.drawable.ic_info_outline_grey_24dp;
+            case SafeBrowsingState.DISABLED_BY_ADMIN:
+                return R.drawable.ic_business;
             default:
                 assert false : "Unknown SafeBrowsingState value.";
         }
diff --git a/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/share_sheet/ShareSheetPropertyModelBuilder.java b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/share_sheet/ShareSheetPropertyModelBuilder.java
index f554b5bf..cb16aff 100644
--- a/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/share_sheet/ShareSheetPropertyModelBuilder.java
+++ b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/share_sheet/ShareSheetPropertyModelBuilder.java
@@ -28,6 +28,7 @@
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
@@ -44,7 +45,7 @@
 // TODO(crbug/1022172): Should be package-protected once modularization is complete.
 public class ShareSheetPropertyModelBuilder {
     @IntDef({ContentType.LINK_PAGE_VISIBLE, ContentType.LINK_PAGE_NOT_VISIBLE, ContentType.TEXT,
-            ContentType.IMAGE, ContentType.OTHER_FILE_TYPE, ContentType.HIGHLIGHTED_TEXT})
+            ContentType.IMAGE, ContentType.HIGHLIGHTED_TEXT, ContentType.OTHER_FILE_TYPE})
     @Retention(RetentionPolicy.SOURCE)
     @interface ContentType {
         int LINK_PAGE_VISIBLE = 0;
@@ -229,14 +230,14 @@
                     ShareHelper.getShareLinkAppCompatibilityIntent(), 0);
         }
         List<ResolveInfo> resolveInfoList = new ArrayList<>();
-        if (contentTypes.contains(ContentType.LINK_PAGE_NOT_VISIBLE)
-                || contentTypes.contains(ContentType.LINK_PAGE_VISIBLE)
-                || contentTypes.contains(ContentType.TEXT)) {
+        if (!Collections.disjoint(contentTypes,
+                    Arrays.asList(ContentType.LINK_PAGE_NOT_VISIBLE, ContentType.LINK_PAGE_VISIBLE,
+                            ContentType.TEXT, ContentType.HIGHLIGHTED_TEXT))) {
             resolveInfoList.addAll(mPackageManager.queryIntentActivities(
                     ShareHelper.getShareLinkAppCompatibilityIntent(), 0));
         }
-        if (contentTypes.contains(ContentType.IMAGE)
-                || contentTypes.contains(ContentType.OTHER_FILE_TYPE)) {
+        if (!Collections.disjoint(
+                    contentTypes, Arrays.asList(ContentType.IMAGE, ContentType.OTHER_FILE_TYPE))) {
             resolveInfoList.addAll(mPackageManager.queryIntentActivities(
                     ShareHelper.createShareFileAppCompatibilityIntent(fileContentType), 0));
         }
diff --git a/chrome/browser/ssl/ssl_browsertest.cc b/chrome/browser/ssl/ssl_browsertest.cc
index 1d8a3c1..5da0460 100644
--- a/chrome/browser/ssl/ssl_browsertest.cc
+++ b/chrome/browser/ssl/ssl_browsertest.cc
@@ -3507,7 +3507,8 @@
     }
 
     EXPECT_EQ(expected_show_blocked,
-              content_settings::TabSpecificContentSettings::FromWebContents(tab)
+              content_settings::TabSpecificContentSettings::GetForFrame(
+                  tab->GetMainFrame())
                   ->IsContentBlocked(ContentSettingsType::MIXEDSCRIPT));
     ssl_test_util::CheckSecurityState(
         tab, CertError::NONE,
@@ -3530,7 +3531,8 @@
     }
 
     EXPECT_EQ(expected_show_blocked_after_allow,
-              content_settings::TabSpecificContentSettings::FromWebContents(tab)
+              content_settings::TabSpecificContentSettings::GetForFrame(
+                  tab->GetMainFrame())
                   ->IsContentBlocked(ContentSettingsType::MIXEDSCRIPT));
     ssl_test_util::CheckSecurityState(
         tab, CertError::NONE,
@@ -3555,9 +3557,9 @@
 
   void CheckErrorStateIsCleared() {
     WebContents* tab = browser()->tab_strip_model()->GetActiveWebContents();
-    EXPECT_FALSE(
-        content_settings::TabSpecificContentSettings::FromWebContents(tab)
-            ->IsContentBlocked(ContentSettingsType::MIXEDSCRIPT));
+    EXPECT_FALSE(content_settings::TabSpecificContentSettings::GetForFrame(
+                     tab->GetMainFrame())
+                     ->IsContentBlocked(ContentSettingsType::MIXEDSCRIPT));
     ssl_test_util::CheckSecurityState(tab, CertError::NONE,
                                       security_state::NONE, AuthState::NONE);
     EXPECT_FALSE(SecurityStateTabHelper::FromWebContents(tab)
diff --git a/chrome/browser/subresource_filter/chrome_subresource_filter_client.cc b/chrome/browser/subresource_filter/chrome_subresource_filter_client.cc
index 86fde3de..4f395895 100644
--- a/chrome/browser/subresource_filter/chrome_subresource_filter_client.cc
+++ b/chrome/browser/subresource_filter/chrome_subresource_filter_client.cc
@@ -161,9 +161,13 @@
       InfoBarService::FromWebContents(web_contents());
   AdsBlockedInfobarDelegate::Create(infobar_service);
 #endif
+  // TODO(https://crbug.com/1103176): Plumb the actual frame reference here
+  // (it comes  from
+  // ContentSubresourceFilterThrottleManager::DidDisallowFirstSubresource, which
+  // comes from a specific frame).
   content_settings::TabSpecificContentSettings* content_settings =
-      content_settings::TabSpecificContentSettings::FromWebContents(
-          web_contents());
+      content_settings::TabSpecificContentSettings::GetForFrame(
+          web_contents()->GetMainFrame());
   content_settings->OnContentBlocked(ContentSettingsType::ADS);
 
   LogAction(SubresourceFilterAction::kUIShown);
diff --git a/chrome/browser/subresource_filter/subresource_filter_popup_browsertest.cc b/chrome/browser/subresource_filter/subresource_filter_popup_browsertest.cc
index 7efd118..d5dda04 100644
--- a/chrome/browser/subresource_filter/subresource_filter_popup_browsertest.cc
+++ b/chrome/browser/subresource_filter/subresource_filter_popup_browsertest.cc
@@ -135,8 +135,8 @@
   EXPECT_TRUE(content::ExecuteScriptAndExtractBool(web_contents, "openWindow()",
                                                    &opened_window));
   EXPECT_TRUE(opened_window);
-  EXPECT_FALSE(content_settings::TabSpecificContentSettings::FromWebContents(
-                   web_contents)
+  EXPECT_FALSE(content_settings::TabSpecificContentSettings::GetForFrame(
+                   web_contents->GetMainFrame())
                    ->IsContentBlocked(ContentSettingsType::POPUPS));
 
   // Navigate again to trigger histogram logging. Make sure the navigation
@@ -184,8 +184,8 @@
   EXPECT_FALSE(opened_window);
   tester.ExpectTotalCount(kSubresourceFilterActionsHistogram, 0);
   // Make sure the popup UI was shown.
-  EXPECT_TRUE(content_settings::TabSpecificContentSettings::FromWebContents(
-                  web_contents)
+  EXPECT_TRUE(content_settings::TabSpecificContentSettings::GetForFrame(
+                  web_contents->GetMainFrame())
                   ->IsContentBlocked(ContentSettingsType::POPUPS));
 
   // Block again.
@@ -203,8 +203,8 @@
                                                    &opened_window));
   EXPECT_TRUE(opened_window);
   // Popup UI should not be shown.
-  EXPECT_FALSE(content_settings::TabSpecificContentSettings::FromWebContents(
-                   web_contents)
+  EXPECT_FALSE(content_settings::TabSpecificContentSettings::GetForFrame(
+                   web_contents->GetMainFrame())
                    ->IsContentBlocked(ContentSettingsType::POPUPS));
 }
 
@@ -344,8 +344,8 @@
   EXPECT_TRUE(content::ExecuteScript(web_contents, "openWindow()"));
   tester.ExpectTotalCount(kSubresourceFilterActionsHistogram, 0);
 
-  EXPECT_TRUE(content_settings::TabSpecificContentSettings::FromWebContents(
-                  web_contents)
+  EXPECT_TRUE(content_settings::TabSpecificContentSettings::GetForFrame(
+                  web_contents->GetMainFrame())
                   ->IsContentBlocked(ContentSettingsType::POPUPS));
   const bool enable_adblock_on_abusive_sites = GetParam();
   EXPECT_EQ(enable_adblock_on_abusive_sites, AreDisallowedRequestsBlocked());
@@ -360,8 +360,8 @@
   navigation_observer.Wait();
 
   // Popup UI should not be shown.
-  EXPECT_FALSE(content_settings::TabSpecificContentSettings::FromWebContents(
-                   web_contents)
+  EXPECT_FALSE(content_settings::TabSpecificContentSettings::GetForFrame(
+                   web_contents->GetMainFrame())
                    ->IsContentBlocked(ContentSettingsType::POPUPS));
   EXPECT_FALSE(AreDisallowedRequestsBlocked());
 }
@@ -382,8 +382,8 @@
   EXPECT_TRUE(content::ExecuteScriptAndExtractBool(web_contents, "openWindow()",
                                                    &sent_open));
   EXPECT_TRUE(sent_open);
-  EXPECT_TRUE(content_settings::TabSpecificContentSettings::FromWebContents(
-                  web_contents)
+  EXPECT_TRUE(content_settings::TabSpecificContentSettings::GetForFrame(
+                  web_contents->GetMainFrame())
                   ->IsContentBlocked(ContentSettingsType::POPUPS));
   const bool enable_adblock_on_abusive_sites = GetParam();
   EXPECT_EQ(enable_adblock_on_abusive_sites, AreDisallowedRequestsBlocked());
@@ -405,8 +405,8 @@
 
   content::WebContents* web_contents =
       browser()->tab_strip_model()->GetActiveWebContents();
-  EXPECT_FALSE(content_settings::TabSpecificContentSettings::FromWebContents(
-                   web_contents)
+  EXPECT_FALSE(content_settings::TabSpecificContentSettings::GetForFrame(
+                   web_contents->GetMainFrame())
                    ->IsContentBlocked(ContentSettingsType::POPUPS));
   const bool enable_adblock_on_abusive_sites = GetParam();
   EXPECT_EQ(enable_adblock_on_abusive_sites, AreDisallowedRequestsBlocked());
diff --git a/chrome/browser/task_manager/providers/per_profile_worker_task_tracker.h b/chrome/browser/task_manager/providers/per_profile_worker_task_tracker.h
index a75ab850..48d1199 100644
--- a/chrome/browser/task_manager/providers/per_profile_worker_task_tracker.h
+++ b/chrome/browser/task_manager/providers/per_profile_worker_task_tracker.h
@@ -14,7 +14,7 @@
 #include "content/public/browser/service_worker_context.h"
 #include "content/public/browser/service_worker_context_observer.h"
 #include "content/public/browser/shared_worker_service.h"
-#include "third_party/blink/public/common/tokens/worker_tokens.h"
+#include "third_party/blink/public/common/tokens/tokens.h"
 
 class Profile;
 
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index 2c20670..97fe5ec2ac 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -925,6 +925,10 @@
       "commander/commander_controller.h",
       "commander/commander_view_model.cc",
       "commander/commander_view_model.h",
+      "commander/fuzzy_finder.cc",
+      "commander/fuzzy_finder.h",
+      "commander/simple_command_source.cc",
+      "commander/simple_command_source.h",
       "confirm_bubble_model.cc",
       "confirm_bubble_model.h",
       "content_settings/content_setting_bubble_model.cc",
@@ -1505,6 +1509,7 @@
       "//components/keep_alive_registry",
       "//components/network_session_configurator/common",
       "//components/page_load_metrics/browser",
+      "//components/performance_manager:site_data_proto",
       "//components/profile_metrics",
       "//components/safety_check",
       "//components/search_provider_logos",
diff --git a/chrome/browser/ui/blocked_content/popup_tracker_browsertest.cc b/chrome/browser/ui/blocked_content/popup_tracker_browsertest.cc
index b3561a6..9312c47e2 100644
--- a/chrome/browser/ui/blocked_content/popup_tracker_browsertest.cc
+++ b/chrome/browser/ui/blocked_content/popup_tracker_browsertest.cc
@@ -290,8 +290,8 @@
 
   // Is blocked by the popup blocker.
   ui_test_utils::NavigateToURL(browser(), url);
-  EXPECT_TRUE(content_settings::TabSpecificContentSettings::FromWebContents(
-                  web_contents)
+  EXPECT_TRUE(content_settings::TabSpecificContentSettings::GetForFrame(
+                  web_contents->GetMainFrame())
                   ->IsContentBlocked(ContentSettingsType::POPUPS));
 
   // Click through to open the popup.
diff --git a/chrome/browser/ui/blocked_content/safe_browsing_triggered_popup_blocker_browsertest.cc b/chrome/browser/ui/blocked_content/safe_browsing_triggered_popup_blocker_browsertest.cc
index bd467db..1d3e081f 100644
--- a/chrome/browser/ui/blocked_content/safe_browsing_triggered_popup_blocker_browsertest.cc
+++ b/chrome/browser/ui/blocked_content/safe_browsing_triggered_popup_blocker_browsertest.cc
@@ -247,8 +247,9 @@
   EXPECT_TRUE(content::ExecuteScriptAndExtractBool(web_contents, "openWindow()",
                                                    &opened_window));
   EXPECT_TRUE(opened_window);
-  EXPECT_FALSE(TabSpecificContentSettings::FromWebContents(web_contents)
-                   ->IsContentBlocked(ContentSettingsType::POPUPS));
+  EXPECT_FALSE(
+      TabSpecificContentSettings::GetForFrame(web_contents->GetMainFrame())
+          ->IsContentBlocked(ContentSettingsType::POPUPS));
 }
 
 IN_PROC_BROWSER_TEST_F(SafeBrowsingTriggeredPopupBlockerDisabledTest,
@@ -266,8 +267,9 @@
   EXPECT_TRUE(content::ExecuteScriptAndExtractBool(web_contents, "openWindow()",
                                                    &opened_window));
   EXPECT_TRUE(opened_window);
-  EXPECT_FALSE(TabSpecificContentSettings::FromWebContents(web_contents)
-                   ->IsContentBlocked(ContentSettingsType::POPUPS));
+  EXPECT_FALSE(
+      TabSpecificContentSettings::GetForFrame(web_contents->GetMainFrame())
+          ->IsContentBlocked(ContentSettingsType::POPUPS));
 
   RoundTripAndVerifyLogMessages(console_observer, web_contents, {},
                                 {blocked_content::kAbusiveWarnMessage,
@@ -296,8 +298,9 @@
   EXPECT_TRUE(content::ExecuteScriptAndExtractBool(web_contents, "openWindow()",
                                                    &opened_window));
   EXPECT_TRUE(opened_window);
-  EXPECT_FALSE(TabSpecificContentSettings::FromWebContents(web_contents)
-                   ->IsContentBlocked(ContentSettingsType::POPUPS));
+  EXPECT_FALSE(
+      TabSpecificContentSettings::GetForFrame(web_contents->GetMainFrame())
+          ->IsContentBlocked(ContentSettingsType::POPUPS));
 
   // Since the policy change can take effect without browser restart, verify
   // that enabling the policy here should disallow opening new tabs or windows
@@ -321,8 +324,9 @@
       web_contents1, "openWindow()", &opened_window));
   EXPECT_FALSE(opened_window);
   // Make sure the popup UI was shown.
-  EXPECT_TRUE(TabSpecificContentSettings::FromWebContents(web_contents1)
-                  ->IsContentBlocked(ContentSettingsType::POPUPS));
+  EXPECT_TRUE(
+      TabSpecificContentSettings::GetForFrame(web_contents1->GetMainFrame())
+          ->IsContentBlocked(ContentSettingsType::POPUPS));
 }
 
 IN_PROC_BROWSER_TEST_F(SafeBrowsingTriggeredPopupBlockerBrowserTest,
@@ -343,8 +347,9 @@
   EXPECT_TRUE(content::ExecuteScriptAndExtractBool(web_contents, "openWindow()",
                                                    &opened_window));
   EXPECT_TRUE(opened_window);
-  EXPECT_FALSE(TabSpecificContentSettings::FromWebContents(web_contents)
-                   ->IsContentBlocked(ContentSettingsType::POPUPS));
+  EXPECT_FALSE(
+      TabSpecificContentSettings::GetForFrame(web_contents->GetMainFrame())
+          ->IsContentBlocked(ContentSettingsType::POPUPS));
 }
 
 IN_PROC_BROWSER_TEST_F(SafeBrowsingTriggeredPopupBlockerBrowserTest,
@@ -360,8 +365,9 @@
   EXPECT_TRUE(content::ExecuteScriptAndExtractBool(web_contents, "openWindow()",
                                                    &opened_window));
   EXPECT_TRUE(opened_window);
-  EXPECT_FALSE(TabSpecificContentSettings::FromWebContents(web_contents)
-                   ->IsContentBlocked(ContentSettingsType::POPUPS));
+  EXPECT_FALSE(
+      TabSpecificContentSettings::GetForFrame(web_contents->GetMainFrame())
+          ->IsContentBlocked(ContentSettingsType::POPUPS));
 }
 
 IN_PROC_BROWSER_TEST_F(SafeBrowsingTriggeredPopupBlockerBrowserTest,
@@ -380,8 +386,9 @@
                                                    &opened_window));
   EXPECT_FALSE(opened_window);
   // Make sure the popup UI was shown.
-  EXPECT_TRUE(TabSpecificContentSettings::FromWebContents(web_contents)
-                  ->IsContentBlocked(ContentSettingsType::POPUPS));
+  EXPECT_TRUE(
+      TabSpecificContentSettings::GetForFrame(web_contents->GetMainFrame())
+          ->IsContentBlocked(ContentSettingsType::POPUPS));
 
   // Block again.
   EXPECT_TRUE(content::ExecuteScriptAndExtractBool(web_contents, "openWindow()",
@@ -395,8 +402,9 @@
                                                    &opened_window));
   EXPECT_TRUE(opened_window);
   // Popup UI should not be shown.
-  EXPECT_FALSE(TabSpecificContentSettings::FromWebContents(web_contents)
-                   ->IsContentBlocked(ContentSettingsType::POPUPS));
+  EXPECT_FALSE(
+      TabSpecificContentSettings::GetForFrame(web_contents->GetMainFrame())
+          ->IsContentBlocked(ContentSettingsType::POPUPS));
 }
 
 IN_PROC_BROWSER_TEST_F(SafeBrowsingTriggeredPopupBlockerBrowserTest,
@@ -417,8 +425,9 @@
   EXPECT_FALSE(opened_window);
 
   // Make sure the popup UI was shown.
-  EXPECT_TRUE(TabSpecificContentSettings::FromWebContents(web_contents)
-                  ->IsContentBlocked(ContentSettingsType::POPUPS));
+  EXPECT_TRUE(
+      TabSpecificContentSettings::GetForFrame(web_contents->GetMainFrame())
+          ->IsContentBlocked(ContentSettingsType::POPUPS));
 
   // Click through.
   content::TestNavigationObserver navigation_observer(nullptr, 1);
@@ -501,8 +510,9 @@
       browser()->tab_strip_model()->GetActiveWebContents();
   EXPECT_TRUE(content::ExecuteScript(web_contents, "openWindow()"));
 
-  EXPECT_TRUE(TabSpecificContentSettings::FromWebContents(web_contents)
-                  ->IsContentBlocked(ContentSettingsType::POPUPS));
+  EXPECT_TRUE(
+      TabSpecificContentSettings::GetForFrame(web_contents->GetMainFrame())
+          ->IsContentBlocked(ContentSettingsType::POPUPS));
 
   // Navigate to |b_url|, which should successfully open the popup.
 
@@ -514,8 +524,9 @@
   navigation_observer.Wait();
 
   // Popup UI should not be shown.
-  EXPECT_FALSE(TabSpecificContentSettings::FromWebContents(web_contents)
-                   ->IsContentBlocked(ContentSettingsType::POPUPS));
+  EXPECT_FALSE(
+      TabSpecificContentSettings::GetForFrame(web_contents->GetMainFrame())
+          ->IsContentBlocked(ContentSettingsType::POPUPS));
 }
 
 IN_PROC_BROWSER_TEST_F(SafeBrowsingTriggeredPopupBlockerBrowserTest,
@@ -532,8 +543,9 @@
   EXPECT_TRUE(content::ExecuteScriptAndExtractBool(web_contents, "openWindow()",
                                                    &sent_open));
   EXPECT_TRUE(sent_open);
-  EXPECT_TRUE(TabSpecificContentSettings::FromWebContents(web_contents)
-                  ->IsContentBlocked(ContentSettingsType::POPUPS));
+  EXPECT_TRUE(
+      TabSpecificContentSettings::GetForFrame(web_contents->GetMainFrame())
+          ->IsContentBlocked(ContentSettingsType::POPUPS));
 }
 
 IN_PROC_BROWSER_TEST_F(SafeBrowsingTriggeredPopupBlockerBrowserTest,
@@ -550,9 +562,10 @@
     EXPECT_TRUE(content::ExecuteScriptAndExtractBool(
         web_contents, "openWindow()", &opened_window));
     EXPECT_EQ(expect_block, !opened_window);
-    EXPECT_EQ(expect_block,
-              TabSpecificContentSettings::FromWebContents(web_contents)
-                  ->IsContentBlocked(ContentSettingsType::POPUPS));
+    EXPECT_EQ(
+        expect_block,
+        TabSpecificContentSettings::GetForFrame(web_contents->GetMainFrame())
+            ->IsContentBlocked(ContentSettingsType::POPUPS));
   };
 
   ui_test_utils::NavigateToURL(browser(), url1);
diff --git a/chrome/browser/ui/browser.cc b/chrome/browser/ui/browser.cc
index 4722dd6f84..66c4158 100644
--- a/chrome/browser/ui/browser.cc
+++ b/chrome/browser/ui/browser.cc
@@ -1437,9 +1437,12 @@
     // Note: this is a browser-side-translation of the call to
     // DidBlockContentType from inside
     // ContentSettingsObserver::allowRunningInsecureContent.
+    // TODO(https://crbug.com/1103176): Plumb the actual frame reference here
+    // (MixedContentNavigationThrottle::ShouldBlockNavigation has
+    // |mixed_content_frame| reference)
     content_settings::TabSpecificContentSettings* tab_settings =
-        content_settings::TabSpecificContentSettings::FromWebContents(
-            web_contents);
+        content_settings::TabSpecificContentSettings::GetForFrame(
+            web_contents->GetMainFrame());
     DCHECK(tab_settings);
     tab_settings->OnContentBlocked(ContentSettingsType::MIXEDSCRIPT);
   }
@@ -2129,9 +2132,10 @@
     return;
   }
 
+  // TODO(https://crbug.com/1103176): Plumb the actual frame reference here
   content_settings::TabSpecificContentSettings* tab_content_settings =
-      content_settings::TabSpecificContentSettings::FromWebContents(
-          web_contents);
+      content_settings::TabSpecificContentSettings::GetForFrame(
+          web_contents->GetMainFrame());
 
   HostContentSettingsMap* content_settings =
       HostContentSettingsMapFactory::GetForProfile(profile);
@@ -2160,7 +2164,9 @@
   base::RecordAction(allowed
                          ? base::UserMetricsAction("PPAPI.BrokerSettingAllow")
                          : base::UserMetricsAction("PPAPI.BrokerSettingDeny"));
-  tab_content_settings->SetPepperBrokerAllowed(allowed);
+  if (tab_content_settings) {
+    tab_content_settings->SetPepperBrokerAllowed(allowed);
+  }
   std::move(callback).Run(allowed);
 #endif
 }
diff --git a/chrome/browser/ui/caption_bubble_controller.h b/chrome/browser/ui/caption_bubble_controller.h
index 0be02b9..65e5e23d 100644
--- a/chrome/browser/ui/caption_bubble_controller.h
+++ b/chrome/browser/ui/caption_bubble_controller.h
@@ -37,6 +37,8 @@
 
   static std::unique_ptr<CaptionBubbleController> Create(Browser* browser);
 
+  virtual bool OnSpeechRecognitionReady(content::WebContents* web_contents) = 0;
+
   // Called when a transcription is received from the service. Returns whether
   // the transcription result was set on the caption bubble successfully.
   // Transcriptions will halt if this returns false.
diff --git a/chrome/browser/ui/commander/fuzzy_finder.cc b/chrome/browser/ui/commander/fuzzy_finder.cc
new file mode 100644
index 0000000..9aba2cf5
--- /dev/null
+++ b/chrome/browser/ui/commander/fuzzy_finder.cc
@@ -0,0 +1,31 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/commander/fuzzy_finder.h"
+
+#include "base/i18n/case_conversion.h"
+
+namespace commander {
+
+double FuzzyFind(const base::string16& needle,
+                 const base::string16& haystack,
+                 std::vector<gfx::Range>* matched_ranges) {
+  DCHECK(needle == base::i18n::FoldCase(needle));
+  matched_ranges->clear();
+  const base::string16& folded = base::i18n::FoldCase(haystack);
+  if (folded == needle) {
+    matched_ranges->emplace_back(0, needle.length());
+    return 1.0;
+  }
+  size_t substring_position = folded.find(needle);
+  if (substring_position == std::string::npos)
+    return 0;
+  matched_ranges->emplace_back(substring_position, needle.length());
+  if (substring_position == 0)
+    return .99;
+  return std::min(1 - static_cast<double>(substring_position) / folded.length(),
+                  0.01);
+}
+
+}  // namespace commander
diff --git a/chrome/browser/ui/commander/fuzzy_finder.h b/chrome/browser/ui/commander/fuzzy_finder.h
new file mode 100644
index 0000000..708b981
--- /dev/null
+++ b/chrome/browser/ui/commander/fuzzy_finder.h
@@ -0,0 +1,34 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_COMMANDER_FUZZY_FINDER_H_
+#define CHROME_BROWSER_UI_COMMANDER_FUZZY_FINDER_H_
+
+#include "base/strings/string16.h"
+#include "ui/gfx/range/range.h"
+
+namespace commander {
+
+// TODO(lgrey): Make this actually fuzzy find.
+// Returns a score from 0 to 1 based on how well |needle| matches |haystack|.
+// 0 means no match. |matched_ranges| will be filled with the ranges of
+// |haystack| that match |needle| so they can be highlighted in the UI; see
+// comment on commander::CommandItem |matched_ranges| for a worked example.
+// *** TEMPORARY ***
+// Temporarily, a non-zero match means that |needle| is a substring of
+// |haystack|, with a penalty applied based on how far into |haystack|
+// |needle| begins. Exact matches are 1.0 (vs. a max of .99 for non-exact
+// prefix).
+// This will be replaced with a more sophisticated implementation in the
+// near future.
+// *** END TEMPORARY ***
+// |needle| is expected to already be case folded (this is DCHECKED) to save
+// redundant processing, as the needle will be checked with many haystacks.
+double FuzzyFind(const base::string16& needle,
+                 const base::string16& haystack,
+                 std::vector<gfx::Range>* matched_ranges);
+
+}  // namespace commander
+
+#endif  // CHROME_BROWSER_UI_COMMANDER_FUZZY_FINDER_H_
diff --git a/chrome/browser/ui/commander/fuzzy_finder_unittest.cc b/chrome/browser/ui/commander/fuzzy_finder_unittest.cc
new file mode 100644
index 0000000..362aa4f
--- /dev/null
+++ b/chrome/browser/ui/commander/fuzzy_finder_unittest.cc
@@ -0,0 +1,54 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/commander/fuzzy_finder.h"
+
+#include "base/i18n/case_conversion.h"
+#include "base/strings/utf_string_conversions.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace commander {
+TEST(CommanderFuzzyFinder, NonmatchIsZero) {
+  std::vector<gfx::Range> ranges;
+  EXPECT_EQ(0, FuzzyFind(base::ASCIIToUTF16("orange"),
+                         base::ASCIIToUTF16("orangutan"), &ranges));
+  EXPECT_TRUE(ranges.empty());
+  EXPECT_EQ(0, FuzzyFind(base::ASCIIToUTF16("elephant"),
+                         base::ASCIIToUTF16("orangutan"), &ranges));
+  EXPECT_TRUE(ranges.empty());
+}
+
+TEST(CommanderFuzzyFinder, ExactMatchIsOne) {
+  std::vector<gfx::Range> ranges;
+  EXPECT_EQ(1, FuzzyFind(base::ASCIIToUTF16("orange"),
+                         base::ASCIIToUTF16("orange"), &ranges));
+  EXPECT_EQ(ranges, std::vector<gfx::Range>({{0, 6}}));
+}
+
+TEST(CommanderFuzzyFinder, CaseInsensitive) {
+  std::vector<gfx::Range> ranges;
+  EXPECT_EQ(1, FuzzyFind(base::ASCIIToUTF16("orange"),
+                         base::ASCIIToUTF16("Orange"), &ranges));
+  EXPECT_EQ(ranges, std::vector<gfx::Range>({{0, 6}}));
+}
+
+TEST(CommanderFuzzyFinder, PrefixRanksHigherThanInternal) {
+  std::vector<gfx::Range> ranges;
+
+  double prefix_rank = FuzzyFind(base::ASCIIToUTF16("orange"),
+                                 base::ASCIIToUTF16("Orange juice"), &ranges);
+  EXPECT_EQ(ranges, std::vector<gfx::Range>({{0, 6}}));
+
+  double non_prefix_rank =
+      FuzzyFind(base::ASCIIToUTF16("orange"),
+                base::ASCIIToUTF16("William of Orange"), &ranges);
+  EXPECT_EQ(ranges, std::vector<gfx::Range>({{11, 6}}));
+
+  EXPECT_GT(prefix_rank, 0);
+  EXPECT_GT(non_prefix_rank, 0);
+  EXPECT_LT(prefix_rank, 1);
+  EXPECT_LT(non_prefix_rank, 1);
+  EXPECT_GT(prefix_rank, non_prefix_rank);
+}
+}  // namespace commander
diff --git a/chrome/browser/ui/commander/simple_command_source.cc b/chrome/browser/ui/commander/simple_command_source.cc
new file mode 100644
index 0000000..e66c1a8e
--- /dev/null
+++ b/chrome/browser/ui/commander/simple_command_source.cc
@@ -0,0 +1,78 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/commander/simple_command_source.h"
+
+#include "base/bind.h"
+#include "base/i18n/case_conversion.h"
+#include "chrome/app/chrome_command_ids.h"
+#include "chrome/browser/ui/browser_commands.h"
+#include "chrome/browser/ui/commander/fuzzy_finder.h"
+#include "chrome/grit/generated_resources.h"
+#include "components/strings/grit/components_strings.h"
+#include "ui/base/l10n/l10n_util.h"
+
+namespace commander {
+
+SimpleCommandSource::SimpleCommandSource() {
+  weak_this_ = weak_ptr_factory_.GetWeakPtr();
+}
+SimpleCommandSource::~SimpleCommandSource() = default;
+
+CommandSource::CommandResults SimpleCommandSource::GetCommands(
+    const base::string16& input,
+    Browser* browser) const {
+  static constexpr struct {
+    int command_id;
+    int string_constant;
+  } command_map[] = {
+      {IDC_SHOW_HISTORY, IDS_HISTORY_SHOWFULLHISTORY_LINK},
+      {IDC_FIND, IDS_FIND},
+      {IDC_RELOAD, IDS_TOOLTIP_RELOAD},
+      {IDC_SAVE_PAGE, IDS_SAVE_PAGE},
+      {IDC_PRINT, IDS_PRINT},
+  };
+
+  CommandSource::CommandResults results;
+  const base::string16& folded_input = base::i18n::FoldCase(input);
+  std::vector<gfx::Range> ranges;
+  for (const auto& command_spec : command_map) {
+    if (!chrome::IsCommandEnabled(browser, command_spec.command_id))
+      continue;
+    const base::string16 title =
+        l10n_util::GetStringUTF16(command_spec.string_constant);
+    double score = FuzzyFind(folded_input, title, &ranges);
+    if (score == 0)
+      continue;
+
+    auto item = std::make_unique<CommandItem>();
+    item->title = title;
+    item->score = score;
+    item->matched_ranges = ranges;
+    // TODO(lgrey): For base::Unretained to be safe here, we need to ensure
+    // that if |browser| is destroyed, the palette is reset. It's likely
+    // that this will be the case anyway, but leaving this comment so:
+    // - it doesn't get dropped/forgotten
+    // - as a reminder to replace the comment with the actual explanation
+    //   when we have it
+    item->command =
+        base::BindOnce(&SimpleCommandSource::ExecuteCommand, weak_this_,
+                       base::Unretained(browser), command_spec.command_id);
+    results.push_back(std::move(item));
+  }
+
+  return results;
+}
+
+// Why this is necessary:
+// chrome::ExecuteCommand has a third default argument |time_stamp| which
+// makes it difficult to use with BindOnce. Pre-binding it at command creation
+// is wrong since it defaults to base::TimeTicks::Now(); that means if pre-bound
+// it would get the timestamp when the command was generated, rather than when
+// it was invoked.
+void SimpleCommandSource::ExecuteCommand(Browser* browser, int command_id) {
+  chrome::ExecuteCommand(browser, command_id);
+}
+
+}  // namespace commander
diff --git a/chrome/browser/ui/commander/simple_command_source.h b/chrome/browser/ui/commander/simple_command_source.h
new file mode 100644
index 0000000..a81a6328
--- /dev/null
+++ b/chrome/browser/ui/commander/simple_command_source.h
@@ -0,0 +1,40 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_COMMANDER_SIMPLE_COMMAND_SOURCE_H_
+#define CHROME_BROWSER_UI_COMMANDER_SIMPLE_COMMAND_SOURCE_H_
+
+#include "chrome/browser/ui/commander/command_source.h"
+
+#include "base/memory/weak_ptr.h"
+
+namespace commander {
+
+// A command source which hosts simple one-shot browser commands, most of which
+// are accessible by hotkey. This is an alternative surface to provide for
+// hotkey discovery.
+class SimpleCommandSource : public CommandSource {
+ public:
+  SimpleCommandSource();
+  ~SimpleCommandSource() override;
+
+  // Disallow copy and assign
+  SimpleCommandSource(const SimpleCommandSource& other) = delete;
+  SimpleCommandSource& operator=(const SimpleCommandSource& other) = delete;
+
+  // Command source overrides
+  CommandSource::CommandResults GetCommands(const base::string16& input,
+                                            Browser* browser) const override;
+
+ private:
+  base::WeakPtr<SimpleCommandSource> weak_this_;
+  // Wrapper around chrome::ExecuteCommand. See implementation comment
+  // for details.
+  void ExecuteCommand(Browser* browser, int command_id);
+  base::WeakPtrFactory<SimpleCommandSource> weak_ptr_factory_{this};
+};
+
+}  // namespace commander
+
+#endif  // CHROME_BROWSER_UI_COMMANDER_SIMPLE_COMMAND_SOURCE_H_
diff --git a/chrome/browser/ui/content_settings/content_setting_bubble_model.cc b/chrome/browser/ui/content_settings/content_setting_bubble_model.cc
index de7fa27..7180227 100644
--- a/chrome/browser/ui/content_settings/content_setting_bubble_model.cc
+++ b/chrome/browser/ui/content_settings/content_setting_bubble_model.cc
@@ -208,8 +208,9 @@
 }
 
 void ContentSettingSimpleBubbleModel::SetTitle() {
+  // TODO(https://crbug.com/1103176): Plumb the actual frame reference here
   TabSpecificContentSettings* content_settings =
-      TabSpecificContentSettings::FromWebContents(web_contents());
+      TabSpecificContentSettings::GetForFrame(web_contents()->GetMainFrame());
 
   static const ContentSettingsTypeIdEntry kBlockedTitleIDs[] = {
       {ContentSettingsType::COOKIES, IDS_BLOCKED_COOKIES_TITLE},
@@ -244,7 +245,7 @@
 
 void ContentSettingSimpleBubbleModel::SetMessage() {
   TabSpecificContentSettings* content_settings =
-      TabSpecificContentSettings::FromWebContents(web_contents());
+      TabSpecificContentSettings::GetForFrame(web_contents()->GetMainFrame());
 
   // TODO(https://crbug.com/978882): Make the two arrays below static again once
   // we no longer need to check base::FeatureList.
@@ -537,7 +538,7 @@
 
 void ContentSettingMidiSysExBubbleModel::SetDomainsAndCustomLink() {
   TabSpecificContentSettings* content_settings =
-      TabSpecificContentSettings::FromWebContents(web_contents());
+      TabSpecificContentSettings::GetForFrame(web_contents()->GetMainFrame());
   const ContentSettingsUsagesState& usages_state =
       content_settings->midi_usages_state();
   ContentSettingsUsagesState::FormattedHostsPerState formatted_hosts_per_state;
@@ -567,7 +568,7 @@
   // origins currently on the page.
   const GURL& embedder_url = web_contents()->GetURL();
   TabSpecificContentSettings* content_settings =
-      TabSpecificContentSettings::FromWebContents(web_contents());
+      TabSpecificContentSettings::GetForFrame(web_contents()->GetMainFrame());
   const ContentSettingsUsagesState::StateMap& state_map =
       content_settings->midi_usages_state().state_map();
   HostContentSettingsMap* map =
@@ -630,7 +631,7 @@
 
 void ContentSettingDomainListBubbleModel::SetDomainsAndCustomLink() {
   TabSpecificContentSettings* content_settings =
-      TabSpecificContentSettings::FromWebContents(web_contents());
+      TabSpecificContentSettings::GetForFrame(web_contents()->GetMainFrame());
   const ContentSettingsUsagesState& usages =
       content_settings->geolocation_usages_state();
   ContentSettingsUsagesState::FormattedHostsPerState formatted_hosts_per_state;
@@ -660,7 +661,7 @@
   // origins currently on the page.
   const GURL& embedder_url = web_contents()->GetURL();
   TabSpecificContentSettings* content_settings =
-      TabSpecificContentSettings::FromWebContents(web_contents());
+      TabSpecificContentSettings::GetForFrame(web_contents()->GetMainFrame());
   const ContentSettingsUsagesState::StateMap& state_map =
       content_settings->geolocation_usages_state().state_map();
   HostContentSettingsMap* map =
@@ -720,7 +721,7 @@
     // Disable the "Run all plugins this time" link if the user already clicked
     // on the link and ran all plugins.
     set_custom_link_enabled(
-        TabSpecificContentSettings::FromWebContents(web_contents)
+        TabSpecificContentSettings::GetForFrame(web_contents->GetMainFrame())
             ->load_plugins_link_enabled());
   }
 
@@ -753,7 +754,7 @@
       web_contents(), true, std::string());
 #endif
   set_custom_link_enabled(false);
-  TabSpecificContentSettings::FromWebContents(web_contents())
+  TabSpecificContentSettings::GetForFrame(web_contents()->GetMainFrame())
       ->set_load_plugins_link_enabled(false);
 }
 
@@ -792,7 +793,7 @@
   base::string16 display_host = url_formatter::FormatUrlForSecurityDisplay(url);
 
   TabSpecificContentSettings* content_settings =
-      TabSpecificContentSettings::FromWebContents(web_contents());
+      TabSpecificContentSettings::GetForFrame(web_contents()->GetMainFrame());
   bool allowed = !content_settings->IsContentBlocked(content_type());
 
   // For the frame busting case the content is blocked but its content type is
@@ -1050,7 +1051,7 @@
   radio_item_setting_[1] = CONTENT_SETTING_BLOCK;
 
   TabSpecificContentSettings* content_settings =
-      TabSpecificContentSettings::FromWebContents(web_contents);
+      TabSpecificContentSettings::GetForFrame(web_contents->GetMainFrame());
   state_ = content_settings->GetMicrophoneCameraState();
   DCHECK(CameraAccessed() || MicrophoneAccessed());
 
@@ -1186,7 +1187,7 @@
 
 void ContentSettingMediaStreamBubbleModel::SetRadioGroup() {
   TabSpecificContentSettings* content_settings =
-      TabSpecificContentSettings::FromWebContents(web_contents());
+      TabSpecificContentSettings::GetForFrame(web_contents()->GetMainFrame());
   GURL url = content_settings->media_stream_access_origin();
   RadioGroup radio_group;
   radio_group.url = url;
@@ -1265,7 +1266,7 @@
 void ContentSettingMediaStreamBubbleModel::UpdateSettings(
     ContentSetting setting) {
   TabSpecificContentSettings* tab_content_settings =
-      TabSpecificContentSettings::FromWebContents(web_contents());
+      TabSpecificContentSettings::GetForFrame(web_contents()->GetMainFrame());
   // The same urls must be used as in other places (e.g. the infobar) in
   // order to override the existing rule. Otherwise a new rule is created.
   // TODO(markusheintz): Extract to a helper so that there is only a single
@@ -1367,7 +1368,7 @@
 
 void ContentSettingMediaStreamBubbleModel::SetMediaMenus() {
   TabSpecificContentSettings* content_settings =
-      TabSpecificContentSettings::FromWebContents(web_contents());
+      TabSpecificContentSettings::GetForFrame(web_contents()->GetMainFrame());
   const std::string& requested_microphone =
       content_settings->media_stream_requested_audio_device();
   const std::string& requested_camera =
@@ -1437,7 +1438,7 @@
 
 void ContentSettingMediaStreamBubbleModel::SetCustomLink() {
   TabSpecificContentSettings* content_settings =
-      TabSpecificContentSettings::FromWebContents(web_contents());
+      TabSpecificContentSettings::GetForFrame(web_contents()->GetMainFrame());
   if (content_settings->IsMicrophoneCameraStateChanged()) {
     set_custom_link(
         l10n_util::GetStringUTF16(IDS_MEDIASTREAM_SETTING_CHANGED_MESSAGE));
diff --git a/chrome/browser/ui/content_settings/content_setting_bubble_model_browsertest.cc b/chrome/browser/ui/content_settings/content_setting_bubble_model_browsertest.cc
index 112437b..324a40b 100644
--- a/chrome/browser/ui/content_settings/content_setting_bubble_model_browsertest.cc
+++ b/chrome/browser/ui/content_settings/content_setting_bubble_model_browsertest.cc
@@ -57,8 +57,8 @@
   }
 
   TabSpecificContentSettings* GetActiveTabSpecificContentSettings() {
-    return TabSpecificContentSettings::FromWebContents(
-        browser()->tab_strip_model()->GetActiveWebContents());
+    return TabSpecificContentSettings::GetForFrame(
+        browser()->tab_strip_model()->GetActiveWebContents()->GetMainFrame());
   }
 
   std::unique_ptr<net::EmbeddedTestServer> https_server_;
@@ -173,7 +173,7 @@
     content::WebContents* web_contents = GetActiveTab();
 
     // Create a bubble with the given camera and microphone access state.
-    TabSpecificContentSettings::FromWebContents(web_contents)
+    TabSpecificContentSettings::GetForFrame(web_contents->GetMainFrame())
         ->OnMediaStreamPermissionSet(web_contents->GetLastCommittedURL(), state,
                                      std::string(), std::string(),
                                      std::string(), std::string());
diff --git a/chrome/browser/ui/content_settings/content_setting_bubble_model_unittest.cc b/chrome/browser/ui/content_settings/content_setting_bubble_model_unittest.cc
index 9c3d97d..44c94b9e 100644
--- a/chrome/browser/ui/content_settings/content_setting_bubble_model_unittest.cc
+++ b/chrome/browser/ui/content_settings/content_setting_bubble_model_unittest.cc
@@ -94,7 +94,7 @@
   WebContentsTester::For(web_contents())->
       NavigateAndCommit(GURL("https://www.example.com"));
   TabSpecificContentSettings* content_settings =
-      TabSpecificContentSettings::FromWebContents(web_contents());
+      TabSpecificContentSettings::GetForFrame(web_contents()->GetMainFrame());
   content_settings->OnContentBlocked(ContentSettingsType::IMAGES);
 
   std::unique_ptr<ContentSettingBubbleModel> content_setting_bubble_model(
@@ -113,7 +113,7 @@
   WebContentsTester::For(web_contents())->
       NavigateAndCommit(GURL("https://www.example.com"));
   TabSpecificContentSettings* content_settings =
-      TabSpecificContentSettings::FromWebContents(web_contents());
+      TabSpecificContentSettings::GetForFrame(web_contents()->GetMainFrame());
   content_settings->OnContentBlocked(ContentSettingsType::COOKIES);
 
   std::unique_ptr<ContentSettingBubbleModel> content_setting_bubble_model(
@@ -131,7 +131,7 @@
   WebContentsTester::For(web_contents())
       ->NavigateAndCommit(GURL("https://www.example.com"));
   content_settings =
-      TabSpecificContentSettings::FromWebContents(web_contents());
+      TabSpecificContentSettings::GetForFrame(web_contents()->GetMainFrame());
   content_settings->OnContentAllowed(ContentSettingsType::COOKIES);
   content_setting_bubble_model =
       ContentSettingBubbleModel::CreateContentSettingBubbleModel(
@@ -160,7 +160,7 @@
       DisableDeviceEnumerationForTesting();
 
   TabSpecificContentSettings* content_settings =
-      TabSpecificContentSettings::FromWebContents(web_contents());
+      TabSpecificContentSettings::GetForFrame(web_contents()->GetMainFrame());
   std::string request_host = "google.com";
   GURL security_origin("http://" + request_host);
   TabSpecificContentSettings::MicrophoneCameraState microphone_camera_state =
@@ -216,7 +216,7 @@
       setting);
 
   TabSpecificContentSettings* content_settings =
-      TabSpecificContentSettings::FromWebContents(web_contents());
+      TabSpecificContentSettings::GetForFrame(web_contents()->GetMainFrame());
   TabSpecificContentSettings::MicrophoneCameraState microphone_camera_state =
       TabSpecificContentSettings::MICROPHONE_ACCESSED |
       TabSpecificContentSettings::MICROPHONE_BLOCKED |
@@ -285,7 +285,7 @@
       setting);
 
   TabSpecificContentSettings* content_settings =
-      TabSpecificContentSettings::FromWebContents(web_contents());
+      TabSpecificContentSettings::GetForFrame(web_contents()->GetMainFrame());
   TabSpecificContentSettings::MicrophoneCameraState microphone_camera_state =
       TabSpecificContentSettings::MICROPHONE_ACCESSED |
       TabSpecificContentSettings::MICROPHONE_BLOCKED;
@@ -377,7 +377,7 @@
       audio_devices);
 
   TabSpecificContentSettings* content_settings =
-      TabSpecificContentSettings::FromWebContents(web_contents());
+      TabSpecificContentSettings::GetForFrame(web_contents()->GetMainFrame());
   TabSpecificContentSettings::MicrophoneCameraState microphone_camera_state =
       TabSpecificContentSettings::MICROPHONE_ACCESSED |
       TabSpecificContentSettings::MICROPHONE_BLOCKED;
@@ -523,7 +523,7 @@
       DisableDeviceEnumerationForTesting();
 
   TabSpecificContentSettings* content_settings =
-      TabSpecificContentSettings::FromWebContents(web_contents());
+      TabSpecificContentSettings::GetForFrame(web_contents()->GetMainFrame());
   std::string request_host = "google.com";
   GURL security_origin("http://" + request_host);
   TabSpecificContentSettings::MicrophoneCameraState microphone_camera_state =
@@ -596,7 +596,7 @@
       DisableDeviceEnumerationForTesting();
 
   TabSpecificContentSettings* content_settings =
-      TabSpecificContentSettings::FromWebContents(web_contents());
+      TabSpecificContentSettings::GetForFrame(web_contents()->GetMainFrame());
   std::string request_host = "google.com";
   GURL security_origin("http://" + request_host);
   TabSpecificContentSettings::MicrophoneCameraState microphone_camera_state =
@@ -670,7 +670,7 @@
       DisableDeviceEnumerationForTesting();
 
   TabSpecificContentSettings* content_settings =
-      TabSpecificContentSettings::FromWebContents(web_contents());
+      TabSpecificContentSettings::GetForFrame(web_contents()->GetMainFrame());
   std::string request_host = "google.com";
   GURL security_origin("http://" + request_host);
 
@@ -737,7 +737,7 @@
   WebContentsTester::For(web_contents())->
       NavigateAndCommit(GURL("https://www.example.com"));
   TabSpecificContentSettings* content_settings =
-      TabSpecificContentSettings::FromWebContents(web_contents());
+      TabSpecificContentSettings::GetForFrame(web_contents()->GetMainFrame());
   const base::string16 plugin_name = base::ASCIIToUTF16("plugin_name");
 
   content_settings->OnContentBlocked(ContentSettingsType::PLUGINS);
@@ -760,7 +760,7 @@
   WebContentsTester::For(web_contents())->
       NavigateAndCommit(GURL("https://www.example.com"));
   TabSpecificContentSettings* content_settings =
-      TabSpecificContentSettings::FromWebContents(web_contents());
+      TabSpecificContentSettings::GetForFrame(web_contents()->GetMainFrame());
   content_settings->OnContentBlocked(ContentSettingsType::PPAPI_BROKER);
 
   std::unique_ptr<ContentSettingBubbleModel> content_setting_bubble_model(
@@ -780,7 +780,7 @@
   WebContentsTester::For(web_contents())
       ->NavigateAndCommit(GURL("https://www.example.com"));
   content_settings =
-      TabSpecificContentSettings::FromWebContents(web_contents());
+      TabSpecificContentSettings::GetForFrame(web_contents()->GetMainFrame());
   content_settings->OnContentAllowed(ContentSettingsType::PPAPI_BROKER);
   content_setting_bubble_model =
       ContentSettingBubbleModel::CreateContentSettingBubbleModel(
@@ -804,7 +804,7 @@
 
   NavigateAndCommit(page_url);
   TabSpecificContentSettings* content_settings =
-      TabSpecificContentSettings::FromWebContents(web_contents());
+      TabSpecificContentSettings::GetForFrame(web_contents()->GetMainFrame());
 
   // One permitted frame, but not in the content map: requires reload.
   content_settings->OnGeolocationPermissionSet(frame1_url, true);
@@ -850,7 +850,7 @@
 
   NavigateAndCommit(origin_to_embargo);
   TabSpecificContentSettings* content_settings =
-      TabSpecificContentSettings::FromWebContents(web_contents());
+      TabSpecificContentSettings::GetForFrame(web_contents()->GetMainFrame());
   content_settings->OnGeolocationPermissionSet(origin_to_embargo, false);
 
   // |origin_to_embargo| is not blocked or embargoed. Verify no clear link
@@ -898,7 +898,7 @@
 TEST_F(ContentSettingBubbleModelTest, FileURL) {
   std::string file_url("file:///tmp/test.html");
   NavigateAndCommit(GURL(file_url));
-  TabSpecificContentSettings::FromWebContents(web_contents())
+  TabSpecificContentSettings::GetForFrame(web_contents()->GetMainFrame())
       ->OnContentBlocked(ContentSettingsType::IMAGES);
   std::unique_ptr<ContentSettingBubbleModel> content_setting_bubble_model(
       ContentSettingBubbleModel::CreateContentSettingBubbleModel(
@@ -1059,7 +1059,7 @@
   WebContentsTester::For(web_contents())
       ->NavigateAndCommit(GURL("https://www.example.com"));
   TabSpecificContentSettings* content_settings =
-      TabSpecificContentSettings::FromWebContents(web_contents());
+      TabSpecificContentSettings::GetForFrame(web_contents()->GetMainFrame());
   HostContentSettingsMap* settings_map =
       HostContentSettingsMapFactory::GetForProfile(profile());
 
@@ -1137,7 +1137,7 @@
   WebContentsTester::For(web_contents())
       ->NavigateAndCommit(GURL("https://www.example.com"));
   content_settings =
-      TabSpecificContentSettings::FromWebContents(web_contents());
+      TabSpecificContentSettings::GetForFrame(web_contents()->GetMainFrame());
 
   // Go from block by default to allow by default to block by default.
   {
@@ -1213,7 +1213,7 @@
   WebContentsTester::For(web_contents())
       ->NavigateAndCommit(GURL("https://www.example.com"));
   content_settings =
-      TabSpecificContentSettings::FromWebContents(web_contents());
+      TabSpecificContentSettings::GetForFrame(web_contents()->GetMainFrame());
 
   // Block by default but allow a specific site.
   {
@@ -1246,7 +1246,7 @@
   WebContentsTester::For(web_contents())
       ->NavigateAndCommit(GURL("https://www.example.com"));
   content_settings =
-      TabSpecificContentSettings::FromWebContents(web_contents());
+      TabSpecificContentSettings::GetForFrame(web_contents()->GetMainFrame());
   // Clear site-specific exceptions.
   settings_map->ClearSettingsForOneType(ContentSettingsType::SENSORS);
 
@@ -1283,7 +1283,7 @@
   const GURL url("https://www.example.test/");
   WebContentsTester::For(web_contents())->NavigateAndCommit(url);
   TabSpecificContentSettings* content_settings =
-      TabSpecificContentSettings::FromWebContents(web_contents());
+      TabSpecificContentSettings::GetForFrame(web_contents()->GetMainFrame());
   content_settings->OnContentBlocked(ContentSettingsType::POPUPS);
 
   blocked_content::PopupBlockerTabHelper::CreateForWebContents(web_contents());
@@ -1317,7 +1317,7 @@
       NavigateAndCommit(GURL("https://www.example.com"));
 
   TabSpecificContentSettings* content_settings =
-      TabSpecificContentSettings::FromWebContents(web_contents());
+      TabSpecificContentSettings::GetForFrame(web_contents()->GetMainFrame());
   content_settings->OnContentBlocked(ContentSettingsType::COOKIES);
 
   std::unique_ptr<ContentSettingBubbleModel> content_setting_bubble_model(
@@ -1334,7 +1334,7 @@
       NavigateAndCommit(GURL("about:blank"));
 
   TabSpecificContentSettings* content_settings =
-      TabSpecificContentSettings::FromWebContents(web_contents());
+      TabSpecificContentSettings::GetForFrame(web_contents()->GetMainFrame());
   content_settings->OnContentBlocked(ContentSettingsType::COOKIES);
 
   std::unique_ptr<ContentSettingBubbleModel> content_setting_bubble_model(
diff --git a/chrome/browser/ui/content_settings/content_setting_image_model.cc b/chrome/browser/ui/content_settings/content_setting_image_model.cc
index 5907358..6b63e64 100644
--- a/chrome/browser/ui/content_settings/content_setting_image_model.cc
+++ b/chrome/browser/ui/content_settings/content_setting_image_model.cc
@@ -420,7 +420,7 @@
   // If a content type is blocked by default and was accessed, display the
   // content blocked page action.
   TabSpecificContentSettings* content_settings =
-      TabSpecificContentSettings::FromWebContents(web_contents);
+      TabSpecificContentSettings::GetForFrame(web_contents->GetMainFrame());
   if (!content_settings)
     return false;
 
@@ -484,7 +484,7 @@
 bool ContentSettingGeolocationImageModel::UpdateAndGetVisibility(
     WebContents* web_contents) {
   TabSpecificContentSettings* content_settings =
-      TabSpecificContentSettings::FromWebContents(web_contents);
+      TabSpecificContentSettings::GetForFrame(web_contents->GetMainFrame());
   if (!content_settings)
     return false;
   const ContentSettingsUsagesState& usages_state =
@@ -536,7 +536,7 @@
 bool ContentSettingMIDISysExImageModel::UpdateAndGetVisibility(
     WebContents* web_contents) {
   TabSpecificContentSettings* content_settings =
-      TabSpecificContentSettings::FromWebContents(web_contents);
+      TabSpecificContentSettings::GetForFrame(web_contents->GetMainFrame());
   if (!content_settings)
     return false;
   const ContentSettingsUsagesState& usages_state =
@@ -603,7 +603,7 @@
 bool ContentSettingClipboardReadWriteImageModel::UpdateAndGetVisibility(
     WebContents* web_contents) {
   TabSpecificContentSettings* content_settings =
-      TabSpecificContentSettings::FromWebContents(web_contents);
+      TabSpecificContentSettings::GetForFrame(web_contents->GetMainFrame());
   if (!content_settings)
     return false;
   ContentSettingsType content_type = ContentSettingsType::CLIPBOARD_READ_WRITE;
@@ -628,7 +628,7 @@
     WebContents* web_contents) {
   set_should_auto_open_bubble(false);
   TabSpecificContentSettings* content_settings =
-      TabSpecificContentSettings::FromWebContents(web_contents);
+      TabSpecificContentSettings::GetForFrame(web_contents->GetMainFrame());
   if (!content_settings)
     return false;
   state_ = content_settings->GetMicrophoneCameraState();
@@ -820,7 +820,7 @@
 bool ContentSettingSensorsImageModel::UpdateAndGetVisibility(
     WebContents* web_contents) {
   auto* content_settings =
-      TabSpecificContentSettings::FromWebContents(web_contents);
+      TabSpecificContentSettings::GetForFrame(web_contents->GetMainFrame());
   if (!content_settings)
     return false;
 
@@ -861,7 +861,7 @@
 bool ContentSettingPopupImageModel::UpdateAndGetVisibility(
     WebContents* web_contents) {
   TabSpecificContentSettings* content_settings =
-      TabSpecificContentSettings::FromWebContents(web_contents);
+      TabSpecificContentSettings::GetForFrame(web_contents->GetMainFrame());
   if (!content_settings || !content_settings->IsContentBlocked(content_type()))
     return false;
   set_icon(kWebIcon, vector_icons::kBlockedBadgeIcon);
diff --git a/chrome/browser/ui/content_settings/content_setting_image_model_browsertest.cc b/chrome/browser/ui/content_settings/content_setting_image_model_browsertest.cc
index 9fad6fb..914d8add 100644
--- a/chrome/browser/ui/content_settings/content_setting_image_model_browsertest.cc
+++ b/chrome/browser/ui/content_settings/content_setting_image_model_browsertest.cc
@@ -28,8 +28,8 @@
   WebContents* web_contents =
       browser()->tab_strip_model()->GetActiveWebContents();
   content_settings::TabSpecificContentSettings* content_settings =
-      content_settings::TabSpecificContentSettings::FromWebContents(
-          web_contents);
+      content_settings::TabSpecificContentSettings::GetForFrame(
+          web_contents->GetMainFrame());
   content_settings->BlockAllContentForTesting();
 
   // Automatic downloads are handled by DownloadRequestLimiter.
diff --git a/chrome/browser/ui/content_settings/content_setting_image_model_unittest.cc b/chrome/browser/ui/content_settings/content_setting_image_model_unittest.cc
index 0658050..e71704fb 100644
--- a/chrome/browser/ui/content_settings/content_setting_image_model_unittest.cc
+++ b/chrome/browser/ui/content_settings/content_setting_image_model_unittest.cc
@@ -119,7 +119,7 @@
       std::make_unique<chrome::TabSpecificContentSettingsDelegate>(
           web_contents()));
   TabSpecificContentSettings* content_settings =
-      TabSpecificContentSettings::FromWebContents(web_contents());
+      TabSpecificContentSettings::GetForFrame(web_contents()->GetMainFrame());
   auto content_setting_image_model =
       ContentSettingImageModel::CreateForContentType(
           ContentSettingImageModel::ImageType::IMAGES);
@@ -170,7 +170,7 @@
   std::unique_ptr<net::CanonicalCookie> cookie(net::CanonicalCookie::Create(
       origin, "A=B", base::Time::Now(), base::nullopt /* server_time */));
   ASSERT_TRUE(cookie);
-  TabSpecificContentSettings::FromWebContents(web_contents())
+  TabSpecificContentSettings::GetForFrame(web_contents()->GetMainFrame())
       ->OnCookiesAccessed({content::CookieAccessDetails::Type::kChange,
                            origin,
                            origin,
@@ -193,7 +193,7 @@
       std::make_unique<chrome::TabSpecificContentSettingsDelegate>(
           web_contents()));
   TabSpecificContentSettings* content_settings =
-      TabSpecificContentSettings::FromWebContents(web_contents());
+      TabSpecificContentSettings::GetForFrame(web_contents()->GetMainFrame());
 
   auto content_setting_image_model =
       ContentSettingImageModel::CreateForContentType(
@@ -213,7 +213,7 @@
 
   NavigateAndCommit(controller_, GURL("http://www.google.com"));
   content_settings =
-      TabSpecificContentSettings::FromWebContents(web_contents());
+      TabSpecificContentSettings::GetForFrame(web_contents()->GetMainFrame());
 
   // Allowing by default but blocking (e.g. due to a feature policy) causes the
   // indicator to be shown.
@@ -230,7 +230,7 @@
 
   NavigateAndCommit(controller_, GURL("http://www.google.com"));
   content_settings =
-      TabSpecificContentSettings::FromWebContents(web_contents());
+      TabSpecificContentSettings::GetForFrame(web_contents()->GetMainFrame());
 
   // Blocking by default but allowing (e.g. via a site-specific exception)
   // causes the indicator to be shown.
@@ -247,7 +247,7 @@
 
   NavigateAndCommit(controller_, GURL("http://www.google.com"));
   content_settings =
-      TabSpecificContentSettings::FromWebContents(web_contents());
+      TabSpecificContentSettings::GetForFrame(web_contents()->GetMainFrame());
 
   // Blocking access by default also causes the indicator to be shown so users
   // can set an exception.
@@ -277,7 +277,7 @@
           web_contents()));
   NavigateAndCommit(controller_, GURL("https://www.example.com"));
   TabSpecificContentSettings* content_settings =
-      TabSpecificContentSettings::FromWebContents(web_contents());
+      TabSpecificContentSettings::GetForFrame(web_contents()->GetMainFrame());
   HostContentSettingsMap* settings_map =
       HostContentSettingsMapFactory::GetForProfile(profile());
 
@@ -317,7 +317,7 @@
 
   NavigateAndCommit(controller_, GURL("https://www.example.com"));
   content_settings =
-      TabSpecificContentSettings::FromWebContents(web_contents());
+      TabSpecificContentSettings::GetForFrame(web_contents()->GetMainFrame());
 
   // Go from block by default to allow by default to block by default.
   {
@@ -352,7 +352,7 @@
 
   NavigateAndCommit(controller_, GURL("https://www.example.com"));
   content_settings =
-      TabSpecificContentSettings::FromWebContents(web_contents());
+      TabSpecificContentSettings::GetForFrame(web_contents()->GetMainFrame());
 
   // Block by default but allow a specific site.
   {
@@ -373,7 +373,7 @@
 
   NavigateAndCommit(controller_, GURL("https://www.example.com"));
   content_settings =
-      TabSpecificContentSettings::FromWebContents(web_contents());
+      TabSpecificContentSettings::GetForFrame(web_contents()->GetMainFrame());
   // Clear site-specific exceptions.
   settings_map->ClearSettingsForOneType(ContentSettingsType::SENSORS);
 
@@ -398,8 +398,8 @@
 // Regression test for http://crbug.com/161854.
 TEST_F(ContentSettingImageModelTest, NULLTabSpecificContentSettings) {
   TabSpecificContentSettings::DeleteForWebContentsForTest(web_contents());
-  EXPECT_EQ(nullptr,
-            TabSpecificContentSettings::FromWebContents(web_contents()));
+  EXPECT_EQ(nullptr, TabSpecificContentSettings::GetForFrame(
+                         web_contents()->GetMainFrame()));
   // Should not crash.
   ContentSettingImageModel::CreateForContentType(
       ContentSettingImageModel::ImageType::IMAGES)
@@ -412,7 +412,7 @@
       std::make_unique<chrome::TabSpecificContentSettingsDelegate>(
           web_contents()));
   TabSpecificContentSettings* content_settings =
-      TabSpecificContentSettings::FromWebContents(web_contents());
+      TabSpecificContentSettings::GetForFrame(web_contents()->GetMainFrame());
   auto content_setting_image_model =
       ContentSettingImageModel::CreateForContentType(
           ContentSettingImageModel::ImageType::ADS);
@@ -433,7 +433,7 @@
       std::make_unique<chrome::TabSpecificContentSettingsDelegate>(
           web_contents()));
   TabSpecificContentSettings* content_settings =
-      TabSpecificContentSettings::FromWebContents(web_contents());
+      TabSpecificContentSettings::GetForFrame(web_contents()->GetMainFrame());
   auto content_setting_image_model =
       ContentSettingImageModel::CreateForContentType(
           ContentSettingImageModel::ImageType::NOTIFICATIONS_QUIET_PROMPT);
diff --git a/chrome/browser/ui/content_settings/content_setting_media_image_model_unittest.mm b/chrome/browser/ui/content_settings/content_setting_media_image_model_unittest.mm
index edb702e9..20ae58aa 100644
--- a/chrome/browser/ui/content_settings/content_setting_media_image_model_unittest.mm
+++ b/chrome/browser/ui/content_settings/content_setting_media_image_model_unittest.mm
@@ -91,7 +91,7 @@
       std::make_unique<chrome::TabSpecificContentSettingsDelegate>(
           web_contents()));
   auto* content_settings =
-      TabSpecificContentSettings::FromWebContents(web_contents());
+      TabSpecificContentSettings::GetForFrame(web_contents()->GetMainFrame());
   const GURL kTestOrigin("https://www.example.com");
   auto content_setting_image_model =
       ContentSettingImageModel::CreateForContentType(
diff --git a/chrome/browser/ui/cookie_controls/cookie_controls_controller_unittest.cc b/chrome/browser/ui/cookie_controls/cookie_controls_controller_unittest.cc
index 2040f651..8255692 100644
--- a/chrome/browser/ui/cookie_controls/cookie_controls_controller_unittest.cc
+++ b/chrome/browser/ui/cookie_controls/cookie_controls_controller_unittest.cc
@@ -98,8 +98,8 @@
 
   content_settings::TabSpecificContentSettings*
   tab_specific_content_settings() {
-    return content_settings::TabSpecificContentSettings::FromWebContents(
-        web_contents());
+    return content_settings::TabSpecificContentSettings::GetForFrame(
+        web_contents()->GetMainFrame());
   }
 
  private:
diff --git a/chrome/browser/ui/login/login_handler.cc b/chrome/browser/ui/login/login_handler.cc
index fa652806..fb36970 100644
--- a/chrome/browser/ui/login/login_handler.cc
+++ b/chrome/browser/ui/login/login_handler.cc
@@ -18,6 +18,7 @@
 #include "build/build_config.h"
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/password_manager/chrome_password_manager_client.h"
+#include "chrome/browser/prerender/chrome_prerender_contents_delegate.h"
 #include "chrome/browser/prerender/prerender_contents.h"
 #include "chrome/browser/tab_contents/tab_util.h"
 #include "chrome/common/chrome_features.h"
@@ -520,7 +521,8 @@
     return;
   }
   prerender::PrerenderContents* prerender_contents =
-      prerender::PrerenderContents::FromWebContents(web_contents());
+      prerender::ChromePrerenderContentsDelegate::FromWebContents(
+          web_contents());
   if (prerender_contents) {
     prerender_contents->Destroy(prerender::FINAL_STATUS_AUTH_NEEDED);
     CancelAuth();
diff --git a/chrome/browser/ui/tab_helpers.cc b/chrome/browser/ui/tab_helpers.cc
index 52cf2b1..f888e94 100644
--- a/chrome/browser/ui/tab_helpers.cc
+++ b/chrome/browser/ui/tab_helpers.cc
@@ -139,6 +139,7 @@
 
 #if defined(OS_CHROMEOS)
 #include "chrome/browser/chromeos/child_accounts/time_limits/web_time_navigation_observer.h"
+#include "chrome/browser/chromeos/policy/dlp/dlp_content_tab_helper.h"
 #include "chrome/browser/ui/app_list/search/cros_action_history/cros_action_recorder_tab_tracker.h"
 #endif
 
@@ -374,6 +375,7 @@
   app_list::CrOSActionRecorderTabTracker::CreateForWebContents(web_contents);
   chromeos::app_time::WebTimeNavigationObserver::MaybeCreateForWebContents(
       web_contents);
+  policy::DlpContentTabHelper::CreateForWebContents(web_contents);
 #endif
 
 #if defined(OS_WIN) || defined(OS_MAC) || \
diff --git a/chrome/browser/ui/tab_helpers.h b/chrome/browser/ui/tab_helpers.h
index 4652957..fb9d771 100644
--- a/chrome/browser/ui/tab_helpers.h
+++ b/chrome/browser/ui/tab_helpers.h
@@ -18,7 +18,7 @@
 }
 
 namespace prerender {
-class PrerenderContents;
+class ChromePrerenderContentsDelegate;
 }
 
 namespace thin_webview {
@@ -56,7 +56,7 @@
 
   // Prerendering loads pages that have arbitrary external content; it needs
   // the full set of tab helpers to deal with it.
-  friend class prerender::PrerenderContents;
+  friend class prerender::ChromePrerenderContentsDelegate;
 
   // ThinWebView is used to host WebContents on non-tab UIs in Android. Most
   // clients of ThinWebView will need a major subset of the tab helpers.
diff --git a/chrome/browser/ui/views/accessibility/caption_bubble.cc b/chrome/browser/ui/views/accessibility/caption_bubble.cc
index ee2bbba..aee33ad 100644
--- a/chrome/browser/ui/views/accessibility/caption_bubble.cc
+++ b/chrome/browser/ui/views/accessibility/caption_bubble.cc
@@ -539,6 +539,16 @@
   Redraw();
 }
 
+void CaptionBubble::OnReadyChanged() {
+  // There is a bug in RenderText in which the label text must not be empty when
+  // it is displayed, or otherwise subsequent calculation of the number of lines
+  // (CaptionBubble::GetNumLinesInLabel) will be incorrect. The label text here
+  // is set to a space character.
+  // TODO(1055150): Fix the bug in RenderText and then remove this workaround.
+  label_->SetText(base::ASCIIToUTF16("\u0020"));
+  UpdateBubbleAndWaitTextVisibility();
+}
+
 void CaptionBubble::OnIsExpandedChanged() {
   expand_button_->SetVisible(!is_expanded_);
   collapse_button_->SetVisible(is_expanded_);
@@ -566,14 +576,14 @@
     // Hide the widget if there is no room for it or the model is closed.
     if (GetWidget()->IsVisible())
       GetWidget()->Hide();
-  } else if (label_->GetText().size() > 0 || model_->HasError()) {
-    // Show the widget if it has text or an error to display. Only show the
-    // widget if it isn't already visible. Always calling Widget::Show() will
-    // mean the widget gets focus each time.
+  } else if (model_->IsReady() || model_->HasError()) {
+    // Show the widget if it is ready to receive transcriptions or it has an
+    // error to display. Only show the widget if it isn't already visible.
+    // Always calling Widget::Show() will mean the widget gets focus each time.
     if (!GetWidget()->IsVisible())
       GetWidget()->Show();
   } else if (GetWidget()->IsVisible()) {
-    // No text and no error. Hide it.
+    // Not ready and no error. Hide it.
     GetWidget()->Hide();
   }
 }
diff --git a/chrome/browser/ui/views/accessibility/caption_bubble.h b/chrome/browser/ui/views/accessibility/caption_bubble.h
index 0015a47..70d3fdd 100644
--- a/chrome/browser/ui/views/accessibility/caption_bubble.h
+++ b/chrome/browser/ui/views/accessibility/caption_bubble.h
@@ -103,6 +103,11 @@
   // the model has an error, otherwise displays the latest text.
   void OnErrorChanged();
 
+  // Called by the CaptionBubbleModel to notify this object that the model's
+  // on ready state has changed. Makes the caption bubble become visible and
+  // show the wait text.
+  void OnReadyChanged();
+
   // Called when the caption bubble expanded state has changed. Changes the
   // number of lines displayed.
   void OnIsExpandedChanged();
diff --git a/chrome/browser/ui/views/accessibility/caption_bubble_controller_views.cc b/chrome/browser/ui/views/accessibility/caption_bubble_controller_views.cc
index 00f762f..2392db1 100644
--- a/chrome/browser/ui/views/accessibility/caption_bubble_controller_views.cc
+++ b/chrome/browser/ui/views/accessibility/caption_bubble_controller_views.cc
@@ -69,6 +69,18 @@
   browser_ = nullptr;
 }
 
+bool CaptionBubbleControllerViews::OnSpeechRecognitionReady(
+    content::WebContents* web_contents) {
+  if (!caption_bubble_ || !caption_bubble_models_.count(web_contents) ||
+      caption_bubble_models_[web_contents]->IsClosed())
+    return false;
+
+  CaptionBubbleModel* caption_bubble_model =
+      caption_bubble_models_[web_contents].get();
+  caption_bubble_model->OnReady();
+  return true;
+}
+
 bool CaptionBubbleControllerViews::OnTranscription(
     const chrome::mojom::TranscriptionResultPtr& transcription_result,
     content::WebContents* web_contents) {
diff --git a/chrome/browser/ui/views/accessibility/caption_bubble_controller_views.h b/chrome/browser/ui/views/accessibility/caption_bubble_controller_views.h
index 1ff69ed..d95d82cd 100644
--- a/chrome/browser/ui/views/accessibility/caption_bubble_controller_views.h
+++ b/chrome/browser/ui/views/accessibility/caption_bubble_controller_views.h
@@ -38,6 +38,8 @@
   CaptionBubbleControllerViews& operator=(const CaptionBubbleControllerViews&) =
       delete;
 
+  bool OnSpeechRecognitionReady(content::WebContents* web_contents) override;
+
   // Called when a transcription is received from the service. Returns whether
   // the transcription result was set on the caption bubble successfully.
   // Transcriptions will halt if this returns false.
diff --git a/chrome/browser/ui/views/accessibility/caption_bubble_controller_views_browsertest.cc b/chrome/browser/ui/views/accessibility/caption_bubble_controller_views_browsertest.cc
index f957580..32bd66f 100644
--- a/chrome/browser/ui/views/accessibility/caption_bubble_controller_views_browsertest.cc
+++ b/chrome/browser/ui/views/accessibility/caption_bubble_controller_views_browsertest.cc
@@ -136,6 +136,11 @@
         browser()->tab_strip_model()->GetWebContentsAt(tab_index));
   }
 
+  bool OnSpeechRecognitionReady(int tab_index = 0) {
+    return GetController()->OnSpeechRecognitionReady(
+        browser()->tab_strip_model()->GetWebContentsAt(tab_index));
+  }
+
   void ActivateTabAt(int index) {
     browser()->tab_strip_model()->ActivateTabAt(index);
   }
@@ -162,33 +167,27 @@
 };
 
 IN_PROC_BROWSER_TEST_F(CaptionBubbleControllerViewsTest, ShowsCaptionInBubble) {
+  OnSpeechRecognitionReady();
   OnPartialTranscription("Taylor");
   EXPECT_TRUE(IsWidgetVisible());
   EXPECT_EQ("Taylor", GetLabelText());
-  OnPartialTranscription(
-      "Taylor Alison Swift (born December 13, "
-      "1989)");
-  EXPECT_EQ("Taylor Alison Swift (born December 13, 1989)", GetLabelText());
+  OnPartialTranscription("Taylor Alison Swift\n(born December 13, 1989)");
+  EXPECT_EQ("Taylor Alison Swift\n(born December 13, 1989)", GetLabelText());
+  EXPECT_FALSE(GetWaitText()->GetVisible());
 
-  // Hides the bubble when set to the empty string.
+  // The bubble is still visible when set to the empty string. The wait text
+  // becomes visible.
   OnPartialTranscription("");
-  EXPECT_FALSE(IsWidgetVisible());
-
-  // Shows it again when the caption is no longer empty.
-  OnPartialTranscription(
-      "Taylor Alison Swift (born December 13, "
-      "1989) is an American singer-songwriter.");
   EXPECT_TRUE(IsWidgetVisible());
-  EXPECT_EQ(
-      "Taylor Alison Swift (born December 13, 1989) is an American "
-      "singer-songwriter.",
-      GetLabelText());
+  EXPECT_EQ("", GetLabelText());
+  EXPECT_TRUE(GetWaitText()->GetVisible());
 }
 
 IN_PROC_BROWSER_TEST_F(CaptionBubbleControllerViewsTest, LaysOutCaptionLabel) {
   // A short caption is bottom-aligned with the bubble. The bubble bounds
   // are inset by 18 dip on the the sides and 24 dip on the bottom. The label
   // top can change, but the bubble height and width should not change.
+  OnSpeechRecognitionReady();
   OnPartialTranscription("Cats rock");
   gfx::Rect label_bounds = GetLabel()->GetBoundsInScreen();
   gfx::Rect bubble_bounds = GetBubble()->GetBoundsInScreen();
@@ -217,6 +216,9 @@
 
 IN_PROC_BROWSER_TEST_F(CaptionBubbleControllerViewsTest,
                        CaptionWaitTextShownAtFirst) {
+  OnSpeechRecognitionReady();
+  EXPECT_TRUE(GetWaitText()->GetVisible());
+
   // With one line of text, the wait text is visible and positioned between the
   // top of the bubble and top of the label.
   OnPartialTranscription("Cats rock");
@@ -226,6 +228,14 @@
 
   OnPartialTranscription("Cats rock\nDogs too");
   EXPECT_FALSE(GetWaitText()->GetVisible());
+
+  OnPartialTranscription(
+      "Taylor Alison Swift (born December 13, 1989) is an American "
+      "singer-songwriter. She is known for narrative songs about her personal "
+      "life, which have received widespread media coverage. At age 14, Swift "
+      "became the youngest artist signed by the Sony/ATV Music publishing "
+      "house and, at age 15, she signed her first record deal.");
+  EXPECT_FALSE(GetWaitText()->GetVisible());
 }
 
 IN_PROC_BROWSER_TEST_F(CaptionBubbleControllerViewsTest, BubblePositioning) {
@@ -235,6 +245,7 @@
       BrowserView::GetBrowserViewForBrowser(browser())->GetContentsView();
 
   browser()->window()->SetBounds(gfx::Rect(10, 10, 800, 600));
+  OnSpeechRecognitionReady();
   OnPartialTranscription("Mantis shrimp have 12-16 photoreceptors");
   ExpectInBottomCenter(contents_view->GetBoundsInScreen(),
                        GetCaptionWidget()->GetClientAreaBoundsInScreen());
@@ -363,6 +374,7 @@
 }
 
 IN_PROC_BROWSER_TEST_F(CaptionBubbleControllerViewsTest, ShowsAndHidesError) {
+  OnSpeechRecognitionReady();
   OnPartialTranscription("Elephants' trunks average 6 feet long.");
   EXPECT_TRUE(GetWaitText()->GetVisible());
   EXPECT_TRUE(GetLabel()->GetVisible());
@@ -387,7 +399,9 @@
 }
 
 IN_PROC_BROWSER_TEST_F(CaptionBubbleControllerViewsTest, CloseButtonCloses) {
-  bool success = OnFinalTranscription("Elephants have 3-4 toenails per foot");
+  bool success = OnSpeechRecognitionReady();
+  EXPECT_TRUE(success);
+  success = OnFinalTranscription("Elephants have 3-4 toenails per foot");
   EXPECT_TRUE(success);
   EXPECT_TRUE(GetCaptionWidget());
   EXPECT_TRUE(IsWidgetVisible());
@@ -395,6 +409,8 @@
   ClickButton(GetCloseButton());
   EXPECT_TRUE(GetCaptionWidget());
   EXPECT_FALSE(IsWidgetVisible());
+  success = OnSpeechRecognitionReady();
+  EXPECT_FALSE(success);
   success = OnFinalTranscription(
       "Elephants wander 35 miles a day in search of water");
   EXPECT_FALSE(success);
@@ -403,7 +419,9 @@
 
 IN_PROC_BROWSER_TEST_F(CaptionBubbleControllerViewsTest,
                        MovesWithArrowsWhenFocused) {
-  OnPartialTranscription("Nearly all ants are female.");
+  OnSpeechRecognitionReady();
+  OnPartialTranscription(
+      "Honeybees have tiny hairs on their eyes to help them collect pollen");
   // Not focused initially.
   EXPECT_FALSE(GetBubble()->HasFocus());
 
@@ -452,6 +470,7 @@
 }
 
 IN_PROC_BROWSER_TEST_F(CaptionBubbleControllerViewsTest, FocusableInTabOrder) {
+  OnSpeechRecognitionReady();
   OnPartialTranscription(
       "A narwhal's tusk is an enlarged tooth containing "
       "millions of nerve endings");
@@ -518,6 +537,7 @@
   int errorIconHeight = 20;
 
   GetController()->UpdateCaptionStyle(base::nullopt);
+  OnSpeechRecognitionReady();
   OnPartialTranscription("Hamsters' teeth never stop growing");
   EXPECT_EQ(textSize, GetLabel()->font_list().GetFontSize());
   EXPECT_EQ(textSize, GetWaitText()->font_list().GetFontSize());
@@ -581,6 +601,7 @@
 
 IN_PROC_BROWSER_TEST_F(CaptionBubbleControllerViewsTest,
                        PartialAndFinalTranscriptions) {
+  OnSpeechRecognitionReady();
   OnPartialTranscription("No");
   EXPECT_EQ("No", GetLabelText());
   OnPartialTranscription("No human");
@@ -608,29 +629,25 @@
   SetHasError(false);
   EXPECT_FALSE(IsWidgetVisible());
 
-  // It is shown if there is text, and hidden if the text is removed.
+  // It is shown if the bubble is ready and should not show if it is not.
   OnPartialTranscription("Newborn kangaroos are less than 1 in long");
+  EXPECT_FALSE(IsWidgetVisible());
+  // Visible when the bubble is ready.
+  OnSpeechRecognitionReady();
   EXPECT_TRUE(IsWidgetVisible());
-  // Stays visible when switching to an error state.
-  SetHasError(true);
-  EXPECT_TRUE(IsWidgetVisible());
-  // Even if the text is removed, because of the error.
+  // Even if the text is removed, still visible because it is ready.
   OnFinalTranscription("");
   EXPECT_TRUE(IsWidgetVisible());
-  // No error and no text means not visible.
-  SetHasError(false);
-  EXPECT_FALSE(IsWidgetVisible());
 
 #if !defined(OS_MAC)
   // Shrink it so small the caption bubble can't fit. Ensure it's hidden.
   // Mac windows cannot be shrunk small enough to force the bubble to hide.
-  SetHasError(false);
   browser()->window()->SetBounds(gfx::Rect(50, 50, 200, 100));
   EXPECT_FALSE(IsWidgetVisible());
 
   // Make it bigger again and ensure it's still not visible.
   browser()->window()->SetBounds(gfx::Rect(50, 50, 800, 400));
-  EXPECT_FALSE(IsWidgetVisible());
+  EXPECT_TRUE(IsWidgetVisible());
 
   // Now set some text, and ensure it hides when shrunk but re-shows when
   // grown.
@@ -655,6 +672,7 @@
   // Tab 1 will have the text "A snail can sleep for three years".
   // Tab 2 will have the text "A rhino's horn is made of hair".
 
+  OnSpeechRecognitionReady(0);
   OnPartialTranscription("Polar bears are the largest carnivores on land", 0);
   EXPECT_TRUE(IsWidgetVisible());
   EXPECT_EQ("Polar bears are the largest carnivores on land", GetLabelText());
@@ -672,6 +690,7 @@
 
   // Switch back to tab 1 and send transcriptions.
   ActivateTabAt(1);
+  OnSpeechRecognitionReady(1);
   OnFinalTranscription("A snail can sleep", 1);
   OnPartialTranscription("for two years", 1);
   EXPECT_TRUE(IsWidgetVisible());
@@ -679,6 +698,7 @@
 
   // Send a transcription to tab 2 before activating it.
   InsertNewTab();
+  OnSpeechRecognitionReady(2);
   OnPartialTranscription("A rhino's horn is made of hair", 2);
   ActivateTabAt(2);
   EXPECT_TRUE(IsWidgetVisible());
@@ -720,6 +740,7 @@
   for (int i = 10; i < 40; i++) {
     text += base::NumberToString(i) + line + " ";
   }
+  OnSpeechRecognitionReady();
   OnFinalTranscription(text);
   EXPECT_EQ(text.substr(10500, 15000), GetLabelText());
   EXPECT_EQ(9u, GetBubble()->GetNumLinesInLabel());
@@ -735,6 +756,7 @@
   ui_test_utils::NavigateToURL(browser(), GURL("http://www.google.com"));
   content::WaitForLoadStop(
       browser()->tab_strip_model()->GetActiveWebContents());
+  OnSpeechRecognitionReady();
   OnFinalTranscription("Elephant calves can stand within 20 minutes of birth");
   EXPECT_TRUE(IsWidgetVisible());
   EXPECT_EQ("Elephant calves can stand within 20 minutes of birth",
@@ -748,6 +770,7 @@
 
   // The caption bubble reappears when a transcription is received on the new
   // page.
+  OnSpeechRecognitionReady();
   OnFinalTranscription("A group of toads is called a knot");
   EXPECT_TRUE(IsWidgetVisible());
   EXPECT_EQ("A group of toads is called a knot", GetLabelText());
@@ -759,6 +782,7 @@
   EXPECT_FALSE(IsWidgetVisible());
 
   // The caption bubble reappears when a transcription is received.
+  OnSpeechRecognitionReady();
   OnFinalTranscription("Lemurs, like dogs, have wet noses");
   EXPECT_TRUE(IsWidgetVisible());
   EXPECT_EQ("Lemurs, like dogs, have wet noses", GetLabelText());
@@ -770,6 +794,7 @@
   EXPECT_FALSE(IsWidgetVisible());
 
   // The caption bubble reappears when a transcription is received.
+  OnSpeechRecognitionReady();
   OnFinalTranscription("A blue whale's tongue weighs more than most elephants");
   EXPECT_TRUE(IsWidgetVisible());
   EXPECT_EQ("A blue whale's tongue weighs more than most elephants",
@@ -782,6 +807,7 @@
   EXPECT_FALSE(IsWidgetVisible());
 
   // The caption bubble reappears when a transcription is received.
+  OnSpeechRecognitionReady();
   OnFinalTranscription("All polar bears are left-pawed");
   EXPECT_TRUE(IsWidgetVisible());
   EXPECT_EQ("All polar bears are left-pawed", GetLabelText());
@@ -794,6 +820,7 @@
   content::WaitForLoadStop(
       browser()->tab_strip_model()->GetActiveWebContents());
   EXPECT_FALSE(IsWidgetVisible());
+  OnSpeechRecognitionReady();
   OnFinalTranscription("Rats laugh when they are tickled");
   EXPECT_TRUE(IsWidgetVisible());
   EXPECT_EQ("Rats laugh when they are tickled", GetLabelText());
@@ -810,9 +837,11 @@
 IN_PROC_BROWSER_TEST_F(CaptionBubbleControllerViewsTest,
                        DestroysWithoutCrashing) {
   // Test passes if destroying the controller does not crash.
+  OnSpeechRecognitionReady();
   OnPartialTranscription("Deer have a four-chambered stomach");
   DestroyController();
 
+  OnSpeechRecognitionReady();
   OnPartialTranscription("Deer antlers fall off and regrow every year");
   ClickButton(GetCloseButton());
   DestroyController();
@@ -821,6 +850,7 @@
 IN_PROC_BROWSER_TEST_F(CaptionBubbleControllerViewsTest, ExpandsAndCollapses) {
   int line_height = 24;
 
+  OnSpeechRecognitionReady();
   OnPartialTranscription("Seahorses are monogamous");
   EXPECT_TRUE(GetExpandButton()->GetVisible());
   EXPECT_FALSE(GetCollapseButton()->GetVisible());
@@ -836,8 +866,8 @@
   ActivateTabAt(1);
   EXPECT_FALSE(IsWidgetVisible());
 
-  OnPartialTranscription(
-      "Honeybees have tiny hairs on their eyes to help them collect pollen");
+  OnSpeechRecognitionReady(1);
+  OnPartialTranscription("Nearly all ants are female.");
   EXPECT_TRUE(GetCollapseButton()->GetVisible());
   EXPECT_FALSE(GetExpandButton()->GetVisible());
   EXPECT_EQ(7 * line_height, GetLabel()->GetBoundsInScreen().height());
diff --git a/chrome/browser/ui/views/accessibility/caption_bubble_model.cc b/chrome/browser/ui/views/accessibility/caption_bubble_model.cc
index b86ad571..0df6fab 100644
--- a/chrome/browser/ui/views/accessibility/caption_bubble_model.cc
+++ b/chrome/browser/ui/views/accessibility/caption_bubble_model.cc
@@ -29,6 +29,7 @@
     return;
   observer_ = observer;
   if (observer_) {
+    observer_->OnReadyChanged();
     observer_->OnTextChanged();
     observer_->OnErrorChanged();
   }
@@ -52,9 +53,23 @@
   final_text_.clear();
   partial_text_.clear();
   is_closed_ = true;
+  is_ready_ = false;
   OnTextChanged();
 }
 
+void CaptionBubbleModel::OnReady() {
+  final_text_.clear();
+  partial_text_.clear();
+  is_ready_ = true;
+  // The label text must not be empty when it is displayed, so there is a
+  // special OnReadyChanged() function in the CaptionBubble that handles the
+  // on_ready state change.
+  // TODO(1055150): Fix the bug in RenderText and then change this to
+  // OnTextChanged().
+  if (observer_)
+    observer_->OnReadyChanged();
+}
+
 void CaptionBubbleModel::SetHasError(bool has_error) {
   has_error_ = has_error;
   if (observer_)
@@ -70,6 +85,7 @@
   final_text_.clear();
   partial_text_.clear();
   is_closed_ = false;
+  is_ready_ = false;
   has_error_ = false;
   OnTextChanged();
 }
diff --git a/chrome/browser/ui/views/accessibility/caption_bubble_model.h b/chrome/browser/ui/views/accessibility/caption_bubble_model.h
index e1a8765c..8356693 100644
--- a/chrome/browser/ui/views/accessibility/caption_bubble_model.h
+++ b/chrome/browser/ui/views/accessibility/caption_bubble_model.h
@@ -57,7 +57,10 @@
   // observer.
   void Close();
 
+  void OnReady();
+
   bool IsClosed() const { return is_closed_; }
+  bool IsReady() const { return is_ready_; }
   bool HasError() const { return has_error_; }
   std::string GetFullText() const { return final_text_ + partial_text_; }
 
@@ -76,6 +79,9 @@
   // Whether the bubble has been closed by the user.
   bool is_closed_ = false;
 
+  // Whether bubble is ready to receive transcriptions.
+  bool is_ready_ = false;
+
   // Whether an error should be displayed one the bubble.
   bool has_error_ = false;
 
diff --git a/chrome/browser/ui/views/collected_cookies_views.cc b/chrome/browser/ui/views/collected_cookies_views.cc
index 088df85..e3b52d5 100644
--- a/chrome/browser/ui/views/collected_cookies_views.cc
+++ b/chrome/browser/ui/views/collected_cookies_views.cc
@@ -415,9 +415,11 @@
 }
 
 std::unique_ptr<views::View> CollectedCookiesViews::CreateAllowedPane() {
+  // This captures a snapshot of the allowed cookies of the current page so we
+  // are fine using WebContents::GetMainFrame() here
   content_settings::TabSpecificContentSettings* content_settings =
-      content_settings::TabSpecificContentSettings::FromWebContents(
-          web_contents_);
+      content_settings::TabSpecificContentSettings::GetForFrame(
+          web_contents_->GetMainFrame());
 
   // Create the controls that go into the pane.
   auto allowed_label = std::make_unique<views::Label>(
@@ -473,8 +475,8 @@
 
 std::unique_ptr<views::View> CollectedCookiesViews::CreateBlockedPane() {
   content_settings::TabSpecificContentSettings* content_settings =
-      content_settings::TabSpecificContentSettings::FromWebContents(
-          web_contents_);
+      content_settings::TabSpecificContentSettings::GetForFrame(
+          web_contents_->GetMainFrame());
 
   Profile* profile =
       Profile::FromBrowserContext(web_contents_->GetBrowserContext());
diff --git a/chrome/browser/ui/views/frame/browser_view_browsertest.cc b/chrome/browser/ui/views/frame/browser_view_browsertest.cc
index df3b123..c72879d 100644
--- a/chrome/browser/ui/views/frame/browser_view_browsertest.cc
+++ b/chrome/browser/ui/views/frame/browser_view_browsertest.cc
@@ -397,9 +397,11 @@
           caption_controller->GetCaptionBubbleControllerForBrowser(browser()));
   EXPECT_FALSE(bubble_controller->GetFocusableCaptionBubble());
 
+  content::WebContents* contents =
+      browser()->tab_strip_model()->GetActiveWebContents();
+  caption_controller->OnSpeechRecognitionReady(contents);
   caption_controller->DispatchTranscription(
-      browser()->tab_strip_model()->GetActiveWebContents(),
-      chrome::mojom::TranscriptionResult::New("Hello, world", false));
+      contents, chrome::mojom::TranscriptionResult::New("Hello, world", false));
   // Now the caption bubble exists but is not focused.
   views::View* bubble = bubble_controller->GetFocusableCaptionBubble();
   EXPECT_TRUE(bubble);
diff --git a/chrome/browser/ui/views/frame/glass_browser_frame_view.cc b/chrome/browser/ui/views/frame/glass_browser_frame_view.cc
index 0910246..818abb8 100644
--- a/chrome/browser/ui/views/frame/glass_browser_frame_view.cc
+++ b/chrome/browser/ui/views/frame/glass_browser_frame_view.cc
@@ -536,11 +536,6 @@
              : frame()->GetMinimizeButtonOffset();
 }
 
-bool GlassBrowserFrameView::IsToolbarVisible() const {
-  return browser_view()->IsToolbarVisible() &&
-      !browser_view()->toolbar()->GetPreferredSize().IsEmpty();
-}
-
 bool GlassBrowserFrameView::ShowCustomIcon() const {
   // Web-app windows don't include the window icon as per UI mocks.
   return !web_app_frame_toolbar() && ShouldCustomDrawSystemTitlebar() &&
diff --git a/chrome/browser/ui/views/frame/glass_browser_frame_view.h b/chrome/browser/ui/views/frame/glass_browser_frame_view.h
index 966e712..9ee97faa 100644
--- a/chrome/browser/ui/views/frame/glass_browser_frame_view.h
+++ b/chrome/browser/ui/views/frame/glass_browser_frame_view.h
@@ -117,9 +117,6 @@
   // edge of the caption buttons.
   int MinimizeButtonX() const;
 
-  // Returns whether the toolbar is currently visible.
-  bool IsToolbarVisible() const;
-
   bool ShowCustomIcon() const;
   bool ShowCustomTitle() const;
   bool ShowSystemIcon() const;
diff --git a/chrome/browser/ui/views/location_bar/content_setting_bubble_dialog_browsertest.cc b/chrome/browser/ui/views/location_bar/content_setting_bubble_dialog_browsertest.cc
index 3aab5544..abb8e0da 100644
--- a/chrome/browser/ui/views/location_bar/content_setting_bubble_dialog_browsertest.cc
+++ b/chrome/browser/ui/views/location_bar/content_setting_bubble_dialog_browsertest.cc
@@ -106,8 +106,8 @@
           ? content_settings::TabSpecificContentSettings::CAMERA_ACCESSED
           : 0;
   content_settings::TabSpecificContentSettings* content_settings =
-      content_settings::TabSpecificContentSettings::FromWebContents(
-          browser()->tab_strip_model()->GetActiveWebContents());
+      content_settings::TabSpecificContentSettings::GetForFrame(
+          browser()->tab_strip_model()->GetActiveWebContents()->GetMainFrame());
   content_settings->OnMediaStreamPermissionSet(
       GURL("https://example.com/"), mic_setting | camera_setting, std::string(),
       std::string(), std::string(), std::string());
@@ -118,8 +118,8 @@
   content::WebContents* web_contents =
       browser()->tab_strip_model()->GetActiveWebContents();
   content_settings::TabSpecificContentSettings* content_settings =
-      content_settings::TabSpecificContentSettings::FromWebContents(
-          web_contents);
+      content_settings::TabSpecificContentSettings::GetForFrame(
+          web_contents->GetMainFrame());
   switch (content_type) {
     case ContentSettingsType::AUTOMATIC_DOWNLOADS: {
       // Automatic downloads are handled by DownloadRequestLimiter.
diff --git a/chrome/browser/ui/views/omnibox/omnibox_view_views_unittest.cc b/chrome/browser/ui/views/omnibox/omnibox_view_views_unittest.cc
index 0f8e82a1..adfd31f 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_view_views_unittest.cc
+++ b/chrome/browser/ui/views/omnibox/omnibox_view_views_unittest.cc
@@ -305,8 +305,7 @@
 
   // Sets up tests for the simplified domain field trials.
   void SetUpSimplifiedDomainTest() {
-    location_bar_model()->set_url(
-        GURL(base::ASCIIToUTF16("https://") + kSimplifiedDomainDisplayUrl));
+    location_bar_model()->set_url(GURL(kSimplifiedDomainDisplayUrl));
     location_bar_model()->set_url_for_display(kSimplifiedDomainDisplayUrl);
     omnibox_view()->model()->ResetDisplayTexts();
     omnibox_view()->RevertAll();
@@ -1417,14 +1416,14 @@
 // |subdomain_and_scheme| and |subdomain| should include a trailing ".", and
 // |path| should include a leading "/".
 void ExpectElidedToSimplifiedDomain(gfx::RenderText* render_text,
-                                    const base::string16& subdomain_and_scheme,
+                                    const base::string16& scheme,
                                     const base::string16& subdomain,
                                     const base::string16& hostname_and_scheme,
                                     const base::string16& path,
                                     bool should_elide_to_registrable_domain) {
   gfx::Rect subdomain_and_scheme_rect;
   for (const auto& rect : render_text->GetSubstringBounds(
-           gfx::Range(0, subdomain_and_scheme.size()))) {
+           gfx::Range(0, scheme.size() + subdomain.size()))) {
     subdomain_and_scheme_rect.Union(rect);
   }
   gfx::Rect path_rect;
@@ -1437,20 +1436,38 @@
   if (should_elide_to_registrable_domain) {
     EXPECT_FALSE(
         render_text->display_rect().Contains(subdomain_and_scheme_rect));
-    EXPECT_EQ(subdomain_and_scheme_rect.width(),
+    gfx::Rect registrable_domain_rect;
+    for (const auto& rect : render_text->GetSubstringBounds(gfx::Range(
+             scheme.size() + subdomain.size(), hostname_and_scheme.size()))) {
+      registrable_domain_rect.Union(rect);
+    }
+    EXPECT_TRUE(render_text->display_rect().Contains(registrable_domain_rect));
+    // The text should be scrolled to push the scheme and subdomain offscreen,
+    // so that the text starts at the registrable domain. Note that this code
+    // computes the expected offset by comparing x() values rather than
+    // comparing based on widths (for example, it wouldn't work to check that
+    // the display offset is equal to |subdomain_and_scheme_rect|'s width). This
+    // is because GetSubstringBounds() rounds outward, so the width of
+    // |subdomain_and_scheme_rect| could slightly overlap
+    // |registrable_domain_rect|.
+    EXPECT_EQ(registrable_domain_rect.x() - subdomain_and_scheme_rect.x(),
               -1 * render_text->GetUpdatedDisplayOffset().x());
   } else {
     // When elision to registrable domain is disabled, the scheme should be
     // hidden but the subdomain should not be.
     EXPECT_FALSE(
         render_text->display_rect().Contains(subdomain_and_scheme_rect));
-    gfx::Rect subdomain_rect;
-    for (const auto& rect :
-         render_text->GetSubstringBounds(gfx::Range(0, subdomain.size()))) {
-      subdomain_rect.Union(rect);
+    gfx::Rect hostname_rect;
+    for (const auto& rect : render_text->GetSubstringBounds(
+             gfx::Range(scheme.size(), hostname_and_scheme.size()))) {
+      hostname_rect.Union(rect);
     }
-    EXPECT_EQ(subdomain_rect.x() - render_text->display_rect().x(),
-              render_text->GetUpdatedDisplayOffset().x());
+    // The text should be scrolled to push the scheme offscreen, so that the
+    // text starts at the subdomain. As above, it's important to compute the
+    // expected offset with x() values instead of width()s, since the width()s
+    // of different adjacent substring bounds could overlap.
+    EXPECT_EQ(hostname_rect.x() - subdomain_and_scheme_rect.x(),
+              -1 * render_text->GetUpdatedDisplayOffset().x());
   }
 }
 
@@ -1573,7 +1590,7 @@
   SetUpSimplifiedDomainTest();
   gfx::RenderText* render_text = omnibox_view()->GetRenderText();
   ASSERT_NO_FATAL_FAILURE(ExpectElidedToSimplifiedDomain(
-      render_text, kSimplifiedDomainDisplayUrlSubdomainAndScheme,
+      render_text, kSimplifiedDomainDisplayUrlScheme,
       kSimplifiedDomainDisplayUrlSubdomain,
       kSimplifiedDomainDisplayUrlHostnameAndScheme,
       kSimplifiedDomainDisplayUrlPath, ShouldElideToRegistrableDomain()));
@@ -1598,7 +1615,7 @@
   // After the extended hover threshold has elapsed, the display text shouldn't
   // have changed yet.
   ASSERT_NO_FATAL_FAILURE(ExpectElidedToSimplifiedDomain(
-      render_text, kSimplifiedDomainDisplayUrlSubdomainAndScheme,
+      render_text, kSimplifiedDomainDisplayUrlScheme,
       kSimplifiedDomainDisplayUrlSubdomain,
       kSimplifiedDomainDisplayUrlHostnameAndScheme,
       kSimplifiedDomainDisplayUrlPath, ShouldElideToRegistrableDomain()));
@@ -1626,7 +1643,7 @@
   hover_animation_as_element->Step(base::TimeTicks() +
                                    base::TimeDelta::FromSeconds(1));
   ASSERT_NO_FATAL_FAILURE(ExpectElidedToSimplifiedDomain(
-      render_text, kSimplifiedDomainDisplayUrlSubdomainAndScheme,
+      render_text, kSimplifiedDomainDisplayUrlScheme,
       kSimplifiedDomainDisplayUrlSubdomain,
       kSimplifiedDomainDisplayUrlHostnameAndScheme,
       kSimplifiedDomainDisplayUrlPath, ShouldElideToRegistrableDomain()));
@@ -1698,7 +1715,7 @@
   elide_as_element->SetStartTime(base::TimeTicks());
   elide_as_element->Step(base::TimeTicks() + base::TimeDelta::FromSeconds(1));
   ASSERT_NO_FATAL_FAILURE(ExpectElidedToSimplifiedDomain(
-      render_text, kSimplifiedDomainDisplayUrlSubdomainAndScheme,
+      render_text, kSimplifiedDomainDisplayUrlScheme,
       kSimplifiedDomainDisplayUrlSubdomain,
       kSimplifiedDomainDisplayUrlHostnameAndScheme,
       kSimplifiedDomainDisplayUrlPath, ShouldElideToRegistrableDomain()));
@@ -1804,7 +1821,7 @@
   EXPECT_TRUE(elide_animation->IsAnimating());
   omnibox_view()->OnBoundsChanged(gfx::Rect());
   ASSERT_NO_FATAL_FAILURE(ExpectElidedToSimplifiedDomain(
-      render_text, kSimplifiedDomainDisplayUrlSubdomainAndScheme,
+      render_text, kSimplifiedDomainDisplayUrlScheme,
       kSimplifiedDomainDisplayUrlSubdomain,
       kSimplifiedDomainDisplayUrlHostnameAndScheme,
       kSimplifiedDomainDisplayUrlPath, ShouldElideToRegistrableDomain()));
@@ -1817,7 +1834,7 @@
   gfx::RenderText* render_text = omnibox_view()->GetRenderText();
 
   ASSERT_NO_FATAL_FAILURE(ExpectElidedToSimplifiedDomain(
-      render_text, kSimplifiedDomainDisplayUrlSubdomainAndScheme,
+      render_text, kSimplifiedDomainDisplayUrlScheme,
       kSimplifiedDomainDisplayUrlSubdomain,
       kSimplifiedDomainDisplayUrlHostnameAndScheme,
       kSimplifiedDomainDisplayUrlPath, ShouldElideToRegistrableDomain()));
@@ -1825,7 +1842,7 @@
   // After the bounds change, the URL should remain elided.
   omnibox_view()->OnBoundsChanged(gfx::Rect());
   ASSERT_NO_FATAL_FAILURE(ExpectElidedToSimplifiedDomain(
-      render_text, kSimplifiedDomainDisplayUrlSubdomainAndScheme,
+      render_text, kSimplifiedDomainDisplayUrlScheme,
       kSimplifiedDomainDisplayUrlSubdomain,
       kSimplifiedDomainDisplayUrlHostnameAndScheme,
       kSimplifiedDomainDisplayUrlPath, ShouldElideToRegistrableDomain()));
@@ -1840,7 +1857,7 @@
   EXPECT_TRUE(unelide_animation->IsAnimating());
   omnibox_view()->OnBoundsChanged(gfx::Rect());
   ASSERT_NO_FATAL_FAILURE(ExpectElidedToSimplifiedDomain(
-      render_text, kSimplifiedDomainDisplayUrlSubdomainAndScheme,
+      render_text, kSimplifiedDomainDisplayUrlScheme,
       kSimplifiedDomainDisplayUrlSubdomain,
       kSimplifiedDomainDisplayUrlHostnameAndScheme,
       kSimplifiedDomainDisplayUrlPath, ShouldElideToRegistrableDomain()));
@@ -1915,11 +1932,14 @@
       elide_animation->GetAnimationForTesting());
   elide_as_element->SetStartTime(base::TimeTicks());
   elide_as_element->Step(base::TimeTicks() + base::TimeDelta::FromSeconds(2));
+  // Use should_elide_to_registrable_domain=true here regardless of how the
+  // field trial is set because the "www." should be elided as a trivial
+  // subdomain.
   ASSERT_NO_FATAL_FAILURE(ExpectElidedToSimplifiedDomain(
-      render_text, base::ASCIIToUTF16("https://www."),
-      base::ASCIIToUTF16("www."),
+      render_text, base::ASCIIToUTF16("https://"), base::ASCIIToUTF16("www."),
       base::ASCIIToUTF16("https://www.example.test"),
-      base::ASCIIToUTF16("/foo"), ShouldElideToRegistrableDomain()));
+      base::ASCIIToUTF16("/foo"),
+      /* should_elide_to_registrable_domain=*/true));
 
   // Do another hover and check that the URL gets unelided to the full URL.
   omnibox_view()->OnMouseMoved(CreateMouseEvent(ui::ET_MOUSE_MOVED, {0, 0}));
@@ -1946,10 +1966,10 @@
   elide_as_element->SetStartTime(base::TimeTicks());
   elide_as_element->Step(base::TimeTicks() + base::TimeDelta::FromSeconds(2));
   ASSERT_NO_FATAL_FAILURE(ExpectElidedToSimplifiedDomain(
-      render_text, base::ASCIIToUTF16("https://www."),
-      base::ASCIIToUTF16("www."),
+      render_text, base::ASCIIToUTF16("https://"), base::ASCIIToUTF16("www."),
       base::ASCIIToUTF16("https://www.example.test"),
-      base::ASCIIToUTF16("/foo"), ShouldElideToRegistrableDomain()));
+      base::ASCIIToUTF16("/foo"),
+      /* should_elide_to_registrable_domain=*/true));
   EXPECT_EQ(SK_ColorTRANSPARENT, omnibox_view()->GetLatestColorForRange(
                                      gfx::Range(0, kSchemeAndSubdomainSize)));
 }
@@ -2124,8 +2144,7 @@
     // to the registrable domain because the www subdomain is considered
     // trivial.
     ASSERT_NO_FATAL_FAILURE(ExpectElidedToSimplifiedDomain(
-        render_text, base::ASCIIToUTF16("https://www."),
-        base::ASCIIToUTF16("www."),
+        render_text, base::ASCIIToUTF16("https://"), base::ASCIIToUTF16("www."),
         base::ASCIIToUTF16("https://www.example.test"),
         base::ASCIIToUTF16("/foo"),
         true /* should elide to registrable domain */));
@@ -2338,7 +2357,7 @@
   elide_as_element->SetStartTime(base::TimeTicks());
   elide_as_element->Step(base::TimeTicks() + base::TimeDelta::FromSeconds(1));
   ASSERT_NO_FATAL_FAILURE(ExpectElidedToSimplifiedDomain(
-      render_text, kSimplifiedDomainDisplayUrlSubdomainAndScheme,
+      render_text, kSimplifiedDomainDisplayUrlScheme,
       kSimplifiedDomainDisplayUrlSubdomain,
       kSimplifiedDomainDisplayUrlHostnameAndScheme,
       kSimplifiedDomainDisplayUrlPath, ShouldElideToRegistrableDomain()));
@@ -2350,7 +2369,7 @@
     navigation.set_is_same_document(true);
     omnibox_view()->DidFinishNavigation(&navigation);
     ASSERT_NO_FATAL_FAILURE(ExpectElidedToSimplifiedDomain(
-        render_text, kSimplifiedDomainDisplayUrlSubdomainAndScheme,
+        render_text, kSimplifiedDomainDisplayUrlScheme,
         kSimplifiedDomainDisplayUrlSubdomain,
         kSimplifiedDomainDisplayUrlHostnameAndScheme,
         kSimplifiedDomainDisplayUrlPath, ShouldElideToRegistrableDomain()));
@@ -2508,7 +2527,7 @@
   elide_as_element->SetStartTime(base::TimeTicks());
   elide_as_element->Step(base::TimeTicks() + base::TimeDelta::FromSeconds(1));
   ASSERT_NO_FATAL_FAILURE(ExpectElidedToSimplifiedDomain(
-      render_text, kSimplifiedDomainDisplayUrlSubdomainAndScheme,
+      render_text, kSimplifiedDomainDisplayUrlScheme,
       kSimplifiedDomainDisplayUrlSubdomain,
       kSimplifiedDomainDisplayUrlHostnameAndScheme,
       kSimplifiedDomainDisplayUrlPath, ShouldElideToRegistrableDomain()));
@@ -2528,7 +2547,7 @@
     navigation.set_render_frame_host(subframe);
     omnibox_view()->DidFinishNavigation(&navigation);
     ASSERT_NO_FATAL_FAILURE(ExpectElidedToSimplifiedDomain(
-        render_text, kSimplifiedDomainDisplayUrlSubdomainAndScheme,
+        render_text, kSimplifiedDomainDisplayUrlScheme,
         kSimplifiedDomainDisplayUrlSubdomain,
         kSimplifiedDomainDisplayUrlHostnameAndScheme,
         kSimplifiedDomainDisplayUrlPath, ShouldElideToRegistrableDomain()));
@@ -2555,7 +2574,7 @@
   elide_as_element->SetStartTime(base::TimeTicks());
   elide_as_element->Step(base::TimeTicks() + base::TimeDelta::FromSeconds(1));
   ASSERT_NO_FATAL_FAILURE(ExpectElidedToSimplifiedDomain(
-      render_text, kSimplifiedDomainDisplayUrlSubdomainAndScheme,
+      render_text, kSimplifiedDomainDisplayUrlScheme,
       kSimplifiedDomainDisplayUrlSubdomain,
       kSimplifiedDomainDisplayUrlHostnameAndScheme,
       kSimplifiedDomainDisplayUrlPath, ShouldElideToRegistrableDomain()));
@@ -2602,8 +2621,7 @@
   omnibox_view()->OnFocus();
   omnibox_view()->OnBlur();
   ASSERT_NO_FATAL_FAILURE(ExpectElidedToSimplifiedDomain(
-      omnibox_view()->GetRenderText(),
-      kSimplifiedDomainDisplayUrlSubdomainAndScheme,
+      omnibox_view()->GetRenderText(), kSimplifiedDomainDisplayUrlScheme,
       kSimplifiedDomainDisplayUrlSubdomain,
       kSimplifiedDomainDisplayUrlHostnameAndScheme,
       kSimplifiedDomainDisplayUrlPath, ShouldElideToRegistrableDomain()));
diff --git a/chrome/browser/ui/webui/discards/DEPS b/chrome/browser/ui/webui/discards/DEPS
new file mode 100644
index 0000000..43d1a4a
--- /dev/null
+++ b/chrome/browser/ui/webui/discards/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+  "+components/performance_manager/persistence/site_data",
+]
diff --git a/chrome/browser/ui/webui/discards/discards_ui.cc b/chrome/browser/ui/webui/discards/discards_ui.cc
index 95448e0..74a7a2a 100644
--- a/chrome/browser/ui/webui/discards/discards_ui.cc
+++ b/chrome/browser/ui/webui/discards/discards_ui.cc
@@ -18,7 +18,6 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/resource_coordinator/lifecycle_unit.h"
 #include "chrome/browser/resource_coordinator/lifecycle_unit_state.mojom.h"
-#include "chrome/browser/resource_coordinator/local_site_characteristics_data_store_inspector.h"
 #include "chrome/browser/resource_coordinator/tab_activity_watcher.h"
 #include "chrome/browser/resource_coordinator/tab_lifecycle_unit_external.h"
 #include "chrome/browser/resource_coordinator/tab_manager.h"
@@ -46,10 +45,6 @@
 #include "url/gurl.h"
 #include "url/origin.h"
 
-namespace resource_coordinator {
-class LocalSiteCharacteristicsDataStoreInspector;
-}  // namespace resource_coordinator
-
 namespace {
 
 discards::mojom::LifecycleUnitVisibility GetLifecycleUnitVisibility(
@@ -258,8 +253,7 @@
       profile, std::make_unique<FaviconSource>(
                    profile, chrome::FaviconUrlFormat::kFavicon2));
 
-  data_store_inspector_ = resource_coordinator::
-      LocalSiteCharacteristicsDataStoreInspector::GetForProfile(profile);
+  profile_id_ = profile->UniqueId();
 }
 
 WEB_UI_CONTROLLER_TYPE_IMPL(DiscardsUI)
@@ -274,8 +268,12 @@
 
 void DiscardsUI::BindInterface(
     mojo::PendingReceiver<discards::mojom::SiteDataProvider> receiver) {
-  site_data_provider_ = std::make_unique<SiteDataProviderImpl>(
-      data_store_inspector_, std::move(receiver));
+  if (performance_manager::PerformanceManager::IsAvailable()) {
+    // Forward the interface receiver directly to the service.
+    performance_manager::PerformanceManager::CallOnGraph(
+        FROM_HERE, base::BindOnce(&SiteDataProviderImpl::CreateAndBind,
+                                  std::move(receiver), profile_id_));
+  }
 }
 
 void DiscardsUI::BindInterface(
diff --git a/chrome/browser/ui/webui/discards/discards_ui.h b/chrome/browser/ui/webui/discards/discards_ui.h
index f454366..9f9baf3 100644
--- a/chrome/browser/ui/webui/discards/discards_ui.h
+++ b/chrome/browser/ui/webui/discards/discards_ui.h
@@ -13,10 +13,6 @@
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "ui/webui/mojo_web_ui_controller.h"
 
-namespace resource_coordinator {
-class LocalSiteCharacteristicsDataStoreInspector;
-}  // namespace resource_coordinator
-
 // Controller for chrome://discards. Corresponding resources are in
 // file://chrome/browser/resources/discards.
 class DiscardsUI : public ui::MojoWebUIController {
@@ -42,8 +38,7 @@
  private:
   std::unique_ptr<discards::mojom::DetailsProvider> ui_handler_;
   std::unique_ptr<discards::mojom::SiteDataProvider> site_data_provider_;
-  resource_coordinator::LocalSiteCharacteristicsDataStoreInspector*
-      data_store_inspector_;
+  std::string profile_id_;
 
   WEB_UI_CONTROLLER_TYPE_DECL();
 
diff --git a/chrome/browser/ui/webui/discards/graph_dump_impl.h b/chrome/browser/ui/webui/discards/graph_dump_impl.h
index d01ba675..79b4762 100644
--- a/chrome/browser/ui/webui/discards/graph_dump_impl.h
+++ b/chrome/browser/ui/webui/discards/graph_dump_impl.h
@@ -213,9 +213,6 @@
       int64_t serialization_id,
       scoped_refptr<base::RefCountedMemory> bitmap_data);
 
-  static void BindOnPMSequence(
-      mojo::PendingReceiver<discards::mojom::GraphDump> receiver,
-      performance_manager::Graph* graph);
   static void OnConnectionError(DiscardsGraphDumpImpl* impl);
 
   performance_manager::Graph* graph_ = nullptr;
diff --git a/chrome/browser/ui/webui/discards/site_data.mojom b/chrome/browser/ui/webui/discards/site_data.mojom
index 1084c80..cb24e09 100644
--- a/chrome/browser/ui/webui/discards/site_data.mojom
+++ b/chrome/browser/ui/webui/discards/site_data.mojom
@@ -4,7 +4,7 @@
 
 module discards.mojom;
 
-struct SiteCharacteristicsFeature {
+struct SiteDataFeature {
   // The cumulative observation time for this feature in seconds, set to 0 once
   // this feature has been observed.
   int64 observation_duration;
@@ -13,7 +13,7 @@
   int64 use_timestamp;
 };
 
-struct SiteCharacteristicsPerformanceMeasurement {
+struct SiteDataPerformanceMeasurement {
   // A decaying average of the CPU usage measurements. Units: microseconds.
   float avg_cpu_usage_us;
   // A decaying average of the process footprint measurements. Units: kilobytes.
@@ -23,7 +23,7 @@
   float avg_load_duration_us;
 };
 
-struct SiteCharacteristicsDatabaseSize {
+struct SiteDataDatabaseSize {
   // The total number of rows in the database, or -1 if the value is not
   // available.
   int64 num_rows;
@@ -36,24 +36,24 @@
 // The data stored for a given origin, this should mirror the
 // SiteDataProto structure in
 // performance_manager/persistence/site_data/site_data.proto.
-struct SiteCharacteristicsDatabaseValue {
+struct SiteDataValue {
   // The last time this site has been in the loaded state, in seconds since
   // epoch.
   uint32 last_loaded;
 
-  SiteCharacteristicsFeature updates_favicon_in_background;
-  SiteCharacteristicsFeature updates_title_in_background;
-  SiteCharacteristicsFeature uses_audio_in_background;
+  SiteDataFeature updates_favicon_in_background;
+  SiteDataFeature updates_title_in_background;
+  SiteDataFeature uses_audio_in_background;
 
   // Load time performance measurement estimates. This maintains a decaying
   // average of the resource usage of a page until shortly after it becomes
   // idle.
-  SiteCharacteristicsPerformanceMeasurement? load_time_estimates;
+  SiteDataPerformanceMeasurement? load_time_estimates;
 };
 
 // Provides the key and miscellaneous in-memory only data pertaining to a
 // row that potentially exists in a database.
-struct SiteCharacteristicsDatabaseEntry {
+struct SiteDataEntry {
   // The origin associated with this row.
   string origin;
 
@@ -61,13 +61,13 @@
   bool is_dirty;
 
   // NULL if the database entry doesn't exist on disk or in memory.
-  SiteCharacteristicsDatabaseValue? value;
+  SiteDataValue? value;
 };
 
-// Contains information about a specific DB instance.
-struct SiteCharacteristicsDatabase {
+// Contains information about a specific set of SiteData entries.
+struct SiteDataArray {
   // Contains the entries requested.
-  array<SiteCharacteristicsDatabaseEntry> db_rows;
+  array<SiteDataEntry> db_rows;
 };
 
 // Interface for providing information about the site data database. Lives in
@@ -77,14 +77,14 @@
   // Returns the in-memory entries and the entries for the requested origins.
   // Note that any entry may take some time to load from disk, and so there may
   // not be any data for a given entry until on the second or subsequent
-  // requests. The WebUI is expected to poll this function.
-  GetSiteCharacteristicsDatabase(
+  // requests.
+  GetSiteDataArray(
       array<string> explicitly_requested_origins) =>
-          (SiteCharacteristicsDatabase? result);
+          (SiteDataArray? result);
 
   // Returns the size of the database in number of rows and kilobytes.
   // Note that this may be fairly expensive to acquire, and so shouldn't be
   // called frequently.
-  GetSiteCharacteristicsDatabaseSize() =>
-      (SiteCharacteristicsDatabaseSize? db_size);
+  GetSiteDataDatabaseSize() =>
+      (SiteDataDatabaseSize? db_size);
 };
diff --git a/chrome/browser/ui/webui/discards/site_data_provider_impl.cc b/chrome/browser/ui/webui/discards/site_data_provider_impl.cc
index 8d701889f..8527dd8 100644
--- a/chrome/browser/ui/webui/discards/site_data_provider_impl.cc
+++ b/chrome/browser/ui/webui/discards/site_data_provider_impl.cc
@@ -5,34 +5,46 @@
 #include "chrome/browser/ui/webui/discards/site_data_provider_impl.h"
 
 #include "base/bind_helpers.h"
-#include "chrome/browser/resource_coordinator/local_site_characteristics_data_reader.h"
-#include "chrome/browser/resource_coordinator/local_site_characteristics_data_store.h"
-#include "chrome/browser/resource_coordinator/local_site_characteristics_data_store_inspector.h"
+#include "base/sequence_checker.h"
+#include "components/performance_manager/persistence/site_data/site_data.pb.h"
+#include "components/performance_manager/persistence/site_data/site_data_cache_factory.h"
+#include "components/performance_manager/persistence/site_data/site_data_cache_inspector.h"
+#include "components/performance_manager/public/graph/graph.h"
+#include "components/performance_manager/public/graph/page_node.h"
+#include "components/performance_manager/public/persistence/site_data/site_data_reader.h"
+#include "url/gurl.h"
 
 namespace {
 
-discards::mojom::SiteCharacteristicsFeaturePtr ConvertFeatureFromProto(
+discards::mojom::SiteDataFeaturePtr ConvertFeatureFromProto(
     const SiteDataFeatureProto& proto) {
-  discards::mojom::SiteCharacteristicsFeaturePtr feature =
-      discards::mojom::SiteCharacteristicsFeature::New();
+  discards::mojom::SiteDataFeaturePtr feature =
+      discards::mojom::SiteDataFeature::New();
 
-  if (proto.has_observation_duration())
+  if (proto.has_observation_duration()) {
     feature->observation_duration = proto.observation_duration();
+  } else {
+    feature->observation_duration = 0;
+  }
 
-  if (proto.has_use_timestamp())
+  if (proto.has_use_timestamp()) {
     feature->use_timestamp = proto.use_timestamp();
+  } else {
+    feature->use_timestamp = 0;
+  }
 
   return feature;
 }
 
-discards::mojom::SiteCharacteristicsDatabaseEntryPtr ConvertEntryFromProto(
-    SiteDataProto* proto) {
-  discards::mojom::SiteCharacteristicsDatabaseValuePtr value =
-      discards::mojom::SiteCharacteristicsDatabaseValue::New();
+discards::mojom::SiteDataEntryPtr ConvertEntryFromProto(SiteDataProto* proto) {
+  discards::mojom::SiteDataValuePtr value =
+      discards::mojom::SiteDataValue::New();
 
-  if (proto->has_last_loaded())
+  if (proto->has_last_loaded()) {
     value->last_loaded = proto->last_loaded();
-
+  } else {
+    value->last_loaded = 0;
+  }
   value->updates_favicon_in_background =
       ConvertFeatureFromProto(proto->updates_favicon_in_background());
   value->updates_title_in_background =
@@ -45,9 +57,8 @@
     DCHECK(load_time_estimates_proto.has_avg_cpu_usage_us());
     DCHECK(load_time_estimates_proto.has_avg_footprint_kb());
 
-    discards::mojom::SiteCharacteristicsPerformanceMeasurementPtr
-        load_time_estimates =
-            discards::mojom::SiteCharacteristicsPerformanceMeasurement::New();
+    discards::mojom::SiteDataPerformanceMeasurementPtr load_time_estimates =
+        discards::mojom::SiteDataPerformanceMeasurement::New();
     if (load_time_estimates_proto.has_avg_cpu_usage_us()) {
       load_time_estimates->avg_cpu_usage_us =
           load_time_estimates_proto.avg_cpu_usage_us();
@@ -64,26 +75,42 @@
     value->load_time_estimates = std::move(load_time_estimates);
   }
 
-  discards::mojom::SiteCharacteristicsDatabaseEntryPtr entry =
-      discards::mojom::SiteCharacteristicsDatabaseEntry::New();
+  discards::mojom::SiteDataEntryPtr entry =
+      discards::mojom::SiteDataEntry::New();
   entry->value = std::move(value);
   return entry;
 }
 
 }  // namespace
 
-SiteDataProviderImpl::SiteDataProviderImpl(
-    resource_coordinator::LocalSiteCharacteristicsDataStoreInspector*
-        data_store_inspector,
-    mojo::PendingReceiver<discards::mojom::SiteDataProvider> receiver)
-    : data_store_inspector_(data_store_inspector),
-      receiver_(this, std::move(receiver)) {}
+SiteDataProviderImpl::SiteDataProviderImpl(const std::string& profile_id)
+    : profile_id_(profile_id) {}
 
 SiteDataProviderImpl::~SiteDataProviderImpl() = default;
 
-void SiteDataProviderImpl::GetSiteCharacteristicsDatabase(
+// static
+void SiteDataProviderImpl::CreateAndBind(
+    mojo::PendingReceiver<discards::mojom::SiteDataProvider> receiver,
+    const std::string& profile_id_,
+    performance_manager::Graph* graph) {
+  std::unique_ptr<SiteDataProviderImpl> site_data_provider =
+      std::make_unique<SiteDataProviderImpl>(profile_id_);
+
+  site_data_provider->Bind(std::move(receiver));
+  graph->PassToGraph(std::move(site_data_provider));
+}
+
+void SiteDataProviderImpl::GetSiteDataArray(
     const std::vector<std::string>& explicitly_requested_origins,
-    GetSiteCharacteristicsDatabaseCallback callback) {
+    GetSiteDataArrayCallback callback) {
+  auto* inspector = performance_manager::SiteDataCacheFactory::GetInstance()
+                        ->GetInspectorForBrowserContext(profile_id_);
+  if (!inspector) {
+    // Early return with a nullptr if there's no inspector.
+    std::move(callback).Run(nullptr);
+    return;
+  }
+
   // Move all previously explicitly requested origins to this local map.
   // Move any currently requested origins over to the member variable, or
   // populate them if they weren't previously requested.
@@ -91,30 +118,31 @@
   // this function.
   OriginToReaderMap prev_requested_origins;
   prev_requested_origins.swap(requested_origins_);
-  resource_coordinator::SiteCharacteristicsDataStore* data_store =
-      data_store_inspector_->GetDataStore();
-  DCHECK(data_store);
+  performance_manager::SiteDataCache* data_cache = inspector->GetDataCache();
+  DCHECK(data_cache);
+
   for (const std::string& origin : explicitly_requested_origins) {
     auto it = prev_requested_origins.find(origin);
     if (it == prev_requested_origins.end()) {
       GURL url(origin);
       requested_origins_[origin] =
-          data_store->GetReaderForOrigin(url::Origin::Create(url));
+          data_cache->GetReaderForOrigin(url::Origin::Create(url));
     } else {
       requested_origins_[origin] = std::move(it->second);
+      prev_requested_origins.erase(it);
     }
   }
 
-  discards::mojom::SiteCharacteristicsDatabasePtr result =
-      discards::mojom::SiteCharacteristicsDatabase::New();
+  discards::mojom::SiteDataArrayPtr result =
+      discards::mojom::SiteDataArray::New();
   std::vector<url::Origin> in_memory_origins =
-      data_store_inspector_->GetAllInMemoryOrigins();
+      inspector->GetAllInMemoryOrigins();
   for (const url::Origin& origin : in_memory_origins) {
     // Get the data for this origin and convert it from proto to the
     // corresponding mojo structure.
     std::unique_ptr<SiteDataProto> proto;
     bool is_dirty = false;
-    if (data_store_inspector_->GetDataForOrigin(origin, &is_dirty, &proto)) {
+    if (inspector->GetDataForOrigin(origin, &is_dirty, &proto)) {
       auto entry = ConvertEntryFromProto(proto.get());
       entry->origin = origin.Serialize();
       entry->is_dirty = is_dirty;
@@ -126,15 +154,23 @@
   std::move(callback).Run(std::move(result));
 }
 
-void SiteDataProviderImpl::GetSiteCharacteristicsDatabaseSize(
-    GetSiteCharacteristicsDatabaseSizeCallback callback) {
+void SiteDataProviderImpl::GetSiteDataDatabaseSize(
+    GetSiteDataDatabaseSizeCallback callback) {
+  auto* inspector = performance_manager::SiteDataCacheFactory::GetInstance()
+                        ->GetInspectorForBrowserContext(profile_id_);
+  if (!inspector) {
+    // Early return with a nullptr if there's no inspector.
+    std::move(callback).Run(nullptr);
+    return;
+  }
+
   // Adapt the inspector callback to the mojom callback with this lambda.
   auto inspector_callback = base::BindOnce(
-      [](GetSiteCharacteristicsDatabaseSizeCallback callback,
+      [](GetSiteDataDatabaseSizeCallback callback,
          base::Optional<int64_t> num_rows,
          base::Optional<int64_t> on_disk_size_kb) {
-        discards::mojom::SiteCharacteristicsDatabaseSizePtr result =
-            discards::mojom::SiteCharacteristicsDatabaseSize::New();
+        discards::mojom::SiteDataDatabaseSizePtr result =
+            discards::mojom::SiteDataDatabaseSize::New();
         result->num_rows = num_rows.has_value() ? num_rows.value() : -1;
         result->on_disk_size_kb =
             on_disk_size_kb.has_value() ? on_disk_size_kb.value() : -1;
@@ -143,5 +179,28 @@
       },
       std::move(callback));
 
-  data_store_inspector_->GetDatabaseSize(std::move(inspector_callback));
+  inspector->GetDataStoreSize(std::move(inspector_callback));
+}
+
+// static
+void SiteDataProviderImpl::OnConnectionError(SiteDataProviderImpl* impl) {
+  std::unique_ptr<performance_manager::GraphOwned> owned_impl =
+      impl->graph_->TakeFromGraph(impl);
+}
+
+void SiteDataProviderImpl::OnPassedToGraph(performance_manager::Graph* graph) {
+  DCHECK(!graph_);
+  graph_ = graph;
+}
+
+void SiteDataProviderImpl::OnTakenFromGraph(performance_manager::Graph* graph) {
+  DCHECK_EQ(graph_, graph);
+  graph_ = nullptr;
+}
+
+void SiteDataProviderImpl::Bind(
+    mojo::PendingReceiver<discards::mojom::SiteDataProvider> receiver) {
+  receiver_.Bind(std::move(receiver));
+  receiver_.set_disconnect_handler(base::BindOnce(
+      &SiteDataProviderImpl::OnConnectionError, base::Unretained(this)));
 }
diff --git a/chrome/browser/ui/webui/discards/site_data_provider_impl.h b/chrome/browser/ui/webui/discards/site_data_provider_impl.h
index 2d67c742..07efc00 100644
--- a/chrome/browser/ui/webui/discards/site_data_provider_impl.h
+++ b/chrome/browser/ui/webui/discards/site_data_provider_impl.h
@@ -5,52 +5,64 @@
 #ifndef CHROME_BROWSER_UI_WEBUI_DISCARDS_SITE_DATA_PROVIDER_IMPL_H_
 #define CHROME_BROWSER_UI_WEBUI_DISCARDS_SITE_DATA_PROVIDER_IMPL_H_
 
-#include <map>
 #include <memory>
 
 #include "base/sequence_checker.h"
 #include "chrome/browser/ui/webui/discards/site_data.mojom.h"
+#include "components/performance_manager/public/graph/graph.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "mojo/public/cpp/bindings/receiver.h"
 #include "mojo/public/cpp/bindings/remote.h"
 
-namespace resource_coordinator {
-class LocalSiteCharacteristicsDataStoreInspector;
-class SiteCharacteristicsDataReader;
-}  // namespace resource_coordinator
+namespace performance_manager {
+class Graph;
+class SiteDataReader;
+}  // namespace performance_manager
 
-class SiteDataProviderImpl : public discards::mojom::SiteDataProvider {
+class SiteDataProviderImpl : public discards::mojom::SiteDataProvider,
+                             public performance_manager::GraphOwned {
  public:
-  SiteDataProviderImpl(
-      resource_coordinator::LocalSiteCharacteristicsDataStoreInspector*
-          data_store_inspector,
-      mojo::PendingReceiver<discards::mojom::SiteDataProvider> receiver);
+  explicit SiteDataProviderImpl(const std::string& profile_id);
   ~SiteDataProviderImpl() override;
   SiteDataProviderImpl(const SiteDataProviderImpl& other) = delete;
   SiteDataProviderImpl& operator=(const SiteDataProviderImpl&) = delete;
 
-  void GetSiteCharacteristicsDatabase(
+  // Creates a new SiteDataProviderImpl to service |receiver| and passes its
+  // ownership to |graph|.
+  static void CreateAndBind(
+      mojo::PendingReceiver<discards::mojom::SiteDataProvider> receiver,
+      const std::string& profile_id_,
+      performance_manager::Graph* graph);
+
+  void GetSiteDataArray(
       const std::vector<std::string>& explicitly_requested_origins,
-      GetSiteCharacteristicsDatabaseCallback callback) override;
-  void GetSiteCharacteristicsDatabaseSize(
-      GetSiteCharacteristicsDatabaseSizeCallback callback) override;
+      GetSiteDataArrayCallback callback) override;
+  void GetSiteDataDatabaseSize(
+      GetSiteDataDatabaseSizeCallback callback) override;
 
  private:
-  using LocalSiteCharacteristicsDataStoreInspector =
-      resource_coordinator::LocalSiteCharacteristicsDataStoreInspector;
-  using SiteCharacteristicsDataReader =
-      resource_coordinator::SiteCharacteristicsDataReader;
+  using SiteDataReader = performance_manager::SiteDataReader;
   using OriginToReaderMap =
-      std::map<std::string, std::unique_ptr<SiteCharacteristicsDataReader>>;
+      base::flat_map<std::string, std::unique_ptr<SiteDataReader>>;
+
+  static void OnConnectionError(SiteDataProviderImpl* impl);
+
+  // GraphOwned implementation.
+  void OnPassedToGraph(performance_manager::Graph* graph) override;
+  void OnTakenFromGraph(performance_manager::Graph* graph) override;
+
+  // Binds |receiver_| by consuming |receiver|, which must be valid.
+  void Bind(mojo::PendingReceiver<discards::mojom::SiteDataProvider> receiver);
 
   // This map pins requested readers and their associated data in memory until
   // after the next read finishes. This is necessary to allow the database reads
   // to go through and populate the requested entries.
   OriginToReaderMap requested_origins_;
 
-  resource_coordinator::LocalSiteCharacteristicsDataStoreInspector*
-      data_store_inspector_ = nullptr;
+  std::string profile_id_;
+
+  performance_manager::Graph* graph_ = nullptr;
 
   mojo::Receiver<discards::mojom::SiteDataProvider> receiver_{this};
 };
diff --git a/chrome/browser/ui/webui/nearby_share/BUILD.gn b/chrome/browser/ui/webui/nearby_share/BUILD.gn
index 2f04001..d3d4329 100644
--- a/chrome/browser/ui/webui/nearby_share/BUILD.gn
+++ b/chrome/browser/ui/webui/nearby_share/BUILD.gn
@@ -4,10 +4,17 @@
 
 import("//mojo/public/tools/bindings/mojom.gni")
 
+mojom("nearby_share_target_types") {
+  sources = [ "nearby_share_target_types.mojom" ]
+}
+
 mojom("mojom") {
   sources = [ "nearby_share.mojom" ]
 
-  public_deps = [ "//mojo/public/mojom/base" ]
+  public_deps = [
+    ":nearby_share_target_types",
+    "//mojo/public/mojom/base",
+  ]
 
   cpp_typemaps = [
     {
@@ -19,6 +26,7 @@
       ]
       traits_headers = [ "nearby_share_mojom_traits.h" ]
       traits_sources = [ "nearby_share_mojom_traits.cc" ]
+      traits_deps = [ "//chrome/browser/nearby_sharing:share_target" ]
     },
   ]
 }
diff --git a/chrome/browser/ui/webui/nearby_share/nearby_share.mojom b/chrome/browser/ui/webui/nearby_share/nearby_share.mojom
index aad18b6..edff98d4 100644
--- a/chrome/browser/ui/webui/nearby_share/nearby_share.mojom
+++ b/chrome/browser/ui/webui/nearby_share/nearby_share.mojom
@@ -4,20 +4,9 @@
 
 module nearby_share.mojom;
 
+import "chrome/browser/ui/webui/nearby_share/nearby_share_target_types.mojom";
 import "mojo/public/mojom/base/unguessable_token.mojom";
 
-// Describes the type of device for a share target.
-enum ShareTargetType {
-  // Unknown device type.
-  kUnknown,
-  // A phone.
-  kPhone,
-  // A tablet.
-  kTablet,
-  // A laptop.
-  kLaptop,
-};
-
 // Describes a nearby device that is able to receive data via Nearby Share.
 struct ShareTarget {
   // Unique identifier during the current discovery session. The same physical
diff --git a/chrome/browser/ui/webui/nearby_share/nearby_share_dialog_ui.cc b/chrome/browser/ui/webui/nearby_share/nearby_share_dialog_ui.cc
index 043562a..8e5076a 100644
--- a/chrome/browser/ui/webui/nearby_share/nearby_share_dialog_ui.cc
+++ b/chrome/browser/ui/webui/nearby_share/nearby_share_dialog_ui.cc
@@ -47,8 +47,12 @@
 
   html_source->AddResourcePath("nearby_share.mojom-lite.js",
                                IDR_NEARBY_SHARE_MOJO_JS);
+  html_source->AddResourcePath("nearby_share_target_types.mojom-lite.js",
+                               IDR_NEARBY_SHARE_TARGET_TYPES_MOJO_JS);
 
   RegisterNearbySharedMojoResources(html_source);
+  RegisterNearbySharedStrings(html_source);
+  html_source->UseStringsJs();
 
   content::WebUIDataSource::Add(profile, html_source);
 }
diff --git a/chrome/browser/ui/webui/nearby_share/nearby_share_target_types.mojom b/chrome/browser/ui/webui/nearby_share/nearby_share_target_types.mojom
new file mode 100644
index 0000000..64acbb05
--- /dev/null
+++ b/chrome/browser/ui/webui/nearby_share/nearby_share_target_types.mojom
@@ -0,0 +1,17 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module nearby_share.mojom;
+
+// Describes the type of device for a share target.
+enum ShareTargetType {
+  // Unknown device type.
+  kUnknown,
+  // A phone.
+  kPhone,
+  // A tablet.
+  kTablet,
+  // A laptop.
+  kLaptop,
+};
\ No newline at end of file
diff --git a/chrome/browser/ui/webui/nearby_share/shared_resources.cc b/chrome/browser/ui/webui/nearby_share/shared_resources.cc
index b820b9d..8d98094 100644
--- a/chrome/browser/ui/webui/nearby_share/shared_resources.cc
+++ b/chrome/browser/ui/webui/nearby_share/shared_resources.cc
@@ -4,9 +4,14 @@
 
 #include "chrome/browser/ui/webui/nearby_share/shared_resources.h"
 
+#include <string>
+
 #include "base/containers/span.h"
+#include "chrome/browser/ui/webui/webui_util.h"
+#include "chrome/grit/generated_resources.h"
 #include "chrome/grit/nearby_share_dialog_resources.h"
 #include "chrome/grit/nearby_share_dialog_resources_map.h"
+#include "ui/base/webui/web_ui_util.h"
 
 const char kNearbyShareGeneratedPath[] =
     "@out_folder@/gen/chrome/browser/resources/nearby_share/";
@@ -28,3 +33,10 @@
   }
   RegisterNearbySharedMojoResources(data_source);
 }
+
+void RegisterNearbySharedStrings(content::WebUIDataSource* data_source) {
+  static constexpr webui::LocalizedString kLocalizedStrings[] = {
+      {"secureConnectionId", IDS_NEARBY_SECURE_CONNECTION_ID},
+  };
+  webui::AddLocalizedStringsBulk(data_source, kLocalizedStrings);
+}
diff --git a/chrome/browser/ui/webui/nearby_share/shared_resources.h b/chrome/browser/ui/webui/nearby_share/shared_resources.h
index 5f59b66..377cf86 100644
--- a/chrome/browser/ui/webui/nearby_share/shared_resources.h
+++ b/chrome/browser/ui/webui/nearby_share/shared_resources.h
@@ -11,5 +11,6 @@
 
 void RegisterNearbySharedMojoResources(content::WebUIDataSource* data_source);
 void RegisterNearbySharedResources(content::WebUIDataSource* data_source);
+void RegisterNearbySharedStrings(content::WebUIDataSource* data_source);
 
 #endif  // CHROME_BROWSER_UI_WEBUI_NEARBY_SHARE_SHARED_RESOURCES_H_
diff --git a/chrome/browser/ui/webui/settings/chromeos/main_section.cc b/chrome/browser/ui/webui/settings/chromeos/main_section.cc
index 95363a7..2a1e91a 100644
--- a/chrome/browser/ui/webui/settings/chromeos/main_section.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/main_section.cc
@@ -6,8 +6,13 @@
 
 #include "ash/public/cpp/resources/grit/ash_public_unscaled_resources.h"
 #include "base/feature_list.h"
+#include "base/i18n/number_formatting.h"
 #include "base/no_destructor.h"
 #include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/browser_process_platform_part.h"
+#include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h"
+#include "chrome/browser/chromeos/policy/minimum_version_policy_handler.h"
 #include "chrome/browser/chromeos/profiles/profile_helper.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/webui/metrics_handler.h"
@@ -17,6 +22,7 @@
 #include "chrome/browser/ui/webui/webui_util.h"
 #include "chrome/browser/web_applications/system_web_app_manager.h"
 #include "chrome/common/url_constants.h"
+#include "chrome/common/webui_url_constants.h"
 #include "chrome/grit/browser_resources.h"
 #include "chrome/grit/chromium_strings.h"
 #include "chrome/grit/generated_resources.h"
@@ -54,6 +60,45 @@
       base::FeatureList::IsEnabled(::chromeos::features::kNewOsSettingsSearch));
 }
 
+void AddUpdateRequiredEolStrings(content::WebUIDataSource* html_source) {
+  policy::BrowserPolicyConnectorChromeOS* connector =
+      g_browser_process->platform_part()->browser_policy_connector_chromeos();
+  policy::MinimumVersionPolicyHandler* handler =
+      connector->GetMinimumVersionPolicyHandler();
+
+  // |eol_return_banner_text| contains the update required end of life banner
+  // text which is left empty when the banner should not be shown.
+  base::string16 eol_return_banner_text;
+  if (handler && handler->IsUpdateRequiredEol()) {
+    base::Optional<int> days = handler->GetTimeRemainingInDays();
+    // We only need to show the banner if less than equal to one week remains to
+    // reach the update required deadline.
+    if (days && days.value() <= 7) {
+      // |days| could have value equal to zero if we are very close to the
+      // deadline.
+      int days_remaining = days.value() ? days.value() : 1;
+      base::string16 domain_name =
+          base::UTF8ToUTF16(connector->GetEnterpriseDisplayDomain());
+      base::string16 link_url =
+          base::UTF8ToUTF16(chrome::kChromeUIManagementURL);
+      if (days_remaining == 1) {
+        eol_return_banner_text = l10n_util::GetStringFUTF16(
+            IDS_SETTINGS_UPDATE_REQUIRED_EOL_BANNER_LAST_DAY, domain_name,
+            link_url);
+      } else if (days_remaining == 7) {
+        eol_return_banner_text = l10n_util::GetStringFUTF16(
+            IDS_SETTINGS_UPDATE_REQUIRED_EOL_BANNER_ONE_WEEK, domain_name,
+            link_url);
+      } else {
+        eol_return_banner_text = l10n_util::GetStringFUTF16(
+            IDS_SETTINGS_UPDATE_REQUIRED_EOL_BANNER_DAYS, domain_name,
+            base::FormatNumber(days_remaining), link_url);
+      }
+    }
+  }
+  html_source->AddString("updateRequiredEolBannerText", eol_return_banner_text);
+}
+
 }  // namespace
 
 MainSection::MainSection(Profile* profile,
@@ -148,6 +193,7 @@
 
   AddSearchInSettingsStrings(html_source);
   AddChromeOSUserStrings(html_source);
+  AddUpdateRequiredEolStrings(html_source);
 
   policy_indicator::AddLocalizedStrings(html_source);
 }
diff --git a/chrome/browser/usb/usb_tab_helper_unittest.cc b/chrome/browser/usb/usb_tab_helper_unittest.cc
index 0b901bc..c95d76d 100644
--- a/chrome/browser/usb/usb_tab_helper_unittest.cc
+++ b/chrome/browser/usb/usb_tab_helper_unittest.cc
@@ -4,7 +4,6 @@
 
 #include "chrome/browser/usb/usb_tab_helper.h"
 
-#include "chrome/browser/resource_coordinator/local_site_characteristics_data_unittest_utils.h"
 #include "chrome/browser/usb/frame_usb_services.h"
 #include "chrome/browser/usb/usb_chooser_context.h"
 #include "chrome/browser/usb/usb_chooser_context_factory.h"
@@ -12,6 +11,7 @@
 #include "components/performance_manager/embedder/performance_manager_registry.h"
 #include "components/performance_manager/public/decorators/page_live_state_decorator.h"
 #include "components/performance_manager/test_support/decorators_utils.h"
+#include "components/performance_manager/test_support/test_harness_helper.h"
 #include "services/device/public/cpp/test/fake_usb_device_manager.h"
 #include "services/service_manager/public/cpp/test/test_service.h"
 #include "services/service_manager/public/cpp/test/test_service_manager.h"
@@ -19,14 +19,17 @@
 #include "third_party/blink/public/mojom/usb/web_usb_service.mojom.h"
 #include "url/url_constants.h"
 
-class UsbTabHelperTest
-    : public resource_coordinator::testing::ChromeTestHarnessWithLocalDB {
+class UsbTabHelperTest : public ChromeRenderViewHostTestHarness {
  protected:
   UsbTabHelperTest() = default;
   ~UsbTabHelperTest() override = default;
 
   void SetUp() override {
-    resource_coordinator::testing::ChromeTestHarnessWithLocalDB::SetUp();
+    ChromeRenderViewHostTestHarness::SetUp();
+    pm_harness_.SetUp();
+    // Reset the test contents to ensure that it has a PageNode associated to it
+    // in the PerformanceManager graph.
+    SetContents(CreateTestWebContents());
 
     auto* chooser_context = UsbChooserContextFactory::GetForProfile(profile());
     mojo::PendingRemote<device::mojom::UsbDeviceManager> device_manager;
@@ -37,7 +40,13 @@
     NavigateAndCommit(GURL("https://www.google.com"));
   }
 
+  void TearDown() override {
+    pm_harness_.TearDown();
+    ChromeRenderViewHostTestHarness::TearDown();
+  }
+
  private:
+  performance_manager::PerformanceManagerTestHarnessHelper pm_harness_;
   device::FakeUsbDeviceManager device_manager_;
 };
 
diff --git a/chrome/browser/vr/ui_host/vr_ui_host_impl.cc b/chrome/browser/vr/ui_host/vr_ui_host_impl.cc
index e2b5e0684..35dc73d 100644
--- a/chrome/browser/vr/ui_host/vr_ui_host_impl.cc
+++ b/chrome/browser/vr/ui_host/vr_ui_host_impl.cc
@@ -391,9 +391,11 @@
 
   // location, microphone, camera, midi.
   CapturingStateModel active_capturing = active_capturing_;
+  // TODO(https://crbug.com/1103176): Plumb the actual frame reference here (we
+  // should get a RFH from VRServiceImpl instead of WebContents)
   content_settings::TabSpecificContentSettings* settings =
-      content_settings::TabSpecificContentSettings::FromWebContents(
-          web_contents_);
+      content_settings::TabSpecificContentSettings::GetForFrame(
+          web_contents_->GetMainFrame());
   if (settings) {
     const ContentSettingsUsagesState& usages_state =
         settings->geolocation_usages_state();
diff --git a/chrome/build/mac.pgo.txt b/chrome/build/mac.pgo.txt
index a3c42a83..953a100 100644
--- a/chrome/build/mac.pgo.txt
+++ b/chrome/build/mac.pgo.txt
@@ -1 +1 @@
-chrome-mac-master-1596088294-d7afac6507733c270f9eabdc2010773cc2ccd03c.profdata
+chrome-mac-master-1596193635-6128c4835e91dec9617eb88a5ffdf98aa76979d0.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt
index 66031da..edc1710 100644
--- a/chrome/build/win32.pgo.txt
+++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@
-chrome-win32-master-1596160292-be7478816f9ea1c3a3e60ecafceb33fda007c691.profdata
+chrome-win32-master-1596192746-6fea58b93463f5fc9ccbb164a8054cb0a38af6db.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index bb99fd8..108ca52 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-master-1596153340-6d725ede4117dca447ca41731e1643697c638085.profdata
+chrome-win64-master-1596193635-02bce97a1becd63464acfea57c5b1e9a80097f4f.profdata
diff --git a/chrome/common/caption.mojom b/chrome/common/caption.mojom
index 05ca09f..332ebf1 100644
--- a/chrome/common/caption.mojom
+++ b/chrome/common/caption.mojom
@@ -7,6 +7,9 @@
 // Browser process interface exposed to the renderer for communication about
 // the Live Caption feature.
 interface CaptionHost {
+  // Called when speech recognition service is ready to send transcriptions.
+  OnSpeechRecognitionReady() => (bool success);
+
   // Called when the speech recognition client receives a transcription from the
   // speech service. Returns whether the transcription result was received
   // successfully. Transcriptions will halt if this returns false.
diff --git a/chrome/renderer/media/chrome_speech_recognition_client.cc b/chrome/renderer/media/chrome_speech_recognition_client.cc
index 70da74b25..9f7d95f 100644
--- a/chrome/renderer/media/chrome_speech_recognition_client.cc
+++ b/chrome/renderer/media/chrome_speech_recognition_client.cc
@@ -5,6 +5,7 @@
 #include "chrome/renderer/media/chrome_speech_recognition_client.h"
 
 #include <utility>
+#include <vector>
 
 #include "base/metrics/field_trial_params.h"
 #include "content/public/renderer/render_frame.h"
@@ -57,7 +58,6 @@
     bool is_multichannel_supported) {
   is_multichannel_supported_ = is_multichannel_supported;
   is_recognizer_bound_ = true;
-
   if (on_ready_callback_)
     std::move(on_ready_callback_).Run();
 }
@@ -102,11 +102,11 @@
   caption_host_->OnTranscription(
       chrome::mojom::TranscriptionResult::New(result->transcription,
                                               result->is_final),
-      base::BindOnce(&ChromeSpeechRecognitionClient::OnTranscriptionCallback,
+      base::BindOnce(&ChromeSpeechRecognitionClient::OnBrowserCallback,
                      base::Unretained(this)));
 }
 
-void ChromeSpeechRecognitionClient::OnTranscriptionCallback(bool success) {
+void ChromeSpeechRecognitionClient::OnBrowserCallback(bool success) {
   is_browser_requesting_transcription_ = success;
 }
 
@@ -145,6 +145,17 @@
   if (IsSpeechRecognitionAvailable()) {
     speech_recognition_recognizer_->SendAudioToSpeechRecognitionService(
         std::move(audio_data));
+
+    // When the speech recognition client receives speech, it alerts the
+    // live caption host in the browser that it is ready so that the UI can
+    // display a message. This happens at the time of playing the video and not
+    // at the time of construction of this object.
+    if (!on_ready_message_sent_to_caption_host_) {
+      caption_host_->OnSpeechRecognitionReady(
+          base::BindOnce(&ChromeSpeechRecognitionClient::OnBrowserCallback,
+                         base::Unretained(this)));
+      on_ready_message_sent_to_caption_host_ = true;
+    }
   }
 }
 
diff --git a/chrome/renderer/media/chrome_speech_recognition_client.h b/chrome/renderer/media/chrome_speech_recognition_client.h
index df0e0574..82be158 100644
--- a/chrome/renderer/media/chrome_speech_recognition_client.h
+++ b/chrome/renderer/media/chrome_speech_recognition_client.h
@@ -65,8 +65,8 @@
   media::mojom::AudioDataS16Ptr ConvertToAudioDataS16(
       scoped_refptr<media::AudioBuffer> buffer);
 
-  // Called as a response to sending a transcription to the browser.
-  void OnTranscriptionCallback(bool success);
+  // Called as a response to sending a message to the browser.
+  void OnBrowserCallback(bool success);
 
   media::mojom::AudioDataS16Ptr ConvertToAudioDataS16(
       std::unique_ptr<media::AudioBus> audio_bus,
@@ -108,6 +108,9 @@
 
   bool is_recognizer_bound_ = false;
 
+  // Whether or not the on ready message has been sent to the caption host.
+  bool on_ready_message_sent_to_caption_host_ = false;
+
   // The temporary audio bus used to mix multichannel audio into a single
   // channel.
   std::unique_ptr<media::AudioBus> monaural_audio_bus_;
diff --git a/chrome/renderer/net/net_error_helper.cc b/chrome/renderer/net/net_error_helper.cc
index 747497b6..930636d1 100644
--- a/chrome/renderer/net/net_error_helper.cc
+++ b/chrome/renderer/net/net_error_helper.cc
@@ -25,7 +25,6 @@
 #include "chrome/common/render_messages.h"
 #include "chrome/renderer/chrome_render_thread_observer.h"
 #include "components/error_page/common/error.h"
-#include "components/error_page/common/error_page_params.h"
 #include "components/error_page/common/localized_error.h"
 #include "components/error_page/common/net_error_info.h"
 #include "components/grit/components_resources.h"
@@ -76,7 +75,6 @@
 using content::kUnreachableWebDataURL;
 using error_page::DnsProbeStatus;
 using error_page::DnsProbeStatusToString;
-using error_page::ErrorPageParams;
 using error_page::LocalizedError;
 
 namespace {
@@ -249,7 +247,6 @@
     const error_page::Error& error,
     bool is_failed_post,
     bool can_show_network_diagnostics_dialog,
-    std::unique_ptr<ErrorPageParams> params,
     std::string* error_html) const {
   error_html->clear();
 
@@ -266,8 +263,7 @@
       error.stale_copy_in_cache(), can_show_network_diagnostics_dialog,
       ChromeRenderThreadObserver::is_incognito_process(),
       IsOfflineContentOnNetErrorFeatureEnabled(), IsAutoFetchFeatureEnabled(),
-      IsRunningInForcedAppMode(), RenderThread::Get()->GetLocale(),
-      std::move(params));
+      IsRunningInForcedAppMode(), RenderThread::Get()->GetLocale());
   DCHECK(!template_html.empty()) << "unable to load template.";
   // "t" is the id of the template's root node.
   *error_html =
@@ -293,8 +289,7 @@
       error.stale_copy_in_cache(), can_show_network_diagnostics_dialog,
       ChromeRenderThreadObserver::is_incognito_process(),
       IsOfflineContentOnNetErrorFeatureEnabled(), IsAutoFetchFeatureEnabled(),
-      IsRunningInForcedAppMode(), RenderThread::Get()->GetLocale(),
-      std::unique_ptr<ErrorPageParams>());
+      IsRunningInForcedAppMode(), RenderThread::Get()->GetLocale());
 
   std::string json;
   JSONWriter::Write(page_state.strings, &json);
diff --git a/chrome/renderer/net/net_error_helper.h b/chrome/renderer/net/net_error_helper.h
index 7fde1e1..e2ed1cd9 100644
--- a/chrome/renderer/net/net_error_helper.h
+++ b/chrome/renderer/net/net_error_helper.h
@@ -32,7 +32,6 @@
 
 namespace error_page {
 class Error;
-struct ErrorPageParams;
 }
 
 // Listens for NetErrorInfo messages from the NetErrorTabHelper on the
@@ -95,7 +94,6 @@
       const error_page::Error& error,
       bool is_failed_post,
       bool can_use_local_diagnostics_service,
-      std::unique_ptr<error_page::ErrorPageParams> params,
       std::string* html) const override;
 
   void EnablePageHelperFunctions() override;
diff --git a/chrome/renderer/net/net_error_helper_core.cc b/chrome/renderer/net/net_error_helper_core.cc
index 8e5edea0..59bb789 100644
--- a/chrome/renderer/net/net_error_helper_core.cc
+++ b/chrome/renderer/net/net_error_helper_core.cc
@@ -13,7 +13,6 @@
 #include "base/logging.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/stl_util.h"
-#include "components/error_page/common/error_page_params.h"
 #include "components/error_page/common/localized_error.h"
 #include "content/public/common/content_switches.h"
 #include "content/public/renderer/render_thread.h"
@@ -163,8 +162,7 @@
   } else if (error_html) {
     delegate_->GenerateLocalizedErrorPage(
         error, is_failed_post,
-        false /* No diagnostics dialogs allowed for subframes. */, nullptr,
-        error_html);
+        false /* No diagnostics dialogs allowed for subframes. */, error_html);
   }
 }
 
@@ -215,7 +213,7 @@
   if (error_html) {
     pending_error_page_info->page_state = delegate_->GenerateLocalizedErrorPage(
         error, pending_error_page_info->was_failed_post,
-        can_show_network_diagnostics_dialog_, nullptr, error_html);
+        can_show_network_diagnostics_dialog_, error_html);
   }
 }
 
diff --git a/chrome/renderer/net/net_error_helper_core.h b/chrome/renderer/net/net_error_helper_core.h
index b2befb0..08e63ad 100644
--- a/chrome/renderer/net/net_error_helper_core.h
+++ b/chrome/renderer/net/net_error_helper_core.h
@@ -26,9 +26,6 @@
 namespace content {
 class RenderFrame;
 }
-namespace error_page {
-struct ErrorPageParams;
-}
 
 // Class that contains the logic for how the NetErrorHelper.  This allows for
 // testing the logic without a RenderView or WebFrame, which are difficult to
@@ -64,7 +61,6 @@
         const error_page::Error& error,
         bool is_failed_post,
         bool can_show_network_diagnostics_dialog,
-        std::unique_ptr<error_page::ErrorPageParams> params,
         std::string* html) const = 0;
 
     // Create extra Javascript bindings in the error page. Will only be invoked
diff --git a/chrome/renderer/net/net_error_helper_core_unittest.cc b/chrome/renderer/net/net_error_helper_core_unittest.cc
index e9dbc64..91243ba 100644
--- a/chrome/renderer/net/net_error_helper_core_unittest.cc
+++ b/chrome/renderer/net/net_error_helper_core_unittest.cc
@@ -25,7 +25,6 @@
 #include "chrome/common/available_offline_content.mojom.h"
 #include "chrome/renderer/net/available_offline_content_helper.h"
 #include "components/error_page/common/error.h"
-#include "components/error_page/common/error_page_params.h"
 #include "components/error_page/common/net_error_info.h"
 #include "content/public/common/service_names.mojom.h"
 #include "content/public/common/url_constants.h"
@@ -199,7 +198,6 @@
       const error_page::Error& error,
       bool is_failed_post,
       bool can_show_network_diagnostics_dialog,
-      std::unique_ptr<error_page::ErrorPageParams> params,
       std::string* html) const override {
     last_can_show_network_diagnostics_dialog_ =
         can_show_network_diagnostics_dialog;
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 479b502..25ed99c 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -1137,6 +1137,7 @@
       "../browser/profiles/profile_browsertest.cc",
       "../browser/profiles/profile_list_desktop_browsertest.cc",
       "../browser/profiles/profile_manager_browsertest.cc",
+      "../browser/profiles/profile_theme_update_service_browsertest.cc",
       "../browser/push_messaging/push_messaging_browsertest.cc",
       "../browser/referrer_policy_browsertest.cc",
       "../browser/renderer_context_menu/accessibility_labels_menu_observer_browsertest.cc",
@@ -1153,10 +1154,6 @@
       "../browser/resource_coordinator/discard_before_unload_helper.cc",
       "../browser/resource_coordinator/discard_before_unload_helper.h",
       "../browser/resource_coordinator/discard_before_unload_helper_browsertest.cc",
-      "../browser/resource_coordinator/local_site_characteristics_data_store_factory_browsertest.cc",
-      "../browser/resource_coordinator/local_site_characteristics_data_unittest_utils.cc",
-      "../browser/resource_coordinator/local_site_characteristics_data_unittest_utils.h",
-      "../browser/resource_coordinator/local_site_characteristics_database_browsertest.cc",
       "../browser/resource_coordinator/tab_activity_watcher_browsertest.cc",
       "../browser/resource_coordinator/tab_manager_browsertest.cc",
       "../browser/safe_browsing/chrome_cleaner/reporter_runner_browsertest_win.cc",
@@ -3662,6 +3659,8 @@
       "../browser/media/feeds/media_feeds_fetcher_unittest.cc",
       "../browser/media/feeds/media_feeds_service_unittest.cc",
       "../browser/media/kaleidoscope/kaleidoscope_switches_unittest.cc",
+      "../browser/nearby_sharing/fake_nearby_connection.cc",
+      "../browser/nearby_sharing/fake_nearby_connection.h",
       "../browser/nearby_sharing/fake_nearby_connections_manager.cc",
       "../browser/nearby_sharing/fake_nearby_connections_manager.h",
       "../browser/nearby_sharing/fast_initiation_manager_unittest.cc",
@@ -3686,12 +3685,12 @@
       "../browser/nearby_sharing/nearby_process_manager_unittest.cc",
       "../browser/nearby_sharing/nearby_share_settings_unittest.cc",
       "../browser/nearby_sharing/nearby_sharing_service_impl_unittest.cc",
-      "../browser/nearby_sharing/transfer_metadata_builder.cc",
-      "../browser/nearby_sharing/transfer_metadata_builder.h",
       "../browser/nearby_sharing/webrtc_signaling_messenger_unittest.cc",
       "../browser/password_manager/generated_password_leak_detection_pref_unittest.cc",
       "../browser/performance_manager/test_support/page_discarding_utils.cc",
       "../browser/performance_manager/test_support/page_discarding_utils.h",
+      "../browser/performance_manager/test_support/site_data_utils.cc",
+      "../browser/performance_manager/test_support/site_data_utils.h",
       "../browser/profiles/profile_avatar_icon_util_unittest.cc",
       "../browser/profiles/profile_destroyer_unittest.cc",
       "../browser/safe_browsing/generated_safe_browsing_pref_unittest.cc",
@@ -4130,17 +4129,8 @@
       "../browser/resource_coordinator/decision_details_unittest.cc",
       "../browser/resource_coordinator/discard_metrics_lifecycle_unit_observer_unittest.cc",
       "../browser/resource_coordinator/intervention_policy_database_unittest.cc",
-      "../browser/resource_coordinator/leveldb_site_characteristics_database_unittest.cc",
       "../browser/resource_coordinator/lifecycle_unit_base_unittest.cc",
       "../browser/resource_coordinator/lifecycle_unit_unittest.cc",
-      "../browser/resource_coordinator/local_site_characteristics_data_impl_unittest.cc",
-      "../browser/resource_coordinator/local_site_characteristics_data_reader_unittest.cc",
-      "../browser/resource_coordinator/local_site_characteristics_data_store_unittest.cc",
-      "../browser/resource_coordinator/local_site_characteristics_data_unittest_utils.cc",
-      "../browser/resource_coordinator/local_site_characteristics_data_unittest_utils.h",
-      "../browser/resource_coordinator/local_site_characteristics_data_writer_unittest.cc",
-      "../browser/resource_coordinator/local_site_characteristics_non_recording_data_store_unittest.cc",
-      "../browser/resource_coordinator/local_site_characteristics_webcontents_observer_unittest.cc",
       "../browser/resource_coordinator/session_restore_policy_unittest.cc",
       "../browser/resource_coordinator/tab_activity_watcher_unittest.cc",
       "../browser/resource_coordinator/tab_lifecycle_unit_source_unittest.cc",
@@ -4239,6 +4229,7 @@
       "../browser/ui/browser_unittest.cc",
       "../browser/ui/browser_window_state_unittest.cc",
       "../browser/ui/commander/commander_controller_unittest.cc",
+      "../browser/ui/commander/fuzzy_finder_unittest.cc",
       "../browser/ui/content_settings/content_setting_bubble_model_unittest.cc",
       "../browser/ui/content_settings/content_setting_image_model_unittest.cc",
       "../browser/ui/content_settings/content_setting_media_image_model_unittest.mm",
diff --git a/chrome/test/data/pdf/BUILD.gn b/chrome/test/data/pdf/BUILD.gn
index 79c17d1d..e2d573e 100644
--- a/chrome/test/data/pdf/BUILD.gn
+++ b/chrome/test/data/pdf/BUILD.gn
@@ -35,6 +35,7 @@
 
     #":toolbar_manager_test",
     #":touch_handling_test",
+    ":viewer_pdf_sidenav_test",
     ":viewer_pdf_toolbar_new_test",
 
     #":viewport_test",
@@ -159,6 +160,11 @@
   externs_list = [ "$externs_path/test.js" ]
 }
 
+js_library("viewer_pdf_sidenav_test") {
+  deps = [ "//chrome/browser/resources/pdf/elements:viewer-pdf-sidenav" ]
+  externs_list = [ "$externs_path/test.js" ]
+}
+
 js_library("viewer_pdf_toolbar_new_test") {
   deps = [
     "//chrome/browser/resources/pdf:constants",
diff --git a/chrome/test/data/pdf/viewer_pdf_sidenav_test.js b/chrome/test/data/pdf/viewer_pdf_sidenav_test.js
new file mode 100644
index 0000000..065d0d6
--- /dev/null
+++ b/chrome/test/data/pdf/viewer_pdf_sidenav_test.js
@@ -0,0 +1,55 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import {ViewerPdfSidenavElement} from 'chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai/elements/viewer-pdf-sidenav.js';
+
+/** @return {!ViewerPdfSidenavElement} */
+function createSidenav() {
+  document.body.innerHTML = '';
+  const sidenav = /** @type {!ViewerPdfSidenavElement} */ (
+      document.createElement('viewer-pdf-sidenav'));
+  document.body.appendChild(sidenav);
+  return sidenav;
+}
+
+// Unit tests for the viewer-pdf-sidenav element.
+const tests = [
+  /**
+   * Test that the sidenav toggles between outline and thumbnail view.
+   */
+  function testViewToggle() {
+    const sidenav = createSidenav();
+    const content = sidenav.shadowRoot.querySelector('#content');
+    const buttons = /** @type {!NodeList<!CrIconButtonElement>} */ (
+        sidenav.shadowRoot.querySelectorAll('cr-icon-button'));
+
+    let visibleContent = content.querySelector('div:not([hidden])');
+    chrome.test.assertTrue(
+        buttons[0].parentNode.classList.contains('selected'));
+    chrome.test.assertFalse(
+        buttons[1].parentNode.classList.contains('selected'));
+    chrome.test.assertEq('Thumbnails', visibleContent.textContent);
+
+    // Click on outline view.
+    buttons[1].click();
+    visibleContent = content.querySelector('div:not([hidden])');
+    chrome.test.assertFalse(
+        buttons[0].parentNode.classList.contains('selected'));
+    chrome.test.assertTrue(
+        buttons[1].parentNode.classList.contains('selected'));
+    chrome.test.assertEq('Outline', visibleContent.textContent);
+
+    // Return to thumbnail view.
+    buttons[0].click();
+    visibleContent = content.querySelector('div:not([hidden])');
+    chrome.test.assertTrue(
+        buttons[0].parentNode.classList.contains('selected'));
+    chrome.test.assertFalse(
+        buttons[1].parentNode.classList.contains('selected'));
+    chrome.test.assertEq('Thumbnails', visibleContent.textContent);
+    chrome.test.succeed();
+  },
+];
+
+chrome.test.runTests(tests);
diff --git a/chrome/test/data/policy/policy_test_cases.json b/chrome/test/data/policy/policy_test_cases.json
index e49dc22..e98360f 100644
--- a/chrome/test/data/policy/policy_test_cases.json
+++ b/chrome/test/data/policy/policy_test_cases.json
@@ -5872,15 +5872,21 @@
     "policy_pref_mapping_test": [
       {
         "policies": {
-          "DeviceMinimumVersion": [
-            {"chromeos_version" : "13121.0.0", "warning_period" : 0, "aue_warning_period" : 14}
-          ]
+          "DeviceMinimumVersion": {
+            "requirements": [
+              {"chromeos_version" : "13121.0.0", "warning_period" : 0, "aue_warning_period" : 14}
+            ],
+            "unmanaged_user_restricted": true
+          }
         },
         "prefs": {
           "cros.device.min_version": {
-            "value": [
-              {"chromeos_version" : "13121.0.0", "warning_period" : 0, "aue_warning_period" : 14}
-            ]
+            "value": {
+              "requirements": [
+                {"chromeos_version" : "13121.0.0", "warning_period" : 0, "aue_warning_period" : 14}
+              ],
+              "unmanaged_user_restricted": true
+            }
           }
         }
       }
diff --git a/chrome/test/data/push_messaging/push_test.js b/chrome/test/data/push_messaging/push_test.js
index 4d8ea78..c5fdb63 100644
--- a/chrome/test/data/push_messaging/push_test.js
+++ b/chrome/test/data/push_messaging/push_test.js
@@ -140,6 +140,18 @@
   }).catch(sendErrorToTest);
 }
 
+function documentSubscribePushGetExpirationTime() {
+  navigator.serviceWorker.ready.then(function(swRegistration) {
+    return swRegistration.pushManager.subscribe({
+          userVisibleOnly: true,
+          applicationServerKey: kApplicationServerKey.buffer
+        })
+        .then(function(subscription) {
+          sendResultToTest(subscription.expirationTime);
+        });
+  }).catch(sendErrorToTest);
+}
+
 function workerSubscribePush() {
   // Send the message to the worker for it to subscribe
   navigator.serviceWorker.controller.postMessage({command: 'workerSubscribe'});
@@ -176,6 +188,15 @@
   }).catch(sendErrorToTest);
 }
 
+function GetSubscriptionExpirationTime() {
+  navigator.serviceWorker.ready.then(function(swRegistration) {
+    return swRegistration.pushManager.getSubscription()
+        .then(function(subscription) {
+          sendResultToTest(subscription.expirationTime);
+        });
+  }).catch(sendErrorToTest);
+}
+
 function pushManagerPermissionState() {
   navigator.serviceWorker.ready.then(function(swRegistration) {
     return swRegistration.pushManager.permissionState({userVisibleOnly: true})
diff --git a/chrome/test/data/webui/settings/chromeos/os_settings_page_test.js b/chrome/test/data/webui/settings/chromeos/os_settings_page_test.js
index 8198ad81..1db2500 100644
--- a/chrome/test/data/webui/settings/chromeos/os_settings_page_test.js
+++ b/chrome/test/data/webui/settings/chromeos/os_settings_page_test.js
@@ -123,4 +123,27 @@
       assertTrue(!!section, 'Expected section ' + name);
     }
   });
+
+  test('Update required end of life banner visibility', function() {
+    Polymer.dom.flush();
+    assertFalse(settingsPage.showUpdateRequiredEolBanner_);
+    assertFalse(!!settingsPage.$$('#updateRequiredEolBanner'));
+
+    settingsPage.showUpdateRequiredEolBanner_ = true;
+    Polymer.dom.flush();
+    assertTrue(!!settingsPage.$$('#updateRequiredEolBanner'));
+  });
+
+  test('Update required end of life banner close button click', function() {
+    settingsPage.showUpdateRequiredEolBanner_ = true;
+    Polymer.dom.flush();
+    const banner = settingsPage.$$('#updateRequiredEolBanner');
+    assertTrue(!!banner);
+
+    const closeButton = assert(settingsPage.$$('#closeUpdateRequiredEol'));
+    closeButton.click();
+    Polymer.dom.flush();
+    assertFalse(settingsPage.showUpdateRequiredEolBanner_);
+    assertEquals('none', banner.style.display);
+  });
 });
diff --git a/chrome/test/data/webui/settings/passwords_section_test.js b/chrome/test/data/webui/settings/passwords_section_test.js
index c60c440b..e159f4e 100644
--- a/chrome/test/data/webui/settings/passwords_section_test.js
+++ b/chrome/test/data/webui/settings/passwords_section_test.js
@@ -143,6 +143,46 @@
 }
 
 /**
+ * Helper function to test for an element is visible.
+ */
+function isElementVisible(element) {
+  return element && !element.hidden;
+}
+
+/**
+ * Helper function to test if all components of edit dialog are shown correctly.
+ */
+function editDialogPartsAreShownCorrectly(passwordDialog) {
+  assertEquals(
+      passwordDialog.i18n('editCompromisedPasswordTitle'),
+      passwordDialog.$.title.textContent.trim());
+  assertFalse(passwordDialog.$.passwordInput.readonly);
+  assertTrue(passwordDialog.$.passwordInput.required);
+  assertTrue(isElementVisible(passwordDialog.$.footnote));
+  assertTrue(isElementVisible(passwordDialog.$.cancel));
+  assertEquals(
+      passwordDialog.i18n('save'),
+      passwordDialog.$.actionButton.textContent.trim());
+}
+
+/**
+ * Helper function to test if all components of details dialog are shown
+ * correctly.
+ */
+function detailsDialogPartsAreShownCorrectly(passwordDialog) {
+  assertEquals(
+      passwordDialog.i18n('passwordDetailsTitle'),
+      passwordDialog.$.title.textContent.trim());
+  assertTrue(passwordDialog.$.passwordInput.readonly);
+  assertFalse(passwordDialog.$.passwordInput.required);
+  assertFalse(isElementVisible(passwordDialog.$.footnote));
+  assertFalse(isElementVisible(passwordDialog.$.cancel));
+  assertEquals(
+      passwordDialog.i18n('done'),
+      passwordDialog.$.actionButton.textContent.trim());
+}
+
+/**
  * Simulates user who is eligible and opted-in for account storage. Should be
  * called after the PasswordsSection element is created. The load time value for
  * enableAccountStorage must be overridden separately.
@@ -562,6 +602,82 @@
         passwordsSection.$.passwordsListHandler.$$('#menuCopyPassword').hidden);
   });
 
+  // Test verifies that 'Edit' button is replaced to 'Details' for Federated
+  // (passwordless) credentials. Does not test Details and Edit button.
+  test('verifyEditReplacedToDetailsForFederatedPasswordInMenu', function() {
+    const passwordList = [
+      createPasswordEntry({federationText: 'with chromium.org'}),
+    ];
+    const passwordsSection = elementFactory.createPasswordsSection(
+        passwordManager, passwordList, []);
+
+    getFirstPasswordListItem(passwordsSection).$.moreActionsButton.click();
+    flush();
+    assertEquals(
+        passwordsSection.i18n('passwordViewDetails'),
+        passwordsSection.$.passwordsListHandler.$$('#menuEditPassword')
+            .textContent.trim());
+  });
+
+  // Test verifies that 'Edit' button is replaced to 'Details' for Federated
+  // (passwordless) credentials when EditPasswordsInSettings flag is enabled.
+  // Does not test Details and Edit button.
+  test(
+      'verifyDetailsForFederatedPasswordInMenuEnabledEditPasswordsInSettings',
+      function() {
+        const passwordList = [
+          createPasswordEntry({federationText: 'with chromium.org'}),
+        ];
+        loadTimeData.overrideValues({editPasswordsInSettings: true});
+        const passwordsSection = elementFactory.createPasswordsSection(
+            passwordManager, passwordList, []);
+
+        getFirstPasswordListItem(passwordsSection).$.moreActionsButton.click();
+        flush();
+        assertEquals(
+            passwordsSection.i18n('passwordViewDetails'),
+            passwordsSection.$.passwordsListHandler.$$('#menuEditPassword')
+                .textContent.trim());
+      });
+
+  // Test verifies that 'Edit' button is shown instead of 'Details' for
+  // common credentials when the flag editPasswordsInSettings is enabled.
+  // Does not test Details and Edit button.
+  test('verifyEditButtonInMenuEnabledEditPasswordsInSettings', function() {
+    const passwordList = [
+      createPasswordEntry({url: 'one.com', username: 'hey'}),
+    ];
+    loadTimeData.overrideValues({editPasswordsInSettings: true});
+    const passwordsSection = elementFactory.createPasswordsSection(
+        passwordManager, passwordList, []);
+
+    getFirstPasswordListItem(passwordsSection).$.moreActionsButton.click();
+    flush();
+    assertEquals(
+        passwordsSection.i18n('editCompromisedPassword'),
+        passwordsSection.$.passwordsListHandler.$$('#menuEditPassword')
+            .textContent.trim());
+  });
+
+  // Test verifies that 'Details' button is shown instead of 'Edit' for
+  // non-federated credentials when the flag editPasswordsInSettings is
+  // disabled. Does not test Details and Edit button.
+  test('verifyDetailsButtonInMenuDisabledEditPasswordsInSettings', function() {
+    const passwordList = [
+      createPasswordEntry({url: 'one.com', username: 'hey'}),
+    ];
+    loadTimeData.overrideValues({editPasswordsInSettings: false});
+    const passwordsSection = elementFactory.createPasswordsSection(
+        passwordManager, passwordList, []);
+
+    getFirstPasswordListItem(passwordsSection).$.moreActionsButton.click();
+    flush();
+    assertEquals(
+        passwordsSection.i18n('passwordViewDetails'),
+        passwordsSection.$.passwordsListHandler.$$('#menuEditPassword')
+            .textContent.trim());
+  });
+
   test('verifyFilterPasswords', function() {
     const passwordList = [
       createPasswordEntry({url: 'one.com', username: 'SHOW', id: 0}),
@@ -832,14 +948,81 @@
   });
 
   test('verifyFederatedPassword', function() {
-    const item = createMultiStorePasswordEntry(
+    const federationEntry = createMultiStorePasswordEntry(
         {federationText: 'with chromium.org', username: 'bart', deviceId: 42});
-    const passwordDialog = elementFactory.createPasswordEditDialog(item);
+    const passwordDialog =
+        elementFactory.createPasswordEditDialog(federationEntry);
 
-    assertEquals(item.federationText, passwordDialog.$.passwordInput.value);
+    assertEquals(
+        federationEntry.federationText, passwordDialog.$.passwordInput.value);
     // Text should be readable.
     assertEquals('text', passwordDialog.$.passwordInput.type);
     assertTrue(passwordDialog.$.showPasswordButton.hidden);
+    detailsDialogPartsAreShownCorrectly(passwordDialog);
+  });
+
+  test('verifyDetailsDialogDisabledEditPasswordsInSettings', function() {
+    const federationEntry = createMultiStorePasswordEntry(
+        {federationText: 'with chromium.org', username: 'bart', deviceId: 42});
+    loadTimeData.overrideValues({editPasswordsInSettings: false});
+    const passwordDialogFederation =
+        elementFactory.createPasswordEditDialog(federationEntry);
+    detailsDialogPartsAreShownCorrectly(passwordDialogFederation);
+
+    const commonEntry = createMultiStorePasswordEntry(
+        {url: 'goo.gl', username: 'bart', accountId: 42});
+    const passwordDialogCommon =
+        elementFactory.createPasswordEditDialog(commonEntry);
+    detailsDialogPartsAreShownCorrectly(passwordDialogCommon);
+  });
+
+  test('verifyEditOrDetailsDialogEnabledEditPasswordsInSettings', function() {
+    const federationEntry = createMultiStorePasswordEntry(
+        {federationText: 'with chromium.org', username: 'bart', deviceId: 42});
+    loadTimeData.overrideValues({editPasswordsInSettings: true});
+    const passwordDialogFederation =
+        elementFactory.createPasswordEditDialog(federationEntry);
+    detailsDialogPartsAreShownCorrectly(passwordDialogFederation);
+
+    const commonEntry = createMultiStorePasswordEntry(
+        {url: 'goo.gl', username: 'bart', accountId: 42});
+    const passwordDialogCommon =
+        elementFactory.createPasswordEditDialog(commonEntry);
+    // Should show edit dialog for common credetial when editPasswordsInSettings
+    // flag is enabled.
+    editDialogPartsAreShownCorrectly(passwordDialogCommon);
+  });
+
+  test('editDialogChangePassword', async function() {
+    loadTimeData.overrideValues({editPasswordsInSettings: true});
+
+    const PASSWORD1 = 'hello_world';
+    const commonEntry = createMultiStorePasswordEntry(
+        {url: 'goo.gl', username: 'bart', accountId: 42});
+
+    const editDialog = elementFactory.createPasswordEditDialog(commonEntry);
+
+    editDialog.password = PASSWORD1;
+    flush();
+
+    assertEquals(PASSWORD1, editDialog.$.passwordInput.value);
+
+    // Empty password should be consider invalid and disables the save button.
+    editDialog.$.passwordInput.value = '';
+    assertTrue(editDialog.$.passwordInput.invalid);
+    assertTrue(editDialog.$.actionButton.disabled);
+
+    const PASSWORD2 = 'hello_world_2';
+    editDialog.$.passwordInput.value = PASSWORD2;
+    assertFalse(editDialog.$.passwordInput.invalid);
+    assertFalse(editDialog.$.actionButton.disabled);
+
+    editDialog.$.actionButton.click();
+
+    // Check that the changeSavedPassword is called with the right arguments.
+    const {newPassword} =
+        await passwordManager.whenCalled('changeSavedPassword');
+    assertEquals(PASSWORD2, newPassword);
   });
 
   // Test verifies that the edit dialog informs the password is stored in the
diff --git a/chrome/test/data/webui/settings/test_password_manager_proxy.js b/chrome/test/data/webui/settings/test_password_manager_proxy.js
index 7182551..f460916 100644
--- a/chrome/test/data/webui/settings/test_password_manager_proxy.js
+++ b/chrome/test/data/webui/settings/test_password_manager_proxy.js
@@ -299,8 +299,8 @@
   }
 
   /** override */
-  changeSavedPassword(id, new_password) {
-    this.methodCalled('changeSavedPassword', {id, new_password});
+  changeSavedPassword(id, newPassword) {
+    this.methodCalled('changeSavedPassword', {id, newPassword});
     return Promise.resolve();
   }
 
diff --git a/chromeos/CHROMEOS_LKGM b/chromeos/CHROMEOS_LKGM
index de42e5a8..1bec8d4 100644
--- a/chromeos/CHROMEOS_LKGM
+++ b/chromeos/CHROMEOS_LKGM
@@ -1 +1 @@
-13379.0.0
\ No newline at end of file
+13380.0.0
\ No newline at end of file
diff --git a/chromeos/profiles/orderfile.newest.txt b/chromeos/profiles/orderfile.newest.txt
index 4fa984f..9500187c 100644
--- a/chromeos/profiles/orderfile.newest.txt
+++ b/chromeos/profiles/orderfile.newest.txt
@@ -1 +1 @@
-chromeos-chrome-orderfile-field-86-4181.3-1594638142-benchmark-86.0.4205.0-r1.orderfile.xz
+chromeos-chrome-orderfile-field-86-4183.39-1595842708-benchmark-86.0.4208.0-r1.orderfile.xz
diff --git a/chromeos/settings/cros_settings_names.cc b/chromeos/settings/cros_settings_names.cc
index e476d859..68153c6 100644
--- a/chromeos/settings/cros_settings_names.cc
+++ b/chromeos/settings/cros_settings_names.cc
@@ -386,9 +386,10 @@
 // functionality.
 const char kTPMFirmwareUpdateSettings[] = "cros.tpm_firmware_update_settings";
 
-// A list of entries in JSON form representing the minimum version of Chrome OS
-// along with warning times required to allow user sign in or stay in session.
-// If the list is empty no restrictions will be applied.
+// A dictionary containing a list of entries in JSON form representing the
+// minimum version of Chrome OS along with warning times required to allow user
+// sign in or stay in session. If the list is empty no restrictions will be
+// applied.
 const char kDeviceMinimumVersion[] = "cros.device.min_version";
 
 // String shown on the update required dialog on the the login screen containing
diff --git a/components/autofill/core/browser/data_model/autofill_structured_address_component.h b/components/autofill/core/browser/data_model/autofill_structured_address_component.h
index 732570b..dbb085f 100644
--- a/components/autofill/core/browser/data_model/autofill_structured_address_component.h
+++ b/components/autofill/core/browser/data_model/autofill_structured_address_component.h
@@ -17,17 +17,18 @@
 namespace autofill {
 namespace structured_address {
 
+// Represents the validation status of value stored in the AutofillProfile.
 enum class VerificationStatus {
   // No verification status assigned.
   kNoStatus,
-  // The user used the autofill settings to verify and store this token.
-  kUserVerified,
-  // The value was observed in a form transmission.
-  kObserved,
-  // Value was built from its subcomponents.
-  kFormatted,
   // The value token was parsed from a parent token.
   kParsed,
+  // Value was built from its subcomponents.
+  kFormatted,
+  // The value was observed in a form transmission.
+  kObserved,
+  // The user used the autofill settings to verify and store this token.
+  kUserVerified,
 };
 
 // An AddressComponent is a tree structure that represents a semi-structured
diff --git a/components/autofill_assistant/browser/BUILD.gn b/components/autofill_assistant/browser/BUILD.gn
index c574ac5..652fa5c 100644
--- a/components/autofill_assistant/browser/BUILD.gn
+++ b/components/autofill_assistant/browser/BUILD.gn
@@ -95,6 +95,8 @@
     "chip.cc",
     "chip.h",
     "client.h",
+    "client_context.cc",
+    "client_context.h",
     "client_settings.cc",
     "client_settings.h",
     "client_status.cc",
@@ -268,6 +270,7 @@
     "actions/wait_for_dom_action_unittest.cc",
     "basic_interactions_unittest.cc",
     "batch_element_checker_unittest.cc",
+    "client_context_unittest.cc",
     "controller_unittest.cc",
     "details_unittest.cc",
     "element_area_unittest.cc",
@@ -282,7 +285,6 @@
     "script_precondition_unittest.cc",
     "script_tracker_unittest.cc",
     "selector_unittest.cc",
-    "service_impl_unittest.cc",
     "string_conversions_util_unittest.cc",
     "trigger_context_unittest.cc",
     "user_data_util_unittest.cc",
@@ -300,6 +302,7 @@
     "//components/autofill/core/browser:test_support",
     "//components/password_manager/core/browser:test_support",
     "//components/strings:components_strings_grit",
+    "//components/version_info",
     "//content/test:test_support",
     "//testing/gmock",
     "//testing/gtest",
diff --git a/components/autofill_assistant/browser/client_context.cc b/components/autofill_assistant/browser/client_context.cc
new file mode 100644
index 0000000..4d7c49a
--- /dev/null
+++ b/components/autofill_assistant/browser/client_context.cc
@@ -0,0 +1,81 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/autofill_assistant/browser/client_context.h"
+#include "base/metrics/field_trial.h"
+#include "base/strings/string_util.h"
+#include "components/version_info/version_info.h"
+#include "crypto/sha2.h"
+
+namespace autofill_assistant {
+
+ClientContextImpl::ClientContextImpl(const Client* client) : client_(client) {
+  proto_.mutable_chrome()->set_chrome_version(
+      version_info::GetProductNameAndVersionForUserAgent());
+  proto_.set_locale(client->GetLocale());
+  proto_.set_country(client->GetCountryCode());
+
+  base::FieldTrial::ActiveGroups active_groups;
+  base::FieldTrialList::GetActiveFieldTrialGroups(&active_groups);
+  for (const auto& group : active_groups) {
+    FieldTrialProto* field_trial =
+        proto_.mutable_chrome()->add_active_field_trials();
+    field_trial->set_trial_name(group.trial_name);
+    field_trial->set_group_name(group.group_name);
+  }
+
+  client_->GetDeviceContext().ToProto(proto_.mutable_device_context());
+  auto empty_trigger_context = TriggerContext::CreateEmpty();
+  Update(*empty_trigger_context);
+}
+
+void ClientContextImpl::Update(const TriggerContext& trigger_context) {
+  proto_.set_accessibility_enabled(client_->IsAccessibilityEnabled());
+  std::string chrome_signed_in_email_address =
+      client_->GetChromeSignedInEmailAddress();
+  proto_.set_signed_into_chrome_status(chrome_signed_in_email_address.empty()
+                                           ? ClientContextProto::NOT_SIGNED_IN
+                                           : ClientContextProto::SIGNED_IN);
+
+  std::string experiment_ids = trigger_context.experiment_ids();
+  if (!experiment_ids.empty()) {
+    proto_.set_experiment_ids(experiment_ids);
+  }
+  if (trigger_context.is_cct()) {
+    proto_.set_is_cct(true);
+  }
+  if (trigger_context.is_onboarding_shown()) {
+    proto_.set_is_onboarding_shown(true);
+  }
+  if (trigger_context.is_direct_action()) {
+    proto_.set_is_direct_action(true);
+  }
+
+  // TODO(b/156882027): Add an integration test for accounts handling.
+  std::string chrome_account_sha_bin =
+      crypto::SHA256HashString(chrome_signed_in_email_address);
+  std::string client_account_hash = base::ToLowerASCII(base::HexEncode(
+      chrome_account_sha_bin.data(), chrome_account_sha_bin.size()));
+  if (trigger_context.get_caller_account_hash().empty()) {
+    proto_.set_accounts_matching_status(ClientContextProto::UNKNOWN);
+  } else {
+    if (trigger_context.get_caller_account_hash() == client_account_hash) {
+      proto_.set_accounts_matching_status(
+          ClientContextProto::ACCOUNTS_MATCHING);
+    } else {
+      proto_.set_accounts_matching_status(
+          ClientContextProto::ACCOUNTS_NOT_MATCHING);
+    }
+  }
+}
+
+ClientContextProto ClientContextImpl::AsProto() const {
+  return proto_;
+}
+
+ClientContextProto EmptyClientContext::AsProto() const {
+  return ClientContextProto();
+}
+
+}  // namespace autofill_assistant
diff --git a/components/autofill_assistant/browser/client_context.h b/components/autofill_assistant/browser/client_context.h
new file mode 100644
index 0000000..695807d5
--- /dev/null
+++ b/components/autofill_assistant/browser/client_context.h
@@ -0,0 +1,48 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_CLIENT_CONTEXT_H_
+#define COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_CLIENT_CONTEXT_H_
+
+#include "components/autofill_assistant/browser/client.h"
+#include "components/autofill_assistant/browser/service.pb.h"
+#include "components/autofill_assistant/browser/trigger_context.h"
+
+namespace autofill_assistant {
+
+// Provides a ClientContextProto and a way to update its values dynamically.
+class ClientContext {
+ public:
+  virtual ~ClientContext() = default;
+  // Updates the client context based on the current state of the client.
+  virtual void Update(const TriggerContext& trigger_context) = 0;
+  // Returns the proto representation of this client context.
+  virtual ClientContextProto AsProto() const = 0;
+};
+
+// Represents the client context for a given |client| instance.
+class ClientContextImpl : public ClientContext {
+ public:
+  // |client| must outlive this instance.
+  ClientContextImpl(const Client* client);
+  ~ClientContextImpl() override = default;
+  void Update(const TriggerContext& trigger_context) override;
+  ClientContextProto AsProto() const override;
+
+ private:
+  const Client* client_;
+  ClientContextProto proto_;
+};
+
+// An empty client context that does not contain any data.
+class EmptyClientContext : public ClientContext {
+ public:
+  EmptyClientContext() = default;
+  ~EmptyClientContext() override = default;
+  void Update(const TriggerContext& trigger_context) override {}
+  ClientContextProto AsProto() const override;
+};
+
+}  // namespace autofill_assistant
+#endif  // COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_CLIENT_CONTEXT_H_
diff --git a/components/autofill_assistant/browser/client_context_unittest.cc b/components/autofill_assistant/browser/client_context_unittest.cc
new file mode 100644
index 0000000..e3247b6
--- /dev/null
+++ b/components/autofill_assistant/browser/client_context_unittest.cc
@@ -0,0 +1,137 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/autofill_assistant/browser/client_context.h"
+#include "base/test/gmock_callback_support.h"
+#include "base/test/mock_callback.h"
+#include "components/autofill_assistant/browser/device_context.h"
+#include "components/autofill_assistant/browser/mock_client.h"
+#include "components/version_info/version_info.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace autofill_assistant {
+
+using ::testing::_;
+using ::testing::ElementsAre;
+using ::testing::Eq;
+using ::testing::InSequence;
+using ::testing::NiceMock;
+using ::testing::Property;
+using ::testing::Return;
+using ::testing::StrEq;
+
+namespace {
+
+class ClientContextTest : public testing::Test {
+ protected:
+  ClientContextTest() {}
+  ~ClientContextTest() override {}
+
+  NiceMock<MockClient> mock_client_;
+  DeviceContext device_context_;
+};
+
+TEST_F(ClientContextTest, Initialize) {
+  device_context_.version.sdk_int = 123;
+  device_context_.manufacturer.assign("manufacturer");
+  device_context_.model.assign("model");
+
+  EXPECT_CALL(mock_client_, GetLocale()).WillOnce(Return("de-DE"));
+  EXPECT_CALL(mock_client_, GetCountryCode()).WillOnce(Return("ZZ"));
+  EXPECT_CALL(mock_client_, GetDeviceContext())
+      .WillOnce(Return(device_context_));
+  EXPECT_CALL(mock_client_, GetChromeSignedInEmailAddress())
+      .WillOnce(Return("john.doe@chromium.org"));
+  EXPECT_CALL(mock_client_, IsAccessibilityEnabled()).WillOnce(Return(true));
+
+  ClientContextImpl client_context(&mock_client_);
+
+  auto actual_client_context = client_context.AsProto();
+  EXPECT_THAT(actual_client_context.chrome().chrome_version(),
+              Eq(version_info::GetProductNameAndVersionForUserAgent()));
+  EXPECT_THAT(actual_client_context.locale(), Eq("de-DE"));
+  EXPECT_THAT(actual_client_context.country(), Eq("ZZ"));
+  EXPECT_THAT(actual_client_context.experiment_ids(), Eq(""));
+  EXPECT_THAT(actual_client_context.is_cct(), Eq(false));
+  EXPECT_THAT(actual_client_context.is_onboarding_shown(), Eq(false));
+  EXPECT_THAT(actual_client_context.is_direct_action(), Eq(false));
+  EXPECT_THAT(actual_client_context.accessibility_enabled(), Eq(true));
+  EXPECT_THAT(actual_client_context.signed_into_chrome_status(),
+              Eq(ClientContextProto::SIGNED_IN));
+  EXPECT_THAT(actual_client_context.accounts_matching_status(),
+              Eq(ClientContextProto::UNKNOWN));
+
+  auto actual_device_context = actual_client_context.device_context();
+  EXPECT_THAT(actual_device_context.version().sdk_int(), Eq(123));
+  EXPECT_THAT(actual_device_context.manufacturer(), Eq("manufacturer"));
+  EXPECT_THAT(actual_device_context.model(), Eq("model"));
+}
+
+TEST_F(ClientContextTest, UpdateWithTriggerContext) {
+  EXPECT_CALL(mock_client_, GetChromeSignedInEmailAddress())
+      .WillRepeatedly(Return(""));
+  EXPECT_CALL(mock_client_, IsAccessibilityEnabled())
+      .WillRepeatedly(Return(true));
+
+  TriggerContextImpl trigger_context(/* params = */ {}, /* exp = */ "1,2,3");
+  trigger_context.SetCCT(true);
+  trigger_context.SetOnboardingShown(true);
+  trigger_context.SetDirectAction(true);
+  trigger_context.SetCallerAccountHash("some_value");
+
+  ClientContextImpl client_context(&mock_client_);
+  client_context.Update(trigger_context);
+
+  auto actual_client_context = client_context.AsProto();
+  EXPECT_THAT(actual_client_context.experiment_ids(), Eq("1,2,3"));
+  EXPECT_THAT(actual_client_context.is_cct(), Eq(true));
+  EXPECT_THAT(actual_client_context.is_onboarding_shown(), Eq(true));
+  EXPECT_THAT(actual_client_context.is_direct_action(), Eq(true));
+  EXPECT_THAT(actual_client_context.accessibility_enabled(), Eq(true));
+  EXPECT_THAT(actual_client_context.experiment_ids(), Eq("1,2,3"));
+  EXPECT_THAT(actual_client_context.signed_into_chrome_status(),
+              Eq(ClientContextProto::NOT_SIGNED_IN));
+  EXPECT_THAT(actual_client_context.accounts_matching_status(),
+              Eq(ClientContextProto::ACCOUNTS_NOT_MATCHING));
+}
+
+TEST_F(ClientContextTest, AccountMatching) {
+  EXPECT_CALL(mock_client_, GetChromeSignedInEmailAddress())
+      .WillRepeatedly(Return("john.doe@chromium.org"));
+
+  ClientContextImpl client_context(&mock_client_);
+  EXPECT_THAT(client_context.AsProto().accounts_matching_status(),
+              Eq(ClientContextProto::UNKNOWN));
+
+  // Should match john.doe@chromium.org.
+  TriggerContextImpl trigger_context;
+  trigger_context.SetCallerAccountHash(
+      "2c8fa87717fab622bb5cc4d18135fe30dae339efd274b450022d361be92b48c3");
+  client_context.Update(trigger_context);
+  EXPECT_THAT(client_context.AsProto().accounts_matching_status(),
+              Eq(ClientContextProto::ACCOUNTS_MATCHING));
+
+  trigger_context.SetCallerAccountHash("different");
+  client_context.Update(trigger_context);
+  EXPECT_THAT(client_context.AsProto().accounts_matching_status(),
+              Eq(ClientContextProto::ACCOUNTS_NOT_MATCHING));
+}
+
+TEST_F(ClientContextTest, SignedInStatus) {
+  EXPECT_CALL(mock_client_, GetChromeSignedInEmailAddress())
+      .WillOnce(Return(""));
+
+  ClientContextImpl client_context_a(&mock_client_);
+  EXPECT_THAT(client_context_a.AsProto().signed_into_chrome_status(),
+              Eq(ClientContextProto::NOT_SIGNED_IN));
+
+  EXPECT_CALL(mock_client_, GetChromeSignedInEmailAddress())
+      .WillOnce(Return("john.doe@chromium.org"));
+  ClientContextImpl client_context_b(&mock_client_);
+  EXPECT_THAT(client_context_b.AsProto().signed_into_chrome_status(),
+              Eq(ClientContextProto::SIGNED_IN));
+}
+
+}  // namespace
+}  // namespace autofill_assistant
diff --git a/components/autofill_assistant/browser/controller.cc b/components/autofill_assistant/browser/controller.cc
index cdca895e..b15ec88 100644
--- a/components/autofill_assistant/browser/controller.cc
+++ b/components/autofill_assistant/browser/controller.cc
@@ -1154,13 +1154,10 @@
 
   dict.SetKey("status", base::Value(status_message_));
   if (trigger_context_) {
-    google::protobuf::RepeatedPtrField<ScriptParameterProto> parameters_proto;
-    trigger_context_->AddParameters(&parameters_proto);
     std::vector<base::Value> parameters_js;
-    for (const auto& parameter_proto : parameters_proto) {
+    for (const auto& parameter : trigger_context_->GetParameters()) {
       base::Value parameter_js = base::Value(base::Value::Type::DICTIONARY);
-      parameter_js.SetKey(parameter_proto.name(),
-                          base::Value(parameter_proto.value()));
+      parameter_js.SetKey(parameter.first, base::Value(parameter.second));
       parameters_js.push_back(std::move(parameter_js));
     }
     dict.SetKey("parameters", base::Value(parameters_js));
diff --git a/components/autofill_assistant/browser/device_context.h b/components/autofill_assistant/browser/device_context.h
index 4e0c143b..4b2272b 100644
--- a/components/autofill_assistant/browser/device_context.h
+++ b/components/autofill_assistant/browser/device_context.h
@@ -15,7 +15,7 @@
   Version(const Version& orig);
   ~Version();
 
-  int sdk_int;
+  int sdk_int = -1;
 };
 
 // Information about the device.
diff --git a/components/autofill_assistant/browser/mock_service.cc b/components/autofill_assistant/browser/mock_service.cc
index 5951a6f..005ca78 100644
--- a/components/autofill_assistant/browser/mock_service.cc
+++ b/components/autofill_assistant/browser/mock_service.cc
@@ -11,14 +11,7 @@
 namespace autofill_assistant {
 
 MockService::MockService()
-    : ServiceImpl("api_key",
-                  GURL("http://fake"),
-                  nullptr,
-                  nullptr,
-                  "en_US",
-                  "",
-                  DeviceContext(),
-                  nullptr) {}
+    : ServiceImpl("api_key", GURL("http://fake"), nullptr, nullptr, nullptr) {}
 MockService::~MockService() {}
 
 }  // namespace autofill_assistant
diff --git a/components/autofill_assistant/browser/protocol_utils.cc b/components/autofill_assistant/browser/protocol_utils.cc
index acea58f..2f4d16f 100644
--- a/components/autofill_assistant/browser/protocol_utils.cc
+++ b/components/autofill_assistant/browser/protocol_utils.cc
@@ -44,35 +44,13 @@
 
 namespace {
 
-void FillClientContext(const ClientContextProto& client_context,
-                       const TriggerContext& trigger_context,
-                       const std::string& client_account_hash,
-                       ClientContextProto* proto) {
-  proto->CopyFrom(client_context);
-  std::string experiment_ids = trigger_context.experiment_ids();
-  if (!experiment_ids.empty()) {
-    proto->set_experiment_ids(experiment_ids);
-  }
-  if (trigger_context.is_cct()) {
-    proto->set_is_cct(true);
-  }
-  if (trigger_context.is_onboarding_shown()) {
-    proto->set_is_onboarding_shown(true);
-  }
-  if (trigger_context.is_direct_action()) {
-    proto->set_is_direct_action(true);
-  }
-  // TODO(b/156882027): Add an integration test for accounts handling.
-  if (trigger_context.get_caller_account_hash().empty()) {
-    proto->set_accounts_matching_status(ClientContextProto::UNKNOWN);
-  } else {
-    if (trigger_context.get_caller_account_hash() == client_account_hash) {
-      proto->set_accounts_matching_status(
-          ClientContextProto::ACCOUNTS_MATCHING);
-    } else {
-      proto->set_accounts_matching_status(
-          ClientContextProto::ACCOUNTS_NOT_MATCHING);
-    }
+void AppendScriptParametersToRepeatedField(
+    const std::map<std::string, std::string>& script_parameters,
+    google::protobuf::RepeatedPtrField<ScriptParameterProto>* dest) {
+  for (const auto& param_entry : script_parameters) {
+    ScriptParameterProto* parameter = dest->Add();
+    parameter->set_name(param_entry.first);
+    parameter->set_value(param_entry.second);
   }
 }
 
@@ -81,16 +59,15 @@
 // static
 std::string ProtocolUtils::CreateGetScriptsRequest(
     const GURL& url,
-    const TriggerContext& trigger_context,
     const ClientContextProto& client_context,
-    const std::string& client_account_hash) {
+    const std::map<std::string, std::string>& script_parameters) {
   DCHECK(!url.is_empty());
 
   SupportsScriptRequestProto script_proto;
   script_proto.set_url(url.spec());
-  FillClientContext(client_context, trigger_context, client_account_hash,
-                    script_proto.mutable_client_context());
-  trigger_context.AddParameters(script_proto.mutable_script_parameters());
+  *script_proto.mutable_client_context() = client_context;
+  AppendScriptParametersToRepeatedField(
+      script_parameters, script_proto.mutable_script_parameters());
   std::string serialized_script_proto;
   bool success = script_proto.SerializeToString(&serialized_script_proto);
   DCHECK(success);
@@ -137,11 +114,10 @@
 std::string ProtocolUtils::CreateInitialScriptActionsRequest(
     const std::string& script_path,
     const GURL& url,
-    const TriggerContext& trigger_context,
     const std::string& global_payload,
     const std::string& script_payload,
     const ClientContextProto& client_context,
-    const std::string& client_account_hash) {
+    const std::map<std::string, std::string>& script_parameters) {
   ScriptActionRequestProto request_proto;
   InitialScriptActionsRequestProto* initial_request_proto =
       request_proto.mutable_initial_request();
@@ -150,10 +126,9 @@
   query->add_script_path(script_path);
   query->set_url(url.spec());
   query->set_policy(PolicyType::SCRIPT);
-  FillClientContext(client_context, trigger_context, client_account_hash,
-                    request_proto.mutable_client_context());
-  trigger_context.AddParameters(
-      initial_request_proto->mutable_script_parameters());
+  *request_proto.mutable_client_context() = client_context;
+  AppendScriptParametersToRepeatedField(
+      script_parameters, initial_request_proto->mutable_script_parameters());
   if (!global_payload.empty()) {
     request_proto.set_global_payload(global_payload);
   }
@@ -170,12 +145,10 @@
 
 // static
 std::string ProtocolUtils::CreateNextScriptActionsRequest(
-    const TriggerContext& trigger_context,
     const std::string& global_payload,
     const std::string& script_payload,
     const std::vector<ProcessedActionProto>& processed_actions,
-    const ClientContextProto& client_context,
-    const std::string& client_account_hash) {
+    const ClientContextProto& client_context) {
   ScriptActionRequestProto request_proto;
   request_proto.set_global_payload(global_payload);
   request_proto.set_script_payload(script_payload);
@@ -184,8 +157,7 @@
   for (const auto& processed_action : processed_actions) {
     next_request->add_processed_actions()->MergeFrom(processed_action);
   }
-  FillClientContext(client_context, trigger_context, client_account_hash,
-                    request_proto.mutable_client_context());
+  *request_proto.mutable_client_context() = client_context;
   std::string serialized_request_proto;
   bool success = request_proto.SerializeToString(&serialized_request_proto);
   DCHECK(success);
diff --git a/components/autofill_assistant/browser/protocol_utils.h b/components/autofill_assistant/browser/protocol_utils.h
index 3ebfe51..11af7daa 100644
--- a/components/autofill_assistant/browser/protocol_utils.h
+++ b/components/autofill_assistant/browser/protocol_utils.h
@@ -14,7 +14,6 @@
 #include "components/autofill_assistant/browser/actions/action.h"
 #include "components/autofill_assistant/browser/script.h"
 #include "components/autofill_assistant/browser/service.pb.h"
-#include "components/autofill_assistant/browser/trigger_context.h"
 
 class GURL;
 
@@ -26,9 +25,8 @@
   // |url|.
   static std::string CreateGetScriptsRequest(
       const GURL& url,
-      const TriggerContext& trigger_context,
       const ClientContextProto& client_context,
-      const std::string& client_account);
+      const std::map<std::string, std::string>& script_parameters);
 
   // Convert |script_proto| to a script struct and if the script is valid, add
   // it to |scripts|.
@@ -42,20 +40,17 @@
   static std::string CreateInitialScriptActionsRequest(
       const std::string& script_path,
       const GURL& url,
-      const TriggerContext& trigger_context,
       const std::string& global_payload,
       const std::string& script_payload,
       const ClientContextProto& client_context,
-      const std::string& client_account);
+      const std::map<std::string, std::string>& script_parameters);
 
   // Create request to get next sequence of actions for a script.
   static std::string CreateNextScriptActionsRequest(
-      const TriggerContext& trigger_context,
       const std::string& global_payload,
       const std::string& script_payload,
       const std::vector<ProcessedActionProto>& processed_actions,
-      const ClientContextProto& client_context,
-      const std::string& client_account);
+      const ClientContextProto& client_context);
 
   // Create an action from the |action|.
   static std::unique_ptr<Action> CreateAction(ActionDelegate* delegate,
diff --git a/components/autofill_assistant/browser/protocol_utils_unittest.cc b/components/autofill_assistant/browser/protocol_utils_unittest.cc
index 4fb14bd0..481d1377 100644
--- a/components/autofill_assistant/browser/protocol_utils_unittest.cc
+++ b/components/autofill_assistant/browser/protocol_utils_unittest.cc
@@ -16,26 +16,54 @@
 using ::testing::Eq;
 using ::testing::IsEmpty;
 using ::testing::Not;
+using ::testing::Pair;
 using ::testing::SizeIs;
+using ::testing::UnorderedElementsAreArray;
 
-ClientContextProto CreateClientContextProto() {
-  ClientContextProto context;
-  context.mutable_chrome()->set_chrome_version("v");
-  auto* device_context = context.mutable_device_context();
-  device_context->mutable_version()->set_sdk_int(1);
-  device_context->set_manufacturer("ma");
-  device_context->set_model("mo");
-  return context;
-}
+class ProtocolUtilsTest : public testing::Test {
+ protected:
+  ProtocolUtilsTest() {
+    client_context_proto_.set_experiment_ids("1,2,3");
+    client_context_proto_.set_is_cct(true);
+    client_context_proto_.mutable_chrome()->set_chrome_version("v");
+    auto* device_context = client_context_proto_.mutable_device_context();
+    device_context->mutable_version()->set_sdk_int(1);
+    device_context->set_manufacturer("ma");
+    device_context->set_model("mo");
+    client_context_proto_.set_is_onboarding_shown(false);
+    client_context_proto_.set_is_direct_action(false);
+    client_context_proto_.set_accounts_matching_status(
+        ClientContextProto::UNKNOWN);
+  }
+  ~ProtocolUtilsTest() override {}
+
+  ClientContextProto client_context_proto_;
+};
 
 void AssertClientContext(const ClientContextProto& context) {
+  EXPECT_EQ("1,2,3", context.experiment_ids());
+  EXPECT_TRUE(context.is_cct());
   EXPECT_EQ("v", context.chrome().chrome_version());
   EXPECT_EQ(1, context.device_context().version().sdk_int());
   EXPECT_EQ("ma", context.device_context().manufacturer());
   EXPECT_EQ("mo", context.device_context().model());
+  EXPECT_FALSE(context.is_onboarding_shown());
+  EXPECT_FALSE(context.is_direct_action());
+  EXPECT_THAT(context.accounts_matching_status(),
+              Eq(ClientContextProto::UNKNOWN));
 }
 
-TEST(ProtocolUtilsTest, ScriptMissingPath) {
+void AssertScriptParameters(
+    const google::protobuf::RepeatedPtrField<ScriptParameterProto>& actual,
+    const std::map<std::string, std::string>& expected) {
+  std::map<std::string, std::string> actual_as_map;
+  for (const auto& actual_parameter : actual) {
+    actual_as_map[actual_parameter.name()] = actual_parameter.value();
+  }
+  EXPECT_THAT(actual_as_map, UnorderedElementsAreArray(expected));
+}
+
+TEST_F(ProtocolUtilsTest, ScriptMissingPath) {
   SupportedScriptProto script;
   script.mutable_presentation()->mutable_chip()->set_text("missing path");
   std::vector<std::unique_ptr<Script>> scripts;
@@ -44,7 +72,7 @@
   EXPECT_THAT(scripts, IsEmpty());
 }
 
-TEST(ProtocolUtilsTest, MinimalValidScript) {
+TEST_F(ProtocolUtilsTest, MinimalValidScript) {
   SupportedScriptProto script;
   script.set_path("path");
   script.mutable_presentation()->mutable_chip()->set_text("name");
@@ -57,7 +85,7 @@
   EXPECT_NE(nullptr, scripts[0]->precondition);
 }
 
-TEST(ProtocolUtilsTest, AllowInterruptsWithNoName) {
+TEST_F(ProtocolUtilsTest, AllowInterruptsWithNoName) {
   SupportedScriptProto script_proto;
   script_proto.set_path("path");
   auto* presentation = script_proto.mutable_presentation();
@@ -74,7 +102,7 @@
   EXPECT_TRUE(scripts[0]->handle.interrupt);
 }
 
-TEST(ProtocolUtilsTest, InterruptsCannotBeAutostart) {
+TEST_F(ProtocolUtilsTest, InterruptsCannotBeAutostart) {
   SupportedScriptProto script_proto;
   script_proto.set_path("path");
   auto* presentation = script_proto.mutable_presentation();
@@ -89,244 +117,59 @@
   EXPECT_TRUE(scripts[0]->handle.interrupt);
 }
 
-TEST(ProtocolUtilsTest, CreateInitialScriptActionsRequest) {
-  std::map<std::string, std::string> parameters;
-  parameters["a"] = "b";
-  parameters["c"] = "d";
-  TriggerContextImpl trigger_context(parameters, "1,2,3");
-  trigger_context.SetCCT(true);
-
+TEST_F(ProtocolUtilsTest, CreateInitialScriptActionsRequest) {
+  std::map<std::string, std::string> parameters = {{"key_a", "value_a"},
+                                                   {"key_b", "value_b"}};
   ScriptActionRequestProto request;
   EXPECT_TRUE(
       request.ParseFromString(ProtocolUtils::CreateInitialScriptActionsRequest(
-          "script_path", GURL("http://example.com/"), trigger_context,
-          "global_payload", "script_payload", CreateClientContextProto(),
-          "accountsha")));
-
-  AssertClientContext(request.client_context());
-  EXPECT_THAT(request.client_context().experiment_ids(), Eq("1,2,3"));
-  EXPECT_TRUE(request.client_context().is_cct());
-  EXPECT_FALSE(request.client_context().is_onboarding_shown());
-  EXPECT_FALSE(request.client_context().is_direct_action());
-  EXPECT_THAT(request.client_context().accounts_matching_status(),
-              Eq(ClientContextProto::UNKNOWN));
+          "script_path", GURL("http://example.com/"), "global_payload",
+          "script_payload", client_context_proto_, parameters)));
 
   const InitialScriptActionsRequestProto& initial = request.initial_request();
   EXPECT_THAT(initial.query().script_path(), ElementsAre("script_path"));
   EXPECT_EQ(initial.query().url(), "http://example.com/");
-  ASSERT_EQ(2, initial.script_parameters_size());
-  EXPECT_EQ("a", initial.script_parameters(0).name());
-  EXPECT_EQ("b", initial.script_parameters(0).value());
-  EXPECT_EQ("c", initial.script_parameters(1).name());
-  EXPECT_EQ("d", initial.script_parameters(1).value());
+  AssertScriptParameters(initial.script_parameters(), parameters);
+
+  AssertClientContext(request.client_context());
   EXPECT_EQ("global_payload", request.global_payload());
   EXPECT_EQ("script_payload", request.script_payload());
 }
 
-TEST(ProtocolUtilsTest, TestCreateInitialScriptActionsRequestFlags) {
-  std::map<std::string, std::string> parameters;
-
-  ScriptActionRequestProto request;
-
-  // With flags.
-  TriggerContextImpl trigger_context_flags(parameters, std::string());
-  trigger_context_flags.SetCCT(true);
-  trigger_context_flags.SetOnboardingShown(true);
-  trigger_context_flags.SetDirectAction(true);
-  trigger_context_flags.SetCallerAccountHash("accountsha");
-
-  EXPECT_TRUE(
-      request.ParseFromString(ProtocolUtils::CreateInitialScriptActionsRequest(
-          "script_path", GURL("http://example.com/"), trigger_context_flags,
-          "global_payload", "script_payload", CreateClientContextProto(),
-          "accountsha")));
-
-  AssertClientContext(request.client_context());
-  EXPECT_TRUE(request.client_context().is_cct());
-  EXPECT_TRUE(request.client_context().is_onboarding_shown());
-  EXPECT_TRUE(request.client_context().is_direct_action());
-  EXPECT_THAT(request.client_context().accounts_matching_status(),
-              Eq(ClientContextProto::ACCOUNTS_MATCHING));
-
-  // Without flags.
-  TriggerContextImpl trigger_context_no_flags(parameters, std::string());
-
-  EXPECT_TRUE(
-      request.ParseFromString(ProtocolUtils::CreateInitialScriptActionsRequest(
-          "script_path", GURL("http://example.com/"), trigger_context_no_flags,
-          "global_payload", "script_payload", CreateClientContextProto(),
-          "accountsha")));
-
-  AssertClientContext(request.client_context());
-  EXPECT_FALSE(request.client_context().is_cct());
-  EXPECT_FALSE(request.client_context().is_onboarding_shown());
-  EXPECT_FALSE(request.client_context().is_direct_action());
-  EXPECT_THAT(request.client_context().accounts_matching_status(),
-              Eq(ClientContextProto::UNKNOWN));
-}
-
-TEST(ProtocolUtilsTest,
-     TestCreateInitialScriptActionsRequestAccountsNotMatching) {
-  std::map<std::string, std::string> parameters;
-
-  ScriptActionRequestProto request;
-
-  // With flags.
-  TriggerContextImpl trigger_context_flags(parameters, std::string());
-  trigger_context_flags.SetCCT(true);
-  trigger_context_flags.SetOnboardingShown(true);
-  trigger_context_flags.SetDirectAction(true);
-  trigger_context_flags.SetCallerAccountHash("accountsha");
-
-  EXPECT_TRUE(
-      request.ParseFromString(ProtocolUtils::CreateInitialScriptActionsRequest(
-          "script_path", GURL("http://example.com/"), trigger_context_flags,
-          "global_payload", "script_payload", CreateClientContextProto(),
-          "differentaccountsha")));
-
-  AssertClientContext(request.client_context());
-  EXPECT_TRUE(request.client_context().is_cct());
-  EXPECT_TRUE(request.client_context().is_onboarding_shown());
-  EXPECT_TRUE(request.client_context().is_direct_action());
-  EXPECT_THAT(request.client_context().accounts_matching_status(),
-              Eq(ClientContextProto::ACCOUNTS_NOT_MATCHING));
-}
-
-TEST(ProtocolUtilsTest, CreateNextScriptActionsRequest) {
-  std::map<std::string, std::string> parameters;
-  parameters["a"] = "b";
-  parameters["c"] = "d";
-  TriggerContextImpl trigger_context(parameters, "1,2,3");
-
+TEST_F(ProtocolUtilsTest, CreateNextScriptActionsRequest) {
   ScriptActionRequestProto request;
   std::vector<ProcessedActionProto> processed_actions;
   processed_actions.emplace_back(ProcessedActionProto());
   EXPECT_TRUE(
       request.ParseFromString(ProtocolUtils::CreateNextScriptActionsRequest(
-          trigger_context, "global_payload", "script_payload",
-          processed_actions, CreateClientContextProto(), "accountsha")));
+          "global_payload", "script_payload", processed_actions,
+          client_context_proto_)));
 
   AssertClientContext(request.client_context());
-  EXPECT_THAT(request.client_context().experiment_ids(), Eq("1,2,3"));
   EXPECT_EQ(1, request.next_request().processed_actions().size());
 }
 
-TEST(ProtocolUtilsTest, TestCreateNextScriptActionsRequestFlags) {
-  std::map<std::string, std::string> parameters;
-
-  std::vector<ProcessedActionProto> processed_actions;
-  processed_actions.emplace_back(ProcessedActionProto());
-
-  ScriptActionRequestProto request;
-
-  // With flags.
-  TriggerContextImpl trigger_context_flags(parameters, std::string());
-  trigger_context_flags.SetCCT(true);
-  trigger_context_flags.SetOnboardingShown(true);
-  trigger_context_flags.SetDirectAction(true);
-  trigger_context_flags.SetCallerAccountHash("accountsha");
-
-  EXPECT_TRUE(
-      request.ParseFromString(ProtocolUtils::CreateNextScriptActionsRequest(
-          trigger_context_flags, "global_payload", "script_payload",
-          processed_actions, CreateClientContextProto(), "accountsha")));
-
-  AssertClientContext(request.client_context());
-  EXPECT_TRUE(request.client_context().is_cct());
-  EXPECT_TRUE(request.client_context().is_onboarding_shown());
-  EXPECT_TRUE(request.client_context().is_direct_action());
-  EXPECT_THAT(request.client_context().accounts_matching_status(),
-              Eq(ClientContextProto::ACCOUNTS_MATCHING));
-
-  // Without flags.
-  TriggerContextImpl trigger_context_no_flags(parameters, std::string());
-
-  EXPECT_TRUE(
-      request.ParseFromString(ProtocolUtils::CreateNextScriptActionsRequest(
-          trigger_context_no_flags, "global_payload", "script_payload",
-          processed_actions, CreateClientContextProto(), "accountsha")));
-
-  AssertClientContext(request.client_context());
-  EXPECT_FALSE(request.client_context().is_cct());
-  EXPECT_FALSE(request.client_context().is_onboarding_shown());
-  EXPECT_FALSE(request.client_context().is_direct_action());
-  EXPECT_THAT(request.client_context().accounts_matching_status(),
-              Eq(ClientContextProto::UNKNOWN));
-}
-
-TEST(ProtocolUtilsTest, CreateGetScriptsRequest) {
-  std::map<std::string, std::string> parameters;
-  parameters["a"] = "b";
-  parameters["c"] = "d";
-  TriggerContextImpl trigger_context(parameters, "1,2,3");
-  trigger_context.SetDirectAction(true);
+TEST_F(ProtocolUtilsTest, CreateGetScriptsRequest) {
+  std::map<std::string, std::string> parameters = {{"key_a", "value_a"},
+                                                   {"key_b", "value_b"}};
 
   SupportsScriptRequestProto request;
   EXPECT_TRUE(request.ParseFromString(ProtocolUtils::CreateGetScriptsRequest(
-      GURL("http://example.com/"), trigger_context, CreateClientContextProto(),
-      "accountsha")));
+      GURL("http://example.com/"), client_context_proto_, parameters)));
 
   AssertClientContext(request.client_context());
-  EXPECT_THAT(request.client_context().experiment_ids(), Eq("1,2,3"));
-  EXPECT_FALSE(request.client_context().is_cct());
-  EXPECT_FALSE(request.client_context().is_onboarding_shown());
-  EXPECT_TRUE(request.client_context().is_direct_action());
-  EXPECT_THAT(request.client_context().accounts_matching_status(),
-              Eq(ClientContextProto::UNKNOWN));
-
+  AssertScriptParameters(request.script_parameters(), parameters);
   EXPECT_EQ("http://example.com/", request.url());
-  ASSERT_EQ(2, request.script_parameters_size());
-  EXPECT_EQ("a", request.script_parameters(0).name());
-  EXPECT_EQ("b", request.script_parameters(0).value());
-  EXPECT_EQ("c", request.script_parameters(1).name());
-  EXPECT_EQ("d", request.script_parameters(1).value());
 }
 
-TEST(ProtocolUtilsTest, TestCreateGetScriptsRequestFlags) {
-  std::map<std::string, std::string> parameters;
-  SupportsScriptRequestProto request;
-
-  // With flags.
-  TriggerContextImpl trigger_context_flags(parameters, std::string());
-  trigger_context_flags.SetCCT(true);
-  trigger_context_flags.SetOnboardingShown(true);
-  trigger_context_flags.SetDirectAction(true);
-  trigger_context_flags.SetCallerAccountHash("accountsha");
-
-  EXPECT_TRUE(request.ParseFromString(ProtocolUtils::CreateGetScriptsRequest(
-      GURL("http://example.com/"), trigger_context_flags,
-      CreateClientContextProto(), "accountsha")));
-
-  AssertClientContext(request.client_context());
-  EXPECT_TRUE(request.client_context().is_cct());
-  EXPECT_TRUE(request.client_context().is_onboarding_shown());
-  EXPECT_TRUE(request.client_context().is_direct_action());
-  EXPECT_THAT(request.client_context().accounts_matching_status(),
-              Eq(ClientContextProto::ACCOUNTS_MATCHING));
-
-  // Without flags.
-  TriggerContextImpl trigger_context_no_flags(parameters, std::string());
-
-  EXPECT_TRUE(request.ParseFromString(ProtocolUtils::CreateGetScriptsRequest(
-      GURL("http://example.com/"), trigger_context_no_flags,
-      CreateClientContextProto(), "accountsha")));
-
-  AssertClientContext(request.client_context());
-  EXPECT_FALSE(request.client_context().is_cct());
-  EXPECT_FALSE(request.client_context().is_onboarding_shown());
-  EXPECT_FALSE(request.client_context().is_direct_action());
-  EXPECT_THAT(request.client_context().accounts_matching_status(),
-              Eq(ClientContextProto::UNKNOWN));
-}
-
-TEST(ProtocolUtilsTest, AddScriptIgnoreInvalid) {
+TEST_F(ProtocolUtilsTest, AddScriptIgnoreInvalid) {
   SupportedScriptProto script_proto;
   std::vector<std::unique_ptr<Script>> scripts;
   ProtocolUtils::AddScript(script_proto, &scripts);
   EXPECT_TRUE(scripts.empty());
 }
 
-TEST(ProtocolUtilsTest, AddScriptWithChip) {
+TEST_F(ProtocolUtilsTest, AddScriptWithChip) {
   SupportedScriptProto script_proto;
   script_proto.set_path("path");
   auto* presentation = script_proto.mutable_presentation();
@@ -346,7 +189,7 @@
   EXPECT_NE(nullptr, script->precondition);
 }
 
-TEST(ProtocolUtilsTest, AddScriptWithDirectAction) {
+TEST_F(ProtocolUtilsTest, AddScriptWithDirectAction) {
   SupportedScriptProto script_proto;
   script_proto.set_path("path");
   auto* presentation = script_proto.mutable_presentation();
@@ -365,7 +208,7 @@
   EXPECT_NE(nullptr, script->precondition);
 }
 
-TEST(ProtocolUtilsTest, AddAutostartableScript) {
+TEST_F(ProtocolUtilsTest, AddAutostartableScript) {
   SupportedScriptProto script_proto;
   script_proto.set_path("path");
   auto* presentation = script_proto.mutable_presentation();
@@ -384,7 +227,7 @@
   EXPECT_NE(nullptr, script->precondition);
 }
 
-TEST(ProtocolUtilsTest, SkipAutostartableScriptWithoutName) {
+TEST_F(ProtocolUtilsTest, SkipAutostartableScriptWithoutName) {
   SupportedScriptProto script_proto;
   script_proto.set_path("path");
   auto* presentation = script_proto.mutable_presentation();
@@ -396,7 +239,7 @@
   EXPECT_THAT(scripts, IsEmpty());
 }
 
-TEST(ProtocolUtilsTest, ParseActionsParseError) {
+TEST_F(ProtocolUtilsTest, ParseActionsParseError) {
   bool unused;
   std::vector<std::unique_ptr<Action>> unused_actions;
   std::vector<std::unique_ptr<Script>> unused_scripts;
@@ -405,7 +248,7 @@
                                            &unused));
 }
 
-TEST(ProtocolUtilsTest, ParseActionsValid) {
+TEST_F(ProtocolUtilsTest, ParseActionsValid) {
   ActionsResponseProto proto;
   proto.set_global_payload("global_payload");
   proto.set_script_payload("script_payload");
@@ -431,7 +274,7 @@
   EXPECT_TRUE(scripts.empty());
 }
 
-TEST(ProtocolUtilsTest, ParseActionsEmptyUpdateScriptList) {
+TEST_F(ProtocolUtilsTest, ParseActionsEmptyUpdateScriptList) {
   ActionsResponseProto proto;
   proto.mutable_update_script_list();
 
@@ -450,7 +293,7 @@
   EXPECT_TRUE(scripts.empty());
 }
 
-TEST(ProtocolUtilsTest, ParseActionsUpdateScriptListFullFeatured) {
+TEST_F(ProtocolUtilsTest, ParseActionsUpdateScriptListFullFeatured) {
   ActionsResponseProto proto;
   auto* script_list = proto.mutable_update_script_list();
   auto* script_a = script_list->add_scripts();
diff --git a/components/autofill_assistant/browser/service_impl.cc b/components/autofill_assistant/browser/service_impl.cc
index b18851ce..0c18a73a 100644
--- a/components/autofill_assistant/browser/service_impl.cc
+++ b/components/autofill_assistant/browser/service_impl.cc
@@ -10,7 +10,6 @@
 
 #include "base/bind.h"
 #include "base/command_line.h"
-#include "base/metrics/field_trial.h"
 #include "base/strings/strcat.h"
 #include "base/strings/stringprintf.h"
 #include "components/autofill_assistant/browser/client.h"
@@ -20,7 +19,6 @@
 #include "components/version_info/version_info.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/storage_partition.h"
-#include "crypto/sha2.h"
 #include "google_apis/google_api_keys.h"
 #include "net/base/load_flags.h"
 #include "net/http/http_request_headers.h"
@@ -91,27 +89,37 @@
 
   return std::make_unique<ServiceImpl>(
       GetAPIKey(client->GetChannel()), server_url, context,
-      client->GetAccessTokenFetcher(), client->GetLocale(),
-      client->GetCountryCode(), client->GetDeviceContext(), client);
+      std::make_unique<ClientContextImpl>(client),
+      client->GetAccessTokenFetcher());
 }
 
 ServiceImpl::ServiceImpl(const std::string& api_key,
                          const GURL& server_url,
                          content::BrowserContext* context,
+                         std::unique_ptr<ClientContext> client_context,
+                         AccessTokenFetcher* access_token_fetcher)
+    : ServiceImpl(
+          api_key,
+          server_url,
+          context,
+          std::move(client_context),
+          access_token_fetcher,
+          /* auth_enabled = */ "false" !=
+              base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
+                  switches::kAutofillAssistantAuth)) {}
+
+ServiceImpl::ServiceImpl(const std::string& api_key,
+                         const GURL& server_url,
+                         content::BrowserContext* context,
+                         std::unique_ptr<ClientContext> client_context,
                          AccessTokenFetcher* access_token_fetcher,
-                         const std::string& locale,
-                         const std::string& country_code,
-                         const DeviceContext& device_context,
-                         const Client* client,
                          bool auth_enabled)
     : context_(context),
       api_key_(api_key),
+      client_context_(std::move(client_context)),
       access_token_fetcher_(access_token_fetcher),
       fetching_token_(false),
       auth_enabled_(auth_enabled),
-      client_context_(
-          CreateClientContext(locale, country_code, device_context)),
-      client_(client),
       weak_ptr_factory_(this) {
   DCHECK(server_url.is_valid());
 
@@ -125,27 +133,6 @@
   VLOG(1) << "Using script domain " << script_action_server_url_.host();
 }
 
-ServiceImpl::ServiceImpl(const std::string& api_key,
-                         const GURL& server_url,
-                         content::BrowserContext* context,
-                         AccessTokenFetcher* access_token_fetcher,
-                         const std::string& locale,
-                         const std::string& country_code,
-                         const DeviceContext& device_context,
-                         const Client* client)
-    : ServiceImpl(
-          api_key,
-          server_url,
-          context,
-          access_token_fetcher,
-          locale,
-          country_code,
-          device_context,
-          client,
-          /* auth_enabled = */ "false" !=
-              base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
-                  switches::kAutofillAssistantAuth)) {}
-
 ServiceImpl::~ServiceImpl() {}
 
 void ServiceImpl::GetScriptsForUrl(const GURL& url,
@@ -153,11 +140,11 @@
                                    ResponseCallback callback) {
   DCHECK(url.is_valid());
 
-  UpdateMutableClientContextFields();
+  client_context_->Update(trigger_context);
   SendRequest(AddLoader(
       script_server_url_,
-      ProtocolUtils::CreateGetScriptsRequest(
-          url, trigger_context, client_context_, GetClientAccountHash()),
+      ProtocolUtils::CreateGetScriptsRequest(url, client_context_->AsProto(),
+                                             trigger_context.GetParameters()),
       std::move(callback)));
 }
 
@@ -169,13 +156,13 @@
                              ResponseCallback callback) {
   DCHECK(!script_path.empty());
 
-  UpdateMutableClientContextFields();
-  SendRequest(
-      AddLoader(script_action_server_url_,
-                ProtocolUtils::CreateInitialScriptActionsRequest(
-                    script_path, url, trigger_context, global_payload,
-                    script_payload, client_context_, GetClientAccountHash()),
-                std::move(callback)));
+  client_context_->Update(trigger_context);
+  SendRequest(AddLoader(
+      script_action_server_url_,
+      ProtocolUtils::CreateInitialScriptActionsRequest(
+          script_path, url, global_payload, script_payload,
+          client_context_->AsProto(), trigger_context.GetParameters()),
+      std::move(callback)));
 }
 
 void ServiceImpl::GetNextActions(
@@ -184,13 +171,12 @@
     const std::string& previous_script_payload,
     const std::vector<ProcessedActionProto>& processed_actions,
     ResponseCallback callback) {
-  UpdateMutableClientContextFields();
-  SendRequest(AddLoader(
-      script_action_server_url_,
-      ProtocolUtils::CreateNextScriptActionsRequest(
-          trigger_context, previous_global_payload, previous_script_payload,
-          processed_actions, client_context_, GetClientAccountHash()),
-      std::move(callback)));
+  client_context_->Update(trigger_context);
+  SendRequest(AddLoader(script_action_server_url_,
+                        ProtocolUtils::CreateNextScriptActionsRequest(
+                            previous_global_payload, previous_script_payload,
+                            processed_actions, client_context_->AsProto()),
+                        std::move(callback)));
 }
 
 void ServiceImpl::SendRequest(Loader* loader) {
@@ -328,42 +314,4 @@
   }
 }
 
-std::string ServiceImpl::GetClientAccountHash() const {
-  std::string chrome_account_sha_bin =
-      crypto::SHA256HashString(client_->GetChromeSignedInEmailAddress());
-  return base::ToLowerASCII(base::HexEncode(chrome_account_sha_bin.data(),
-                                            chrome_account_sha_bin.size()));
-}
-
-// static
-ClientContextProto ServiceImpl::CreateClientContext(
-    const std::string& locale,
-    const std::string& country_code,
-    const DeviceContext& device_context) {
-  ClientContextProto context;
-  context.mutable_chrome()->set_chrome_version(
-      version_info::GetProductNameAndVersionForUserAgent());
-  context.set_locale(locale);
-  context.set_country(country_code);
-  device_context.ToProto(context.mutable_device_context());
-
-  base::FieldTrial::ActiveGroups active_groups;
-  base::FieldTrialList::GetActiveFieldTrialGroups(&active_groups);
-  for (const auto& group : active_groups) {
-    FieldTrialProto* field_trial =
-        context.mutable_chrome()->add_active_field_trials();
-    field_trial->set_trial_name(group.trial_name);
-    field_trial->set_group_name(group.group_name);
-  }
-  return context;
-}
-
-void ServiceImpl::UpdateMutableClientContextFields() {
-  client_context_.set_accessibility_enabled(client_->IsAccessibilityEnabled());
-  client_context_.set_signed_into_chrome_status(
-      client_->GetChromeSignedInEmailAddress().empty()
-          ? ClientContextProto::NOT_SIGNED_IN
-          : ClientContextProto::SIGNED_IN);
-}
-
 }  // namespace autofill_assistant
diff --git a/components/autofill_assistant/browser/service_impl.h b/components/autofill_assistant/browser/service_impl.h
index 0f4c085..333e98e6 100644
--- a/components/autofill_assistant/browser/service_impl.h
+++ b/components/autofill_assistant/browser/service_impl.h
@@ -13,6 +13,7 @@
 #include "base/callback.h"
 #include "base/memory/weak_ptr.h"
 #include "components/autofill_assistant/browser/access_token_fetcher.h"
+#include "components/autofill_assistant/browser/client_context.h"
 #include "components/autofill_assistant/browser/device_context.h"
 #include "components/autofill_assistant/browser/service.h"
 #include "components/autofill_assistant/browser/service.pb.h"
@@ -33,8 +34,8 @@
 // TODO(b/158998456): Add unit tests.
 class ServiceImpl : public Service {
  public:
-  // Convenience method for creating a service from a client.
-  // |client| must remain valid for the lifetime of the service instance.
+  // Convenience method for creating a service. |context| and |client| must
+  // remain valid for the lifetime of the service instance.
   static std::unique_ptr<ServiceImpl> Create(content::BrowserContext* context,
                                              Client* client);
 
@@ -44,20 +45,14 @@
   ServiceImpl(const std::string& api_key,
               const GURL& server_url,
               content::BrowserContext* context,
-              AccessTokenFetcher* token_fetcher,
-              const std::string& locale,
-              const std::string& country_code,
-              const DeviceContext& device_context,
-              const Client* client);
+              std::unique_ptr<ClientContext> client_context,
+              AccessTokenFetcher* access_token_fetcher);
   // Same as above, but allows overriding authentication settings.
   ServiceImpl(const std::string& api_key,
               const GURL& server_url,
               content::BrowserContext* context,
-              AccessTokenFetcher* token_fetcher,
-              const std::string& locale,
-              const std::string& country_code,
-              const DeviceContext& device_context,
-              const Client* client,
+              std::unique_ptr<ClientContext> client_context,
+              AccessTokenFetcher* access_token_fetcher,
               bool auth_enabled);
   ~ServiceImpl() override;
 
@@ -115,16 +110,6 @@
   // in |loaders_|.
   void FetchAccessToken();
   void OnFetchAccessToken(bool success, const std::string& access_token);
-  std::string GetClientAccountHash() const;
-
-  // Creates and fills a client context protobuf message.
-  static ClientContextProto CreateClientContext(
-      const std::string& locale,
-      const std::string& country_code,
-      const DeviceContext& device_context);
-  // Updates the subset of |client_context_| fields that may change during a
-  // flow.
-  void UpdateMutableClientContextFields();
 
   content::BrowserContext* context_;
   GURL script_server_url_;
@@ -136,6 +121,9 @@
   // API key to add to the URL of unauthenticated requests.
   std::string api_key_;
 
+  // The client context to send to the backend.
+  std::unique_ptr<ClientContext> client_context_;
+
   // Pointer must remain valid for the lifetime of the Service instance.
   AccessTokenFetcher* access_token_fetcher_;
 
@@ -149,12 +137,6 @@
   // invalidated.
   std::string access_token_;
 
-  // The client context is cached here to avoid having to recreate it for
-  // every message.
-  ClientContextProto client_context_;
-
-  const Client* client_;
-
   base::WeakPtrFactory<ServiceImpl> weak_ptr_factory_;
 
   FRIEND_TEST_ALL_PREFIXES(ServiceImplTestSignedInStatus, SetsSignedInStatus);
diff --git a/components/autofill_assistant/browser/service_impl_unittest.cc b/components/autofill_assistant/browser/service_impl_unittest.cc
deleted file mode 100644
index ce97f93..0000000
--- a/components/autofill_assistant/browser/service_impl_unittest.cc
+++ /dev/null
@@ -1,62 +0,0 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/autofill_assistant/browser/service_impl.h"
-
-#include "components/autofill/core/browser/personal_data_manager.h"
-#include "components/autofill_assistant/browser/client.h"
-#include "components/autofill_assistant/browser/device_context.h"
-#include "components/autofill_assistant/browser/metrics.h"
-#include "components/autofill_assistant/browser/mock_client.h"
-#include "components/autofill_assistant/browser/website_login_manager.h"
-#include "testing/gmock/include/gmock/gmock.h"
-
-namespace autofill_assistant {
-
-using ::testing::Return;
-
-class ServiceImplTest : public ::testing::Test {
- public:
-  ServiceImplTest() {
-    service_impl_ = ServiceImpl::Create(nullptr, &mock_client_);
-  }
-
- protected:
-  std::string GetClientAccountHash() {
-    return service_impl_->GetClientAccountHash();
-  }
-
-  MockClient mock_client_;
-  std::unique_ptr<ServiceImpl> service_impl_;
-};
-
-TEST_F(ServiceImplTest, CreatesValidHashFromEmail) {
-  ON_CALL(mock_client_, GetChromeSignedInEmailAddress)
-      .WillByDefault(Return("john.doe@chromium.org"));
-  EXPECT_EQ(GetClientAccountHash(),
-            "2c8fa87717fab622bb5cc4d18135fe30dae339efd274b450022d361be92b48c3");
-}
-
-class ServiceImplTestSignedInStatus
-    : public ServiceImplTest,
-      public testing::WithParamInterface<
-          std::pair<std::string, ClientContextProto::SignedIntoChromeStatus>> {
-};
-
-INSTANTIATE_TEST_SUITE_P(
-    ParametrizedTests,
-    ServiceImplTestSignedInStatus,
-    testing::Values(std::make_pair("", ClientContextProto::NOT_SIGNED_IN),
-                    std::make_pair("bob@email.com",
-                                   ClientContextProto::SIGNED_IN)));
-
-TEST_P(ServiceImplTestSignedInStatus, SetsSignedInStatus) {
-  ON_CALL(mock_client_, GetChromeSignedInEmailAddress)
-      .WillByDefault(Return(GetParam().first));
-  service_impl_->UpdateMutableClientContextFields();
-  EXPECT_EQ(service_impl_->client_context_.signed_into_chrome_status(),
-            GetParam().second);
-}
-
-}  // namespace autofill_assistant
diff --git a/components/autofill_assistant/browser/trigger_context.cc b/components/autofill_assistant/browser/trigger_context.cc
index ab59be5..ae904963 100644
--- a/components/autofill_assistant/browser/trigger_context.cc
+++ b/components/autofill_assistant/browser/trigger_context.cc
@@ -38,13 +38,8 @@
 
 TriggerContextImpl::~TriggerContextImpl() = default;
 
-void TriggerContextImpl::AddParameters(
-    google::protobuf::RepeatedPtrField<ScriptParameterProto>* dest) const {
-  for (const auto& param_entry : parameters_) {
-    ScriptParameterProto* parameter = dest->Add();
-    parameter->set_name(param_entry.first);
-    parameter->set_value(param_entry.second);
-  }
+std::map<std::string, std::string> TriggerContextImpl::GetParameters() const {
+  return parameters_;
 }
 
 base::Optional<std::string> TriggerContextImpl::GetParameter(
@@ -91,11 +86,14 @@
 
 MergedTriggerContext::~MergedTriggerContext() {}
 
-void MergedTriggerContext::AddParameters(
-    google::protobuf::RepeatedPtrField<ScriptParameterProto>* dest) const {
+std::map<std::string, std::string> MergedTriggerContext::GetParameters() const {
+  std::map<std::string, std::string> merged_parameters;
   for (const TriggerContext* context : contexts_) {
-    context->AddParameters(dest);
+    for (const auto& parameter : context->GetParameters()) {
+      merged_parameters.insert(parameter);
+    }
   }
+  return merged_parameters;
 }
 
 base::Optional<std::string> MergedTriggerContext::GetParameter(
diff --git a/components/autofill_assistant/browser/trigger_context.h b/components/autofill_assistant/browser/trigger_context.h
index 735a1b5c..5c6695f 100644
--- a/components/autofill_assistant/browser/trigger_context.h
+++ b/components/autofill_assistant/browser/trigger_context.h
@@ -37,9 +37,8 @@
   TriggerContext();
   virtual ~TriggerContext();
 
-  // Adds all parameters to the given proto field.
-  virtual void AddParameters(
-      google::protobuf::RepeatedPtrField<ScriptParameterProto>* dest) const = 0;
+  // Returns all parameters as a map.
+  virtual std::map<std::string, std::string> GetParameters() const = 0;
 
   // Returns the value of a specific parameter, if present.
   virtual base::Optional<std::string> GetParameter(
@@ -86,8 +85,7 @@
   }
 
   // Implements TriggerContext:
-  void AddParameters(google::protobuf::RepeatedPtrField<ScriptParameterProto>*
-                         dest) const override;
+  std::map<std::string, std::string> GetParameters() const override;
   base::Optional<std::string> GetParameter(
       const std::string& name) const override;
   std::string experiment_ids() const override;
@@ -123,8 +121,7 @@
   ~MergedTriggerContext() override;
 
   // Implements TriggerContext:
-  void AddParameters(google::protobuf::RepeatedPtrField<ScriptParameterProto>*
-                         dest) const override;
+  std::map<std::string, std::string> GetParameters() const override;
   base::Optional<std::string> GetParameter(
       const std::string& name) const override;
   std::string experiment_ids() const override;
diff --git a/components/autofill_assistant/browser/trigger_context_unittest.cc b/components/autofill_assistant/browser/trigger_context_unittest.cc
index 2268ae16d..5773fdb1 100644
--- a/components/autofill_assistant/browser/trigger_context_unittest.cc
+++ b/components/autofill_assistant/browser/trigger_context_unittest.cc
@@ -10,51 +10,40 @@
 namespace autofill_assistant {
 namespace {
 
+using ::testing::Eq;
 using ::testing::IsEmpty;
+using ::testing::Pair;
 using ::testing::SizeIs;
+using ::testing::UnorderedElementsAre;
 
 TEST(TriggerContextTest, Empty) {
   auto empty = TriggerContext::CreateEmpty();
   ASSERT_TRUE(empty);
 
-  google::protobuf::RepeatedPtrField<ScriptParameterProto> parameters_proto;
-  empty->AddParameters(&parameters_proto);
-  EXPECT_THAT(parameters_proto, IsEmpty());
-
+  EXPECT_THAT(empty->GetParameters(), IsEmpty());
   EXPECT_EQ("", empty->experiment_ids());
 }
 
 TEST(TriggerContextTest, Create) {
-  std::map<std::string, std::string> parameters;
-  parameters["a"] = "b";
-  parameters["c"] = "d";
+  std::map<std::string, std::string> parameters = {{"key_a", "value_a"},
+                                                   {"key_b", "value_b"}};
   auto context = TriggerContext::Create(parameters, "exps");
   ASSERT_TRUE(context);
 
-  google::protobuf::RepeatedPtrField<ScriptParameterProto> parameters_proto;
-  context->AddParameters(&parameters_proto);
-  EXPECT_THAT(parameters_proto, SizeIs(2));
+  EXPECT_THAT(
+      context->GetParameters(),
+      UnorderedElementsAre(Pair("key_a", "value_a"), Pair("key_b", "value_b")));
 
-  EXPECT_EQ("a", parameters_proto.Get(0).name());
-  EXPECT_EQ("b", parameters_proto.Get(0).value());
-  EXPECT_EQ("c", parameters_proto.Get(1).name());
-  EXPECT_EQ("d", parameters_proto.Get(1).value());
-
-  EXPECT_EQ("b", context->GetParameter("a").value_or(""));
-  EXPECT_EQ("d", context->GetParameter("c").value_or(""));
-  EXPECT_FALSE(context->GetParameter("not_found"));
+  EXPECT_THAT(context->GetParameter("key_a"), Eq("value_a"));
+  EXPECT_THAT(context->GetParameter("key_b"), Eq("value_b"));
+  EXPECT_THAT(context->GetParameter("not_found"), Eq(base::nullopt));
 
   EXPECT_EQ("exps", context->experiment_ids());
 }
 
 TEST(TriggerContextTest, Merge) {
-  std::map<std::string, std::string> parameters;
-  parameters["a"] = "b";
-  auto context1 = TriggerContext::Create(parameters, "exp1");
-
-  parameters.clear();
-  parameters["c"] = "d";
-  auto context2 = TriggerContext::Create(parameters, "exp2");
+  auto context1 = TriggerContext::Create({{"key_a", "value_a"}}, "exp1");
+  auto context2 = TriggerContext::Create({{"key_b", "value_b"}}, "exp2");
 
   // Adding empty to make sure empty contexts are properly skipped
   auto empty = TriggerContext::CreateEmpty();
@@ -64,20 +53,15 @@
 
   ASSERT_TRUE(merged);
 
-  google::protobuf::RepeatedPtrField<ScriptParameterProto> parameters_proto;
-  merged->AddParameters(&parameters_proto);
-  EXPECT_THAT(parameters_proto, SizeIs(2));
+  EXPECT_THAT(
+      merged->GetParameters(),
+      UnorderedElementsAre(Pair("key_a", "value_a"), Pair("key_b", "value_b")));
 
-  EXPECT_EQ("a", parameters_proto.Get(0).name());
-  EXPECT_EQ("b", parameters_proto.Get(0).value());
-  EXPECT_EQ("c", parameters_proto.Get(1).name());
-  EXPECT_EQ("d", parameters_proto.Get(1).value());
+  EXPECT_THAT(merged->GetParameter("key_a"), Eq("value_a"));
+  EXPECT_THAT(merged->GetParameter("key_b"), Eq("value_b"));
+  EXPECT_THAT(merged->GetParameter("not_found"), Eq(base::nullopt));
 
-  EXPECT_EQ("b", merged->GetParameter("a").value_or(""));
-  EXPECT_EQ("d", merged->GetParameter("c").value_or(""));
-  EXPECT_FALSE(merged->GetParameter("not_found"));
-
-  EXPECT_EQ("exp1,exp2", merged->experiment_ids());
+  EXPECT_EQ(merged->experiment_ids(), "exp1,exp2");
 }
 
 TEST(TriggerContextTest, CCT) {
@@ -162,15 +146,13 @@
 }
 
 TEST(TriggerContextTest, HasExperimentId) {
-  std::map<std::string, std::string> parameters;
-
-  auto context = TriggerContext::Create(parameters, "1,2,3");
+  auto context = TriggerContext::Create({}, "1,2,3");
   ASSERT_TRUE(context);
 
   EXPECT_TRUE(context->HasExperimentId("2"));
   EXPECT_FALSE(context->HasExperimentId("4"));
 
-  auto other_context = TriggerContext::Create(parameters, "4,5,6");
+  auto other_context = TriggerContext::Create({}, "4,5,6");
   ASSERT_TRUE(other_context);
 
   EXPECT_TRUE(other_context->HasExperimentId("4"));
@@ -184,20 +166,20 @@
   EXPECT_FALSE(merged->HasExperimentId("7"));
 
   // Double commas should not allow empty element to match.
-  auto double_comma = TriggerContext::Create(parameters, "1,,2");
+  auto double_comma = TriggerContext::Create({}, "1,,2");
   EXPECT_TRUE(double_comma->HasExperimentId("2"));
   EXPECT_FALSE(double_comma->HasExperimentId(""));
 
   // Empty context should not aloow empty element to match.
-  auto empty = TriggerContext::Create(parameters, "");
+  auto empty = TriggerContext::Create({}, "");
   EXPECT_FALSE(empty->HasExperimentId(""));
 
   // Lone comma does not create empty elements.
-  auto lone_comma = TriggerContext::Create(parameters, ",");
+  auto lone_comma = TriggerContext::Create({}, ",");
   EXPECT_FALSE(lone_comma->HasExperimentId(""));
 
   // Single element should match.
-  auto single_element = TriggerContext::Create(parameters, "1");
+  auto single_element = TriggerContext::Create({}, "1");
   EXPECT_TRUE(single_element->HasExperimentId("1"));
 }
 
diff --git a/components/blocked_content/android/popup_blocked_infobar_delegate_unittest.cc b/components/blocked_content/android/popup_blocked_infobar_delegate_unittest.cc
index e9d3bdf2..cd89e47 100644
--- a/components/blocked_content/android/popup_blocked_infobar_delegate_unittest.cc
+++ b/components/blocked_content/android/popup_blocked_infobar_delegate_unittest.cc
@@ -108,6 +108,7 @@
 TEST_F(PopupBlockedInfoBarDelegateTest, ShowsBlockedPopups) {
   TestPopupNavigationDelegate::ResultHolder result;
   helper()->AddBlockedPopup(
+      web_contents()->GetMainFrame(),
       std::make_unique<TestPopupNavigationDelegate>(GURL(kPopupUrl), &result),
       blink::mojom::WindowFeatures(), PopupBlockType::kNoGesture);
   bool on_accept_called = false;
diff --git a/components/blocked_content/popup_blocker.cc b/components/blocked_content/popup_blocker.cc
index 6b278ac2..a1d9226 100644
--- a/components/blocked_content/popup_blocker.cc
+++ b/components/blocked_content/popup_blocker.cc
@@ -125,8 +125,8 @@
   // first.
   content::RenderFrameHost* source_frame =
       GetSourceFrameForPopup(delegate.get(), open_url_params, web_contents);
-  popup_blocker->AddBlockedPopup(std::move(delegate), window_features,
-                                 block_type);
+  popup_blocker->AddBlockedPopup(source_frame, std::move(delegate),
+                                 window_features, block_type);
   auto* trigger = safe_browsing::AdPopupTrigger::FromWebContents(web_contents);
   if (trigger) {
     trigger->PopupWasBlocked(source_frame);
diff --git a/components/blocked_content/popup_blocker_tab_helper.cc b/components/blocked_content/popup_blocker_tab_helper.cc
index c3537d8..97509a4 100644
--- a/components/blocked_content/popup_blocker_tab_helper.cc
+++ b/components/blocked_content/popup_blocker_tab_helper.cc
@@ -75,13 +75,14 @@
 }
 
 void PopupBlockerTabHelper::HidePopupNotification() {
-  auto* tscs = content_settings::TabSpecificContentSettings::FromWebContents(
-      web_contents());
+  auto* tscs = content_settings::TabSpecificContentSettings::GetForFrame(
+      web_contents()->GetMainFrame());
   if (tscs)
     tscs->ClearPopupsBlocked();
 }
 
 void PopupBlockerTabHelper::AddBlockedPopup(
+    content::RenderFrameHost* source_frame,
     std::unique_ptr<PopupNavigationDelegate> delegate,
     const blink::mojom::WindowFeatures& window_features,
     PopupBlockType block_type) {
@@ -93,9 +94,17 @@
   next_id_++;
   blocked_popups_[id] = std::make_unique<BlockedRequest>(
       std::move(delegate), window_features, block_type);
-  content_settings::TabSpecificContentSettings::FromWebContents(web_contents())
-      ->OnContentBlocked(ContentSettingsType::POPUPS);
+
+  // TODO(carlscab): TabSpecificContentSettings is really
+  // PageSpecificContentSettings so it does not matter that we use the
+  // source_frame here and the main frame in HidePopupNotification
+  auto* content_settings =
+      content_settings::TabSpecificContentSettings::GetForFrame(source_frame);
+  if (content_settings) {
+    content_settings->OnContentBlocked(ContentSettingsType::POPUPS);
+  }
   auto* raw_delegate = blocked_popups_[id]->delegate.get();
+
   manager_.NotifyObservers(id, raw_delegate->GetURL());
 
   raw_delegate->OnPopupBlocked(web_contents(), GetBlockedPopupsCount());
diff --git a/components/blocked_content/popup_blocker_tab_helper.h b/components/blocked_content/popup_blocker_tab_helper.h
index 8d1381f..2da2c48 100644
--- a/components/blocked_content/popup_blocker_tab_helper.h
+++ b/components/blocked_content/popup_blocker_tab_helper.h
@@ -19,6 +19,10 @@
 #include "ui/base/window_open_disposition.h"
 #include "url/gurl.h"
 
+namespace content {
+class RenderFrameHost;
+}
+
 namespace blocked_content {
 class PopupNavigationDelegate;
 
@@ -65,7 +69,8 @@
   void ShowBlockedPopup(int32_t popup_id, WindowOpenDisposition disposition);
 
   // Adds a new blocked popup to the UI.
-  void AddBlockedPopup(std::unique_ptr<PopupNavigationDelegate> delegate,
+  void AddBlockedPopup(content::RenderFrameHost* source_frame,
+                       std::unique_ptr<PopupNavigationDelegate> delegate,
                        const blink::mojom::WindowFeatures& window_features,
                        PopupBlockType block_type);
 
diff --git a/components/blocked_content/popup_blocker_tab_helper_unittest.cc b/components/blocked_content/popup_blocker_tab_helper_unittest.cc
index 1eedfe04..1720115 100644
--- a/components/blocked_content/popup_blocker_tab_helper_unittest.cc
+++ b/components/blocked_content/popup_blocker_tab_helper_unittest.cc
@@ -83,6 +83,7 @@
   blink::mojom::WindowFeatures window_features;
   window_features.has_x = true;
   helper()->AddBlockedPopup(
+      web_contents()->GetMainFrame(),
       std::make_unique<TestPopupNavigationDelegate>(GURL(kUrl1), &result),
       window_features, PopupBlockType::kNoGesture);
   EXPECT_EQ(result.total_popups_blocked_on_page, 1);
@@ -101,6 +102,7 @@
   BlockedUrlListObserver observer(helper());
   TestPopupNavigationDelegate::ResultHolder result1;
   helper()->AddBlockedPopup(
+      web_contents()->GetMainFrame(),
       std::make_unique<TestPopupNavigationDelegate>(GURL(kUrl1), &result1),
       blink::mojom::WindowFeatures(), PopupBlockType::kNoGesture);
   EXPECT_EQ(result1.total_popups_blocked_on_page, 1);
@@ -110,6 +112,7 @@
 
   TestPopupNavigationDelegate::ResultHolder result2;
   helper()->AddBlockedPopup(
+      web_contents()->GetMainFrame(),
       std::make_unique<TestPopupNavigationDelegate>(GURL(kUrl2), &result2),
       blink::mojom::WindowFeatures(), PopupBlockType::kNoGesture);
   EXPECT_EQ(result2.total_popups_blocked_on_page, 2);
@@ -133,6 +136,7 @@
 TEST_F(PopupBlockerTabHelperTest, DoesNotShowPopupWithInvalidID) {
   TestPopupNavigationDelegate::ResultHolder result;
   helper()->AddBlockedPopup(
+      web_contents()->GetMainFrame(),
       std::make_unique<TestPopupNavigationDelegate>(GURL(kUrl1), &result),
       blink::mojom::WindowFeatures(), PopupBlockType::kNoGesture);
   EXPECT_EQ(helper()->GetBlockedPopupsCount(), 1u);
@@ -149,17 +153,19 @@
 
 TEST_F(PopupBlockerTabHelperTest, SetsContentSettingsPopupState) {
   auto* content_settings =
-      content_settings::TabSpecificContentSettings::FromWebContents(
-          web_contents());
+      content_settings::TabSpecificContentSettings::GetForFrame(
+          web_contents()->GetMainFrame());
   EXPECT_FALSE(content_settings->IsContentBlocked(ContentSettingsType::POPUPS));
 
   TestPopupNavigationDelegate::ResultHolder result;
   helper()->AddBlockedPopup(
+      web_contents()->GetMainFrame(),
       std::make_unique<TestPopupNavigationDelegate>(GURL(kUrl1), &result),
       blink::mojom::WindowFeatures(), PopupBlockType::kNoGesture);
   EXPECT_TRUE(content_settings->IsContentBlocked(ContentSettingsType::POPUPS));
 
   helper()->AddBlockedPopup(
+      web_contents()->GetMainFrame(),
       std::make_unique<TestPopupNavigationDelegate>(GURL(kUrl2), &result),
       blink::mojom::WindowFeatures(), PopupBlockType::kNoGesture);
   EXPECT_TRUE(content_settings->IsContentBlocked(ContentSettingsType::POPUPS));
@@ -174,15 +180,16 @@
 TEST_F(PopupBlockerTabHelperTest, ClearsContentSettingsPopupStateOnNavigation) {
   TestPopupNavigationDelegate::ResultHolder result;
   helper()->AddBlockedPopup(
+      web_contents()->GetMainFrame(),
       std::make_unique<TestPopupNavigationDelegate>(GURL(kUrl1), &result),
       blink::mojom::WindowFeatures(), PopupBlockType::kNoGesture);
-  EXPECT_TRUE(content_settings::TabSpecificContentSettings::FromWebContents(
-                  web_contents())
+  EXPECT_TRUE(content_settings::TabSpecificContentSettings::GetForFrame(
+                  web_contents()->GetMainFrame())
                   ->IsContentBlocked(ContentSettingsType::POPUPS));
 
   NavigateAndCommit(GURL(kUrl2));
-  EXPECT_FALSE(content_settings::TabSpecificContentSettings::FromWebContents(
-                   web_contents())
+  EXPECT_FALSE(content_settings::TabSpecificContentSettings::GetForFrame(
+                   web_contents()->GetMainFrame())
                    ->IsContentBlocked(ContentSettingsType::POPUPS));
 }
 
diff --git a/components/browser_ui/settings/android/BUILD.gn b/components/browser_ui/settings/android/BUILD.gn
index 037331e..0def3cf 100644
--- a/components/browser_ui/settings/android/BUILD.gn
+++ b/components/browser_ui/settings/android/BUILD.gn
@@ -39,14 +39,14 @@
     "//ui/android:ui_java_resources",
   ]
   sources = [
-    "java/res/drawable-hdpi/controlled_setting_mandatory.png",
     "java/res/drawable-hdpi/ic_account_child_grey600_36dp.png",
-    "java/res/drawable-mdpi/controlled_setting_mandatory.png",
+    "java/res/drawable-hdpi/ic_business_small.png",
     "java/res/drawable-mdpi/ic_account_child_grey600_36dp.png",
-    "java/res/drawable-xhdpi/controlled_setting_mandatory.png",
+    "java/res/drawable-mdpi/ic_business_small.png",
     "java/res/drawable-xhdpi/ic_account_child_grey600_36dp.png",
-    "java/res/drawable-xxhdpi/controlled_setting_mandatory.png",
+    "java/res/drawable-xhdpi/ic_business_small.png",
     "java/res/drawable-xxhdpi/ic_account_child_grey600_36dp.png",
+    "java/res/drawable-xxhdpi/ic_business_small.png",
     "java/res/drawable-xxxhdpi/ic_account_child_grey600_36dp.png",
     "java/res/layout/button_preference_button.xml",
     "java/res/layout/button_preference_layout.xml",
diff --git a/components/browser_ui/settings/android/java/res/drawable-hdpi/controlled_setting_mandatory.png b/components/browser_ui/settings/android/java/res/drawable-hdpi/ic_business_small.png
similarity index 100%
rename from components/browser_ui/settings/android/java/res/drawable-hdpi/controlled_setting_mandatory.png
rename to components/browser_ui/settings/android/java/res/drawable-hdpi/ic_business_small.png
Binary files differ
diff --git a/components/browser_ui/settings/android/java/res/drawable-mdpi/controlled_setting_mandatory.png b/components/browser_ui/settings/android/java/res/drawable-mdpi/ic_business_small.png
similarity index 100%
rename from components/browser_ui/settings/android/java/res/drawable-mdpi/controlled_setting_mandatory.png
rename to components/browser_ui/settings/android/java/res/drawable-mdpi/ic_business_small.png
Binary files differ
diff --git a/components/browser_ui/settings/android/java/res/drawable-xhdpi/controlled_setting_mandatory.png b/components/browser_ui/settings/android/java/res/drawable-xhdpi/ic_business_small.png
similarity index 100%
rename from components/browser_ui/settings/android/java/res/drawable-xhdpi/controlled_setting_mandatory.png
rename to components/browser_ui/settings/android/java/res/drawable-xhdpi/ic_business_small.png
Binary files differ
diff --git a/components/browser_ui/settings/android/java/res/drawable-xxhdpi/controlled_setting_mandatory.png b/components/browser_ui/settings/android/java/res/drawable-xxhdpi/ic_business_small.png
similarity index 100%
rename from components/browser_ui/settings/android/java/res/drawable-xxhdpi/controlled_setting_mandatory.png
rename to components/browser_ui/settings/android/java/res/drawable-xxhdpi/ic_business_small.png
Binary files differ
diff --git a/components/browser_ui/settings/android/java/src/org/chromium/components/browser_ui/settings/ManagedPreferencesUtils.java b/components/browser_ui/settings/android/java/src/org/chromium/components/browser_ui/settings/ManagedPreferencesUtils.java
index b1d1742..8386b0b6 100644
--- a/components/browser_ui/settings/android/java/src/org/chromium/components/browser_ui/settings/ManagedPreferencesUtils.java
+++ b/components/browser_ui/settings/android/java/src/org/chromium/components/browser_ui/settings/ManagedPreferencesUtils.java
@@ -69,7 +69,7 @@
      * @return The resource ID for the Managed By Enterprise icon.
      */
     public static @DrawableRes int getManagedByEnterpriseIconId() {
-        return R.drawable.controlled_setting_mandatory;
+        return R.drawable.ic_business_small;
     }
 
     /**
diff --git a/components/browser_ui/styles/android/BUILD.gn b/components/browser_ui/styles/android/BUILD.gn
index d2dd1d0..3b99590 100644
--- a/components/browser_ui/styles/android/BUILD.gn
+++ b/components/browser_ui/styles/android/BUILD.gn
@@ -99,6 +99,7 @@
     "java/res/drawable-xxxhdpi/plus.png",
     "java/res/drawable-xxxhdpi/settings_all_sites.png",
     "java/res/drawable-xxxhdpi/top_round.9.png",
+    "java/res/drawable/ic_business.xml",
     "java/res/drawable/ic_data_viz_grey.xml",
     "java/res/drawable/ic_done_blue.xml",
     "java/res/drawable/ic_eye_crossed.xml",
diff --git a/components/browser_ui/styles/android/java/res/drawable/ic_business.xml b/components/browser_ui/styles/android/java/res/drawable/ic_business.xml
new file mode 100644
index 0000000..03a2fb2
--- /dev/null
+++ b/components/browser_ui/styles/android/java/res/drawable/ic_business.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2020 The Chromium Authors. All rights reserved.
+     Use of this source code is governed by a BSD-style license that can be
+     found in the LICENSE file. -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="@color/default_icon_color"
+        android:pathData="M12,7L12,3L2,3v18h20L22,7L12,7zM6,19L4,19v-2h2v2zM6,15L4,15v-2h2v2zM6,11L4,11L4,9h2v2zM6,7L4,7L4,5h2v2zM10,19L8,19v-2h2v2zM10,15L8,15v-2h2v2zM10,11L8,11L8,9h2v2zM10,7L8,7L8,5h2v2zM20,19h-8v-2h2v-2h-2v-2h2v-2h-2L12,9h8v10zM18,11h-2v2h2v-2zM18,15h-2v2h2v-2z"/>
+</vector>
diff --git a/components/content_settings/browser/content_settings_manager_impl.cc b/components/content_settings/browser/content_settings_manager_impl.cc
index d513379..066dc48 100644
--- a/components/content_settings/browser/content_settings_manager_impl.cc
+++ b/components/content_settings/browser/content_settings_manager_impl.cc
@@ -46,17 +46,11 @@
                           const GURL& top_origin_url,
                           bool local,
                           bool blocked_by_policy) {
-  content::RenderFrameHost* frame =
-      content::RenderFrameHost::FromID(process_id, frame_id);
-  content::WebContents* web_contents =
-      content::WebContents::FromRenderFrameHost(frame);
-  if (!web_contents)
-    return;
-
-  TabSpecificContentSettings* tab_settings =
-      TabSpecificContentSettings::FromWebContents(web_contents);
-  if (tab_settings)
-    tab_settings->OnDomStorageAccessed(origin_url, local, blocked_by_policy);
+  TabSpecificContentSettings* settings =
+      TabSpecificContentSettings::GetForFrame(
+          content::RenderFrameHost::FromID(process_id, frame_id));
+  if (settings)
+    settings->OnDomStorageAccessed(origin_url, local, blocked_by_policy);
 }
 
 }  // namespace
@@ -150,17 +144,11 @@
 
 void ContentSettingsManagerImpl::OnContentBlocked(int32_t render_frame_id,
                                                   ContentSettingsType type) {
-  content::RenderFrameHost* frame =
-      content::RenderFrameHost::FromID(render_process_id_, render_frame_id);
-  content::WebContents* web_contents =
-      content::WebContents::FromRenderFrameHost(frame);
-  if (!web_contents)
-    return;
-
-  TabSpecificContentSettings* tab_settings =
-      TabSpecificContentSettings::FromWebContents(web_contents);
-  if (tab_settings)
-    tab_settings->OnContentBlocked(type);
+  TabSpecificContentSettings* settings =
+      TabSpecificContentSettings::GetForFrame(render_process_id_,
+                                              render_frame_id);
+  if (settings)
+    settings->OnContentBlocked(type);
 }
 
 ContentSettingsManagerImpl::ContentSettingsManagerImpl(
diff --git a/components/content_settings/browser/tab_specific_content_settings.cc b/components/content_settings/browser/tab_specific_content_settings.cc
index 9437ad7..2a667d6 100644
--- a/components/content_settings/browser/tab_specific_content_settings.cc
+++ b/components/content_settings/browser/tab_specific_content_settings.cc
@@ -384,21 +384,27 @@
     int render_process_id,
     int render_frame_id) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-
-  content::RenderFrameHost* frame =
-      content::RenderFrameHost::FromID(render_process_id, render_frame_id);
-  if (!frame)
-    return nullptr;
-  return TabSpecificContentSettings::GetForCurrentDocument(
-      frame->GetMainFrame());
+  return GetForFrame(
+      content::RenderFrameHost::FromID(render_process_id, render_frame_id));
 }
 
 // static
-TabSpecificContentSettings* TabSpecificContentSettings::FromWebContents(
-    content::WebContents* web_contents) {
+TabSpecificContentSettings* TabSpecificContentSettings::GetForFrame(
+    content::RenderFrameHost* rfh) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  return TabSpecificContentSettings::GetForCurrentDocument(
-      web_contents->GetMainFrame());
+  return rfh ? TabSpecificContentSettings::GetForCurrentDocument(
+                   rfh->GetMainFrame())
+             : nullptr;
+}
+
+// static
+TabSpecificContentSettings::Delegate*
+TabSpecificContentSettings::GetDelegateForWebContents(
+    content::WebContents* web_contents) {
+  auto* handler =
+      TabSpecificContentSettings::WebContentsHandler::FromWebContents(
+          web_contents);
+  return handler ? handler->delegate() : nullptr;
 }
 
 // static
diff --git a/components/content_settings/browser/tab_specific_content_settings.h b/components/content_settings/browser/tab_specific_content_settings.h
index fa48ac5..6c3d78e 100644
--- a/components/content_settings/browser/tab_specific_content_settings.h
+++ b/components/content_settings/browser/tab_specific_content_settings.h
@@ -165,12 +165,21 @@
                                    std::unique_ptr<Delegate> delegate);
   static void DeleteForWebContentsForTest(content::WebContents* web_contents);
 
-  // Returns the object given a RenderFrameHost ids.
+  // Returns the object given a RenderFrameHost ids. Returns nullptr if the
+  // frame no longer exist or there are no TabSpecificContentSettings attached
+  // to the document.
   static TabSpecificContentSettings* GetForFrame(int render_process_id,
                                                  int render_frame_id);
-  // TODO(carlscab): Get rid of this and use GetForFrame instead
-  static TabSpecificContentSettings* FromWebContents(
-      content::WebContents* contents);
+  // Returns the object given a RenderFrameHost. Returns nullptr if the frame
+  // is nullptr or there are no TabSpecificContentSettings attached to the
+  // document.
+  static TabSpecificContentSettings* GetForFrame(content::RenderFrameHost* rfh);
+
+  // Returns the Delegate that was associated to |web_contents| in
+  // CreateForWebContents. Null if CreateForWebContents was not called for
+  // |web_contents|.
+  static TabSpecificContentSettings::Delegate* GetDelegateForWebContents(
+      content::WebContents* web_contents);
 
   // Called when a specific Web database in the current page was accessed. If
   // access was blocked due to the user's content settings,
@@ -369,8 +378,6 @@
   // since the last navigation.
   bool HasContentSettingChangedViaPageInfo(ContentSettingsType type) const;
 
-  Delegate* delegate() { return delegate_; }
-
  private:
   friend class content::RenderDocumentHostUserData<TabSpecificContentSettings>;
 
@@ -398,6 +405,8 @@
     // Notifies all registered |SiteDataObserver|s.
     void NotifySiteDataObservers();
 
+    Delegate* delegate() { return delegate_.get(); }
+
    private:
     friend class content::WebContentsUserData<WebContentsHandler>;
 
diff --git a/components/content_settings/browser/tab_specific_content_settings_unittest.cc b/components/content_settings/browser/tab_specific_content_settings_unittest.cc
index 9477ad9..806028e 100644
--- a/components/content_settings/browser/tab_specific_content_settings_unittest.cc
+++ b/components/content_settings/browser/tab_specific_content_settings_unittest.cc
@@ -75,7 +75,7 @@
 TEST_F(TabSpecificContentSettingsTest, BlockedContent) {
   NavigateAndCommit(GURL("http://google.com"));
   TabSpecificContentSettings* content_settings =
-      TabSpecificContentSettings::FromWebContents(web_contents());
+      TabSpecificContentSettings::GetForFrame(web_contents()->GetMainFrame());
 
   // Check that after initializing, nothing is blocked.
 #if !defined(OS_ANDROID)
@@ -107,7 +107,7 @@
                                   {*cookie1},
                                   false});
   content_settings =
-      TabSpecificContentSettings::FromWebContents(web_contents());
+      TabSpecificContentSettings::GetForFrame(web_contents()->GetMainFrame());
 #if !defined(OS_ANDROID)
   content_settings->OnContentBlocked(ContentSettingsType::IMAGES);
 #endif
@@ -168,12 +168,12 @@
       simulator->GetNavigationHandle(), GURL("http://google.com"),
       content::AllowServiceWorkerResult::FromPolicy(true, false));
   content_settings =
-      TabSpecificContentSettings::FromWebContents(web_contents());
+      TabSpecificContentSettings::GetForFrame(web_contents()->GetMainFrame());
   EXPECT_FALSE(
       content_settings->IsContentBlocked(ContentSettingsType::JAVASCRIPT));
   simulator->Commit();
   content_settings =
-      TabSpecificContentSettings::FromWebContents(web_contents());
+      TabSpecificContentSettings::GetForFrame(web_contents()->GetMainFrame());
 
   // Block a javascript when page starts to start ServiceWorker.
   GetHandle()->OnServiceWorkerAccessed(
@@ -185,7 +185,7 @@
   // Reset blocked content settings.
   NavigateAndCommit(GURL("http://google.com"));
   content_settings =
-      TabSpecificContentSettings::FromWebContents(web_contents());
+      TabSpecificContentSettings::GetForFrame(web_contents()->GetMainFrame());
 #if !defined(OS_ANDROID)
   EXPECT_FALSE(content_settings->IsContentBlocked(ContentSettingsType::IMAGES));
   EXPECT_FALSE(
@@ -205,7 +205,7 @@
 TEST_F(TabSpecificContentSettingsTest, BlockedFileSystems) {
   NavigateAndCommit(GURL("http://google.com"));
   TabSpecificContentSettings* content_settings =
-      TabSpecificContentSettings::FromWebContents(web_contents());
+      TabSpecificContentSettings::GetForFrame(web_contents()->GetMainFrame());
 
   // Access a file system.
   content_settings->OnFileSystemAccessed(GURL("http://google.com"), false);
@@ -220,7 +220,7 @@
 TEST_F(TabSpecificContentSettingsTest, AllowedContent) {
   NavigateAndCommit(GURL("http://google.com"));
   TabSpecificContentSettings* content_settings =
-      TabSpecificContentSettings::FromWebContents(web_contents());
+      TabSpecificContentSettings::GetForFrame(web_contents()->GetMainFrame());
 
   // Test default settings.
   ASSERT_FALSE(content_settings->IsContentAllowed(ContentSettingsType::IMAGES));
@@ -265,7 +265,7 @@
 TEST_F(TabSpecificContentSettingsTest, EmptyCookieList) {
   NavigateAndCommit(GURL("http://google.com"));
   TabSpecificContentSettings* content_settings =
-      TabSpecificContentSettings::FromWebContents(web_contents());
+      TabSpecificContentSettings::GetForFrame(web_contents()->GetMainFrame());
 
   ASSERT_FALSE(
       content_settings->IsContentAllowed(ContentSettingsType::COOKIES));
@@ -284,7 +284,7 @@
 TEST_F(TabSpecificContentSettingsTest, SiteDataObserver) {
   NavigateAndCommit(GURL("http://google.com"));
   TabSpecificContentSettings* content_settings =
-      TabSpecificContentSettings::FromWebContents(web_contents());
+      TabSpecificContentSettings::GetForFrame(web_contents()->GetMainFrame());
   MockSiteDataObserver mock_observer(web_contents());
   EXPECT_CALL(mock_observer, OnSiteDataAccessed()).Times(6);
 
@@ -325,7 +325,7 @@
 TEST_F(TabSpecificContentSettingsTest, LocalSharedObjectsContainer) {
   NavigateAndCommit(GURL("http://google.com"));
   TabSpecificContentSettings* content_settings =
-      TabSpecificContentSettings::FromWebContents(web_contents());
+      TabSpecificContentSettings::GetForFrame(web_contents()->GetMainFrame());
   bool blocked_by_policy = false;
   auto cookie = net::CanonicalCookie::Create(GURL("http://google.com"), "k=v",
                                              base::Time::Now(),
@@ -362,7 +362,7 @@
 TEST_F(TabSpecificContentSettingsTest, LocalSharedObjectsContainerCookie) {
   NavigateAndCommit(GURL("http://google.com"));
   TabSpecificContentSettings* content_settings =
-      TabSpecificContentSettings::FromWebContents(web_contents());
+      TabSpecificContentSettings::GetForFrame(web_contents()->GetMainFrame());
   bool blocked_by_policy = false;
   auto cookie1 = net::CanonicalCookie::Create(GURL("http://google.com"), "k1=v",
                                               base::Time::Now(),
@@ -403,7 +403,7 @@
   NavigateAndCommit(GURL("http://google.com"));
 
   TabSpecificContentSettings* content_settings =
-      TabSpecificContentSettings::FromWebContents(web_contents());
+      TabSpecificContentSettings::GetForFrame(web_contents()->GetMainFrame());
 
   // First trigger OnContentBlocked.
   EXPECT_FALSE(content_settings->IsContentBlocked(
diff --git a/components/error_page/common/BUILD.gn b/components/error_page/common/BUILD.gn
index c91f2e6..98936b2 100644
--- a/components/error_page/common/BUILD.gn
+++ b/components/error_page/common/BUILD.gn
@@ -6,8 +6,6 @@
   sources = [
     "error.cc",
     "error.h",
-    "error_page_params.cc",
-    "error_page_params.h",
     "error_page_switches.cc",
     "error_page_switches.h",
     "localized_error.cc",
diff --git a/components/error_page/common/error_page_params.cc b/components/error_page/common/error_page_params.cc
deleted file mode 100644
index c60931a..0000000
--- a/components/error_page/common/error_page_params.cc
+++ /dev/null
@@ -1,16 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/error_page/common/error_page_params.h"
-
-#include "base/values.h"
-
-namespace error_page {
-
-ErrorPageParams::ErrorPageParams() : suggest_reload(false) {}
-
-ErrorPageParams::~ErrorPageParams() {
-}
-
-}  // namespace error_page
diff --git a/components/error_page/common/error_page_params.h b/components/error_page/common/error_page_params.h
deleted file mode 100644
index eb7d4abb..0000000
--- a/components/error_page/common/error_page_params.h
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_NET_ERROR_COMMON_ERROR_PAGE_PARAMS_H_
-#define COMPONENTS_NET_ERROR_COMMON_ERROR_PAGE_PARAMS_H_
-
-namespace error_page {
-
-// Optional parameters that affect the display of an error page.
-//
-// TODO(mmenke): Now that this is only one value, it should probably be removed.
-struct ErrorPageParams {
-  ErrorPageParams();
-  ~ErrorPageParams();
-
-  // Overrides whether reloading is suggested.
-  bool suggest_reload;
-};
-
-}  // namespace error_page
-
-#endif  // COMPONENTS_NET_ERROR_COMMON_ERROR_PAGE_PARAMS_H_
diff --git a/components/error_page/common/localized_error.cc b/components/error_page/common/localized_error.cc
index 1298449..0114a78 100644
--- a/components/error_page/common/localized_error.cc
+++ b/components/error_page/common/localized_error.cc
@@ -23,7 +23,6 @@
 #include "base/values.h"
 #include "build/build_config.h"
 #include "components/error_page/common/error.h"
-#include "components/error_page/common/error_page_params.h"
 #include "components/error_page/common/error_page_switches.h"
 #include "components/error_page/common/net_error_info.h"
 #include "components/offline_pages/core/offline_page_feature.h"
@@ -896,8 +895,7 @@
     bool offline_content_feature_enabled,
     bool auto_fetch_feature_enabled,
     bool is_kiosk_mode,
-    const std::string& locale,
-    std::unique_ptr<error_page::ErrorPageParams> params) {
+    const std::string& locale) {
   LocalizedError::PageState result;
   result.is_offline_error = IsOfflineError(error_domain, error_code);
 
@@ -999,20 +997,14 @@
   }
   result.strings.SetString("errorCode", error_string);
 
-  // If no parameters were provided, use the defaults.
-  if (!params) {
-    params.reset(new error_page::ErrorPageParams());
-    params->suggest_reload = !!(options.buttons & SHOW_BUTTON_RELOAD);
-  }
-
   base::ListValue* suggestions_details = result.strings.SetList(
       "suggestionsDetails", std::make_unique<base::ListValue>());
   base::ListValue* suggestions_summary_list = result.strings.SetList(
       "suggestionsSummaryList", std::make_unique<base::ListValue>());
 
-  // Add the reload suggestion, if needed for pages that didn't come
+  // Add the reload suggestion, if needed, for pages that didn't come
   // from a post.
-  if (params->suggest_reload && !is_post) {
+  if ((options.buttons & SHOW_BUTTON_RELOAD) && !is_post) {
     auto reload_button = std::make_unique<base::DictionaryValue>();
     result.reload_button_shown = true;
     reload_button->SetString(
diff --git a/components/error_page/common/localized_error.h b/components/error_page/common/localized_error.h
index 5f35ad74..65f819b2 100644
--- a/components/error_page/common/localized_error.h
+++ b/components/error_page/common/localized_error.h
@@ -19,8 +19,6 @@
 
 namespace error_page {
 
-struct ErrorPageParams;
-
 class LocalizedError {
  public:
   // Information about elements shown on the error page.
@@ -56,8 +54,7 @@
       bool auto_fetch_feature_enabled,
       bool is_kiosk_mode,  // whether device is currently in single app (kiosk)
                            // mode
-      const std::string& locale,
-      std::unique_ptr<error_page::ErrorPageParams> params);
+      const std::string& locale);
 
   // Returns a description of the encountered error.
   static base::string16 GetErrorDetails(const std::string& error_domain,
diff --git a/components/federated_learning/floc_id.cc b/components/federated_learning/floc_id.cc
index 87ae073..38caf08 100644
--- a/components/federated_learning/floc_id.cc
+++ b/components/federated_learning/floc_id.cc
@@ -14,7 +14,8 @@
 
 constexpr char kFlocVersion[] = "1.0.0";
 
-constexpr size_t kNumberOfBitsInFloc = 16;
+// This is only for experimentation and won't be served to websites.
+constexpr size_t kNumberOfBitsInFloc = 50;
 static_assert(kNumberOfBitsInFloc > 0 &&
                   kNumberOfBitsInFloc <= std::numeric_limits<uint64_t>::digits,
               "Number of bits in the floc id must be greater than 0 and no "
diff --git a/components/page_info/page_info.cc b/components/page_info/page_info.cc
index 60441eb..38306ee 100644
--- a/components/page_info/page_info.cc
+++ b/components/page_info/page_info.cc
@@ -1133,8 +1133,10 @@
 
 content_settings::TabSpecificContentSettings*
 PageInfo::GetTabSpecificContentSettings() const {
-  return content_settings::TabSpecificContentSettings::FromWebContents(
-      web_contents());
+  // TODO(https://crbug.com/1103176): PageInfo should be per page. Why is it
+  // a WebContentsObserver if it is not observing anything?
+  return content_settings::TabSpecificContentSettings::GetForFrame(
+      web_contents()->GetMainFrame());
 }
 
 bool PageInfo::HasContentSettingChangedViaPageInfo(ContentSettingsType type) {
diff --git a/components/paint_preview/common/paint_preview_tracker.cc b/components/paint_preview/common/paint_preview_tracker.cc
index dede9c03..adfbed4 100644
--- a/components/paint_preview/common/paint_preview_tracker.cc
+++ b/components/paint_preview/common/paint_preview_tracker.cc
@@ -46,7 +46,51 @@
       embedding_token_(embedding_token),
       is_main_frame_(is_main_frame),
       scroll_(SkISize::Make(0, 0)) {}
-PaintPreviewTracker::~PaintPreviewTracker() = default;
+
+PaintPreviewTracker::~PaintPreviewTracker() {
+  DCHECK(states_.empty());
+}
+
+void PaintPreviewTracker::Save() {
+  states_.push_back(matrix_);
+}
+
+void PaintPreviewTracker::SetMatrix(const SkMatrix& matrix) {
+  matrix_ = matrix;
+}
+
+void PaintPreviewTracker::Restore() {
+  if (states_.size() == 0) {
+    DLOG(ERROR) << "No state to restore";
+    return;
+  }
+  matrix_ = states_.back();
+  states_.pop_back();
+}
+
+void PaintPreviewTracker::Concat(const SkMatrix& matrix) {
+  if (matrix.isIdentity())
+    return;
+  matrix_.preConcat(matrix);
+}
+
+void PaintPreviewTracker::Scale(SkScalar x, SkScalar y) {
+  if (x != 1 || y != 1) {
+    matrix_.preScale(x, y);
+  }
+}
+
+void PaintPreviewTracker::Rotate(SkScalar degrees) {
+  SkMatrix m;
+  m.setRotate(degrees);
+  Concat(m);
+}
+
+void PaintPreviewTracker::Translate(SkScalar x, SkScalar y) {
+  if (x || y) {
+    matrix_.preTranslate(x, y);
+  }
+}
 
 uint32_t PaintPreviewTracker::CreateContentForRemoteFrame(
     const gfx::Rect& rect,
@@ -95,8 +139,12 @@
   }
 }
 
-void PaintPreviewTracker::AnnotateLink(const GURL& url, const gfx::Rect& rect) {
-  links_.push_back(mojom::LinkData::New(url, rect));
+void PaintPreviewTracker::AnnotateLink(const GURL& url, const SkRect& rect) {
+  SkRect out_rect;
+  matrix_.mapRect(&out_rect, rect);
+  links_.push_back(mojom::LinkData::New(
+      url, gfx::Rect(out_rect.x(), out_rect.y(), out_rect.width(),
+                     out_rect.height())));
 }
 
 void PaintPreviewTracker::CustomDataToSkPictureCallback(SkCanvas* canvas,
diff --git a/components/paint_preview/common/paint_preview_tracker.h b/components/paint_preview/common/paint_preview_tracker.h
index 0041603..c7a1151 100644
--- a/components/paint_preview/common/paint_preview_tracker.h
+++ b/components/paint_preview/common/paint_preview_tracker.h
@@ -41,6 +41,18 @@
   }
   bool IsMainFrame() const { return is_main_frame_; }
 
+  // Transform methods --------------------------------------------------------
+
+  // Used to transform the position of links when parsing the paint op buffer.
+  // These are inspired by the methods in SkCanvas.
+  void Save();
+  void Restore();
+  void SetMatrix(const SkMatrix& matrix);
+  void Concat(const SkMatrix& matrix);
+  void Scale(SkScalar x, SkScalar y);
+  void Rotate(SkScalar degrees);
+  void Translate(SkScalar x, SkScalar y);
+
   // Data Collection ----------------------------------------------------------
 
   // Creates a placeholder SkPicture for an OOP subframe located at |rect|
@@ -58,7 +70,7 @@
   void AddGlyphs(const SkTextBlob* blob);
 
   // Adds |link| with bounding box |rect| to the list of links.
-  void AnnotateLink(const GURL& link, const gfx::Rect& rect);
+  void AnnotateLink(const GURL& link, const SkRect& rect);
 
   // Data Serialization -------------------------------------------------------
   // NOTE: once any of these methods are called the PaintPreviewTracker should
@@ -87,6 +99,8 @@
   const bool is_main_frame_;
 
   SkISize scroll_;
+  SkMatrix matrix_;
+  std::vector<SkMatrix> states_;
 
   std::vector<mojom::LinkDataPtr> links_;
   PictureSerializationContext content_id_to_embedding_token_;
diff --git a/components/paint_preview/common/paint_preview_tracker_unittest.cc b/components/paint_preview/common/paint_preview_tracker_unittest.cc
index 8575ba71..e9e2bd08 100644
--- a/components/paint_preview/common/paint_preview_tracker_unittest.cc
+++ b/components/paint_preview/common/paint_preview_tracker_unittest.cc
@@ -114,11 +114,11 @@
   PaintPreviewTracker tracker(base::UnguessableToken::Create(), kEmbeddingToken,
                               true);
   const GURL url_1("https://www.chromium.org");
-  const gfx::Rect rect_1(10, 20, 30, 40);
+  const auto rect_1 = SkRect::MakeXYWH(10, 20, 30, 40);
   tracker.AnnotateLink(url_1, rect_1);
 
   const GURL url_2("https://www.w3.org");
-  const gfx::Rect rect_2(15, 25, 35, 45);
+  const auto rect_2 = SkRect::MakeXYWH(15, 25, 35, 45);
   tracker.AnnotateLink(url_2, rect_2);
 
   ASSERT_EQ(tracker.GetLinks().size(), 2U);
@@ -141,11 +141,11 @@
   PaintPreviewTracker tracker(base::UnguessableToken::Create(), kEmbeddingToken,
                               true);
   const GURL url_1("https://www.chromium.org");
-  const gfx::Rect rect_1(10, 20, 30, 40);
+  const auto rect_1 = SkRect::MakeXYWH(10, 20, 30, 40);
   tracker.AnnotateLink(url_1, rect_1);
 
   const GURL url_2("https://www.w3.org");
-  const gfx::Rect rect_2(15, 25, 35, 45);
+  const auto rect_2 = SkRect::MakeXYWH(15, 25, 35, 45);
   tracker.AnnotateLink(url_2, rect_2);
 
   std::vector<mojom::LinkDataPtr> links;
@@ -165,4 +165,81 @@
   EXPECT_EQ(links[1]->rect.y(), rect_2.y());
 }
 
+TEST(PaintPreviewTrackerTest, AnnotateLinksWithTransform) {
+  const base::UnguessableToken kEmbeddingToken =
+      base::UnguessableToken::Create();
+  PaintPreviewTracker tracker(base::UnguessableToken::Create(), kEmbeddingToken,
+                              true);
+
+  const GURL url("http://www.chromium.org");
+  const auto rect = SkRect::MakeXYWH(10, 20, 30, 40);
+  tracker.AnnotateLink(url, rect);
+
+  std::vector<mojom::LinkDataPtr> links;
+  tracker.MoveLinks(&links);
+  ASSERT_EQ(links.size(), 1U);
+  EXPECT_EQ(links[0]->url, url);
+  EXPECT_EQ(links[0]->rect.width(), rect.width());
+  EXPECT_EQ(links[0]->rect.height(), rect.height());
+  EXPECT_EQ(links[0]->rect.x(), rect.x());
+  EXPECT_EQ(links[0]->rect.y(), rect.y());
+
+  tracker.Save();
+  tracker.Scale(2, 4);
+  tracker.AnnotateLink(url, rect);
+  links.clear();
+  tracker.MoveLinks(&links);
+  EXPECT_EQ(links[0]->url, url);
+  EXPECT_EQ(links[0]->rect.width(), rect.width() * 2);
+  EXPECT_EQ(links[0]->rect.height(), rect.height() * 4);
+  EXPECT_EQ(links[0]->rect.x(), rect.x() * 2);
+  EXPECT_EQ(links[0]->rect.y(), rect.y() * 4);
+
+  tracker.Translate(10, 20);
+  tracker.AnnotateLink(url, rect);
+  links.clear();
+  tracker.MoveLinks(&links);
+  EXPECT_EQ(links[0]->url, url);
+  EXPECT_EQ(links[0]->rect.width(), rect.width() * 2);
+  EXPECT_EQ(links[0]->rect.height(), rect.height() * 4);
+  EXPECT_EQ(links[0]->rect.x(), (10 + rect.x()) * 2);
+  EXPECT_EQ(links[0]->rect.y(), (20 + rect.y()) * 4);
+
+  tracker.Restore();
+  links.clear();
+  tracker.AnnotateLink(url, rect);
+  tracker.MoveLinks(&links);
+  ASSERT_EQ(links.size(), 1U);
+  EXPECT_EQ(links[0]->url, url);
+  EXPECT_EQ(links[0]->rect.width(), rect.width());
+  EXPECT_EQ(links[0]->rect.height(), rect.height());
+  EXPECT_EQ(links[0]->rect.x(), rect.x());
+  EXPECT_EQ(links[0]->rect.y(), rect.y());
+
+  tracker.Concat(SkMatrix::Translate(30, 100));
+  links.clear();
+  tracker.AnnotateLink(url, rect);
+  tracker.MoveLinks(&links);
+  ASSERT_EQ(links.size(), 1U);
+  EXPECT_EQ(links[0]->url, url);
+  EXPECT_EQ(links[0]->rect.width(), rect.width());
+  EXPECT_EQ(links[0]->rect.height(), rect.height());
+  EXPECT_EQ(links[0]->rect.x(), rect.x() + 30);
+  EXPECT_EQ(links[0]->rect.y(), rect.y() + 100);
+
+  tracker.Rotate(30);
+  links.clear();
+  tracker.AnnotateLink(url, rect);
+  tracker.MoveLinks(&links);
+  ASSERT_EQ(links.size(), 1U);
+  EXPECT_EQ(links[0]->url, url);
+  EXPECT_EQ(links[0]->rect.width(), 45);
+  EXPECT_EQ(links[0]->rect.height(), 49);
+  EXPECT_EQ(links[0]->rect.x(), 8);
+  EXPECT_EQ(links[0]->rect.y(), 122);
+
+  // no-op (ensure this doesn't crash).
+  tracker.Restore();
+}
+
 }  // namespace paint_preview
diff --git a/components/paint_preview/renderer/paint_preview_recorder_browsertest.cc b/components/paint_preview/renderer/paint_preview_recorder_browsertest.cc
index 3cd4ab3b..cdad89e 100644
--- a/components/paint_preview/renderer/paint_preview_recorder_browsertest.cc
+++ b/components/paint_preview/renderer/paint_preview_recorder_browsertest.cc
@@ -9,6 +9,7 @@
 #include "base/files/scoped_temp_dir.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/threading/thread_restrictions.h"
+#include "build/build_config.h"
 #include "components/paint_preview/common/file_stream.h"
 #include "components/paint_preview/common/mojom/paint_preview_recorder.mojom.h"
 #include "content/public/renderer/render_frame.h"
@@ -60,6 +61,31 @@
     return temp_dir_.GetPath().AppendASCII(filename);
   }
 
+  base::FilePath RunCapture(content::RenderFrame* frame,
+                            mojom::PaintPreviewCaptureResponsePtr* out_response,
+                            bool is_main_frame = true) {
+    base::FilePath skp_path = MakeTestFilePath("test.skp");
+
+    mojom::PaintPreviewCaptureParamsPtr params =
+        mojom::PaintPreviewCaptureParams::New();
+    auto token = base::UnguessableToken::Create();
+    params->guid = token;
+    params->clip_rect = gfx::Rect();
+    params->is_main_frame = is_main_frame;
+    params->capture_links = true;
+    base::File skp_file(
+        skp_path, base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
+    params->file = std::move(skp_file);
+
+    PaintPreviewRecorderImpl paint_preview_recorder(frame);
+    paint_preview_recorder.CapturePaintPreview(
+        std::move(params),
+        base::BindOnce(&OnCaptureFinished, mojom::PaintPreviewStatus::kOk,
+                       out_response));
+    content::RunAllTasksUntilIdle();
+    return skp_path;
+  }
+
  private:
   base::ScopedTempDir temp_dir_;
   base::test::ScopedFeatureList feature_list_;
@@ -67,6 +93,7 @@
 
 TEST_F(PaintPreviewRecorderRenderViewTest, TestCaptureMainFrameAndClipping) {
   LoadHTML(
+      "<!doctype html>"
       "<body>"
       "  <div style='width: 600px; height: 80vh; "
       "              background-color: #ff0000'>&nbsp;</div>"
@@ -80,28 +107,9 @@
       "  </div>"
       "</body>");
 
-  base::FilePath skp_path = MakeTestFilePath("test.skp");
-
-  mojom::PaintPreviewCaptureParamsPtr params =
-      mojom::PaintPreviewCaptureParams::New();
-  auto token = base::UnguessableToken::Create();
-  params->guid = token;
-  params->clip_rect = gfx::Rect();
-  params->is_main_frame = true;
-  params->capture_links = true;
-  params->max_capture_size = 0;
-  base::File skp_file(skp_path,
-                      base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
-  params->file = std::move(skp_file);
-
   auto out_response = mojom::PaintPreviewCaptureResponse::New();
   content::RenderFrame* frame = GetFrame();
-  PaintPreviewRecorderImpl paint_preview_recorder(frame);
-  paint_preview_recorder.CapturePaintPreview(
-      std::move(params),
-      base::BindOnce(&OnCaptureFinished, mojom::PaintPreviewStatus::kOk,
-                     base::Unretained(&out_response)));
-  content::RunAllTasksUntilIdle();
+  base::FilePath skp_path = RunCapture(frame, &out_response);
 
   EXPECT_TRUE(out_response->embedding_token.has_value());
   EXPECT_EQ(frame->GetWebFrame()->GetEmbeddingToken(),
@@ -150,6 +158,7 @@
 
 TEST_F(PaintPreviewRecorderRenderViewTest, TestCaptureMainFrameWithScroll) {
   LoadHTML(
+      "<!doctype html>"
       "<body>"
       "  <div style='width: 600px; height: 80vh; "
       "              background-color: #ff0000'>&nbsp;</div>"
@@ -160,28 +169,10 @@
   // Scroll to bottom of page to ensure scroll position has no effect on
   // capture.
   ExecuteJavaScriptForTests("window.scrollTo(0,document.body.scrollHeight);");
-  base::FilePath skp_path = MakeTestFilePath("test.skp");
-
-  mojom::PaintPreviewCaptureParamsPtr params =
-      mojom::PaintPreviewCaptureParams::New();
-  auto token = base::UnguessableToken::Create();
-  params->guid = token;
-  params->clip_rect = gfx::Rect();
-  params->is_main_frame = true;
-  params->capture_links = true;
-  params->max_capture_size = 0;
-  base::File skp_file(skp_path,
-                      base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
-  params->file = std::move(skp_file);
 
   auto out_response = mojom::PaintPreviewCaptureResponse::New();
   content::RenderFrame* frame = GetFrame();
-  PaintPreviewRecorderImpl paint_preview_recorder(frame);
-  paint_preview_recorder.CapturePaintPreview(
-      std::move(params),
-      base::BindOnce(&OnCaptureFinished, mojom::PaintPreviewStatus::kOk,
-                     base::Unretained(&out_response)));
-  content::RunAllTasksUntilIdle();
+  base::FilePath skp_path = RunCapture(frame, &out_response);
 
   EXPECT_TRUE(out_response->embedding_token.has_value());
   EXPECT_EQ(frame->GetWebFrame()->GetEmbeddingToken(),
@@ -215,33 +206,16 @@
   // Use position absolute position to check that the captured link dimensions
   // match what is specified.
   LoadHTML(
+      "<!doctype html>"
       "<body style='min-height:1000px;'>"
       "  <a style='position: absolute; left: -15px; top: 0px; width: 40px; "
       "   height: 30px;' href='#fragment'>Foo</a>"
       "  <h1 id='fragment'>I'm a fragment</h1>"
       "</body>");
-  base::FilePath skp_path = MakeTestFilePath("test.skp");
-
-  mojom::PaintPreviewCaptureParamsPtr params =
-      mojom::PaintPreviewCaptureParams::New();
-  auto token = base::UnguessableToken::Create();
-  params->guid = token;
-  params->clip_rect = gfx::Rect();
-  params->is_main_frame = true;
-  params->capture_links = true;
-  params->max_capture_size = 0;
-  base::File skp_file(skp_path,
-                      base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
-  params->file = std::move(skp_file);
-
   auto out_response = mojom::PaintPreviewCaptureResponse::New();
   content::RenderFrame* frame = GetFrame();
-  PaintPreviewRecorderImpl paint_preview_recorder(frame);
-  paint_preview_recorder.CapturePaintPreview(
-      std::move(params),
-      base::BindOnce(&OnCaptureFinished, mojom::PaintPreviewStatus::kOk,
-                     &out_response));
-  content::RunAllTasksUntilIdle();
+
+  RunCapture(frame, &out_response);
 
   EXPECT_TRUE(out_response->embedding_token.has_value());
   EXPECT_EQ(frame->GetWebFrame()->GetEmbeddingToken(),
@@ -281,33 +255,16 @@
 
 TEST_F(PaintPreviewRecorderRenderViewTest, TestCaptureMainFrameAndLocalFrame) {
   LoadHTML(
+      "<!doctype html>"
       "<body style='min-height:1000px;'>"
       "  <iframe style='width: 500px, height: 500px'"
       "          srcdoc=\"<div style='width: 100px; height: 100px;"
       "          background-color: #000000'>&nbsp;</div>\"></iframe>"
       "</body>");
-  base::FilePath skp_path = MakeTestFilePath("test.skp");
-
-  mojom::PaintPreviewCaptureParamsPtr params =
-      mojom::PaintPreviewCaptureParams::New();
-  auto token = base::UnguessableToken::Create();
-  params->guid = token;
-  params->clip_rect = gfx::Rect();
-  params->is_main_frame = true;
-  params->capture_links = true;
-  params->max_capture_size = 0;
-  base::File skp_file(skp_path,
-                      base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
-  params->file = std::move(skp_file);
-
   auto out_response = mojom::PaintPreviewCaptureResponse::New();
   content::RenderFrame* frame = GetFrame();
-  PaintPreviewRecorderImpl paint_preview_recorder(frame);
-  paint_preview_recorder.CapturePaintPreview(
-      std::move(params),
-      base::BindOnce(&OnCaptureFinished, mojom::PaintPreviewStatus::kOk,
-                     base::Unretained(&out_response)));
-  content::RunAllTasksUntilIdle();
+
+  RunCapture(frame, &out_response);
 
   EXPECT_TRUE(out_response->embedding_token.has_value());
   EXPECT_EQ(frame->GetWebFrame()->GetEmbeddingToken(),
@@ -317,38 +274,242 @@
 
 TEST_F(PaintPreviewRecorderRenderViewTest, TestCaptureLocalFrame) {
   LoadHTML(
+      "<!doctype html>"
       "<body style='min-height:1000px;'>"
       "  <iframe style='width: 500px, height: 500px'"
       "          srcdoc=\"<div style='width: 100px; height: 100px;"
       "          background-color: #000000'>&nbsp;</div>\"></iframe>"
       "</body>");
-  base::FilePath skp_path = MakeTestFilePath("test.skp");
-
-  mojom::PaintPreviewCaptureParamsPtr params =
-      mojom::PaintPreviewCaptureParams::New();
-  auto token = base::UnguessableToken::Create();
-  params->guid = token;
-  params->clip_rect = gfx::Rect();
-  params->is_main_frame = false;
-  params->capture_links = true;
-  params->max_capture_size = 0;
-  base::File skp_file(skp_path,
-                      base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
-  params->file = std::move(skp_file);
-
   auto out_response = mojom::PaintPreviewCaptureResponse::New();
   auto* child_frame = content::RenderFrame::FromWebFrame(
       GetFrame()->GetWebFrame()->FirstChild()->ToWebLocalFrame());
   ASSERT_TRUE(child_frame);
-  PaintPreviewRecorderImpl paint_preview_recorder(child_frame);
-  paint_preview_recorder.CapturePaintPreview(
-      std::move(params),
-      base::BindOnce(&OnCaptureFinished, mojom::PaintPreviewStatus::kOk,
-                     base::Unretained(&out_response)));
-  content::RunAllTasksUntilIdle();
+
+  RunCapture(child_frame, &out_response, false);
 
   EXPECT_TRUE(out_response->embedding_token.has_value());
   EXPECT_EQ(out_response->content_id_to_embedding_token.size(), 0U);
 }
 
+TEST_F(PaintPreviewRecorderRenderViewTest, CaptureWithTranslate) {
+  // URLs should be annotated correctly when a CSS transform is applied.
+  LoadHTML(
+      R"(
+      <!doctype html>
+      <body>
+      <div style="display: inline-block;
+                  padding: 16px;
+                  font-size: 16px;">
+        <div style="padding: 16px;
+                    transform: translate(10px, 20px);
+                    margin-bottom: 30px;">
+          <div>
+            <a href="http://www.example.com" style="display: block;
+                                                    width: 70px;
+                                                    height: 20px;">
+              <div>Example</div>
+            </a>
+          </div>
+        </div>
+      </div>
+    </body>)");
+  auto out_response = mojom::PaintPreviewCaptureResponse::New();
+  content::RenderFrame* frame = GetFrame();
+
+  RunCapture(frame, &out_response);
+
+  EXPECT_TRUE(out_response->embedding_token.has_value());
+  EXPECT_EQ(frame->GetWebFrame()->GetEmbeddingToken(),
+            out_response->embedding_token.value());
+  EXPECT_EQ(out_response->content_id_to_embedding_token.size(), 0U);
+
+  ASSERT_EQ(out_response->links.size(), 1U);
+  EXPECT_EQ(out_response->links[0]->url, GURL("http://www.example.com"));
+  EXPECT_NEAR(out_response->links[0]->rect.x(), 50, 3);
+  EXPECT_NEAR(out_response->links[0]->rect.y(), 60, 3);
+  EXPECT_NEAR(out_response->links[0]->rect.width(), 70, 3);
+  EXPECT_NEAR(out_response->links[0]->rect.height(), 20, 3);
+}
+
+TEST_F(PaintPreviewRecorderRenderViewTest, CaptureWithTranslateThenRotate) {
+  // URLs should be annotated correctly when a CSS transform is applied.
+  LoadHTML(
+      R"(
+      <!doctype html>
+      <body>
+      <div style="display: inline-block;
+                  padding: 16px;
+                  font-size: 16px;">
+        <div style="padding: 16px;
+                    transform: translate(100px, 0) rotate(45deg);
+                    margin-bottom: 30px;">
+          <div>
+            <a href="http://www.example.com" style="display: block;
+                                                    width: 70px;
+                                                    height: 20px;">
+              <div>Example</div>
+            </a>
+          </div>
+        </div>
+      </div>
+    </body>)");
+  auto out_response = mojom::PaintPreviewCaptureResponse::New();
+  content::RenderFrame* frame = GetFrame();
+
+  RunCapture(frame, &out_response);
+
+  EXPECT_TRUE(out_response->embedding_token.has_value());
+  EXPECT_EQ(frame->GetWebFrame()->GetEmbeddingToken(),
+            out_response->embedding_token.value());
+  EXPECT_EQ(out_response->content_id_to_embedding_token.size(), 0U);
+
+  ASSERT_EQ(out_response->links.size(), 1U);
+  EXPECT_EQ(out_response->links[0]->url, GURL("http://www.example.com"));
+  EXPECT_NEAR(out_response->links[0]->rect.x(), 141, 5);
+  EXPECT_NEAR(out_response->links[0]->rect.y(), 18, 5);
+#if !defined(OS_ANDROID)
+  EXPECT_NEAR(out_response->links[0]->rect.width(), 58, 10);
+  EXPECT_NEAR(out_response->links[0]->rect.height(), 58, 10);
+#endif
+}
+
+TEST_F(PaintPreviewRecorderRenderViewTest, CaptureWithRotateThenTranslate) {
+  // URLs should be annotated correctly when a CSS transform is applied.
+  LoadHTML(
+      R"(
+      <!doctype html>
+      <body>
+      <div style="display: inline-block;
+                  padding: 16px;
+                  font-size: 16px;">
+        <div style="padding: 16px;
+                    transform: rotate(45deg) translate(100px, 0);
+                    margin-bottom: 30px;">
+          <div>
+            <a href="http://www.example.com" style="display: block;
+                                                    width: 70px;
+                                                    height: 20px;">
+              <div>Example</div>
+            </a>
+          </div>
+        </div>
+      </div>
+    </body>)");
+  auto out_response = mojom::PaintPreviewCaptureResponse::New();
+  content::RenderFrame* frame = GetFrame();
+
+  RunCapture(frame, &out_response);
+
+  EXPECT_TRUE(out_response->embedding_token.has_value());
+  EXPECT_EQ(frame->GetWebFrame()->GetEmbeddingToken(),
+            out_response->embedding_token.value());
+  EXPECT_EQ(out_response->content_id_to_embedding_token.size(), 0U);
+
+  ASSERT_EQ(out_response->links.size(), 1U);
+  EXPECT_EQ(out_response->links[0]->url, GURL("http://www.example.com"));
+  EXPECT_NEAR(out_response->links[0]->rect.x(), 111, 5);
+  EXPECT_NEAR(out_response->links[0]->rect.y(), 88, 5);
+#if !defined(OS_ANDROID)
+  EXPECT_NEAR(out_response->links[0]->rect.width(), 58, 10);
+  EXPECT_NEAR(out_response->links[0]->rect.height(), 58, 10);
+#endif
+}
+
+TEST_F(PaintPreviewRecorderRenderViewTest, CaptureWithScale) {
+  // URLs should be annotated correctly when a CSS transform is applied.
+  LoadHTML(
+      R"(
+      <!doctype html>
+      <body>
+      <div style="display: inline-block;
+                  padding: 16px;
+                  font-size: 16px;">
+        <div style="padding: 16px;
+                    transform: scale(2, 1);
+                    margin-bottom: 30px;">
+          <div>
+            <a href="http://www.example.com" style="display: block;
+                                                    width: 70px;
+                                                    height: 20px;">
+              <div>Example</div>
+            </a>
+          </div>
+        </div>
+      </div>
+    </body>)");
+  auto out_response = mojom::PaintPreviewCaptureResponse::New();
+  content::RenderFrame* frame = GetFrame();
+
+  RunCapture(frame, &out_response);
+
+  EXPECT_TRUE(out_response->embedding_token.has_value());
+  EXPECT_EQ(frame->GetWebFrame()->GetEmbeddingToken(),
+            out_response->embedding_token.value());
+  EXPECT_EQ(out_response->content_id_to_embedding_token.size(), 0U);
+
+  ASSERT_EQ(out_response->links.size(), 1U);
+  EXPECT_EQ(out_response->links[0]->url, GURL("http://www.example.com"));
+  EXPECT_NEAR(out_response->links[0]->rect.x(), 5, 3);
+  EXPECT_NEAR(out_response->links[0]->rect.y(), 40, 3);
+  EXPECT_NEAR(out_response->links[0]->rect.width(), 140, 3);
+  EXPECT_NEAR(out_response->links[0]->rect.height(), 20, 3);
+}
+
+TEST_F(PaintPreviewRecorderRenderViewTest, CaptureSaveRestore) {
+  // URLs should be annotated correctly when a CSS transform is applied.
+  LoadHTML(
+      R"(
+      <!doctype html>
+      <body>
+      <div style="display: inline-block;
+                  padding: 16px;
+                  font-size: 16px;">
+        <div style="padding: 16px;
+                    transform: translate(20px, 0);
+                    margin-bottom: 30px;">
+          <div>
+            <a href="http://www.example.com" style="display: block;
+                                                    width: 70px;
+                                                    height: 20px;">
+              <div>Example</div>
+            </a>
+          </div>
+        </div>
+        <div style="padding: 16px;
+                    transform: none;
+                    margin-bottom: 30px;">
+          <div>
+            <a href="http://www.chromium.org" style="display: block;
+                                                     width: 80px;
+                                                     height: 20px;">
+              <div>Chromium</div>
+            </a>
+          </div>
+        </div>
+      </div>
+    </body>)");
+  auto out_response = mojom::PaintPreviewCaptureResponse::New();
+  content::RenderFrame* frame = GetFrame();
+
+  RunCapture(frame, &out_response);
+
+  EXPECT_TRUE(out_response->embedding_token.has_value());
+  EXPECT_EQ(frame->GetWebFrame()->GetEmbeddingToken(),
+            out_response->embedding_token.value());
+  EXPECT_EQ(out_response->content_id_to_embedding_token.size(), 0U);
+
+  ASSERT_EQ(out_response->links.size(), 2U);
+  EXPECT_EQ(out_response->links[0]->url, GURL("http://www.chromium.org"));
+  EXPECT_NEAR(out_response->links[0]->rect.x(), 40, 3);
+  EXPECT_NEAR(out_response->links[0]->rect.y(), 122, 3);
+  EXPECT_NEAR(out_response->links[0]->rect.width(), 80, 3);
+  EXPECT_NEAR(out_response->links[0]->rect.height(), 20, 3);
+
+  EXPECT_EQ(out_response->links[1]->url, GURL("http://www.example.com"));
+  EXPECT_NEAR(out_response->links[1]->rect.x(), 60, 3);
+  EXPECT_NEAR(out_response->links[1]->rect.y(), 40, 3);
+  EXPECT_NEAR(out_response->links[1]->rect.width(), 70, 3);
+  EXPECT_NEAR(out_response->links[1]->rect.height(), 20, 3);
+}
+
 }  // namespace paint_preview
diff --git a/components/paint_preview/renderer/paint_preview_recorder_impl.cc b/components/paint_preview/renderer/paint_preview_recorder_impl.cc
index 1a08834..1de00be 100644
--- a/components/paint_preview/renderer/paint_preview_recorder_impl.cc
+++ b/components/paint_preview/renderer/paint_preview_recorder_impl.cc
@@ -57,9 +57,9 @@
     return out;
   }
 
-  TRACE_EVENT_BEGIN0("paint_preview", "ParseGlyphs");
-  ParseGlyphs(recording.get(), tracker.get());
-  TRACE_EVENT_END0("paint_preview", "ParseGlyphs");
+  TRACE_EVENT_BEGIN0("paint_preview", "ParseGlyphsAndLinks");
+  ParseGlyphsAndLinks(recording.get(), tracker.get());
+  TRACE_EVENT_END0("paint_preview", "ParseGlyphsAndLinks");
   size_t serialized_size = 0;
 
   bool success = false;
diff --git a/components/paint_preview/renderer/paint_preview_recorder_utils.cc b/components/paint_preview/renderer/paint_preview_recorder_utils.cc
index 28eda0b..d15b478 100644
--- a/components/paint_preview/renderer/paint_preview_recorder_utils.cc
+++ b/components/paint_preview/renderer/paint_preview_recorder_utils.cc
@@ -12,20 +12,80 @@
 #include "components/paint_preview/common/file_stream.h"
 #include "components/paint_preview/common/paint_preview_tracker.h"
 #include "mojo/public/cpp/base/shared_memory_utils.h"
+#include "third_party/skia/include/core/SkData.h"
+#include "third_party/skia/include/core/SkRefCnt.h"
 
 namespace paint_preview {
 
-void ParseGlyphs(const cc::PaintOpBuffer* buffer,
-                 PaintPreviewTracker* tracker) {
+void ParseGlyphsAndLinks(const cc::PaintOpBuffer* buffer,
+                         PaintPreviewTracker* tracker) {
   for (cc::PaintOpBuffer::Iterator it(buffer); it; ++it) {
-    if (it->GetType() == cc::PaintOpType::DrawTextBlob) {
-      auto* text_blob_op = static_cast<cc::DrawTextBlobOp*>(*it);
-      tracker->AddGlyphs(text_blob_op->blob.get());
-    } else if (it->GetType() == cc::PaintOpType::DrawRecord) {
-      // Recurse into nested records if they contain text blobs (equivalent to
-      // nested SkPictures).
-      auto* record_op = static_cast<cc::DrawRecordOp*>(*it);
-      ParseGlyphs(record_op->record.get(), tracker);
+    switch (it->GetType()) {
+      case cc::PaintOpType::DrawTextBlob: {
+        auto* text_blob_op = static_cast<cc::DrawTextBlobOp*>(*it);
+        tracker->AddGlyphs(text_blob_op->blob.get());
+        break;
+      }
+      case cc::PaintOpType::DrawRecord: {
+        // Recurse into nested records if they contain text blobs (equivalent to
+        // nested SkPictures).
+        auto* record_op = static_cast<cc::DrawRecordOp*>(*it);
+        ParseGlyphsAndLinks(record_op->record.get(), tracker);
+        break;
+      }
+      case cc::PaintOpType::Annotate: {
+        auto* annotate_op = static_cast<cc::AnnotateOp*>(*it);
+        tracker->AnnotateLink(GURL(std::string(reinterpret_cast<const char*>(
+                                                   annotate_op->data->data()),
+                                               annotate_op->data->size())),
+                              annotate_op->rect);
+        // Delete the data. We no longer need it.
+        annotate_op->data.reset();
+        return;
+      }
+      case cc::PaintOpType::Save: {
+        tracker->Save();
+        break;
+      }
+      case cc::PaintOpType::SaveLayer: {
+        tracker->Save();
+        break;
+      }
+      case cc::PaintOpType::SaveLayerAlpha: {
+        tracker->Save();
+        break;
+      }
+      case cc::PaintOpType::Restore: {
+        tracker->Restore();
+        break;
+      }
+      case cc::PaintOpType::SetMatrix: {
+        auto* matrix_op = static_cast<cc::SetMatrixOp*>(*it);
+        tracker->SetMatrix(matrix_op->matrix);
+        break;
+      }
+      case cc::PaintOpType::Concat: {
+        auto* concat_op = static_cast<cc::ConcatOp*>(*it);
+        tracker->Concat(concat_op->matrix);
+        break;
+      }
+      case cc::PaintOpType::Scale: {
+        auto* scale_op = static_cast<cc::ScaleOp*>(*it);
+        tracker->Scale(scale_op->sx, scale_op->sy);
+        break;
+      }
+      case cc::PaintOpType::Rotate: {
+        auto* rotate_op = static_cast<cc::RotateOp*>(*it);
+        tracker->Rotate(rotate_op->degrees);
+        break;
+      }
+      case cc::PaintOpType::Translate: {
+        auto* translate_op = static_cast<cc::TranslateOp*>(*it);
+        tracker->Translate(translate_op->dx, translate_op->dy);
+        break;
+      }
+      default:
+        continue;
     }
   }
 }
diff --git a/components/paint_preview/renderer/paint_preview_recorder_utils.h b/components/paint_preview/renderer/paint_preview_recorder_utils.h
index 69acb1f..89ca935 100644
--- a/components/paint_preview/renderer/paint_preview_recorder_utils.h
+++ b/components/paint_preview/renderer/paint_preview_recorder_utils.h
@@ -21,9 +21,10 @@
 
 class PaintPreviewTracker;
 
-// Walks |buffer| to extract all the glyphs from its text blobs and writes
-// them to |tracker|.
-void ParseGlyphs(const cc::PaintOpBuffer* buffer, PaintPreviewTracker* tracker);
+// Walks |buffer| to extract all the glyphs from its text blobs and links. The
+// extracted data is written to to |tracker|.
+void ParseGlyphsAndLinks(const cc::PaintOpBuffer* buffer,
+                         PaintPreviewTracker* tracker);
 
 // Serializes |record| to |out_stream| as an SkPicture of size |dimensions|.
 // |tracker| supplies metadata required during serialization.
diff --git a/components/paint_preview/renderer/paint_preview_recorder_utils_unittest.cc b/components/paint_preview/renderer/paint_preview_recorder_utils_unittest.cc
index 68858f5..f67984c 100644
--- a/components/paint_preview/renderer/paint_preview_recorder_utils_unittest.cc
+++ b/components/paint_preview/renderer/paint_preview_recorder_utils_unittest.cc
@@ -33,6 +33,19 @@
 
 namespace paint_preview {
 
+namespace {
+
+sk_sp<cc::PaintRecord> AddLink(const std::string& link, const SkRect& rect) {
+  cc::PaintRecorder link_recorder;
+  cc::PaintCanvas* link_canvas = link_recorder.beginRecording(
+      rect.x() + rect.width(), rect.y() + rect.height());
+  link_canvas->Annotate(cc::PaintCanvas::AnnotationType::URL, rect,
+                        SkData::MakeWithCString(link.c_str()));
+  return link_recorder.finishRecordingAsPicture();
+}
+
+}  // namespace
+
 TEST(PaintPreviewRecorderUtilsTest, TestParseGlyphs) {
   auto typeface = SkTypeface::MakeDefault();
   SkFont font(typeface);
@@ -53,7 +66,7 @@
 
   PaintPreviewTracker tracker(base::UnguessableToken::Create(),
                               base::UnguessableToken::Create(), true);
-  ParseGlyphs(record.get(), &tracker);
+  ParseGlyphsAndLinks(record.get(), &tracker);
   auto* usage_map = tracker.GetTypefaceUsageMap();
   EXPECT_TRUE(usage_map->count(typeface->uniqueID()));
   EXPECT_TRUE(
@@ -70,6 +83,80 @@
       (*usage_map)[typeface->uniqueID()]->IsSet(typeface->unicharToGlyph('g')));
 }
 
+TEST(PaintPreviewRecorderUtilsTest, TestParseLinks) {
+  cc::PaintFlags flags;
+  cc::PaintRecorder outer_recorder;
+  cc::PaintCanvas* outer_canvas = outer_recorder.beginRecording(500, 500);
+
+  outer_canvas->save();
+  outer_canvas->translate(10, 20);
+  std::string link_1 = "http://www.foo.com/";
+  SkRect rect_1 = SkRect::MakeXYWH(10, 20, 30, 40);
+  outer_canvas->drawPicture(AddLink(link_1, rect_1));
+  outer_canvas->restore();
+
+  outer_canvas->save();
+  outer_canvas->concat(SkMatrix::Translate(40, 50));
+  outer_canvas->scale(2, 4);
+  std::string link_2 = "http://www.bar.com/";
+  SkRect rect_2 = SkRect::MakeXYWH(1, 2, 3, 4);
+  outer_canvas->drawPicture(AddLink(link_2, rect_2));
+
+  cc::PaintRecorder inner_recorder;
+  cc::PaintCanvas* inner_canvas = inner_recorder.beginRecording(500, 500);
+  inner_canvas->rotate(20);
+  std::string link_3 = "http://www.baz.com/";
+  SkRect rect_3 = SkRect::MakeXYWH(5, 7, 9, 13);
+  inner_canvas->drawPicture(AddLink(link_3, rect_3));
+
+  outer_canvas->drawPicture(inner_recorder.finishRecordingAsPicture());
+  outer_canvas->restore();
+
+  outer_canvas->save();
+  outer_canvas->translate(20, 50);
+  outer_canvas->setMatrix(SkMatrix::Translate(10, 30));
+  std::string link_4 = "http://www.example.com/";
+  SkRect rect_4 = SkRect::MakeXYWH(10, 30, 40, 50);
+  outer_canvas->drawPicture(AddLink(link_4, rect_4));
+  outer_canvas->restore();
+
+  outer_canvas->saveLayer(&rect_1, nullptr);
+  outer_canvas->saveLayerAlpha(&rect_1, 8);
+  outer_canvas->restoreToCount(1);
+  auto record = outer_recorder.finishRecordingAsPicture();
+
+  PaintPreviewTracker tracker(base::UnguessableToken::Create(),
+                              base::UnguessableToken::Create(), true);
+  ParseGlyphsAndLinks(record.get(), &tracker);
+
+  std::vector<mojom::LinkDataPtr> links;
+  tracker.MoveLinks(&links);
+  ASSERT_EQ(links.size(), 4U);
+  EXPECT_EQ(links[0]->url, link_1);
+  EXPECT_EQ(links[0]->rect.x(), rect_1.x() + 10);
+  EXPECT_EQ(links[0]->rect.y(), rect_1.y() + 20);
+  EXPECT_EQ(links[0]->rect.width(), rect_1.width());
+  EXPECT_EQ(links[0]->rect.height(), rect_1.height());
+
+  EXPECT_EQ(links[1]->url, link_2);
+  EXPECT_EQ(links[1]->rect.x(), rect_2.x() * 2 + 40);
+  EXPECT_EQ(links[1]->rect.y(), rect_2.y() * 4 + 50);
+  EXPECT_EQ(links[1]->rect.width(), rect_2.width() * 2);
+  EXPECT_EQ(links[1]->rect.height(), rect_2.height() * 4);
+
+  EXPECT_EQ(links[2]->url, link_3);
+  EXPECT_EQ(links[2]->rect.x(), 35);
+  EXPECT_EQ(links[2]->rect.y(), 83);
+  EXPECT_EQ(links[2]->rect.width(), 25);
+  EXPECT_EQ(links[2]->rect.height(), 61);
+
+  EXPECT_EQ(links[3]->url, link_4);
+  EXPECT_EQ(links[3]->rect.x(), rect_4.x() + 10);
+  EXPECT_EQ(links[3]->rect.y(), rect_4.y() + 30);
+  EXPECT_EQ(links[3]->rect.width(), rect_4.width());
+  EXPECT_EQ(links[3]->rect.height(), rect_4.height());
+}
+
 class PaintPreviewRecorderUtilsSerializeAsSkPictureTest
     : public testing::TestWithParam<mojom::Persistence> {
  public:
@@ -191,8 +278,9 @@
   auto token = base::UnguessableToken::Create();
   auto embedding_token = base::UnguessableToken::Create();
   PaintPreviewTracker tracker(token, embedding_token, true);
-  tracker.AnnotateLink(GURL("www.google.com"), gfx::Rect(1, 2, 3, 4));
-  tracker.AnnotateLink(GURL("www.chromium.org"), gfx::Rect(10, 20, 10, 20));
+  tracker.AnnotateLink(GURL("www.google.com"), SkRect::MakeXYWH(1, 2, 3, 4));
+  tracker.AnnotateLink(GURL("www.chromium.org"),
+                       SkRect::MakeXYWH(10, 20, 10, 20));
   tracker.CreateContentForRemoteFrame(gfx::Rect(1, 1, 1, 1),
                                       base::UnguessableToken::Create());
   tracker.CreateContentForRemoteFrame(gfx::Rect(1, 2, 4, 8),
diff --git a/components/payments/content/android/java/src/org/chromium/components/payments/PaymentFeatureList.java b/components/payments/content/android/java/src/org/chromium/components/payments/PaymentFeatureList.java
index 47070c97..79b22ff 100644
--- a/components/payments/content/android/java/src/org/chromium/components/payments/PaymentFeatureList.java
+++ b/components/payments/content/android/java/src/org/chromium/components/payments/PaymentFeatureList.java
@@ -28,6 +28,7 @@
     public static final String WEB_PAYMENTS = "WebPayments";
     public static final String WEB_PAYMENTS_ALWAYS_ALLOW_JUST_IN_TIME_PAYMENT_APP =
             "AlwaysAllowJustInTimePaymentApp";
+    public static final String WEB_PAYMENTS_APP_STORE_BILLING = "AppStoreBilling";
     public static final String WEB_PAYMENTS_APP_STORE_BILLING_DEBUG = "AppStoreBillingDebug";
     public static final String WEB_PAYMENTS_EXPERIMENTAL_FEATURES =
             "WebPaymentsExperimentalFeatures";
diff --git a/components/payments/content/android/payment_feature_list.cc b/components/payments/content/android/payment_feature_list.cc
index 03fe29b..3a105c0 100644
--- a/components/payments/content/android/payment_feature_list.cc
+++ b/components/payments/content/android/payment_feature_list.cc
@@ -24,6 +24,7 @@
     &::features::kWebPayments,
     &::features::kWebPaymentsMinimalUI,
     &features::kAlwaysAllowJustInTimePaymentApp,
+    &features::kAppStoreBilling,
     &features::kAppStoreBillingDebug,
     &features::kEnforceFullDelegation,
     &features::kPaymentRequestSkipToGPay,
diff --git a/components/payments/core/features.cc b/components/payments/core/features.cc
index 341ecf7..b129c3e 100644
--- a/components/payments/core/features.cc
+++ b/components/payments/core/features.cc
@@ -4,6 +4,8 @@
 
 #include "components/payments/core/features.h"
 
+#include "build/build_config.h"
+
 namespace payments {
 namespace features {
 
@@ -44,6 +46,15 @@
 const base::Feature kWebPaymentsRedactShippingAddress{
     "WebPaymentsRedactShippingAddress", base::FEATURE_ENABLED_BY_DEFAULT};
 
+const base::Feature kAppStoreBilling {
+  "AppStoreBilling",
+#if defined(OS_ANDROID)
+      base::FEATURE_ENABLED_BY_DEFAULT
+#else
+      base::FEATURE_DISABLED_BY_DEFAULT
+#endif  // OS_ANDROID
+};
+
 const base::Feature kAppStoreBillingDebug{"AppStoreBillingDebug",
                                           base::FEATURE_DISABLED_BY_DEFAULT};
 
diff --git a/components/payments/core/features.h b/components/payments/core/features.h
index 2a9e3c1b..60b86bf 100644
--- a/components/payments/core/features.h
+++ b/components/payments/core/features.h
@@ -35,6 +35,10 @@
 // with a single URL based payment app and no other info requested.
 extern const base::Feature kWebPaymentsSingleAppUiSkip;
 
+// Used to control whether the invoking TWA can handle payments for app store
+// payment method identifiers.
+extern const base::Feature kAppStoreBilling;
+
 // Used to control whether to remove the restriction that TWA has to be
 // installed from specific app stores.
 extern const base::Feature kAppStoreBillingDebug;
diff --git a/components/performance_manager/BUILD.gn b/components/performance_manager/BUILD.gn
index 83ee545..41693801 100644
--- a/components/performance_manager/BUILD.gn
+++ b/components/performance_manager/BUILD.gn
@@ -155,10 +155,8 @@
   if (!is_android) {
     sources += [
       "decorators/site_data_recorder.cc",
-      "decorators/site_data_recorder.h",
       "persistence/site_data/exponential_moving_average.cc",
       "persistence/site_data/exponential_moving_average.h",
-      "persistence/site_data/feature_usage.h",
       "persistence/site_data/leveldb_site_data_store.cc",
       "persistence/site_data/leveldb_site_data_store.h",
       "persistence/site_data/non_recording_site_data_cache.cc",
@@ -174,11 +172,13 @@
       "persistence/site_data/site_data_impl.cc",
       "persistence/site_data/site_data_impl.h",
       "persistence/site_data/site_data_reader.cc",
-      "persistence/site_data/site_data_reader.h",
       "persistence/site_data/site_data_store.h",
       "persistence/site_data/site_data_writer.cc",
       "persistence/site_data/site_data_writer.h",
       "persistence/site_data/tab_visibility.h",
+      "public/decorators/site_data_recorder.h",
+      "public/persistence/site_data/feature_usage.h",
+      "public/persistence/site_data/site_data_reader.h",
     ]
 
     public_deps += [
diff --git a/components/performance_manager/decorators/site_data_recorder.cc b/components/performance_manager/decorators/site_data_recorder.cc
index 6150f1a..07c67086 100644
--- a/components/performance_manager/decorators/site_data_recorder.cc
+++ b/components/performance_manager/decorators/site_data_recorder.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "components/performance_manager/decorators/site_data_recorder.h"
+#include "components/performance_manager/public/decorators/site_data_recorder.h"
 
 #include "base/time/time.h"
 #include "components/performance_manager/graph/node_attached_data_impl.h"
@@ -10,6 +10,7 @@
 #include "components/performance_manager/persistence/site_data/site_data_cache.h"
 #include "components/performance_manager/persistence/site_data/site_data_cache_factory.h"
 #include "components/performance_manager/persistence/site_data/site_data_writer.h"
+#include "components/performance_manager/public/persistence/site_data/site_data_reader.h"
 
 namespace performance_manager {
 
@@ -74,6 +75,11 @@
     return writer_.get();
   }
 
+  SiteDataReader* reader() const override {
+    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+    return reader_.get();
+  }
+
  private:
   // The features tracked by the SiteDataRecorder class.
   enum class FeatureType {
@@ -110,6 +116,7 @@
   base::TimeTicks loaded_time_;
 
   std::unique_ptr<SiteDataWriter> writer_;
+  std::unique_ptr<SiteDataReader> reader_;
 
   SEQUENCE_CHECKER(sequence_checker_);
 
@@ -131,6 +138,7 @@
     return;
 
   writer_ = data_cache_->GetWriterForOrigin(origin);
+  reader_ = data_cache_->GetReaderForOrigin(origin);
 
   // The writer is assumed to be in an unloaded state by default, set the proper
   // loading state if necessary.
@@ -193,6 +201,7 @@
     loaded_time_ = base::TimeTicks();
   }
   writer_.reset();
+  reader_.reset();
 }
 
 bool SiteDataNodeData::ShouldIgnoreFeatureUsageEvent(FeatureType feature_type) {
@@ -338,6 +347,12 @@
 SiteDataNodeData::Data::~Data() = default;
 
 // static
+const SiteDataRecorder::Data* SiteDataRecorder::Data::FromPageNode(
+    const PageNode* page_node) {
+  return SiteDataNodeData::Get(PageNodeImpl::FromNode(page_node));
+}
+
+// static
 SiteDataRecorder::Data* SiteDataRecorder::Data::GetForTesting(
     const PageNode* page_node) {
   return GetSiteDataNodeDataFromPageNode(page_node);
diff --git a/components/performance_manager/decorators/site_data_recorder_unittest.cc b/components/performance_manager/decorators/site_data_recorder_unittest.cc
index cb6309d1..bc89aea 100644
--- a/components/performance_manager/decorators/site_data_recorder_unittest.cc
+++ b/components/performance_manager/decorators/site_data_recorder_unittest.cc
@@ -2,7 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "components/performance_manager/decorators/site_data_recorder.h"
+#include "components/performance_manager/public/decorators/site_data_recorder.h"
+
 #include <memory>
 
 #include "base/callback_forward.h"
diff --git a/components/performance_manager/performance_manager_lifetime.cc b/components/performance_manager/performance_manager_lifetime.cc
index cb08a59..e402b2b3 100644
--- a/components/performance_manager/performance_manager_lifetime.cc
+++ b/components/performance_manager/performance_manager_lifetime.cc
@@ -5,6 +5,7 @@
 #include "components/performance_manager/embedder/performance_manager_lifetime.h"
 
 #include "base/bind.h"
+#include "build/build_config.h"
 #include "components/performance_manager/decorators/page_load_tracker_decorator.h"
 #include "components/performance_manager/execution_context/execution_context_registry_impl.h"
 #include "components/performance_manager/graph/frame_node_impl_describer.h"
@@ -16,6 +17,10 @@
 #include "components/performance_manager/public/decorators/tab_properties_decorator.h"
 #include "components/performance_manager/public/graph/graph.h"
 
+#if !defined(OS_ANDROID)
+#include "components/performance_manager/public/decorators/site_data_recorder.h"
+#endif
+
 namespace performance_manager {
 
 namespace {
@@ -32,6 +37,9 @@
   graph->PassToGraph(std::make_unique<ProcessNodeImplDescriber>());
   graph->PassToGraph(std::make_unique<TabPropertiesDecorator>());
   graph->PassToGraph(std::make_unique<WorkerNodeImplDescriber>());
+#if !defined(OS_ANDROID)
+  graph->PassToGraph(std::make_unique<SiteDataRecorder>());
+#endif
   std::move(external_graph_created_callback).Run(graph);
 }
 
diff --git a/components/performance_manager/persistence/site_data/non_recording_site_data_cache.cc b/components/performance_manager/persistence/site_data/non_recording_site_data_cache.cc
index 63fa384c..b1e627a 100644
--- a/components/performance_manager/persistence/site_data/non_recording_site_data_cache.cc
+++ b/components/performance_manager/persistence/site_data/non_recording_site_data_cache.cc
@@ -7,8 +7,8 @@
 #include "base/memory/ptr_util.h"
 #include "components/performance_manager/persistence/site_data/noop_site_data_writer.h"
 #include "components/performance_manager/persistence/site_data/site_data_cache_factory.h"
-#include "components/performance_manager/persistence/site_data/site_data_reader.h"
 #include "components/performance_manager/persistence/site_data/site_data_writer.h"
+#include "components/performance_manager/public/persistence/site_data/site_data_reader.h"
 
 namespace performance_manager {
 
diff --git a/components/performance_manager/persistence/site_data/site_data_cache.h b/components/performance_manager/persistence/site_data/site_data_cache.h
index 01753812..61d37fb 100644
--- a/components/performance_manager/persistence/site_data/site_data_cache.h
+++ b/components/performance_manager/persistence/site_data/site_data_cache.h
@@ -8,9 +8,9 @@
 #include <memory>
 
 #include "base/macros.h"
-#include "components/performance_manager/persistence/site_data/site_data_reader.h"
 #include "components/performance_manager/persistence/site_data/site_data_writer.h"
 #include "components/performance_manager/persistence/site_data/tab_visibility.h"
+#include "components/performance_manager/public/persistence/site_data/site_data_reader.h"
 #include "url/origin.h"
 
 namespace performance_manager {
diff --git a/components/performance_manager/persistence/site_data/site_data_cache_impl.cc b/components/performance_manager/persistence/site_data/site_data_cache_impl.cc
index 8fe99d9..369eab9d6 100644
--- a/components/performance_manager/persistence/site_data/site_data_cache_impl.cc
+++ b/components/performance_manager/persistence/site_data/site_data_cache_impl.cc
@@ -12,8 +12,8 @@
 #include "base/stl_util.h"
 #include "components/performance_manager/persistence/site_data/leveldb_site_data_store.h"
 #include "components/performance_manager/persistence/site_data/site_data_cache_factory.h"
-#include "components/performance_manager/persistence/site_data/site_data_reader.h"
 #include "components/performance_manager/persistence/site_data/site_data_writer.h"
+#include "components/performance_manager/public/persistence/site_data/site_data_reader.h"
 
 namespace performance_manager {
 
diff --git a/components/performance_manager/persistence/site_data/site_data_impl.h b/components/performance_manager/persistence/site_data/site_data_impl.h
index eef1eb1..ee090e32 100644
--- a/components/performance_manager/persistence/site_data/site_data_impl.h
+++ b/components/performance_manager/persistence/site_data/site_data_impl.h
@@ -16,10 +16,10 @@
 #include "base/sequence_checker.h"
 #include "base/time/time.h"
 #include "components/performance_manager/persistence/site_data/exponential_moving_average.h"
-#include "components/performance_manager/persistence/site_data/feature_usage.h"
 #include "components/performance_manager/persistence/site_data/site_data.pb.h"
 #include "components/performance_manager/persistence/site_data/site_data_store.h"
 #include "components/performance_manager/persistence/site_data/tab_visibility.h"
+#include "components/performance_manager/public/persistence/site_data/feature_usage.h"
 #include "url/origin.h"
 
 namespace performance_manager {
diff --git a/components/performance_manager/persistence/site_data/site_data_reader.cc b/components/performance_manager/persistence/site_data/site_data_reader.cc
index 197a490..080d6645 100644
--- a/components/performance_manager/persistence/site_data/site_data_reader.cc
+++ b/components/performance_manager/persistence/site_data/site_data_reader.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "components/performance_manager/persistence/site_data/site_data_reader.h"
+#include "components/performance_manager/public/persistence/site_data/site_data_reader.h"
 
 #include <utility>
 
diff --git a/components/performance_manager/persistence/site_data/site_data_reader_unittest.cc b/components/performance_manager/persistence/site_data/site_data_reader_unittest.cc
index 3f43575..f308f179 100644
--- a/components/performance_manager/persistence/site_data/site_data_reader_unittest.cc
+++ b/components/performance_manager/persistence/site_data/site_data_reader_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "components/performance_manager/persistence/site_data/site_data_reader.h"
+#include "components/performance_manager/public/persistence/site_data/site_data_reader.h"
 
 #include <memory>
 #include <utility>
diff --git a/components/performance_manager/persistence/site_data/site_data_writer_unittest.cc b/components/performance_manager/persistence/site_data/site_data_writer_unittest.cc
index 8f0fb427..272089fd 100644
--- a/components/performance_manager/persistence/site_data/site_data_writer_unittest.cc
+++ b/components/performance_manager/persistence/site_data/site_data_writer_unittest.cc
@@ -7,9 +7,9 @@
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
 #include "base/time/time.h"
-#include "components/performance_manager/persistence/site_data/feature_usage.h"
 #include "components/performance_manager/persistence/site_data/site_data_impl.h"
 #include "components/performance_manager/persistence/site_data/unittest_utils.h"
+#include "components/performance_manager/public/persistence/site_data/feature_usage.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/gurl.h"
 
diff --git a/components/performance_manager/decorators/site_data_recorder.h b/components/performance_manager/public/decorators/site_data_recorder.h
similarity index 79%
rename from components/performance_manager/decorators/site_data_recorder.h
rename to components/performance_manager/public/decorators/site_data_recorder.h
index f8e8ddb..5abf5d26 100644
--- a/components/performance_manager/decorators/site_data_recorder.h
+++ b/components/performance_manager/public/decorators/site_data_recorder.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_PERFORMANCE_MANAGER_DECORATORS_SITE_DATA_RECORDER_H_
-#define COMPONENTS_PERFORMANCE_MANAGER_DECORATORS_SITE_DATA_RECORDER_H_
+#ifndef COMPONENTS_PERFORMANCE_MANAGER_PUBLIC_DECORATORS_SITE_DATA_RECORDER_H_
+#define COMPONENTS_PERFORMANCE_MANAGER_PUBLIC_DECORATORS_SITE_DATA_RECORDER_H_
 
 #include "base/macros.h"
 #include "base/sequence_checker.h"
@@ -12,11 +12,13 @@
 
 namespace performance_manager {
 
+class SiteDataReader;
 class SiteDataWriter;
 class SiteDataCache;
 
 // The SiteDataRecorder decorator is responsible for adorning PageNodes with a
-// SiteDataWriter and for forwarding the event of interest to this writer.
+// SiteDataReader and a SiteDataWriter and for forwarding the event of interest
+// to this writer.
 class SiteDataRecorder : public GraphOwned,
                          public PageNode::ObserverDefaultImpl {
  public:
@@ -54,7 +56,8 @@
   SEQUENCE_CHECKER(sequence_checker_);
 };
 
-// Allows retrieving the SiteDataWriter associated with a PageNode.
+// Allows retrieving the SiteDataWriter and SiteDataReader associated with a
+// PageNode.
 class SiteDataRecorder::Data {
  public:
   Data();
@@ -63,11 +66,13 @@
   Data& operator=(const Data&) = delete;
 
   virtual SiteDataWriter* writer() const = 0;
+  virtual SiteDataReader* reader() const = 0;
 
+  static const Data* FromPageNode(const PageNode* page_node);
   virtual void SetDataCacheForTesting(SiteDataCache* cache) = 0;
   static Data* GetForTesting(const PageNode* page_node);
 };
 
 }  // namespace performance_manager
 
-#endif  // COMPONENTS_PERFORMANCE_MANAGER_DECORATORS_SITE_DATA_RECORDER_H_
+#endif  // COMPONENTS_PERFORMANCE_MANAGER_PUBLIC_DECORATORS_SITE_DATA_RECORDER_H_
diff --git a/components/performance_manager/persistence/site_data/feature_usage.h b/components/performance_manager/public/persistence/site_data/feature_usage.h
similarity index 65%
rename from components/performance_manager/persistence/site_data/feature_usage.h
rename to components/performance_manager/public/persistence/site_data/feature_usage.h
index a8db38f..a30540f 100644
--- a/components/performance_manager/persistence/site_data/feature_usage.h
+++ b/components/performance_manager/public/persistence/site_data/feature_usage.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_PERFORMANCE_MANAGER_PERSISTENCE_SITE_DATA_FEATURE_USAGE_H_
-#define COMPONENTS_PERFORMANCE_MANAGER_PERSISTENCE_SITE_DATA_FEATURE_USAGE_H_
+#ifndef COMPONENTS_PERFORMANCE_MANAGER_PUBLIC_PERSISTENCE_SITE_DATA_FEATURE_USAGE_H_
+#define COMPONENTS_PERFORMANCE_MANAGER_PUBLIC_PERSISTENCE_SITE_DATA_FEATURE_USAGE_H_
 
 namespace performance_manager {
 
@@ -17,4 +17,4 @@
 
 }  // namespace performance_manager
 
-#endif  // COMPONENTS_PERFORMANCE_MANAGER_PERSISTENCE_SITE_DATA_FEATURE_USAGE_H_
+#endif  // COMPONENTS_PERFORMANCE_MANAGER_PUBLIC_PERSISTENCE_SITE_DATA_FEATURE_USAGE_H_
diff --git a/components/performance_manager/persistence/site_data/site_data_reader.h b/components/performance_manager/public/persistence/site_data/site_data_reader.h
similarity index 87%
rename from components/performance_manager/persistence/site_data/site_data_reader.h
rename to components/performance_manager/public/persistence/site_data/site_data_reader.h
index 193d02d..3a26cbe 100644
--- a/components/performance_manager/persistence/site_data/site_data_reader.h
+++ b/components/performance_manager/public/persistence/site_data/site_data_reader.h
@@ -2,15 +2,15 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef COMPONENTS_PERFORMANCE_MANAGER_PERSISTENCE_SITE_DATA_SITE_DATA_READER_H_
-#define COMPONENTS_PERFORMANCE_MANAGER_PERSISTENCE_SITE_DATA_SITE_DATA_READER_H_
+#ifndef COMPONENTS_PERFORMANCE_MANAGER_PUBLIC_PERSISTENCE_SITE_DATA_SITE_DATA_READER_H_
+#define COMPONENTS_PERFORMANCE_MANAGER_PUBLIC_PERSISTENCE_SITE_DATA_SITE_DATA_READER_H_
 
 #include "base/callback_forward.h"
 #include "base/gtest_prod_util.h"
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
 #include "base/memory/weak_ptr.h"
-#include "components/performance_manager/persistence/site_data/feature_usage.h"
+#include "components/performance_manager/public/persistence/site_data/feature_usage.h"
 
 namespace performance_manager {
 
@@ -73,4 +73,4 @@
 
 }  // namespace performance_manager
 
-#endif  // COMPONENTS_PERFORMANCE_MANAGER_PERSISTENCE_SITE_DATA_SITE_DATA_READER_H_
+#endif  // COMPONENTS_PERFORMANCE_MANAGER_PUBLIC_PERSISTENCE_SITE_DATA_SITE_DATA_READER_H_
diff --git a/components/performance_manager/test_support/test_harness_helper.h b/components/performance_manager/test_support/test_harness_helper.h
index f8e87f05..ee78514 100644
--- a/components/performance_manager/test_support/test_harness_helper.h
+++ b/components/performance_manager/test_support/test_harness_helper.h
@@ -50,14 +50,14 @@
       const PerformanceManagerTestHarnessHelper&) = delete;
   PerformanceManagerTestHarnessHelper& operator=(
       const PerformanceManagerTestHarnessHelper&) = delete;
-  ~PerformanceManagerTestHarnessHelper();
+  virtual ~PerformanceManagerTestHarnessHelper();
 
   // Sets up the PM and registry, etc.
-  void SetUp();
+  virtual void SetUp();
 
   // Tears down the PM and registry, etc. Blocks on the main thread until they
   // are torn down.
-  void TearDown();
+  virtual void TearDown();
 
   // Attaches tab helpers to the provided |contents|. This should only need to
   // be called explicitly in components_unittests. In unit_tests, browser_tests
diff --git a/components/performance_manager/worker_watcher.cc b/components/performance_manager/worker_watcher.cc
index b5935e1..6e786e4 100644
--- a/components/performance_manager/worker_watcher.cc
+++ b/components/performance_manager/worker_watcher.cc
@@ -13,7 +13,7 @@
 #include "components/performance_manager/graph/worker_node_impl.h"
 #include "components/performance_manager/performance_manager_impl.h"
 #include "components/performance_manager/process_node_source.h"
-#include "third_party/blink/public/common/tokens/worker_tokens.h"
+#include "third_party/blink/public/common/tokens/tokens.h"
 
 namespace performance_manager {
 
diff --git a/components/performance_manager/worker_watcher.h b/components/performance_manager/worker_watcher.h
index 1e8c566..e1a2d00 100644
--- a/components/performance_manager/worker_watcher.h
+++ b/components/performance_manager/worker_watcher.h
@@ -19,7 +19,7 @@
 #include "content/public/browser/service_worker_context.h"
 #include "content/public/browser/service_worker_context_observer.h"
 #include "content/public/browser/shared_worker_service.h"
-#include "third_party/blink/public/common/tokens/worker_tokens.h"
+#include "third_party/blink/public/common/tokens/tokens.h"
 
 namespace performance_manager {
 
diff --git a/components/permissions/contexts/geolocation_permission_context_unittest.cc b/components/permissions/contexts/geolocation_permission_context_unittest.cc
index 8aec360..5c4fbd1 100644
--- a/components/permissions/contexts/geolocation_permission_context_unittest.cc
+++ b/components/permissions/contexts/geolocation_permission_context_unittest.cc
@@ -253,8 +253,9 @@
     const GURL& requesting_frame,
     ContentSetting expected_content_setting) {
   auto* content_settings =
-      content_settings::TabSpecificContentSettings::FromWebContents(
-          web_contents());
+      content_settings::TabSpecificContentSettings::GetForFrame(
+          web_contents()->GetMainFrame());
+
   const ContentSettingsUsagesState::StateMap& state_map =
       content_settings->geolocation_usages_state().state_map();
   EXPECT_EQ(1U, state_map.count(requesting_frame.GetOrigin()));
diff --git a/components/policy/proto/device_management_backend.proto b/components/policy/proto/device_management_backend.proto
index deda32c4..d6dafed 100644
--- a/components/policy/proto/device_management_backend.proto
+++ b/components/policy/proto/device_management_backend.proto
@@ -2911,6 +2911,32 @@
     COMPLETE = 4;
   };
 
+  // Current stage of the extension downloading process. See
+  // ExtensionDownloaderDelegate::Stage for more details.
+  // ExtensionDownloaderDelegate::Stage is the main enum and this is a copy used
+  // for reporting purposes.
+  enum DownloadingStage {
+    DOWNLOAD_PENDING = 0;
+
+    QUEUED_FOR_MANIFEST = 1;
+
+    DOWNLOADING_MANIFEST = 2;
+
+    DOWNLOADING_MANIFEST_RETRY = 3;
+
+    PARSING_MANIFEST = 4;
+
+    MANIFEST_LOADED = 5;
+
+    QUEUED_FOR_CRX = 6;
+
+    DOWNLOADING_CRX = 7;
+
+    DOWNLOADING_CRX_RETRY = 8;
+
+    FINISHED = 9;
+  };
+
   // Timestamp, in microseconds since epoch. Set for all log
   // events.
   optional int64 timestamp = 1;
@@ -2935,6 +2961,9 @@
 
   // Stage of installation process.
   optional InstallationStage installation_stage = 8;
+
+  // Stage of downloading process.
+  optional DownloadingStage downloading_stage = 9;
 }
 
 // A single entry in the push-install log for an app.
diff --git a/components/policy/resources/policy_templates.json b/components/policy/resources/policy_templates.json
index ac6820bb..0e7c41f 100644
--- a/components/policy/resources/policy_templates.json
+++ b/components/policy/resources/policy_templates.json
@@ -15293,43 +15293,56 @@
       'device_only': True,
       'type': 'dict',
       'schema': {
-        'type': 'array',
-        'items': {
-          'type': 'object',
-          'properties': {
-            'chromeos_version': {
-              'description': '''Minimum allowed <ph name="PRODUCT_OS_NAME">$2<ex>Google Chrome OS</ex></ph> version''',
-              'type': 'string',
-            },
-            'warning_period': {
-              'description': '''Time in days after which the user will be signed out if <ph name="PRODUCT_OS_NAME">$2<ex>Google Chrome OS</ex></ph> version is less than the specified <ph name="CHROMEOS_VERSION_PROPERTY_NAME">chromeos_version</ph>''',
-              'type': 'integer',
-              'minimum': 0,
-            },
-            'aue_warning_period': {
-              'description': '''Time in days after auto update expiration post which the user will be signed out if <ph name="PRODUCT_OS_NAME">$2<ex>Google Chrome OS</ex></ph> version is less than the specified <ph name="CHROMEOS_VERSION_PROPERTY_NAME">chromeos_version</ph>''',
-              'type': 'integer',
-              'minimum': 0,
+        'type': 'object',
+        'properties': {
+          'requirements': {
+            'type': 'array',
+            'items': {
+              'type': 'object',
+              'properties': {
+                'chromeos_version': {
+                  'description': '''Minimum allowed <ph name="PRODUCT_OS_NAME">$2<ex>Google Chrome OS</ex></ph> version''',
+                  'type': 'string',
+                },
+                'warning_period': {
+                  'description': '''Time in days after which the user will be signed out if <ph name="PRODUCT_OS_NAME">$2<ex>Google Chrome OS</ex></ph> version is less than the specified <ph name="CHROMEOS_VERSION_PROPERTY_NAME">chromeos_version</ph>''',
+                  'type': 'integer',
+                  'minimum': 0,
+                },
+                'aue_warning_period': {
+                  'description': '''Time in days after auto update expiration post which the user will be signed out if <ph name="PRODUCT_OS_NAME">$2<ex>Google Chrome OS</ex></ph> version is less than the specified <ph name="CHROMEOS_VERSION_PROPERTY_NAME">chromeos_version</ph>''',
+                  'type': 'integer',
+                  'minimum': 0,
+                },
+              },
+              'required': ['chromeos_version'],
             },
           },
-          'required': ['chromeos_version'],
-        },
+          'unmanaged_user_restricted': {
+            'description': '''A boolean flag indicating whether unmanaged user sessions should receive notifications and force log out if update is required as per this policy.''',
+            'type': 'boolean'
+          }
+        }
       },
       'features': {
         'dynamic_refresh': True,
         'per_profile': False,
       },
-      'example_value': [
-        {
-          "chromeos_version" : "12215",
-          "warning_period" : 0,
-          "aue_warning_period" : 14
-        }, {
-          "chromeos_version" : "13315.60.12",
-          "warning_period" : 10,
-          "aue_warning_period" : 21
-        }
-      ],
+      'example_value': {
+        "requirements":
+        [
+          {
+            "chromeos_version" : "12215",
+            "warning_period" : 0,
+            "aue_warning_period" : 14
+          }, {
+            "chromeos_version" : "13315.60.12",
+            "warning_period" : 10,
+            "aue_warning_period" : 21
+          }
+        ],
+        "unmanaged_user_restricted": True
+      },
       'id': 670,
       'caption': '''Configure minimum allowed Chrome OS version for the device.''',
       'tags': [],
@@ -15349,10 +15362,13 @@
       If the current version becomes obsolete during user session and the device has reached auto update expiration, an on-screen notification is shown to return the device within <ph name="AUE_WARNING_PERIOD_PROPERTY_NAME">aue_warning_period</ph>.
       If the device is found to have reached auto update expiration at the time of login with expired <ph name="AUE_WARNING_PERIOD_PROPERTY_NAME">aue_warning_period</ph>, the device is blocked for any user to sign in.
 
+      Unmanaged user sessions do not receive notifications and force log out if <ph name="UNMANAGED_USER_RESTRICTED_PROPERTY_NAME">unmanaged_user_restricted</ph> is unset or set to False.
+
       If this policy is not set or set to empty, no restrictions are applied, already existing restrictions are revoked and user can sign in regardless of <ph name="PRODUCT_OS_NAME">$2<ex>Google Chrome OS</ex></ph> version.
 
       Here <ph name="CHROMEOS_VERSION_PROPERTY_NAME">chromeos_version</ph> can be either an exact version like '13305.0.0' or a version prefix, like '13305'.
-      The <ph name="WARNING_PERIOD_PROPERTY_NAME">warning_period</ph> and <ph name="AUE_WARNING_PERIOD_PROPERTY_NAME">aue_warning_period</ph> are optional values specified in number of days. Default value for them is 0 days, which means that there is no warning period.''',
+      The <ph name="WARNING_PERIOD_PROPERTY_NAME">warning_period</ph> and <ph name="AUE_WARNING_PERIOD_PROPERTY_NAME">aue_warning_period</ph> are optional values specified in number of days. Default value for them is 0 days, which means that there is no warning period.
+      The <ph name="UNMANAGED_USER_RESTRICTED_PROPERTY_NAME">unmanaged_user_restricted</ph> is an optional property with default value as False.''',
     },
     {
       'name': 'DeviceMinimumVersionAueMessage',
diff --git a/components/signin/OWNERS b/components/signin/OWNERS
index e4b8e297..837b6f4 100644
--- a/components/signin/OWNERS
+++ b/components/signin/OWNERS
@@ -1,5 +1,6 @@
 bsazonov@chromium.org
 droger@chromium.org
+msalama@chromium.org
 msarda@chromium.org
 sdefresne@chromium.org
 
diff --git a/components/sync/engine_impl/model_type_worker.cc b/components/sync/engine_impl/model_type_worker.cc
index 5162186..13280dbd 100644
--- a/components/sync/engine_impl/model_type_worker.cc
+++ b/components/sync/engine_impl/model_type_worker.cc
@@ -446,14 +446,6 @@
   model_type_processor_->OnCommitFailed(commit_error);
 }
 
-void ModelTypeWorker::AbortMigration() {
-  DCHECK(!model_type_state_.initial_sync_done());
-  model_type_state_ = sync_pb::ModelTypeState();
-  entries_pending_decryption_.clear();
-  pending_updates_.clear();
-  nudge_handler_->NudgeForInitialDownload(type_);
-}
-
 size_t ModelTypeWorker::EstimateMemoryUsage() const {
   using base::trace_event::EstimateMemoryUsage;
   size_t memory_usage = 0;
diff --git a/components/sync/engine_impl/model_type_worker.h b/components/sync/engine_impl/model_type_worker.h
index cd557f3a7..3da565b 100644
--- a/components/sync/engine_impl/model_type_worker.h
+++ b/components/sync/engine_impl/model_type_worker.h
@@ -123,10 +123,6 @@
   // called when a new encryption mechanism is ready.
   void EncryptionAcceptedMaybeApplyUpdates();
 
-  // If migration the directory encounters an error partway through, we need to
-  // clear the update data that has been added so far.
-  void AbortMigration();
-
   // Public for testing.
   // Returns true if this type should stop communicating because of outstanding
   // encryption issues and must wait for keys to be updated.
diff --git a/components/sync/protocol/autofill_specifics.proto b/components/sync/protocol/autofill_specifics.proto
index 1eb5886..c79d6dd9 100644
--- a/components/sync/protocol/autofill_specifics.proto
+++ b/components/sync/protocol/autofill_specifics.proto
@@ -20,6 +20,28 @@
 
 // An AutofillProfile.
 message AutofillProfileSpecifics {
+  // Represents the validation status of value stored in the AutofillProfile.
+  enum VerificationStatus {
+    // No verification status assigned.
+    VERIFICATION_STATUS_UNSPECIFIED = 0;
+    // The value token was parsed from a parent token.
+    // For example, the first name was derived by splitting a full name into
+    // its components.
+    PARSED = 1;
+    // Value was built from its subcomponents.
+    // For example, the full name was built from the first, middle and last
+    // name.
+    FORMATTED = 2;
+    // The value was observed in a form transmission.
+    // For example, the user filled a form that contained at NAME_FULL field.
+    // The value of NAME_FULL will be stored as OBSERVED.
+    OBSERVED = 3;
+    // The user used the autofill settings to verify and store this token.
+    // This is currently only applicable to the full name, since users cannot
+    // edit individual components of their name.
+    USER_VERIFIED = 4;
+  }
+
   optional string guid = 15;
   optional string origin = 16;
   optional int64 use_count = 22;
@@ -29,15 +51,44 @@
   // from the Windows epoch.
   optional int64 use_date = 23;
 
-  // Contact info.
+  // Contact info name fields.
+  repeated string name_honorific = 26;
   repeated string name_first = 2;
   repeated string name_middle = 3;
   repeated string name_last = 4;
+  // Sometimes the last name is composed of two names as it is common for
+  // Hispanic/Latinx names. In the unstructured representation of the last name,
+  // there may be even a conjunction between the first and the second last
+  // name. For example, the more-complete version of Pablo Picasso's surname is
+  // "Ruiz y Picasso" containing a first last name, a conjunction (the y) and a
+  // second last name.
+  repeated string name_last_first = 27;
+  repeated string name_last_conjuction = 28;
+  repeated string name_last_second = 29;
   repeated string name_full = 21;
+
+  // Validation status records for name fields.
+  repeated VerificationStatus name_honorific_status = 30;
+  repeated VerificationStatus name_first_status = 31;
+  repeated VerificationStatus name_middle_status = 32;
+  repeated VerificationStatus name_last_status = 33;
+  repeated VerificationStatus name_last_first_status = 34;
+  repeated VerificationStatus name_last_conjuction_status = 35;
+  repeated VerificationStatus name_last_second_status = 36;
+  repeated VerificationStatus name_full_status = 37;
+
+  // Contact info additional fields.
   repeated string email_address = 5;
   optional string company_name = 6;
 
-  // Address.
+  // Address field.
+  // The address_home_line1/2 fields are deprecated and
+  // address_home_street_address should be used instead by
+  // joining address_home_line1/2 with a newline ("\n").
+  // Full deprecation can not be achieved before all sync profiles have been
+  // updated with a M86+ client.
+  // TODO(crbug/1111740): Remove usages of address_home_line1/2 and mark field
+  // as deprecated.
   optional string address_home_line1 = 7;
   optional string address_home_line2 = 8;
   optional string address_home_city = 9;
@@ -50,6 +101,27 @@
   optional string address_home_sorting_code = 18;
   optional string address_home_dependent_locality = 19;
   optional string address_home_language_code = 20;
+  optional string address_home_thoroughfare_name = 38;
+  optional string address_home_thoroughfare_number = 39;
+  optional string address_home_dependent_thoroughfare_name = 40;
+  optional string address_home_premise_name = 41;
+  optional string address_home_subpremise_name = 42;
+
+  // Validation status records for address fields.
+  optional VerificationStatus address_home_city_status = 43;
+  optional VerificationStatus address_home_state_status = 44;
+  optional VerificationStatus address_home_zip_status = 45;
+  optional VerificationStatus address_home_country_status = 46;
+  optional VerificationStatus address_home_street_address_status = 47;
+  optional VerificationStatus address_home_sorting_code_status = 48;
+  optional VerificationStatus address_home_dependent_locality_status = 49;
+  optional VerificationStatus address_home_language_code_status = 50;
+  optional VerificationStatus address_home_thoroughfare_name_status = 51;
+  optional VerificationStatus address_home_thoroughfare_number_status = 52;
+  optional VerificationStatus address_home_dependent_thoroughfare_name_status =
+      53;
+  optional VerificationStatus address_home_premise_name_status = 54;
+  optional VerificationStatus address_home_subpremise_name_status = 55;
 
   // Phone.
   repeated string phone_home_whole_number = 13;
diff --git a/components/sync/protocol/proto_enum_conversions.cc b/components/sync/protocol/proto_enum_conversions.cc
index a2e1142..bac8aef9e 100644
--- a/components/sync/protocol/proto_enum_conversions.cc
+++ b/components/sync/protocol/proto_enum_conversions.cc
@@ -47,6 +47,22 @@
 }
 
 const char* ProtoEnumToString(
+    sync_pb::AutofillProfileSpecifics::VerificationStatus status) {
+  ASSERT_ENUM_BOUNDS(sync_pb::AutofillProfileSpecifics, VerificationStatus,
+                     VERIFICATION_STATUS_UNSPECIFIED, USER_VERIFIED);
+  switch (status) {
+    ENUM_CASE(sync_pb::AutofillProfileSpecifics,
+              VERIFICATION_STATUS_UNSPECIFIED);
+    ENUM_CASE(sync_pb::AutofillProfileSpecifics, PARSED);
+    ENUM_CASE(sync_pb::AutofillProfileSpecifics, FORMATTED);
+    ENUM_CASE(sync_pb::AutofillProfileSpecifics, OBSERVED);
+    ENUM_CASE(sync_pb::AutofillProfileSpecifics, USER_VERIFIED);
+  }
+  NOTREACHED();
+  return "";
+}
+
+const char* ProtoEnumToString(
     sync_pb::AutofillWalletSpecifics::WalletInfoType wallet_info_type) {
   ASSERT_ENUM_BOUNDS(sync_pb::AutofillWalletSpecifics, WalletInfoType, UNKNOWN,
                      CREDIT_CARD_CLOUD_TOKEN_DATA);
diff --git a/components/sync/protocol/proto_enum_conversions.h b/components/sync/protocol/proto_enum_conversions.h
index 0b030c8..c49e54e 100644
--- a/components/sync/protocol/proto_enum_conversions.h
+++ b/components/sync/protocol/proto_enum_conversions.h
@@ -108,6 +108,9 @@
     sync_pb::WebAppSpecifics::UserDisplayMode user_display_mode);
 
 const char* ProtoEnumToString(
+    sync_pb::AutofillProfileSpecifics::VerificationStatus status);
+
+const char* ProtoEnumToString(
     sync_pb::WifiConfigurationSpecifics::SecurityType security_type);
 
 const char* ProtoEnumToString(
diff --git a/components/sync/protocol/proto_visitors.h b/components/sync/protocol/proto_visitors.h
index a4091986..8e7d13a3 100644
--- a/components/sync/protocol/proto_visitors.h
+++ b/components/sync/protocol/proto_visitors.h
@@ -141,12 +141,27 @@
   VISIT(origin);
   VISIT(use_count);
   VISIT(use_date);
+  VISIT_REP(name_honorific);
   VISIT_REP(name_first);
   VISIT_REP(name_middle);
+  VISIT_REP(name_last_first);
+  VISIT_REP(name_last_conjuction);
+  VISIT_REP(name_last_second);
   VISIT_REP(name_last);
   VISIT_REP(name_full);
+
+  VISIT_REP(name_honorific_status);
+  VISIT_REP(name_first_status);
+  VISIT_REP(name_middle_status);
+  VISIT_REP(name_last_first_status);
+  VISIT_REP(name_last_conjuction_status);
+  VISIT_REP(name_last_second_status);
+  VISIT_REP(name_last_status);
+  VISIT_REP(name_full_status);
+
   VISIT_REP(email_address);
   VISIT(company_name);
+
   VISIT(address_home_line1);
   VISIT(address_home_line2);
   VISIT(address_home_city);
@@ -156,6 +171,25 @@
   VISIT(address_home_street_address);
   VISIT(address_home_sorting_code);
   VISIT(address_home_dependent_locality);
+  VISIT(address_home_thoroughfare_name);
+  VISIT(address_home_thoroughfare_number);
+  VISIT(address_home_dependent_thoroughfare_name);
+  VISIT(address_home_premise_name);
+  VISIT(address_home_subpremise_name);
+
+  VISIT_ENUM(address_home_city_status);
+  VISIT_ENUM(address_home_state_status);
+  VISIT_ENUM(address_home_zip_status);
+  VISIT_ENUM(address_home_country_status);
+  VISIT_ENUM(address_home_street_address_status);
+  VISIT_ENUM(address_home_sorting_code_status);
+  VISIT_ENUM(address_home_dependent_locality_status);
+  VISIT_ENUM(address_home_thoroughfare_name_status);
+  VISIT_ENUM(address_home_thoroughfare_number_status);
+  VISIT_ENUM(address_home_dependent_thoroughfare_name_status);
+  VISIT_ENUM(address_home_premise_name_status);
+  VISIT_ENUM(address_home_subpremise_name_status);
+
   VISIT(address_home_language_code);
   VISIT_REP(phone_home_whole_number);
   VISIT(validity_state_bitfield);
diff --git a/components/url_formatter/url_formatter.cc b/components/url_formatter/url_formatter.cc
index fbebb29..4f14b548 100644
--- a/components/url_formatter/url_formatter.cc
+++ b/components/url_formatter/url_formatter.cc
@@ -200,6 +200,7 @@
   format_types &= ~kFormatUrlOmitHTTPS;
   format_types &= ~kFormatUrlOmitTrivialSubdomains;
   format_types &= ~kFormatUrlTrimAfterHost;
+  format_types &= ~kFormatUrlOmitFileScheme;
 
   // Format the underlying URL and record adjustments.
   const std::string& url_str(url.possibly_invalid_spec());
diff --git a/components/url_formatter/url_formatter_unittest.cc b/components/url_formatter/url_formatter_unittest.cc
index 80cf386..739eb62 100644
--- a/components/url_formatter/url_formatter_unittest.cc
+++ b/components/url_formatter/url_formatter_unittest.cc
@@ -247,6 +247,19 @@
            kFormatUrlOmitTrivialSubdomains | kFormatUrlTrimAfterHost,
        net::UnescapeRule::NORMAL, L"view-source:https://www.google.com/foo",
        20},
+#if defined(OS_WIN)
+      {"view-source should not omit file on Windows",
+       "view-source:file:///C:/Users/homedirname/folder/file.pdf/",
+       kFormatUrlOmitDefaults | kFormatUrlOmitFileScheme,
+       net::UnescapeRule::NORMAL,
+       L"view-source:file:///C:/Users/homedirname/folder/file.pdf/", 19},
+#else
+      {"view-source should not omit file",
+       "view-source:file:///Users/homedirname/folder/file.pdf/",
+       kFormatUrlOmitDefaults | kFormatUrlOmitFileScheme,
+       net::UnescapeRule::NORMAL,
+       L"view-source:file:///Users/homedirname/folder/file.pdf/", 19},
+#endif
 
       // -------- omit https --------
       {"omit https", "https://www.google.com/", kFormatUrlOmitHTTPS,
diff --git a/components/viz/host/gpu_host_impl.cc b/components/viz/host/gpu_host_impl.cc
index 862abdd9..fbc86206 100644
--- a/components/viz/host/gpu_host_impl.cc
+++ b/components/viz/host/gpu_host_impl.cc
@@ -25,8 +25,6 @@
 #include "gpu/ipc/host/shader_disk_cache.h"
 #include "ui/base/ui_base_features.h"
 #include "ui/gfx/font_render_params.h"
-#include "ui/ozone/public/gpu_platform_support_host.h"
-#include "ui/ozone/public/ozone_platform.h"
 
 #if defined(OS_ANDROID)
 #include "base/android/build_info.h"
@@ -38,6 +36,8 @@
 
 #if defined(USE_OZONE)
 #include "ui/base/ui_base_features.h"
+#include "ui/ozone/public/gpu_platform_support_host.h"
+#include "ui/ozone/public/ozone_platform.h"
 #endif
 
 namespace viz {
diff --git a/components/viz/service/display_embedder/output_presenter.cc b/components/viz/service/display_embedder/output_presenter.cc
index 035c23c5..6679d806a 100644
--- a/components/viz/service/display_embedder/output_presenter.cc
+++ b/components/viz/service/display_embedder/output_presenter.cc
@@ -64,8 +64,10 @@
       gpu::SharedImageRepresentation::AllowUnclearedAccess::kYes);
   DCHECK(scoped_skia_write_access_);
   if (!begin_semaphores.empty()) {
-    scoped_skia_write_access_->surface()->wait(begin_semaphores.size(),
-                                               begin_semaphores.data());
+    scoped_skia_write_access_->surface()->wait(
+        begin_semaphores.size(),
+        begin_semaphores.data(),
+        /*deleteSemaphoresAfterWait=*/false);
   }
 }
 
diff --git a/components/viz/service/display_embedder/skia_output_device_webview.cc b/components/viz/service/display_embedder/skia_output_device_webview.cc
index 6e067db..dd59bb7 100644
--- a/components/viz/service/display_embedder/skia_output_device_webview.cc
+++ b/components/viz/service/display_embedder/skia_output_device_webview.cc
@@ -36,17 +36,7 @@
   DCHECK(context_state_->gr_context());
   DCHECK(context_state_->context());
 
-  // Get alpha bits from the default frame buffer.
-  glBindFramebufferEXT(GL_FRAMEBUFFER,
-                       gl_surface_->GetBackingFramebufferObject());
-  context_state_->gr_context()->resetContext(kRenderTarget_GrGLBackendState);
-  GLint alpha_bits = 0;
-  glGetIntegerv(GL_ALPHA_BITS, &alpha_bits);
-  CHECK_GL_ERROR();
-  supports_alpha_ = alpha_bits > 0;
-
-  capabilities_.sk_color_type =
-      supports_alpha_ ? kRGBA_8888_SkColorType : kRGB_888x_SkColorType;
+  capabilities_.sk_color_type = kRGBA_8888_SkColorType;
   capabilities_.gr_backend_format =
       context_state_->gr_context()->defaultBackendFormat(
           capabilities_.sk_color_type, GrRenderable::kYes);
@@ -109,9 +99,8 @@
 
   GrGLFramebufferInfo framebuffer_info;
   framebuffer_info.fFBOID = fbo;
-  framebuffer_info.fFormat = supports_alpha_ ? GL_RGBA8 : GL_RGB8_OES;
-  DCHECK_EQ(capabilities_.gr_backend_format.asGLFormat(),
-            supports_alpha_ ? GrGLFormat::kRGBA8 : GrGLFormat::kRGB8);
+  framebuffer_info.fFormat = GL_RGBA8;
+  DCHECK_EQ(capabilities_.gr_backend_format.asGLFormat(), GrGLFormat::kRGBA8);
   SkColorType color_type = capabilities_.sk_color_type;
 
   GrBackendRenderTarget render_target(size_.width(), size_.height(),
diff --git a/components/viz/service/display_embedder/skia_output_device_webview.h b/components/viz/service/display_embedder/skia_output_device_webview.h
index a240e49a..c27516e 100644
--- a/components/viz/service/display_embedder/skia_output_device_webview.h
+++ b/components/viz/service/display_embedder/skia_output_device_webview.h
@@ -58,8 +58,6 @@
   gfx::ColorSpace color_space_;
   unsigned int last_frame_buffer_object_ = -1;
 
-  bool supports_alpha_ = false;
-
   base::WeakPtrFactory<SkiaOutputDeviceWebView> weak_ptr_factory_{this};
 };
 
diff --git a/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.cc b/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.cc
index 2d75921..fb553c6 100644
--- a/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.cc
+++ b/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.cc
@@ -543,8 +543,9 @@
     promise_image_access_helper_.BeginAccess(
         std::move(image_contexts), &begin_semaphores, &end_semaphores);
     if (!begin_semaphores.empty()) {
-      auto result = output_sk_surface()->wait(begin_semaphores.size(),
-                                              begin_semaphores.data());
+      auto result = output_sk_surface()->wait(
+          begin_semaphores.size(), begin_semaphores.data(),
+          /*deleteSemaphoresAfterWait=*/false);
       DCHECK(result);
     }
 
@@ -668,8 +669,9 @@
     promise_image_access_helper_.BeginAccess(
         std::move(image_contexts), &begin_semaphores, &end_semaphores);
     if (!begin_semaphores.empty()) {
-      auto result = offscreen.surface()->wait(begin_semaphores.size(),
-                                              begin_semaphores.data());
+      auto result = offscreen.surface()->wait(
+          begin_semaphores.size(), begin_semaphores.data(),
+          /*deleteSemaphoresAfterWait=*/false);
       DCHECK(result);
     }
     offscreen.surface()->draw(ddl);
diff --git a/content/browser/device_sensors/device_sensor_browsertest.cc b/content/browser/device_sensors/device_sensor_browsertest.cc
index 2ac11d6d..ae49fb3 100644
--- a/content/browser/device_sensors/device_sensor_browsertest.cc
+++ b/content/browser/device_sensors/device_sensor_browsertest.cc
@@ -351,16 +351,15 @@
       "https://github.com/WICG/feature-policy/blob/"
       "master/features.md#sensor-features";
 
-  auto console_delegate = std::make_unique<ConsoleObserverDelegate>(
-      shell()->web_contents(), kWarningMessage);
-  shell()->web_contents()->SetDelegate(console_delegate.get());
+  WebContentsConsoleObserver console_observer(shell()->web_contents());
+  console_observer.SetPattern(kWarningMessage);
 
   EXPECT_TRUE(NavigateToURL(shell(), main_frame_url));
   EXPECT_TRUE(NavigateIframeToURL(shell()->web_contents(),
                                   "cross_origin_iframe", iframe_url));
 
-  console_delegate->Wait();
-  EXPECT_EQ(kWarningMessage, console_delegate->message());
+  console_observer.Wait();
+  EXPECT_EQ(kWarningMessage, console_observer.GetMessageAt(0u));
 }
 
 }  //  namespace
diff --git a/content/browser/download/mhtml_generation_browsertest.cc b/content/browser/download/mhtml_generation_browsertest.cc
index 2f66591a..35b8123 100644
--- a/content/browser/download/mhtml_generation_browsertest.cc
+++ b/content/browser/download/mhtml_generation_browsertest.cc
@@ -359,18 +359,14 @@
       return;
 
     // Loads the generated file to check if it is well formed.
-    WebContentsDelegate* old_delegate = shell()->web_contents()->GetDelegate();
-    ConsoleObserverDelegate console_delegate(shell()->web_contents(),
-                                             "Malformed multipart archive: *");
-    shell()->web_contents()->SetDelegate(&console_delegate);
+    WebContentsConsoleObserver console_observer(shell()->web_contents());
+    console_observer.SetPattern("Malformed multipart archive: *");
 
     EXPECT_TRUE(
         NavigateToURL(shell(), net::FilePathToFileURL(params.file_path)))
         << "Error navigating to the generated MHTML file";
-    EXPECT_EQ(0U, console_delegate.message().length())
+    EXPECT_TRUE(console_observer.messages().empty())
         << "The generated MHTML file is malformed";
-
-    shell()->web_contents()->SetDelegate(old_delegate);
   }
 
   void TwoStepSyncTestFor(const TaskOrder order);
diff --git a/content/browser/frame_host/navigation_controller_impl_browsertest.cc b/content/browser/frame_host/navigation_controller_impl_browsertest.cc
index 459e00d..0e28250 100644
--- a/content/browser/frame_host/navigation_controller_impl_browsertest.cc
+++ b/content/browser/frame_host/navigation_controller_impl_browsertest.cc
@@ -7211,6 +7211,10 @@
   GURL start_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
   EXPECT_TRUE(NavigateToURL(shell(), start_url));
 
+  // The test below only makes sense for same-site same-RFH navigations, so we
+  // need to ensure that we won't trigger a same-site cross-RFH navigation.
+  DisableProactiveBrowsingInstanceSwapFor(root->current_frame_host());
+
   GURL same_document_url(
       embedded_test_server()->GetURL("a.com", "/title1.html#foo"));
   EXPECT_TRUE(NavigateToURL(shell(), same_document_url));
@@ -7255,6 +7259,10 @@
   GURL start_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
   EXPECT_TRUE(NavigateToURL(shell(), start_url));
 
+  // The test below only makes sense for same-site same-RFH navigations, so we
+  // need to ensure that we won't trigger a same-site cross-RFH navigation.
+  DisableProactiveBrowsingInstanceSwapFor(root->current_frame_host());
+
   GURL same_document_url(
       embedded_test_server()->GetURL("a.com", "/title1.html#foo"));
   EXPECT_TRUE(NavigateToURL(shell(), same_document_url));
@@ -7321,6 +7329,10 @@
     EXPECT_EQ(0, web_contents->GetController().GetLastCommittedEntryIndex());
   }
 
+  // The test below only makes sense for same-site same-RFH navigations, so we
+  // need to ensure that we won't trigger a same-site cross-RFH navigation.
+  DisableProactiveBrowsingInstanceSwapFor(root->current_frame_host());
+
   // 2. Perform a same-document navigation forward.
   {
     GURL same_document_url(
diff --git a/content/browser/frame_host/render_frame_host_impl.cc b/content/browser/frame_host/render_frame_host_impl.cc
index 8309fdc..9b0365f 100644
--- a/content/browser/frame_host/render_frame_host_impl.cc
+++ b/content/browser/frame_host/render_frame_host_impl.cc
@@ -1028,9 +1028,6 @@
   // the dtor has run.  (It may also be null in tests.)
   unload_event_monitor_timeout_.reset();
 
-  for (auto& iter : visual_state_callbacks_)
-    std::move(iter.second).Run(false);
-
   // Delete this before destroying the widget, to guard against reentrancy
   // by in-process screen readers such as JAWS.
   browser_accessibility_manager_.reset();
@@ -1168,6 +1165,12 @@
   MaybeEvictFromBackForwardCache();
 }
 
+void RenderFrameHostImpl::DisableProactiveBrowsingInstanceSwapForTesting() {
+  // This should only be called on main frames.
+  DCHECK(!GetParent());
+  is_proactive_browsing_instance_swap_disabled_for_testing_ = true;
+}
+
 void RenderFrameHostImpl::OnGrantedMediaStreamAccess() {
   was_granted_media_access_ = true;
   MaybeEvictFromBackForwardCache();
@@ -1633,7 +1636,6 @@
   IPC_BEGIN_MESSAGE_MAP(RenderFrameHostImpl, msg)
     IPC_MESSAGE_HANDLER(FrameHostMsg_Unload_ACK, OnUnloadACK)
     IPC_MESSAGE_HANDLER(FrameHostMsg_ContextMenu, OnContextMenu)
-    IPC_MESSAGE_HANDLER(FrameHostMsg_VisualStateResponse, OnVisualStateResponse)
     IPC_MESSAGE_HANDLER(FrameHostMsg_SelectionChanged, OnSelectionChanged)
   IPC_END_MESSAGE_MAP()
 
@@ -1842,8 +1844,6 @@
   smart_clip_callbacks_.Clear();
 #endif  // defined(OS_ANDROID)
 
-  visual_state_callbacks_.clear();
-
   // Ensure that future remote interface requests are associated with the new
   // process's channel.
   remote_associated_interfaces_.reset();
@@ -3112,16 +3112,6 @@
 }
 #endif  // defined(OS_ANDROID)
 
-void RenderFrameHostImpl::OnVisualStateResponse(uint64_t id) {
-  auto it = visual_state_callbacks_.find(id);
-  if (it != visual_state_callbacks_.end()) {
-    std::move(it->second).Run(true);
-    visual_state_callbacks_.erase(it);
-  } else {
-    NOTREACHED() << "Received script response for unknown request";
-  }
-}
-
 void RenderFrameHostImpl::RunModalAlertDialog(
     const base::string16& alert_message,
     RunModalAlertDialogCallback response_callback) {
@@ -6634,10 +6624,7 @@
 
 void RenderFrameHostImpl::InsertVisualStateCallback(
     VisualStateCallback callback) {
-  static uint64_t next_id = 1;
-  uint64_t key = next_id++;
-  Send(new FrameMsg_VisualStateRequest(routing_id_, key));
-  visual_state_callbacks_.emplace(key, std::move(callback));
+  GetRenderWidgetHost()->InsertVisualStateCallback(std::move(callback));
 }
 
 bool RenderFrameHostImpl::IsRenderFrameCreated() {
diff --git a/content/browser/frame_host/render_frame_host_impl.h b/content/browser/frame_host/render_frame_host_impl.h
index 86af7ca..f34a297 100644
--- a/content/browser/frame_host/render_frame_host_impl.h
+++ b/content/browser/frame_host/render_frame_host_impl.h
@@ -1138,6 +1138,14 @@
     return back_forward_cache_disabled_reasons_;
   }
 
+  // Prevents this frame to do a proactive BrowsingInstance swap (for all
+  // navigations on this frame - cross-site and same-site).
+  void DisableProactiveBrowsingInstanceSwapForTesting();
+
+  bool IsProactiveBrowsingInstanceSwapDisabledForTesting() const {
+    return is_proactive_browsing_instance_swap_disabled_for_testing_;
+  }
+
   void AddServiceWorkerContainerHost(
       const std::string& uuid,
       base::WeakPtr<ServiceWorkerContainerHost> host);
@@ -1879,8 +1887,6 @@
   // IPC Message handlers.
   void OnUnloadACK();
   void OnContextMenu(const UntrustworthyContextMenuParams& params);
-  void OnVisualStateResponse(uint64_t id);
-
   void OnForwardResourceTimingToParent(
       const ResourceTimingInfo& resource_timing);
   void OnSelectionChanged(const base::string16& text,
@@ -2500,8 +2506,6 @@
   // The http status code of the last committed navigation.
   int last_http_status_code_ = 0;
 
-  std::map<uint64_t, VisualStateCallback> visual_state_callbacks_;
-
   // Local root subframes directly own their RenderWidgetHost.
   // Please see comments about the GetLocalRenderWidgetHost() function.
   // TODO(kenrb): Later this will also be used on the top-level frame, when
@@ -2943,6 +2947,12 @@
   // breakdown of NotRestoredReason::kDisableForRenderFrameHostCalled.
   std::set<std::string> back_forward_cache_disabled_reasons_;
 
+  // Whether proactive BrowsingInstance swap is disabled for this frame or not.
+  // Note that even if this is false, proactive BrowsingInstance swap still
+  // might not happen on navigations on this frame due to other reasons.
+  // Should only be used for testing purposes.
+  bool is_proactive_browsing_instance_swap_disabled_for_testing_ = false;
+
   // This used to re-commit when restoring from the BackForwardCache, with the
   // same params as the original navigation.
   // Note: If BackForwardCache is not enabled, this field is not set.
diff --git a/content/browser/frame_host/render_frame_host_impl_browsertest.cc b/content/browser/frame_host/render_frame_host_impl_browsertest.cc
index 62fd6a9c..1f7f0bd8 100644
--- a/content/browser/frame_host/render_frame_host_impl_browsertest.cc
+++ b/content/browser/frame_host/render_frame_host_impl_browsertest.cc
@@ -26,6 +26,7 @@
 #include "content/browser/renderer_host/input/timeout_monitor.h"
 #include "content/browser/renderer_host/render_process_host_impl.h"
 #include "content/browser/web_contents/web_contents_impl.h"
+#include "content/common/content_navigation_policy.h"
 #include "content/common/frame_messages.h"
 #include "content/public/browser/javascript_dialog_manager.h"
 #include "content/public/browser/render_frame_host.h"
@@ -1304,7 +1305,19 @@
   GURL url(embedded_test_server()->GetURL("/title1.html"));
   EXPECT_TRUE(NavigateToURL(shell(), url));
 
-  // Now make a navigation.
+  // This test only makes sense for navigations that stay in the same
+  // RenderFrame, otherwise the document.open() will run on the previous
+  // page's RenderFrame, and the navigation won't get aborted. We need to
+  // ensure that we won't trigger a same-site cross-RFH navigation.
+  // TODO(crbug.com/1099193): This should also work on cross-RFH same-site
+  // navigations.
+  DisableProactiveBrowsingInstanceSwapFor(
+      shell()->web_contents()->GetMainFrame());
+
+  // Now make a navigation. |observer| will make a document.open() call at
+  // ReadyToCommit time - see
+  // NavigationHandleGrabber::SendingNavigationCommitted(). The navigation
+  // should get aborted because of the document.open() in the navigating RFH.
   NavigationHandleGrabber observer(shell()->web_contents());
   const base::string16 title = base::ASCIIToUTF16("done");
   EXPECT_TRUE(ExecuteScript(shell()->web_contents(),
@@ -1671,6 +1684,7 @@
 IN_PROC_BROWSER_TEST_F(
     RenderFrameHostImplBrowserTest,
     EarlyInterfaceRequestsFromNewDocumentDispatchedAfterNavigationFinished) {
+  WebContents* web_contents = shell()->web_contents();
   const GURL first_url(embedded_test_server()->GetURL("/title1.html"));
   const GURL second_url(embedded_test_server()->GetURL("/title2.html"));
 
@@ -1692,7 +1706,7 @@
   // Replace the |interface_broker_receiver| argument in the next
   // DidCommitProvisionalLoad message coming from the renderer with the
   // rigged |interface_broker_with_pending_requests| from above.
-  ScopedFakeInterfaceBrokerRequestInjector injector(shell()->web_contents());
+  ScopedFakeInterfaceBrokerRequestInjector injector(web_contents);
   injector.set_fake_receiver_for_next_commit(
       std::move(interface_broker_receiver_with_pending_receiver));
 
@@ -1700,23 +1714,29 @@
   // dispatched to the RenderFrameHost, WebContentsObserver::DidFinishNavigation
   // will have already been invoked.
   bool did_finish_navigation = false;
-  auto* main_rfh = shell()->web_contents()->GetMainFrame();
+
+  // Start the same-process navigation.
+  TestNavigationManager navigation_manager(web_contents, second_url);
+  shell()->LoadURL(second_url);
+  EXPECT_TRUE(navigation_manager.WaitForResponse());
+  auto* committing_rfh =
+      navigation_manager.GetNavigationHandle()->GetRenderFrameHost();
+
   DidFinishNavigationObserver navigation_finish_observer(
-      main_rfh, base::BindLambdaForTesting([&did_finish_navigation]() {
+      committing_rfh, base::BindLambdaForTesting([&did_finish_navigation]() {
         did_finish_navigation = true;
       }));
 
   base::RunLoop wait_until_interface_request_is_dispatched;
   ScopedInterfaceRequestMonitor monitor(
-      main_rfh, mojom::FrameHostTestInterface::Name_,
+      committing_rfh, mojom::FrameHostTestInterface::Name_,
       base::BindLambdaForTesting([&]() {
         EXPECT_TRUE(did_finish_navigation);
         wait_until_interface_request_is_dispatched.Quit();
       }));
 
-  // Start the same-process navigation.
-  ASSERT_TRUE(NavigateToURL(shell(), second_url));
-  EXPECT_EQ(main_rfh, shell()->web_contents()->GetMainFrame());
+  // Finish the navigation.
+  navigation_manager.WaitForNavigationFinished();
   EXPECT_EQ(second_url, injector.url_of_last_commit());
   EXPECT_TRUE(injector.original_receiver_of_last_commit().is_valid());
 
@@ -1759,6 +1779,11 @@
     ASSERT_TRUE(injector.original_receiver_of_last_commit().is_valid());
   }
 
+  // The test below only works for same-RFH navigations, so we need to ensure
+  // that we won't trigger a same-site cross-RFH navigation.
+  DisableProactiveBrowsingInstanceSwapFor(
+      shell()->web_contents()->GetMainFrame());
+
   // Prepare an interface receiver for FrameHostTestInterface.
   mojo::Remote<mojom::FrameHostTestInterface> test_interface;
   auto test_interface_receiver = test_interface.BindNewPipeAndPassReceiver();
@@ -2317,6 +2342,11 @@
     ASSERT_TRUE(NavigateToURL(shell(), kUrl1));
   }
 
+  // The test below only makes sense for same-RFH navigations, so we need to
+  // ensure that we won't trigger a same-site cross-RFH navigation.
+  DisableProactiveBrowsingInstanceSwapFor(
+      shell()->web_contents()->GetMainFrame());
+
   {
     ScopedFakeInterfaceBrokerRequestInjector injector(shell()->web_contents());
     injector.set_fake_receiver_for_next_commit(
@@ -3998,8 +4028,6 @@
   GURL url2(embedded_test_server()->GetURL("/document2"));
 
   WebContents* web_contents = shell()->web_contents();
-  RenderFrameHostImpl* rfhi =
-      static_cast<RenderFrameHostImpl*>(web_contents->GetMainFrame());
 
   base::RunLoop loop_until_onload;
   DocumentOnLoadObserver onload_observer(web_contents,
@@ -4007,7 +4035,8 @@
   shell()->LoadURL(url1);
   loop_until_onload.Run();
 
-  EXPECT_TRUE(rfhi->IsDOMContentLoaded());
+  EXPECT_TRUE(static_cast<RenderFrameHostImpl*>(web_contents->GetMainFrame())
+                  ->IsDOMContentLoaded());
   EXPECT_TRUE(web_contents->IsDocumentOnLoadCompletedInMainFrame());
 
   // Expect that the loading state will be reset after a navigation.
@@ -4021,8 +4050,8 @@
       "Content-Type: text/html; charset=utf-8\r\n"
       "\r\n");
   navigation_observer.WaitForNavigationFinished();
-
-  EXPECT_FALSE(rfhi->IsDOMContentLoaded());
+  EXPECT_FALSE(static_cast<RenderFrameHostImpl*>(web_contents->GetMainFrame())
+                   ->IsDOMContentLoaded());
   EXPECT_FALSE(web_contents->IsDocumentOnLoadCompletedInMainFrame());
 }
 
diff --git a/content/browser/frame_host/render_frame_host_manager.cc b/content/browser/frame_host/render_frame_host_manager.cc
index c13020a2..2feb42e 100644
--- a/content/browser/frame_host/render_frame_host_manager.cc
+++ b/content/browser/frame_host/render_frame_host_manager.cc
@@ -1292,6 +1292,12 @@
     const GURL& destination_url,
     bool is_reload,
     bool should_replace_current_entry) {
+  auto* current_rfhi =
+      static_cast<RenderFrameHostImpl*>(render_frame_host_.get());
+  // If we've disabled proactive BrowsingInstance swap for this RenderFrameHost,
+  // we should not try to do a proactive swap.
+  if (current_rfhi->IsProactiveBrowsingInstanceSwapDisabledForTesting())
+    return ShouldSwapBrowsingInstance::kNo_ProactiveSwapDisabled;
   // We should only do proactive swap when either the flag is enabled, or if
   // it's needed for the back-forward cache (and the bfcache flag is enabled).
   if (!IsProactivelySwapBrowsingInstanceEnabled() &&
@@ -1355,11 +1361,10 @@
   if (is_reload)
     return ShouldSwapBrowsingInstance::kNo_Reload;
 
-  if (IsCurrentlySameSite(
-          static_cast<RenderFrameHostImpl*>(render_frame_host_.get()),
-          destination_url)) {
-    if (IsProactivelySwapBrowsingInstanceOnSameSiteNavigationEnabled())
+  if (IsCurrentlySameSite(current_rfhi, destination_url)) {
+    if (IsProactivelySwapBrowsingInstanceOnSameSiteNavigationEnabled()) {
       return ShouldSwapBrowsingInstance::kYes_SameSiteProactiveSwap;
+    }
     return ShouldSwapBrowsingInstance::kNo_SameSiteNavigation;
   }
 
diff --git a/content/browser/frame_host/render_frame_host_manager_browsertest.cc b/content/browser/frame_host/render_frame_host_manager_browsertest.cc
index a6acfe0..bafb295 100644
--- a/content/browser/frame_host/render_frame_host_manager_browsertest.cc
+++ b/content/browser/frame_host/render_frame_host_manager_browsertest.cc
@@ -3806,7 +3806,14 @@
   GURL url_b_with_frame(embedded_test_server()->GetURL(
       "b.com", "/navigation_controller/page_with_iframe.html"));
   EXPECT_TRUE(NavigateToURL(shell(), url_b_with_frame));
-  EXPECT_EQ(rfh_b, web_contents->GetMainFrame());
+  if (CanSameSiteMainFrameNavigationsChangeRenderFrameHosts()) {
+    // If same-site ProactivelySwapBrowsingInstance or main-frame RenderDocument
+    // is enabled, the navigation will result in a new RFH.
+    EXPECT_NE(rfh_b, web_contents->GetMainFrame());
+    rfh_b = web_contents->GetMainFrame();
+  } else {
+    EXPECT_EQ(rfh_b, web_contents->GetMainFrame());
+  }
   EXPECT_EQ(url::Origin::Create(url_b), rfh_b->GetLastCommittedOrigin());
   FrameTreeNode* child = root->child_at(0);
   RenderFrameHostImpl* child_rfh_b = root->child_at(0)->current_frame_host();
@@ -4356,8 +4363,16 @@
     scoped_refptr<SiteInstance> error_site_instance =
         shell()->web_contents()->GetMainFrame()->GetSiteInstance();
     EXPECT_NE(success_site_instance, error_site_instance);
-    EXPECT_TRUE(success_site_instance->IsRelatedSiteInstance(
-        error_site_instance.get()));
+    if (CanSameSiteMainFrameNavigationsChangeSiteInstances()) {
+      // When ProactivelySwapBrowsingInstance is enabled on same-site
+      // navigations, the navigation above will result in a new
+      // BrowsingInstance.
+      EXPECT_FALSE(success_site_instance->IsRelatedSiteInstance(
+          error_site_instance.get()));
+    } else {
+      EXPECT_TRUE(success_site_instance->IsRelatedSiteInstance(
+          error_site_instance.get()));
+    }
     EXPECT_NE(success_site_instance->GetProcess()->GetID(),
               error_site_instance->GetProcess()->GetID());
     EXPECT_EQ(GURL(kUnreachableWebDataURL), error_site_instance->GetSiteURL());
@@ -4392,8 +4407,16 @@
     scoped_refptr<SiteInstance> error_site_instance =
         shell()->web_contents()->GetMainFrame()->GetSiteInstance();
     EXPECT_NE(success_site_instance, error_site_instance);
-    EXPECT_TRUE(success_site_instance->IsRelatedSiteInstance(
-        error_site_instance.get()));
+    if (CanSameSiteMainFrameNavigationsChangeSiteInstances()) {
+      // When ProactivelySwapBrowsingInstance is enabled on same-site
+      // navigations, the navigation above will result in a new
+      // BrowsingInstance.
+      EXPECT_FALSE(success_site_instance->IsRelatedSiteInstance(
+          error_site_instance.get()));
+    } else {
+      EXPECT_TRUE(success_site_instance->IsRelatedSiteInstance(
+          error_site_instance.get()));
+    }
     EXPECT_NE(success_site_instance->GetProcess()->GetID(),
               error_site_instance->GetProcess()->GetID());
     EXPECT_EQ(GURL(kUnreachableWebDataURL), error_site_instance->GetSiteURL());
diff --git a/content/browser/navigation_browsertest.cc b/content/browser/navigation_browsertest.cc
index 7e33ceb5..f56ac8e 100644
--- a/content/browser/navigation_browsertest.cc
+++ b/content/browser/navigation_browsertest.cc
@@ -334,11 +334,19 @@
     EXPECT_FALSE(observer.last_initiator_routing_id());
   }
 
-  // The RenderFrameHost should not have changed.
-  EXPECT_EQ(initial_rfh, static_cast<WebContentsImpl*>(shell()->web_contents())
-                             ->GetFrameTree()
-                             ->root()
-                             ->current_frame_host());
+  RenderFrameHost* second_rfh =
+      static_cast<WebContentsImpl*>(shell()->web_contents())
+          ->GetFrameTree()
+          ->root()
+          ->current_frame_host();
+
+  if (CanSameSiteMainFrameNavigationsChangeRenderFrameHosts()) {
+    // If same-site ProactivelySwapBrowsingInstance or main-frame RenderDocument
+    // is enabled, the navigation will result in a new RFH.
+    EXPECT_NE(initial_rfh, second_rfh);
+  } else {
+    EXPECT_EQ(initial_rfh, second_rfh);
+  }
 
   // Perform a cross-site navigation.
   {
@@ -352,10 +360,10 @@
   }
 
   // The RenderFrameHost should have changed.
-  EXPECT_NE(initial_rfh, static_cast<WebContentsImpl*>(shell()->web_contents())
-                             ->GetFrameTree()
-                             ->root()
-                             ->current_frame_host());
+  EXPECT_NE(second_rfh, static_cast<WebContentsImpl*>(shell()->web_contents())
+                            ->GetFrameTree()
+                            ->root()
+                            ->current_frame_host());
 }
 
 // Ensure that renderer initiated same-site navigations work.
@@ -378,6 +386,9 @@
           ->root()
           ->current_frame_host();
 
+  auto initial_rfh_routing_id = GlobalFrameRoutingId(
+      initial_rfh->GetProcess()->GetID(), initial_rfh->GetRoutingID());
+
   // Simulate clicking on a same-site link.
   {
     TestNavigationObserver observer(shell()->web_contents());
@@ -394,16 +405,34 @@
     RenderFrameHost* main_rfh = shell()->web_contents()->GetMainFrame();
     EXPECT_EQ(main_rfh->GetLastCommittedOrigin(),
               observer.last_initiator_origin());
-    EXPECT_EQ(GlobalFrameRoutingId(main_rfh->GetProcess()->GetID(),
-                                   main_rfh->GetRoutingID()),
-              observer.last_initiator_routing_id());
+
+    if (CanSameSiteMainFrameNavigationsChangeRenderFrameHosts()) {
+      // If same-site ProactivelySwapBrowsingInstance or main-frame
+      // RenderDocument is enabled, the navigation will result in a new RFH, so
+      // we need to compare with |initial_rfh|.
+      EXPECT_NE(main_rfh, initial_rfh);
+      EXPECT_EQ(initial_rfh_routing_id, observer.last_initiator_routing_id());
+    } else {
+      EXPECT_EQ(main_rfh, initial_rfh);
+      EXPECT_EQ(GlobalFrameRoutingId(main_rfh->GetProcess()->GetID(),
+                                     main_rfh->GetRoutingID()),
+                observer.last_initiator_routing_id());
+    }
   }
 
-  // The RenderFrameHost should not have changed.
-  EXPECT_EQ(initial_rfh, static_cast<WebContentsImpl*>(shell()->web_contents())
-                             ->GetFrameTree()
-                             ->root()
-                             ->current_frame_host());
+  RenderFrameHost* second_rfh =
+      static_cast<WebContentsImpl*>(shell()->web_contents())
+          ->GetFrameTree()
+          ->root()
+          ->current_frame_host();
+
+  if (CanSameSiteMainFrameNavigationsChangeRenderFrameHosts()) {
+    // If same-site ProactivelySwapBrowsingInstance or main-frame RenderDocument
+    // is enabled, the navigation will result in a new RFH.
+    EXPECT_NE(initial_rfh, second_rfh);
+  } else {
+    EXPECT_EQ(initial_rfh, second_rfh);
+  }
 }
 
 // Ensure that renderer initiated cross-site navigations work.
diff --git a/content/browser/push_messaging/push_messaging_manager.cc b/content/browser/push_messaging/push_messaging_manager.cc
index 01f5cdc8..8b851d2 100644
--- a/content/browser/push_messaging/push_messaging_manager.cc
+++ b/content/browser/push_messaging/push_messaging_manager.cc
@@ -535,7 +535,7 @@
         FROM_HERE, ServiceWorkerContext::GetCoreThreadId(),
         base::BindOnce(&PushMessagingManager::PersistRegistrationOnSW,
                        sw_parent_, std::move(data), push_subscription_id,
-                       endpoint, p256dh, auth,
+                       endpoint, expiration_time, p256dh, auth,
                        subscription_changed
                            ? blink::mojom::PushRegistrationStatus::
                                  SUCCESS_NEW_SUBSCRIPTION_FROM_PUSH_SERVICE
@@ -555,6 +555,7 @@
     RegisterData data,
     const std::string& push_subscription_id,
     const GURL& endpoint,
+    const base::Optional<base::Time>& expiration_time,
     const std::vector<uint8_t>& p256dh,
     const std::vector<uint8_t>& auth,
     blink::mojom::PushRegistrationStatus status) {
@@ -571,12 +572,13 @@
        {kPushSenderIdServiceWorkerKey, application_server_key}},
       base::BindOnce(&PushMessagingManager::DidPersistRegistrationOnSW,
                      weak_factory_.GetWeakPtr(), std::move(data), endpoint,
-                     p256dh, auth, status));
+                     expiration_time, p256dh, auth, status));
 }
 
 void PushMessagingManager::DidPersistRegistrationOnSW(
     RegisterData data,
     const GURL& endpoint,
+    const base::Optional<base::Time>& expiration_time,
     const std::vector<uint8_t>& p256dh,
     const std::vector<uint8_t>& auth,
     blink::mojom::PushRegistrationStatus push_registration_status,
@@ -584,7 +586,7 @@
   DCHECK_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId());
   if (service_worker_status == blink::ServiceWorkerStatusCode::kOk) {
     SendSubscriptionSuccess(std::move(data), push_registration_status, endpoint,
-                            p256dh, auth);
+                            expiration_time, p256dh, auth);
   } else {
     // TODO(johnme): Unregister, so PushMessagingServiceImpl can decrease count.
     SendSubscriptionError(std::move(data),
@@ -604,6 +606,7 @@
     RegisterData data,
     blink::mojom::PushRegistrationStatus status,
     const GURL& endpoint,
+    const base::Optional<base::Time>& expiration_time,
     const std::vector<uint8_t>& p256dh,
     const std::vector<uint8_t>& auth) {
   DCHECK_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId());
@@ -619,8 +622,8 @@
 
   std::move(data.callback)
       .Run(status, blink::mojom::PushSubscription::New(
-                       endpoint, base::nullopt /* expiration_time */,
-                       std::move(data.options), p256dh, auth));
+                       endpoint, expiration_time, std::move(data.options),
+                       p256dh, auth));
 
   RecordRegistrationStatus(status);
 }
diff --git a/content/browser/push_messaging/push_messaging_manager.h b/content/browser/push_messaging/push_messaging_manager.h
index 5d0ca75d..33cac78c 100644
--- a/content/browser/push_messaging/push_messaging_manager.h
+++ b/content/browser/push_messaging/push_messaging_manager.h
@@ -78,16 +78,19 @@
       blink::ServiceWorkerStatusCode service_worker_status);
 
   // Called via PostTask from UI thread.
-  void PersistRegistrationOnSW(RegisterData data,
-                               const std::string& push_subscription_id,
-                               const GURL& endpoint,
-                               const std::vector<uint8_t>& p256dh,
-                               const std::vector<uint8_t>& auth,
-                               blink::mojom::PushRegistrationStatus status);
+  void PersistRegistrationOnSW(
+      RegisterData data,
+      const std::string& push_subscription_id,
+      const GURL& endpoint,
+      const base::Optional<base::Time>& expiration_time,
+      const std::vector<uint8_t>& p256dh,
+      const std::vector<uint8_t>& auth,
+      blink::mojom::PushRegistrationStatus status);
 
   void DidPersistRegistrationOnSW(
       RegisterData data,
       const GURL& endpoint,
+      const base::Optional<base::Time>& expiration_time,
       const std::vector<uint8_t>& p256dh,
       const std::vector<uint8_t>& auth,
       blink::mojom::PushRegistrationStatus push_registration_status,
@@ -97,11 +100,13 @@
   void SendSubscriptionError(RegisterData data,
                              blink::mojom::PushRegistrationStatus status);
   // Called both from "SW core" thread, and via PostTask from UI thread.
-  void SendSubscriptionSuccess(RegisterData data,
-                               blink::mojom::PushRegistrationStatus status,
-                               const GURL& endpoint,
-                               const std::vector<uint8_t>& p256dh,
-                               const std::vector<uint8_t>& auth);
+  void SendSubscriptionSuccess(
+      RegisterData data,
+      blink::mojom::PushRegistrationStatus status,
+      const GURL& endpoint,
+      const base::Optional<base::Time>& expiration_time,
+      const std::vector<uint8_t>& p256dh,
+      const std::vector<uint8_t>& auth);
 
   void UnsubscribeHavingGottenSenderId(
       UnsubscribeCallback callback,
diff --git a/content/browser/renderer_host/render_widget_host_impl.cc b/content/browser/renderer_host/render_widget_host_impl.cc
index de4fd2c..4de45c4 100644
--- a/content/browser/renderer_host/render_widget_host_impl.cc
+++ b/content/browser/renderer_host/render_widget_host_impl.cc
@@ -95,6 +95,7 @@
 #include "gpu/GLES2/gl2extchromium.h"
 #include "gpu/command_buffer/service/gpu_switches.h"
 #include "gpu/ipc/common/gpu_messages.h"
+#include "mojo/public/cpp/bindings/callback_helpers.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/system/platform_handle.h"
 #include "net/base/filename_util.h"
@@ -632,6 +633,7 @@
   blink_frame_widget_host_receiver_.reset();
   blink_frame_widget_.reset();
   frame_widget_input_handler_.reset();
+  widget_compositor_.reset();
   return std::make_pair(
       blink_frame_widget_host_receiver_.BindNewEndpointAndPassRemote(),
       blink_frame_widget_.BindNewEndpointAndPassReceiver());
@@ -646,6 +648,7 @@
   blink_frame_widget_host_receiver_.reset();
   blink_frame_widget_.reset();
   frame_widget_input_handler_.reset();
+  widget_compositor_.reset();
   blink_frame_widget_host_receiver_.Bind(std::move(frame_widget_host));
   blink_frame_widget_.Bind(std::move(frame_widget));
 }
@@ -2894,6 +2897,23 @@
   return false;
 }
 
+void RenderWidgetHostImpl::InsertVisualStateCallback(
+    VisualStateCallback callback) {
+  if (!blink_frame_widget_) {
+    std::move(callback).Run(false);
+    return;
+  }
+
+  if (!widget_compositor_) {
+    blink_frame_widget_->BindWidgetCompositor(
+        widget_compositor_.BindNewPipeAndPassReceiver());
+  }
+
+  widget_compositor_->VisualStateRequest(base::BindOnce(
+      [](VisualStateCallback callback) { std::move(callback).Run(true); },
+      mojo::WrapCallbackWithDefaultInvokeIfNotRun(std::move(callback), false)));
+}
+
 const mojo::AssociatedRemote<blink::mojom::FrameWidget>&
 RenderWidgetHostImpl::GetAssociatedFrameWidget() {
   return blink_frame_widget_;
diff --git a/content/browser/renderer_host/render_widget_host_impl.h b/content/browser/renderer_host/render_widget_host_impl.h
index 8236256..ec4696ba 100644
--- a/content/browser/renderer_host/render_widget_host_impl.h
+++ b/content/browser/renderer_host/render_widget_host_impl.h
@@ -16,6 +16,7 @@
 #include <vector>
 
 #include "base/callback.h"
+#include "base/containers/flat_map.h"
 #include "base/containers/flat_set.h"
 #include "base/containers/queue.h"
 #include "base/gtest_prod_util.h"
@@ -785,6 +786,14 @@
   // Add/ClearPendingUserActivation() for details.
   bool RemovePendingUserActivationIfAvailable();
 
+  // Roundtrips through the renderer and compositor pipeline to ensure that any
+  // changes to the contents resulting from operations executed prior to this
+  // call are visible on screen. The call completes asynchronously by running
+  // the supplied |callback| with a value of true upon successful completion and
+  // false otherwise when the widget is destroyed.
+  using VisualStateCallback = base::OnceCallback<void(bool)>;
+  void InsertVisualStateCallback(VisualStateCallback callback);
+
   const mojo::AssociatedRemote<blink::mojom::FrameWidget>&
   GetAssociatedFrameWidget();
 
@@ -1326,6 +1335,8 @@
       blink_widget_host_receiver_{this};
   mojo::AssociatedRemote<blink::mojom::Widget> blink_widget_;
 
+  mojo::Remote<blink::mojom::WidgetCompositor> widget_compositor_;
+
   base::WeakPtrFactory<RenderWidgetHostImpl> weak_factory_{this};
 
   DISALLOW_COPY_AND_ASSIGN(RenderWidgetHostImpl);
diff --git a/content/browser/service_worker/service_worker_container_host_unittest.cc b/content/browser/service_worker/service_worker_container_host_unittest.cc
index 9c7ef4e9..55a738a 100644
--- a/content/browser/service_worker/service_worker_container_host_unittest.cc
+++ b/content/browser/service_worker/service_worker_container_host_unittest.cc
@@ -39,7 +39,7 @@
 #include "mojo/public/cpp/system/functions.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/common/features.h"
-#include "third_party/blink/public/common/tokens/worker_tokens.h"
+#include "third_party/blink/public/common/tokens/tokens.h"
 #include "third_party/blink/public/mojom/service_worker/service_worker_object.mojom.h"
 #include "third_party/blink/public/mojom/service_worker/service_worker_registration.mojom.h"
 #include "url/url_util.h"
diff --git a/content/browser/service_worker/service_worker_main_resource_loader_interceptor.h b/content/browser/service_worker/service_worker_main_resource_loader_interceptor.h
index 7d94972..9d4ed77 100644
--- a/content/browser/service_worker/service_worker_main_resource_loader_interceptor.h
+++ b/content/browser/service_worker/service_worker_main_resource_loader_interceptor.h
@@ -17,7 +17,7 @@
 #include "content/public/common/child_process_host.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
-#include "third_party/blink/public/common/tokens/worker_tokens.h"
+#include "third_party/blink/public/common/tokens/tokens.h"
 #include "third_party/blink/public/mojom/loader/resource_load_info.mojom-shared.h"
 #include "third_party/blink/public/mojom/service_worker/service_worker_provider.mojom.h"
 
diff --git a/content/browser/tracing/tracing_controller_browsertest.cc b/content/browser/tracing/tracing_controller_browsertest.cc
index 8ea005f..662da43 100644
--- a/content/browser/tracing/tracing_controller_browsertest.cc
+++ b/content/browser/tracing/tracing_controller_browsertest.cc
@@ -352,6 +352,7 @@
 #define MAYBE_EnableAndStopTracingWithEmptyFile \
   DISABLED_EnableAndStopTracingWithEmptyFile
 #define MAYBE_DoubleStopTracing DISABLED_DoubleStopTracing
+#define MAYBE_ProcessesPresentInTrace DISABLED_ProcessesPresentInTrace
 #else
 #define MAYBE_EnableAndStopTracing EnableAndStopTracing
 #define MAYBE_DisableRecordingStoresMetadata DisableRecordingStoresMetadata
@@ -362,6 +363,7 @@
 #define MAYBE_EnableAndStopTracingWithEmptyFile \
   EnableAndStopTracingWithEmptyFile
 #define MAYBE_DoubleStopTracing DoubleStopTracing
+#define MAYBE_ProcessesPresentInTrace ProcessesPresentInTrace
 #endif
 
 IN_PROC_BROWSER_TEST_F(TracingControllerTest, GetCategories) {
@@ -501,10 +503,7 @@
   EXPECT_TRUE(last_data().find("systemTraceEvents") != std::string::npos);
 }
 
-// TODO(crbug.com/1107612): Disabled due to flakiness. Also fails consistently
-// on Android Asan (crbug.com/1045519).
-IN_PROC_BROWSER_TEST_F(TracingControllerTest,
-                       DISABLED_ProcessesPresentInTrace) {
+IN_PROC_BROWSER_TEST_F(TracingControllerTest, MAYBE_ProcessesPresentInTrace) {
   TestStartAndStopTracingString();
   EXPECT_TRUE(last_data().find("CrBrowserMain") != std::string::npos);
   EXPECT_TRUE(last_data().find("CrRendererMain") != std::string::npos);
diff --git a/content/browser/tracing/tracing_ui.cc b/content/browser/tracing/tracing_ui.cc
index 1363a25..3ca7b7f 100644
--- a/content/browser/tracing/tracing_ui.cc
+++ b/content/browser/tracing/tracing_ui.cc
@@ -80,10 +80,11 @@
     g_tracing_session = perfetto::Tracing::NewTrace().release();
     g_tracing_session->Setup(tracing::GetDefaultPerfettoConfig(trace_config));
 
-    auto shared_callback(std::make_shared<WebUIDataSource::GotDataCallback>(
-        std::move(callback)));
+    auto shared_callback = base::MakeRefCounted<
+        base::RefCountedData<WebUIDataSource::GotDataCallback>>(
+        std::move(callback));
     g_tracing_session->SetOnStartCallback([shared_callback] {
-      OnRecordingEnabledAck(std::move(*shared_callback));
+      OnRecordingEnabledAck(std::move(shared_callback->data));
     });
     g_tracing_session->Start();
     return true;
@@ -104,8 +105,9 @@
   if (g_tracing_session) {
     // |callback| is move-only, so in order to pass it through a copied lambda
     // we need to temporarily move it on the heap.
-    auto shared_callback(std::make_shared<WebUIDataSource::GotDataCallback>(
-        std::move(callback)));
+    auto shared_callback = base::MakeRefCounted<
+        base::RefCountedData<WebUIDataSource::GotDataCallback>>(
+        std::move(callback));
     g_tracing_session->GetTraceStats(
         [shared_callback](
             perfetto::TracingSession::GetTraceStatsCallbackArgs args) {
@@ -117,7 +119,7 @@
             double percent_full = tracing::GetTraceBufferUsage(trace_stats);
             usage = base::NumberToString(percent_full);
           }
-          std::move(*shared_callback)
+          std::move(shared_callback->data)
               .Run(base::RefCountedString::TakeString(&usage));
         });
     return true;
diff --git a/content/browser/web_contents/web_contents_impl_browsertest.cc b/content/browser/web_contents/web_contents_impl_browsertest.cc
index cec3daa..4ddeec0d 100644
--- a/content/browser/web_contents/web_contents_impl_browsertest.cc
+++ b/content/browser/web_contents/web_contents_impl_browsertest.cc
@@ -34,6 +34,7 @@
 #include "content/browser/renderer_host/render_widget_host_input_event_router.h"
 #include "content/browser/web_contents/web_contents_impl.h"
 #include "content/browser/web_contents/web_contents_view.h"
+#include "content/common/content_navigation_policy.h"
 #include "content/common/frame.mojom-test-utils.h"
 #include "content/common/frame_messages.h"
 #include "content/common/page_messages.h"
@@ -1230,10 +1231,15 @@
   EXPECT_CALL(observer, OnPageScaleFactorChanged(::testing::FloatEq(1.5)));
   observer.WaitForPageScaleUpdate();
 
-  // Navigate to reset the page scale factor.
-  shell()->LoadURL(embedded_test_server()->GetURL("/title2.html"));
-  EXPECT_CALL(observer, OnPageScaleFactorChanged(::testing::_));
-  observer.WaitForPageScaleUpdate();
+  if (!CanSameSiteMainFrameNavigationsChangeRenderFrameHosts()) {
+    // Navigate to reset the page scale factor. We'll only get the
+    // OnPageScaleFactorChanged if we reuse the same RenderFrameHost, which will
+    // not happen if ProactivelySwapBrowsingInstance or RenderDocument is
+    // enabled for same-site main frame navigations.
+    shell()->LoadURL(embedded_test_server()->GetURL("/title2.html"));
+    EXPECT_CALL(observer, OnPageScaleFactorChanged(::testing::_));
+    observer.WaitForPageScaleUpdate();
+  }
 }
 
 // Test that a direct navigation to a view-source URL works.
diff --git a/content/browser/web_contents/web_contents_observer_browsertest.cc b/content/browser/web_contents/web_contents_observer_browsertest.cc
index 44078c4..cf382d66 100644
--- a/content/browser/web_contents/web_contents_observer_browsertest.cc
+++ b/content/browser/web_contents/web_contents_observer_browsertest.cc
@@ -14,6 +14,7 @@
 #include "content/public/common/content_features.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/content_browser_test.h"
+#include "content/public/test/test_utils.h"
 #include "content/shell/browser/shell.h"
 #include "content/test/test_content_browser_client.h"
 #include "net/dns/mock_host_resolver.h"
@@ -558,21 +559,25 @@
   // 2) Load a page with subresource. Both the page and the resource should get
   // a cookie.
   EXPECT_TRUE(NavigateToURL(web_contents(), url2));
-
+  // If the RFH changes after navigation, the cookie will be attributed to a
+  // different frame.
+  int frame_id_index =
+      CanSameSiteMainFrameNavigationsChangeRenderFrameHosts() ? 1 : 0;
   cookie_tracker.WaitForCookies(2);
-  EXPECT_THAT(cookie_tracker.cookie_accesses(),
-              testing::ElementsAre(
-                  CookieAccess{CookieAccessDetails::Type::kRead,
-                               ContextType::kNavigation,
-                               {},
-                               cookie_tracker.navigation_id(1),
-                               url2,
-                               first_party_url,
-                               "foo",
-                               "bar"},
-                  CookieAccess{CookieAccessDetails::Type::kRead,
-                               ContextType::kFrame, cookie_tracker.frame_id(0),
-                               -1, url2_image, first_party_url, "foo", "bar"}));
+  EXPECT_THAT(
+      cookie_tracker.cookie_accesses(),
+      testing::ElementsAre(
+          CookieAccess{CookieAccessDetails::Type::kRead,
+                       ContextType::kNavigation,
+                       {},
+                       cookie_tracker.navigation_id(1),
+                       url2,
+                       first_party_url,
+                       "foo",
+                       "bar"},
+          CookieAccess{CookieAccessDetails::Type::kRead, ContextType::kFrame,
+                       cookie_tracker.frame_id(frame_id_index), -1, url2_image,
+                       first_party_url, "foo", "bar"}));
   cookie_tracker.cookie_accesses().clear();
 }
 
diff --git a/content/browser/worker_host/dedicated_worker_host.cc b/content/browser/worker_host/dedicated_worker_host.cc
index 153192c..f7039bd 100644
--- a/content/browser/worker_host/dedicated_worker_host.cc
+++ b/content/browser/worker_host/dedicated_worker_host.cc
@@ -31,7 +31,7 @@
 #include "mojo/public/cpp/system/message_pipe.h"
 #include "net/base/isolation_info.h"
 #include "third_party/blink/public/common/features.h"
-#include "third_party/blink/public/common/tokens/worker_tokens.h"
+#include "third_party/blink/public/common/tokens/tokens.h"
 #include "third_party/blink/public/mojom/loader/fetch_client_settings_object.mojom.h"
 
 namespace content {
diff --git a/content/browser/worker_host/dedicated_worker_host.h b/content/browser/worker_host/dedicated_worker_host.h
index 62031989..fb5e88f 100644
--- a/content/browser/worker_host/dedicated_worker_host.h
+++ b/content/browser/worker_host/dedicated_worker_host.h
@@ -19,7 +19,7 @@
 #include "mojo/public/cpp/bindings/receiver.h"
 #include "mojo/public/cpp/bindings/remote.h"
 #include "services/network/public/cpp/cross_origin_embedder_policy.h"
-#include "third_party/blink/public/common/tokens/worker_tokens.h"
+#include "third_party/blink/public/common/tokens/tokens.h"
 #include "third_party/blink/public/mojom/idle/idle_manager.mojom-forward.h"
 #include "third_party/blink/public/mojom/loader/content_security_notifier.mojom.h"
 #include "third_party/blink/public/mojom/sms/sms_receiver.mojom-forward.h"
diff --git a/content/browser/worker_host/dedicated_worker_service_impl_unittest.cc b/content/browser/worker_host/dedicated_worker_service_impl_unittest.cc
index 17c8e64..f96f57aa 100644
--- a/content/browser/worker_host/dedicated_worker_service_impl_unittest.cc
+++ b/content/browser/worker_host/dedicated_worker_service_impl_unittest.cc
@@ -20,8 +20,8 @@
 #include "mojo/public/cpp/bindings/remote.h"
 #include "mojo/public/cpp/bindings/self_owned_receiver.h"
 #include "third_party/blink/public/common/features.h"
-#include "third_party/blink/public/common/tokens/worker_tokens.h"
-#include "third_party/blink/public/common/tokens/worker_tokens_mojom_traits.h"
+#include "third_party/blink/public/common/tokens/tokens.h"
+#include "third_party/blink/public/common/tokens/tokens_mojom_traits.h"
 #include "third_party/blink/public/mojom/worker/dedicated_worker_host_factory.mojom.h"
 #include "third_party/blink/public/mojom/worker/worker_main_script_load_params.mojom.h"
 
diff --git a/content/browser/worker_host/shared_worker_host.h b/content/browser/worker_host/shared_worker_host.h
index d524c95..b7e5464 100644
--- a/content/browser/worker_host/shared_worker_host.h
+++ b/content/browser/worker_host/shared_worker_host.h
@@ -29,7 +29,7 @@
 #include "mojo/public/cpp/bindings/remote.h"
 #include "services/metrics/public/cpp/ukm_source_id.h"
 #include "services/network/public/mojom/url_loader_factory.mojom.h"
-#include "third_party/blink/public/common/tokens/worker_tokens.h"
+#include "third_party/blink/public/common/tokens/tokens.h"
 #include "third_party/blink/public/mojom/appcache/appcache.mojom.h"
 #include "third_party/blink/public/mojom/devtools/devtools_agent.mojom.h"
 #include "third_party/blink/public/mojom/payments/payment_app.mojom-forward.h"
diff --git a/content/browser/worker_host/shared_worker_service_impl.h b/content/browser/worker_host/shared_worker_service_impl.h
index 634a906..0d517bc 100644
--- a/content/browser/worker_host/shared_worker_service_impl.h
+++ b/content/browser/worker_host/shared_worker_service_impl.h
@@ -22,7 +22,7 @@
 #include "services/metrics/public/cpp/ukm_source_id.h"
 #include "services/network/public/mojom/url_loader_factory.mojom.h"
 #include "services/network/public/mojom/url_response_head.mojom.h"
-#include "third_party/blink/public/common/tokens/worker_tokens.h"
+#include "third_party/blink/public/common/tokens/tokens.h"
 #include "third_party/blink/public/mojom/loader/fetch_client_settings_object.mojom.h"
 #include "third_party/blink/public/mojom/worker/shared_worker_connector.mojom.h"
 #include "third_party/blink/public/mojom/worker/shared_worker_factory.mojom.h"
diff --git a/content/browser/worker_host/worker_script_fetch_initiator.h b/content/browser/worker_host/worker_script_fetch_initiator.h
index 5b51a8e..712f2ff 100644
--- a/content/browser/worker_host/worker_script_fetch_initiator.h
+++ b/content/browser/worker_host/worker_script_fetch_initiator.h
@@ -18,7 +18,7 @@
 #include "services/network/public/mojom/url_loader_factory.mojom.h"
 #include "services/network/public/mojom/url_response_head.mojom.h"
 #include "third_party/blink/public/common/loader/url_loader_throttle.h"
-#include "third_party/blink/public/common/tokens/worker_tokens.h"
+#include "third_party/blink/public/common/tokens/tokens.h"
 #include "third_party/blink/public/mojom/loader/fetch_client_settings_object.mojom.h"
 #include "third_party/blink/public/mojom/loader/resource_load_info.mojom-shared.h"
 #include "third_party/blink/public/mojom/service_worker/controller_service_worker.mojom.h"
diff --git a/content/browser/worker_host/worker_script_loader.h b/content/browser/worker_host/worker_script_loader.h
index 44ff24ea..257d3ba 100644
--- a/content/browser/worker_host/worker_script_loader.h
+++ b/content/browser/worker_host/worker_script_loader.h
@@ -22,7 +22,7 @@
 #include "net/url_request/url_request.h"
 #include "services/network/public/cpp/resource_request.h"
 #include "services/network/public/mojom/url_loader.mojom.h"
-#include "third_party/blink/public/common/tokens/worker_tokens.h"
+#include "third_party/blink/public/common/tokens/tokens.h"
 
 namespace blink {
 class ThrottlingURLLoader;
diff --git a/content/browser/worker_host/worker_script_loader_factory.h b/content/browser/worker_host/worker_script_loader_factory.h
index 9c4de27..9347730 100644
--- a/content/browser/worker_host/worker_script_loader_factory.h
+++ b/content/browser/worker_host/worker_script_loader_factory.h
@@ -11,7 +11,7 @@
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "services/network/public/mojom/url_loader_factory.mojom.h"
-#include "third_party/blink/public/common/tokens/worker_tokens.h"
+#include "third_party/blink/public/common/tokens/tokens.h"
 
 namespace network {
 class SharedURLLoaderFactory;
diff --git a/content/browser/worker_host/worker_script_loader_factory_unittest.cc b/content/browser/worker_host/worker_script_loader_factory_unittest.cc
index d1e5bd6..0ac1f5a 100644
--- a/content/browser/worker_host/worker_script_loader_factory_unittest.cc
+++ b/content/browser/worker_host/worker_script_loader_factory_unittest.cc
@@ -19,7 +19,7 @@
 #include "services/network/public/cpp/wrapper_shared_url_loader_factory.h"
 #include "services/network/test/test_url_loader_client.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/blink/public/common/tokens/worker_tokens.h"
+#include "third_party/blink/public/common/tokens/tokens.h"
 
 namespace content {
 
diff --git a/content/common/frame.mojom b/content/common/frame.mojom
index 613f026..b2102804 100644
--- a/content/common/frame.mojom
+++ b/content/common/frame.mojom
@@ -42,7 +42,7 @@
 import "third_party/blink/public/mojom/loader/referrer.mojom";
 import "third_party/blink/public/mojom/service_worker/controller_service_worker.mojom";
 import "third_party/blink/public/mojom/service_worker/service_worker_container.mojom";
-import "third_party/blink/public/mojom/tokens/portal_token.mojom";
+import "third_party/blink/public/mojom/tokens/tokens.mojom";
 import "third_party/blink/public/mojom/widget/visual_properties.mojom";
 import "third_party/blink/public/mojom/window_features/window_features.mojom";
 import "ui/accessibility/mojom/ax_tree_update.mojom";
diff --git a/content/common/frame_messages.h b/content/common/frame_messages.h
index 0973d39..eea8b6a 100644
--- a/content/common/frame_messages.h
+++ b/content/common/frame_messages.h
@@ -426,10 +426,6 @@
                     content::CustomContextMenuContext /* custom_context */,
                     unsigned /* action */)
 
-// Requests that the RenderFrame send back a response after waiting for the
-// commit, activation and frame swap of the current DOM tree in blink.
-IPC_MESSAGE_ROUTED1(FrameMsg_VisualStateRequest, uint64_t /* id */)
-
 #if BUILDFLAG(ENABLE_PLUGINS)
 // Notifies the renderer of updates to the Plugin Power Saver origin allowlist.
 IPC_MESSAGE_ROUTED1(FrameMsg_UpdatePluginContentOriginAllowlist,
@@ -634,10 +630,6 @@
                     uint32_t /* the offset of the text in the document */,
                     gfx::Range /* selection range in the document */)
 
-// Sent as a response to FrameMsg_VisualStateRequest.
-// The message is delivered using RenderWidget::QueueMessage.
-IPC_MESSAGE_ROUTED1(FrameHostMsg_VisualStateResponse, uint64_t /* id */)
-
 // Adding a new message? Stick to the sort order above: first platform
 // independent FrameMsg, then ifdefs for platform specific FrameMsg, then
 // platform independent FrameHostMsg, then ifdefs for platform specific
diff --git a/content/public/android/java/src/org/chromium/content/browser/input/ThreadedInputConnectionProxyView.java b/content/public/android/java/src/org/chromium/content/browser/input/ThreadedInputConnectionProxyView.java
index aa3f6f9..2c28bdb 100644
--- a/content/public/android/java/src/org/chromium/content/browser/input/ThreadedInputConnectionProxyView.java
+++ b/content/public/android/java/src/org/chromium/content/browser/input/ThreadedInputConnectionProxyView.java
@@ -22,7 +22,7 @@
  * This is a fake View that is only exposed to InputMethodManager.
  */
 public class ThreadedInputConnectionProxyView extends View {
-    private static final String TAG = "Ime";
+    private static final String TAG = "ImeProxyView";
     private static final boolean DEBUG_LOGS = false;
 
     private final Handler mImeThreadHandler;
@@ -55,6 +55,7 @@
     }
 
     public void onOriginalViewWindowFocusChanged(boolean gainFocus) {
+        if (DEBUG_LOGS) Log.w(TAG, "onOriginalViewWindowFocusChanged: " + gainFocus);
         mWindowFocused.set(gainFocus);
     }
 
@@ -100,17 +101,19 @@
 
     @Override
     public boolean hasWindowFocus() {
-        if (DEBUG_LOGS) Log.w(TAG, "hasWindowFocus");
-        return mWindowFocused.get();
+        boolean focused = mWindowFocused.get();
+        if (DEBUG_LOGS) Log.w(TAG, "hasWindowFocus: " + focused);
+        return focused;
     }
 
     @Override
     public View getRootView() {
-        if (DEBUG_LOGS) Log.w(TAG, "getRootView");
         // Returning a null here matches mCurRootView being null value in InputMethodManager,
         // which represents that the current focused window is not IME target window.
         // In this case, you are still able to type.
-        return mWindowFocused.get() ? mRootView.get() : null;
+        View rootView = mWindowFocused.get() ? mRootView.get() : null;
+        if (DEBUG_LOGS) Log.w(TAG, "getRootView: " + rootView);
+        return rootView;
     }
 
     @Override
@@ -122,8 +125,9 @@
 
     @Override
     public boolean isFocused() {
-        if (DEBUG_LOGS) Log.w(TAG, "isFocused");
-        return mFocused.get();
+        boolean focused = mFocused.get();
+        if (DEBUG_LOGS) Log.w(TAG, "isFocused: " + focused);
+        return focused;
     }
 
     @Override
diff --git a/content/public/browser/dedicated_worker_service.h b/content/public/browser/dedicated_worker_service.h
index 6bc6a34..de8bec1 100644
--- a/content/public/browser/dedicated_worker_service.h
+++ b/content/public/browser/dedicated_worker_service.h
@@ -8,7 +8,7 @@
 #include "base/observer_list_types.h"
 #include "content/common/content_export.h"
 #include "content/public/browser/global_routing_id.h"
-#include "third_party/blink/public/common/tokens/worker_tokens.h"
+#include "third_party/blink/public/common/tokens/tokens.h"
 
 class GURL;
 
diff --git a/content/public/browser/render_frame_host.h b/content/public/browser/render_frame_host.h
index f285e96..eb08034 100644
--- a/content/public/browser/render_frame_host.h
+++ b/content/public/browser/render_frame_host.h
@@ -305,7 +305,7 @@
   // changes to the contents resulting from operations executed prior to this
   // call are visible on screen. The call completes asynchronously by running
   // the supplied |callback| with a value of true upon successful completion and
-  // false otherwise (when the frame is destroyed, detached, etc..).
+  // false otherwise when the widget is destroyed.
   using VisualStateCallback = base::OnceCallback<void(bool)>;
   virtual void InsertVisualStateCallback(VisualStateCallback callback) = 0;
 
diff --git a/content/public/browser/service_worker_client_info.h b/content/public/browser/service_worker_client_info.h
index 5998925..217f2f2d 100644
--- a/content/public/browser/service_worker_client_info.h
+++ b/content/public/browser/service_worker_client_info.h
@@ -7,7 +7,7 @@
 
 #include "content/common/content_export.h"
 #include "content/public/browser/render_frame_host.h"
-#include "third_party/blink/public/common/tokens/worker_tokens.h"
+#include "third_party/blink/public/common/tokens/tokens.h"
 #include "third_party/blink/public/mojom/service_worker/service_worker_client.mojom.h"
 
 namespace content {
diff --git a/content/public/browser/shared_worker_service.h b/content/public/browser/shared_worker_service.h
index 688e73b..277c156 100644
--- a/content/public/browser/shared_worker_service.h
+++ b/content/public/browser/shared_worker_service.h
@@ -10,7 +10,7 @@
 #include "base/observer_list_types.h"
 #include "content/common/content_export.h"
 #include "content/public/browser/global_routing_id.h"
-#include "third_party/blink/public/common/tokens/worker_tokens.h"
+#include "third_party/blink/public/common/tokens/tokens.h"
 
 class GURL;
 
diff --git a/content/public/test/fake_frame_widget.h b/content/public/test/fake_frame_widget.h
index c52cc1ac..369d8fac 100644
--- a/content/public/test/fake_frame_widget.h
+++ b/content/public/test/fake_frame_widget.h
@@ -63,6 +63,9 @@
   void EnableDeviceEmulation(
       const blink::DeviceEmulationParams& parameters) override {}
   void DisableDeviceEmulation() override {}
+  void BindWidgetCompositor(
+      mojo::PendingReceiver<blink::mojom::WidgetCompositor> receiver) override {
+  }
 
   mojo::AssociatedReceiver<blink::mojom::FrameWidget> receiver_;
   base::i18n::TextDirection text_direction_ =
diff --git a/content/public/test/test_utils.cc b/content/public/test/test_utils.cc
index d0e3f75..9614ae9 100644
--- a/content/public/test/test_utils.cc
+++ b/content/public/test/test_utils.cc
@@ -220,6 +220,17 @@
   return IsProactivelySwapBrowsingInstanceOnSameSiteNavigationEnabled();
 }
 
+void DisableProactiveBrowsingInstanceSwapFor(RenderFrameHost* rfh) {
+  if (!CanSameSiteMainFrameNavigationsChangeSiteInstances())
+    return;
+  // If the RFH is not a main frame, navigations on it will never result in a
+  // proactive BrowsingInstance swap, so we shouldn't really call it on main
+  // frames.
+  DCHECK(!rfh->GetParent());
+  static_cast<RenderFrameHostImpl*>(rfh)
+      ->DisableProactiveBrowsingInstanceSwapForTesting();
+}
+
 GURL GetWebUIURL(const std::string& host) {
   return GURL(GetWebUIURLString(host));
 }
diff --git a/content/public/test/test_utils.h b/content/public/test/test_utils.h
index f92c22d..44f2913 100644
--- a/content/public/test/test_utils.h
+++ b/content/public/test/test_utils.h
@@ -112,6 +112,11 @@
 // above, this will not be true when RenderDocument for main-frame is enabled.
 bool CanSameSiteMainFrameNavigationsChangeSiteInstances();
 
+// Makes sure that navigations that start in |rfh| won't result in a proactive
+// BrowsingInstance swap (note they might still result in a normal
+// BrowsingInstance swap, e.g. in the case of cross-site navigations).
+void DisableProactiveBrowsingInstanceSwapFor(RenderFrameHost* rfh);
+
 // Returns a GURL constructed from the WebUI scheme and the given host.
 GURL GetWebUIURL(const std::string& host);
 
diff --git a/content/public/test/web_contents_tester.h b/content/public/test/web_contents_tester.h
index 92e9030..ef19451e 100644
--- a/content/public/test/web_contents_tester.h
+++ b/content/public/test/web_contents_tester.h
@@ -12,7 +12,7 @@
 #include "content/public/browser/site_instance.h"
 #include "content/public/browser/web_contents.h"
 #include "third_party/blink/public/common/input/web_input_event.h"
-#include "third_party/blink/public/common/tokens/portal_token.h"
+#include "third_party/blink/public/common/tokens/tokens.h"
 #include "third_party/blink/public/mojom/loader/pause_subresource_loading_handle.mojom.h"
 #include "ui/base/page_transition_types.h"
 
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc
index cc5f3bb7..6a515a15 100644
--- a/content/renderer/render_frame_impl.cc
+++ b/content/renderer/render_frame_impl.cc
@@ -2236,8 +2236,6 @@
 #if BUILDFLAG(ENABLE_PLUGINS)
     IPC_MESSAGE_HANDLER(FrameMsg_SetPepperVolume, OnSetPepperVolume)
 #endif
-    IPC_MESSAGE_HANDLER(FrameMsg_VisualStateRequest,
-                        OnVisualStateRequest)
     IPC_MESSAGE_HANDLER(FrameMsg_MixedContentFound, OnMixedContentFound)
     IPC_MESSAGE_HANDLER(UnfreezableFrameMsg_Delete, OnDeleteFrame)
   IPC_END_MESSAGE_MAP()
@@ -2631,11 +2629,6 @@
   return base::Value();
 }
 
-void RenderFrameImpl::OnVisualStateRequest(uint64_t id) {
-  GetLocalRootRenderWidget()->QueueMessage(
-      std::make_unique<FrameHostMsg_VisualStateResponse>(routing_id_, id));
-}
-
 void RenderFrameImpl::OnPortalActivated(
     const blink::PortalToken& portal_token,
     mojo::PendingAssociatedRemote<blink::mojom::Portal> portal,
diff --git a/content/renderer/render_frame_impl.h b/content/renderer/render_frame_impl.h
index 8bd0812e..270adb5c 100644
--- a/content/renderer/render_frame_impl.h
+++ b/content/renderer/render_frame_impl.h
@@ -1004,7 +1004,6 @@
   void OnMoveCaret(const gfx::Point& point);
   void OnScrollFocusedEditableNodeIntoRect(const gfx::Rect& rect);
   void OnSelectRange(const gfx::Point& base, const gfx::Point& extent);
-  void OnVisualStateRequest(uint64_t key);
   void OnSuppressFurtherDialogs();
   void OnMixedContentFound(const FrameMsg_MixedContentFound_Params& params);
 
diff --git a/content/renderer/render_widget.cc b/content/renderer/render_widget.cc
index aa7b701a..31e8817 100644
--- a/content/renderer/render_widget.cc
+++ b/content/renderer/render_widget.cc
@@ -464,21 +464,6 @@
 
 void RenderWidget::UpdateVisualProperties(
     const blink::VisualProperties& visual_properties) {
-  // Inform the rendering thread of the color space indicating the presence of
-  // HDR capabilities. The HDR bit happens to be globally true/false for all
-  // browser windows (on Windows OS) and thus would be the same for all
-  // RenderWidgets, so clobbering each other works out since only the HDR bit is
-  // used. See https://crbug.com/803451 and
-  // https://chromium-review.googlesource.com/c/chromium/src/+/852912/15#message-68bbd3e25c3b421a79cd028b2533629527d21fee
-  //
-  // The RenderThreadImpl can be null in tests.
-  {
-    RenderThreadImpl* render_thread = RenderThreadImpl::current();
-    if (render_thread)
-      render_thread->SetRenderingColorSpace(
-          visual_properties.screen_info.color_space);
-  }
-
   if (delegate()) {
     if (size_ != visual_properties.new_size) {
       // Only hide popups when the size changes. Eg https://crbug.com/761908.
diff --git a/content/renderer/renderer_blink_platform_impl.cc b/content/renderer/renderer_blink_platform_impl.cc
index 47a9dac..c5cf22d0 100644
--- a/content/renderer/renderer_blink_platform_impl.cc
+++ b/content/renderer/renderer_blink_platform_impl.cc
@@ -904,6 +904,15 @@
   return render_thread->GetGpuFactories();
 }
 
+void RendererBlinkPlatformImpl::SetRenderingColorSpace(
+    const gfx::ColorSpace& color_space) {
+  auto* render_thread = RenderThreadImpl::current();
+  if (!render_thread)
+    return;
+
+  render_thread->SetRenderingColorSpace(color_space);
+}
+
 //------------------------------------------------------------------------------
 
 blink::mojom::CodeCacheHost& RendererBlinkPlatformImpl::GetCodeCacheHost() {
diff --git a/content/renderer/renderer_blink_platform_impl.h b/content/renderer/renderer_blink_platform_impl.h
index f48c402..762195e 100644
--- a/content/renderer/renderer_blink_platform_impl.h
+++ b/content/renderer/renderer_blink_platform_impl.h
@@ -208,6 +208,7 @@
       scoped_refptr<network::SharedURLLoaderFactory> factory) override;
 
   media::GpuVideoAcceleratorFactories* GetGpuFactories() override;
+  void SetRenderingColorSpace(const gfx::ColorSpace& color_space) override;
 
   // Tells this platform that the renderer is locked to a site (i.e., a scheme
   // plus eTLD+1, such as https://google.com), or to a more specific origin.
diff --git a/content/renderer/worker/dedicated_worker_host_factory_client.cc b/content/renderer/worker/dedicated_worker_host_factory_client.cc
index 236fd72f..98506b7 100644
--- a/content/renderer/worker/dedicated_worker_host_factory_client.cc
+++ b/content/renderer/worker/dedicated_worker_host_factory_client.cc
@@ -16,13 +16,13 @@
 #include "third_party/blink/public/common/features.h"
 #include "third_party/blink/public/common/loader/feature_utils.h"
 #include "third_party/blink/public/common/loader/worker_main_script_load_parameters.h"
-#include "third_party/blink/public/common/tokens/worker_tokens.h"
-#include "third_party/blink/public/common/tokens/worker_tokens_mojom_traits.h"
+#include "third_party/blink/public/common/tokens/tokens.h"
+#include "third_party/blink/public/common/tokens/tokens_mojom_traits.h"
 #include "third_party/blink/public/mojom/blob/blob_url_store.mojom.h"
 #include "third_party/blink/public/mojom/loader/fetch_client_settings_object.mojom.h"
 #include "third_party/blink/public/mojom/service_worker/controller_service_worker.mojom.h"
 #include "third_party/blink/public/mojom/service_worker/service_worker_provider.mojom.h"
-#include "third_party/blink/public/mojom/tokens/worker_tokens.mojom.h"
+#include "third_party/blink/public/mojom/tokens/tokens.mojom.h"
 #include "third_party/blink/public/mojom/worker/worker_main_script_load_params.mojom.h"
 #include "third_party/blink/public/platform/web_dedicated_worker.h"
 #include "third_party/blink/public/platform/web_url.h"
diff --git a/content/renderer/worker/embedded_shared_worker_stub.h b/content/renderer/worker/embedded_shared_worker_stub.h
index 791c20a1..7ba6c8e 100644
--- a/content/renderer/worker/embedded_shared_worker_stub.h
+++ b/content/renderer/worker/embedded_shared_worker_stub.h
@@ -17,7 +17,7 @@
 #include "mojo/public/cpp/bindings/remote.h"
 #include "services/metrics/public/cpp/ukm_source_id.h"
 #include "services/network/public/mojom/url_loader_factory.mojom-forward.h"
-#include "third_party/blink/public/common/tokens/worker_tokens.h"
+#include "third_party/blink/public/common/tokens/tokens.h"
 #include "third_party/blink/public/common/user_agent/user_agent_metadata.h"
 #include "third_party/blink/public/mojom/browser_interface_broker.mojom-forward.h"
 #include "third_party/blink/public/mojom/devtools/devtools_agent.mojom-forward.h"
diff --git a/content/shell/browser/web_test/web_test_push_messaging_service.cc b/content/shell/browser/web_test/web_test_push_messaging_service.cc
index 401fb327..ff59a236 100644
--- a/content/shell/browser/web_test/web_test_push_messaging_service.cc
+++ b/content/shell/browser/web_test/web_test_push_messaging_service.cc
@@ -40,8 +40,11 @@
 static_assert(sizeof(kAuthentication) == 12,
               "The fake authentication key must be at least 12 bytes in size.");
 
+const int64_t kTestExpirationWindowInDays = 90;
+
 base::Time GetFutureTime() {
-  return base::Time::Now() + base::TimeDelta::FromDays(100);
+  return base::Time::Now() +
+         base::TimeDelta::FromDays(kTestExpirationWindowInDays);
 }
 
 }  // anonymous namespace
diff --git a/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt
index 3ce8bc7..079e2b5 100644
--- a/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt
@@ -379,6 +379,10 @@
 crbug.com/1010942 [ win amd vulkan passthrough ] conformance/glsl/samplers/glsl-function-texture2dproj.html [ Failure ]
 crbug.com/angleproject/4286 [ win amd vulkan passthrough ] conformance/rendering/out-of-bounds-array-buffers.html [ Failure ]
 
+# Vulkan / Win10 / Intel / Passthrough
+# Flaking since an identified Angle roll.
+crbug.com/1111652 [ win10 intel vulkan passthrough ] conformance/context/context-size-change.html [ RetryOnFailure ]
+
 ####################
 # Mac failures     #
 ####################
diff --git a/docs/README.md b/docs/README.md
index 2c89081..90753e4 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -110,6 +110,7 @@
 *   [Code Reviews](code_reviews.md) - Code review requirements and guidelines
 *   [Respectful Code Reviews](cr_respect.md) - A guide for code reviewers
 *   [Respectful Changes](cl_respect.md) - A guide for code authors
+*   [Mandatory Code-Review Rollout](code_review_owners.md) - Upcoming policy changes related to code review and OWNERS
 *   [LUCI Migration FAQ](luci_migration_faq.md) - FAQ on Buildbot-to-LUCI
     builder migration for Chromium
 *   [Tour of Continuous Integration UI](tour_of_luci_ui.md) - A tour of our
diff --git a/docs/code_review_owners.md b/docs/code_review_owners.md
new file mode 100644
index 0000000..ed0cf25
--- /dev/null
+++ b/docs/code_review_owners.md
@@ -0,0 +1,15 @@
+# Mandatory Code-Review and OWNERS
+
+Beginning in Q1 2021, committers@ of Chromium will no longer be able
+to circumvent code review and OWNERS approval on CLs.
+
+Currently, these are circumventable by self-code-review and because the
+enforcement is done by presumit, although rarely done by external
+contributors. In Q1, Gerrit will disallow both bypasses. As part of the
+transition, an audit service will automatically file bugs for CLs that
+land with only self-approval, launching in Q4 2020. Within Google, where
+these bypasses are more common, Googlers can find Google-specific
+information in the internal announcements and landing site.
+
+Periodic updates and FAQs will be sent to chromium-dev@chromium.org
+and updated on this page.
\ No newline at end of file
diff --git a/docs/code_reviews.md b/docs/code_reviews.md
index 0c6df1b..a42add2 100644
--- a/docs/code_reviews.md
+++ b/docs/code_reviews.md
@@ -4,7 +4,9 @@
 All changes must be reviewed.
 
 The general patch, upload, and land process is covered in more detail in the
-[contributing code](contributing.md) page.
+[contributing code](contributing.md) page. To learn about upcoming code review
+and OWNERS policy changes, see
+[Mandatory code review and OWNERS](code_review_owners.md).
 
 # Code review policies
 
diff --git a/extensions/browser/updater/extension_downloader_delegate.h b/extensions/browser/updater/extension_downloader_delegate.h
index bf0380a8..0f18403 100644
--- a/extensions/browser/updater/extension_downloader_delegate.h
+++ b/extensions/browser/updater/extension_downloader_delegate.h
@@ -63,7 +63,10 @@
   // DOWNLOADING_CRX_RETRY -> DOWNLOADING_CRX -> FINISHED.
   // Note: enum used for UMA. Do NOT reorder or remove entries. Don't forget to
   // update enums.xml (name: ExtensionInstallationDownloadingStage) when adding
-  // new entries.
+  // new entries. Don't forget to update device_management_backend.proto (name:
+  // ExtensionInstallReportLogEvent::DownloadingStage) when adding new entries.
+  // Don't forget to update ConvertDownloadingStageToProto method in
+  // ExtensionInstallEventLogCollector.
   enum class Stage {
     // Downloader just received extension download request.
     PENDING = 0,
diff --git a/gpu/command_buffer/service/external_semaphore.cc b/gpu/command_buffer/service/external_semaphore.cc
index f691221..8bfe986 100644
--- a/gpu/command_buffer/service/external_semaphore.cc
+++ b/gpu/command_buffer/service/external_semaphore.cc
@@ -199,10 +199,4 @@
   return semaphore_;
 }
 
-VkSemaphore ExternalSemaphore::TakeVkSemaphore() {
-  VkSemaphore semaphore = GetVkSemaphore();
-  semaphore_ = VK_NULL_HANDLE;
-  return semaphore;
-}
-
 }  // namespace gpu
diff --git a/gpu/command_buffer/service/external_semaphore.h b/gpu/command_buffer/service/external_semaphore.h
index df4e6d5..723da3ac 100644
--- a/gpu/command_buffer/service/external_semaphore.h
+++ b/gpu/command_buffer/service/external_semaphore.h
@@ -45,10 +45,6 @@
   // Get a VkSemaphore. The ownership is not transferred to caller.
   VkSemaphore GetVkSemaphore();
 
-  // Take the VkSemaphore. The ownership is transferred to caller. The caller is
-  // responsible for releasing it.
-  VkSemaphore TakeVkSemaphore();
-
   bool is_valid() const { return context_provider_ && handle_.is_valid(); }
   SemaphoreHandle handle() { return handle_.Duplicate(); }
 
diff --git a/gpu/command_buffer/service/external_vk_image_skia_representation.cc b/gpu/command_buffer/service/external_vk_image_skia_representation.cc
index f5df3ca..5a88fd6 100644
--- a/gpu/command_buffer/service/external_vk_image_skia_representation.cc
+++ b/gpu/command_buffer/service/external_vk_image_skia_representation.cc
@@ -157,7 +157,7 @@
 
   for (auto& external_semaphore : begin_access_semaphores_) {
     DCHECK(external_semaphore);
-    VkSemaphore semaphore = external_semaphore.TakeVkSemaphore();
+    VkSemaphore semaphore = external_semaphore.GetVkSemaphore();
     DCHECK(semaphore != VK_NULL_HANDLE);
     // The ownership of semaphore is passed to caller.
     begin_semaphores->emplace_back();
diff --git a/gpu/command_buffer/service/raster_decoder.cc b/gpu/command_buffer/service/raster_decoder.cc
index 934f5ce..6c00779a 100644
--- a/gpu/command_buffer/service/raster_decoder.cc
+++ b/gpu/command_buffer/service/raster_decoder.cc
@@ -260,7 +260,8 @@
 
     if (!begin_semaphores.empty()) {
       bool result = output_surface_->wait(begin_semaphores.size(),
-                                          begin_semaphores.data());
+                                          begin_semaphores.data(),
+                                          /*deleteSemaphoresAfterWait=*/false);
       DCHECK(result);
     }
 
@@ -2299,8 +2300,9 @@
           &begin_semaphores, &end_semaphores);
 
   if (!begin_semaphores.empty()) {
-    bool result = dest_scoped_access->surface()->wait(begin_semaphores.size(),
-                                                      begin_semaphores.data());
+    bool result = dest_scoped_access->surface()->wait(
+        begin_semaphores.size(), begin_semaphores.data(),
+        /*deleteSemaphoresAfterWait=*/false);
     DCHECK(result);
   }
 
@@ -2422,8 +2424,9 @@
   }
 
   if (!begin_semaphores.empty()) {
-    bool result = dest_scoped_access->surface()->wait(begin_semaphores.size(),
-                                                      begin_semaphores.data());
+    bool result = dest_scoped_access->surface()->wait(
+        begin_semaphores.size(), begin_semaphores.data(),
+        /*deleteSemaphoresAfterWait=*/false);
     if (!result) {
       LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, "glWritePixels",
                          "Unable to obtain write access to dest shared image.");
@@ -2529,7 +2532,8 @@
 
   if (!begin_semaphores.empty()) {
     bool result = shared_context_state_->gr_context()->wait(
-        begin_semaphores.size(), begin_semaphores.data());
+        begin_semaphores.size(), begin_semaphores.data(),
+        /*deleteSemaphoresAfterWait=*/false);
     DCHECK(result);
   }
 
@@ -2694,7 +2698,8 @@
   auto* dest_surface = dest_scoped_access->surface();
   if (!begin_semaphores.empty()) {
     bool result =
-        dest_surface->wait(begin_semaphores.size(), begin_semaphores.data());
+        dest_surface->wait(begin_semaphores.size(), begin_semaphores.data(),
+                           /*deleteSemaphoresAfterWait=*/false);
     DCHECK(result);
   }
 
@@ -2890,7 +2895,8 @@
 
   if (!begin_semaphores.empty()) {
     bool result =
-        sk_surface_->wait(begin_semaphores.size(), begin_semaphores.data());
+        sk_surface_->wait(begin_semaphores.size(), begin_semaphores.data(),
+                          /*deleteSemaphoresAfterWait=*/false);
     DCHECK(result);
   }
 
diff --git a/gpu/command_buffer/service/shared_image_representation.h b/gpu/command_buffer/service/shared_image_representation.h
index b52622d..c309b90 100644
--- a/gpu/command_buffer/service/shared_image_representation.h
+++ b/gpu/command_buffer/service/shared_image_representation.h
@@ -296,7 +296,7 @@
  protected:
   // Begin the write access. The implementations should insert semaphores into
   // begin_semaphores vector which client will wait on before writing the
-  // backing. The ownership of begin_semaphores will be passed to client.
+  // backing. The ownership of begin_semaphores is not passed to client.
   // The implementations can also optionally insert semaphores into
   // end_semaphores. If using end_semaphores, the client must submit them with
   // drawing operations which use the backing. The ownership of end_semaphores
@@ -319,7 +319,7 @@
 
   // Begin the read access. The implementations should insert semaphores into
   // begin_semaphores vector which client will wait on before reading the
-  // backing. The ownership of begin_semaphores will be passed to client.
+  // backing. The ownership of begin_semaphores is not passed to client.
   // The implementations can also optionally insert semaphores into
   // end_semaphores. If using end_semaphores, the client must submit them with
   // drawing operations which use the backing. The ownership of end_semaphores
diff --git a/gpu/command_buffer/service/shared_image_representation_skia_vk_android.cc b/gpu/command_buffer/service/shared_image_representation_skia_vk_android.cc
index 35d8bc9..0858d324 100644
--- a/gpu/command_buffer/service/shared_image_representation_skia_vk_android.cc
+++ b/gpu/command_buffer/service/shared_image_representation_skia_vk_android.cc
@@ -169,13 +169,13 @@
   }
 
   sync_fd = gl::MergeFDs(std::move(sync_fd), std::move(init_read_fence));
-  VkSemaphore begin_access_semaphore = VK_NULL_HANDLE;
+  DCHECK(begin_access_semaphore_ == VK_NULL_HANDLE);
   if (sync_fd.is_valid()) {
-    begin_access_semaphore = vk_implementation()->ImportSemaphoreHandle(
+    begin_access_semaphore_ = vk_implementation()->ImportSemaphoreHandle(
         vk_device(),
         SemaphoreHandle(VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT,
                         std::move(sync_fd)));
-    if (begin_access_semaphore == VK_NULL_HANDLE) {
+    if (begin_access_semaphore_ == VK_NULL_HANDLE) {
       DLOG(ERROR) << "Failed to import semaphore from sync_fd.";
       return false;
     }
@@ -187,17 +187,18 @@
 
     if (end_access_semaphore_ == VK_NULL_HANDLE) {
       DLOG(ERROR) << "Failed to create the external semaphore.";
-      if (begin_access_semaphore != VK_NULL_HANDLE) {
-        vkDestroySemaphore(vk_device(), begin_access_semaphore,
+      if (begin_access_semaphore_ != VK_NULL_HANDLE) {
+        vkDestroySemaphore(vk_device(), begin_access_semaphore_,
                            nullptr /* pAllocator */);
+        begin_access_semaphore_ = VK_NULL_HANDLE;
       }
       return false;
     }
   }
 
-  if (begin_access_semaphore != VK_NULL_HANDLE) {
+  if (begin_access_semaphore_ != VK_NULL_HANDLE) {
     begin_semaphores->emplace_back();
-    begin_semaphores->back().initVulkan(begin_access_semaphore);
+    begin_semaphores->back().initVulkan(begin_access_semaphore_);
   }
   if (end_semaphores) {
     end_semaphores->emplace_back();
@@ -224,14 +225,24 @@
     android_backing()->EndWrite(std::move(sync_fd));
   }
 
+  std::vector<VkSemaphore> semaphores;
+  semaphores.reserve(2);
+  if (begin_access_semaphore_ != VK_NULL_HANDLE) {
+    semaphores.emplace_back(begin_access_semaphore_);
+    begin_access_semaphore_ = VK_NULL_HANDLE;
+  }
   if (end_access_semaphore_ != VK_NULL_HANDLE) {
+    semaphores.emplace_back(end_access_semaphore_);
+    end_access_semaphore_ = VK_NULL_HANDLE;
+  }
+  if (!semaphores.empty()) {
     VulkanFenceHelper* fence_helper = context_state_->vk_context_provider()
                                           ->GetDeviceQueue()
                                           ->GetFenceHelper();
-    fence_helper->EnqueueSemaphoreCleanupForSubmittedWork(
-        end_access_semaphore_);
-    end_access_semaphore_ = VK_NULL_HANDLE;
+    fence_helper->EnqueueSemaphoresCleanupForSubmittedWork(
+        std::move(semaphores));
   }
+
   mode_ = RepresentationAccessMode::kNone;
 }
 
diff --git a/gpu/command_buffer/service/shared_image_representation_skia_vk_android.h b/gpu/command_buffer/service/shared_image_representation_skia_vk_android.h
index 0c4f35f9..d2f1c64 100644
--- a/gpu/command_buffer/service/shared_image_representation_skia_vk_android.h
+++ b/gpu/command_buffer/service/shared_image_representation_skia_vk_android.h
@@ -70,6 +70,7 @@
   int surface_msaa_count_ = 0;
   sk_sp<SkSurface> surface_;
   scoped_refptr<SharedContextState> context_state_;
+  VkSemaphore begin_access_semaphore_ = VK_NULL_HANDLE;
   VkSemaphore end_access_semaphore_ = VK_NULL_HANDLE;
 };
 
diff --git a/infra/scripts/build_directory.py b/infra/scripts/build_directory.py
index 23847be..e50ec4a0 100644
--- a/infra/scripts/build_directory.py
+++ b/infra/scripts/build_directory.py
@@ -52,7 +52,7 @@
   assert not cros_board, "'cros_board' not supported on this platform"
 
   if sys.platform == 'cygwin' or sys.platform.startswith('win') or (
-      sys.platorm == 'darwin'):
+      sys.platform == 'darwin'):
     return os.path.join(src_dir, 'out')
 
   raise NotImplementedError('Unexpected platform %s' % sys.platform)
diff --git a/ios/chrome/app/app_startup_parameters.h b/ios/chrome/app/app_startup_parameters.h
index edbf9505..dc56835 100644
--- a/ios/chrome/app/app_startup_parameters.h
+++ b/ios/chrome/app/app_startup_parameters.h
@@ -36,6 +36,10 @@
 // |externalURL|.
 @property(nonatomic, readonly, assign) const GURL& completeURL;
 
+// The list of URLs to open. First URL in the vector is the same
+// as |externalURL|.
+@property(nonatomic, readonly, assign) const std::vector<GURL>& URLs;
+
 // The URL query string parameters in the case that the app was launched as a
 // result of Universal Link navigation. The map associates query string
 // parameters with their corresponding value.
@@ -64,6 +68,8 @@
 
 - (instancetype)initWithUniversalLink:(const GURL&)universalLink;
 
+- (instancetype)initWithURLs:(const std::vector<GURL>&)URLs;
+
 @end
 
 #endif  // IOS_CHROME_APP_APP_STARTUP_PARAMETERS_H_
diff --git a/ios/chrome/app/app_startup_parameters.mm b/ios/chrome/app/app_startup_parameters.mm
index 26710b9..7f033e4a 100644
--- a/ios/chrome/app/app_startup_parameters.mm
+++ b/ios/chrome/app/app_startup_parameters.mm
@@ -17,6 +17,7 @@
 @implementation AppStartupParameters {
   GURL _externalURL;
   GURL _completeURL;
+  std::vector<GURL> _URLs;
 }
 
 @synthesize externalURLParams = _externalURLParams;
@@ -44,6 +45,20 @@
   return self;
 }
 
+- (instancetype)initWithURLs:(const std::vector<GURL>&)URLs {
+  if (URLs.empty()) {
+    self = [self initWithExternalURL:GURL(kChromeUINewTabURL)
+                         completeURL:GURL(kChromeUINewTabURL)];
+  } else {
+    self = [self initWithExternalURL:URLs.front() completeURL:URLs.front()];
+  }
+
+  if (self) {
+    _URLs = URLs;
+  }
+  return self;
+}
+
 // TODO(crbug.com/1021752): Remove this stub since |universalLink| is unused.
 - (instancetype)initWithUniversalLink:(const GURL&)universalLink {
   // If a new tab with |_externalURL| needs to be opened after the App
diff --git a/ios/chrome/app/application_delegate/metric_kit_subscriber_unittest.mm b/ios/chrome/app/application_delegate/metric_kit_subscriber_unittest.mm
index f844b9a..3af38df 100644
--- a/ios/chrome/app/application_delegate/metric_kit_subscriber_unittest.mm
+++ b/ios/chrome/app/application_delegate/metric_kit_subscriber_unittest.mm
@@ -114,6 +114,7 @@
     tester.ExpectBucketCount("IOS.MetricKit.TimeToFirstDraw", 5, 2);
     tester.ExpectBucketCount("IOS.MetricKit.TimeToFirstDraw", 15, 4);
 
+#if defined(__IPHONE_14_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_14_0
     if (base::ios::IsRunningOnIOS14OrLater()) {
       tester.ExpectTotalCount("IOS.MetricKit.BackgroundExitData", 71);
       tester.ExpectBucketCount("IOS.MetricKit.BackgroundExitData", 2, 1);
@@ -135,6 +136,7 @@
       tester.ExpectBucketCount("IOS.MetricKit.ForegroundExitData", 8, 18);
       tester.ExpectBucketCount("IOS.MetricKit.ForegroundExitData", 2, 19);
     }
+#endif
   }
 }
 
diff --git a/ios/chrome/app/application_delegate/mock_tab_opener.h b/ios/chrome/app/application_delegate/mock_tab_opener.h
index 85f0173..0a3c152 100644
--- a/ios/chrome/app/application_delegate/mock_tab_opener.h
+++ b/ios/chrome/app/application_delegate/mock_tab_opener.h
@@ -13,7 +13,8 @@
 
 // Mocks a class adopting the TabOpening protocol. It saves the arguments of
 // -dismissModalsAndOpenSelectedTabInMode:withUrlLoadParams:dismissOmnibox:
-//  completion:.
+//  completion:. Can also save the arguments of
+// -dismissModalsAndOpenMultipleTabsInMode:URLs:dismissOmnibox:completion:.
 @interface MockTabOpener : NSObject<TabOpening>
 // Arguments for
 // -dismissModalsAndOpenSelectedTabInMode:withUrlLoadParams:dismissOmnibox:
@@ -21,6 +22,9 @@
 @property(nonatomic, readonly) UrlLoadParams urlLoadParams;
 @property(nonatomic, readonly) ApplicationModeForTabOpening applicationMode;
 @property(nonatomic, strong, readonly) void (^completionBlock)(void);
+// Argument for
+// -dismissModalsAndOpenMultipleTabsInMode:URLs:dismissOmnibox:completion:.
+@property(nonatomic, readonly) const std::vector<GURL>& URLs;
 
 // Clear the URL.
 - (void)resetURL;
diff --git a/ios/chrome/app/application_delegate/mock_tab_opener.mm b/ios/chrome/app/application_delegate/mock_tab_opener.mm
index 0a6e6f96..c8e923fd 100644
--- a/ios/chrome/app/application_delegate/mock_tab_opener.mm
+++ b/ios/chrome/app/application_delegate/mock_tab_opener.mm
@@ -7,6 +7,7 @@
 #include "base/ios/block_types.h"
 #include "ios/chrome/app/application_mode.h"
 #import "ios/chrome/browser/url_loading/url_loading_params.h"
+#import "net/base/mac/url_conversions.h"
 #include "ui/base/page_transition_types.h"
 #include "url/gurl.h"
 
@@ -14,7 +15,9 @@
 #error "This file requires ARC support."
 #endif
 
-@implementation MockTabOpener
+@implementation MockTabOpener {
+  std::vector<GURL> _URLs;
+}
 
 - (void)dismissModalsAndOpenSelectedTabInMode:
             (ApplicationModeForTabOpening)targetMode
@@ -25,6 +28,15 @@
   _urlLoadParams = urlLoadParams;
   _applicationMode = targetMode;
   _completionBlock = [completion copy];
+  _URLs.push_back(urlLoadParams.web_params.url);
+}
+
+- (void)dismissModalsAndOpenMultipleTabsInMode:
+            (ApplicationModeForTabOpening)targetMode
+                                          URLs:(const std::vector<GURL>&)URLs
+                                dismissOmnibox:(BOOL)dismissOmnibox
+                                    completion:(ProceduralBlock)completion {
+  _URLs = URLs;
 }
 
 - (void)resetURL {
diff --git a/ios/chrome/app/application_delegate/tab_opening.h b/ios/chrome/app/application_delegate/tab_opening.h
index f7b847a4..ca7624ea 100644
--- a/ios/chrome/app/application_delegate/tab_opening.h
+++ b/ios/chrome/app/application_delegate/tab_opening.h
@@ -31,6 +31,15 @@
                                dismissOmnibox:(BOOL)dismissOmnibox
                                    completion:(ProceduralBlock)completion;
 
+// Dismisses any modal view, excluding the omnibox if |dismissOmnibox| is NO,
+// then opens the list of URLs in |URLs| in either normal or incognito.
+// After opening the array of URLs, run completion |handler| if it not nil.
+- (void)dismissModalsAndOpenMultipleTabsInMode:
+            (ApplicationModeForTabOpening)targetMode
+                                          URLs:(const std::vector<GURL>&)URLs
+                                dismissOmnibox:(BOOL)dismissOmnibox
+                                    completion:(ProceduralBlock)completion;
+
 // Creates a new tab if the launch options are not null.
 - (void)openTabFromLaunchWithParams:(URLOpenerParams*)params
                  startupInformation:(id<StartupInformation>)startupInformation
diff --git a/ios/chrome/app/application_delegate/user_activity_handler.mm b/ios/chrome/app/application_delegate/user_activity_handler.mm
index 135626e..901efa3 100644
--- a/ios/chrome/app/application_delegate/user_activity_handler.mm
+++ b/ios/chrome/app/application_delegate/user_activity_handler.mm
@@ -19,6 +19,7 @@
 #import "ios/chrome/app/application_delegate/startup_information.h"
 #import "ios/chrome/app/application_delegate/tab_opening.h"
 #include "ios/chrome/app/application_mode.h"
+#import "ios/chrome/app/intents/OpenInChromeIncognitoIntent.h"
 #import "ios/chrome/app/intents/OpenInChromeIntent.h"
 #import "ios/chrome/app/intents/SearchInChromeIntent.h"
 #import "ios/chrome/app/spotlight/actions_spotlight_manager.h"
@@ -55,6 +56,11 @@
 NSString* const kShortcutVoiceSearch = @"OpenVoiceSearch";
 NSString* const kShortcutQRScanner = @"OpenQRScanner";
 
+// Constants for Siri shortcut.
+NSString* const kSiriShortcutOpenInChrome = @"OpenInChromeIntent";
+NSString* const kSiriShortcutSearchInChrome = @"SearchInChromeIntent";
+NSString* const kSiriShortcutOpenInIncognito = @"OpenInChromeIncognitoIntent";
+
 }  // namespace
 
 @interface UserActivityHandler ()
@@ -143,7 +149,7 @@
       return YES;
     }
   } else if ([userActivity.activityType
-                 isEqualToString:@"SearchInChromeIntent"]) {
+                 isEqualToString:kSiriShortcutSearchInChrome]) {
     base::RecordAction(UserMetricsAction("IOSLaunchedBySearchInChromeIntent"));
 
     AppStartupParameters* startupParams = [[AppStartupParameters alloc]
@@ -166,7 +172,7 @@
     webpageURL =
         [NSURL URLWithString:base::SysUTF8ToNSString(kChromeUINewTabURL)];
   } else if ([userActivity.activityType
-                 isEqualToString:@"OpenInChromeIntent"]) {
+                 isEqualToString:kSiriShortcutOpenInChrome]) {
     base::RecordAction(UserMetricsAction("IOSLaunchedByOpenInChromeIntent"));
     OpenInChromeIntent* intent = base::mac::ObjCCastStrict<OpenInChromeIntent>(
         userActivity.interaction.intent);
@@ -182,6 +188,28 @@
                                               completeURL:webpageGURL];
     [connectionInformation setStartupParameters:startupParams];
     webpageURL = intent.url;
+  } else if ([userActivity.activityType
+                 isEqualToString:kSiriShortcutOpenInIncognito]) {
+    base::RecordAction(UserMetricsAction("IOSLaunchedByOpenInIncognitoIntent"));
+    OpenInChromeIncognitoIntent* intent =
+        base::mac::ObjCCastStrict<OpenInChromeIncognitoIntent>(
+            userActivity.interaction.intent);
+
+    if (!intent.urls || intent.urls.count == 0) {
+      return NO;
+    }
+
+    std::vector<GURL> URLs;
+    for (NSURL* URL in intent.urls) {
+      URLs.push_back(net::GURLWithNSURL(URL));
+    }
+
+    AppStartupParameters* startupParams =
+        [[AppStartupParameters alloc] initWithURLs:URLs];
+    startupParams.launchInIncognito = YES;
+    [connectionInformation setStartupParameters:startupParams];
+    return YES;
+
   } else {
     // Do nothing for unknown activity type.
     return NO;
@@ -282,8 +310,39 @@
   // Do not load the external URL if the user has not accepted the terms of
   // service. This corresponds to the case when the user installed Chrome,
   // has never launched it and attempts to open an external URL in Chrome.
-  if ([startupInformation isPresentingFirstRunUI])
+  if ([startupInformation isPresentingFirstRunUI]) {
     return;
+  }
+
+  if (!connectionInformation.startupParameters.URLs.empty()) {
+    ApplicationModeForTabOpening mode =
+        connectionInformation.startupParameters.launchInIncognito
+            ? ApplicationModeForTabOpening::INCOGNITO
+            : ApplicationModeForTabOpening::NORMAL;
+
+    BOOL dismissOmnibox = [[connectionInformation startupParameters]
+                              postOpeningAction] != FOCUS_OMNIBOX;
+
+    // Using a weak reference to |connectionInformation| to solve a memory leak
+    // issue. |tabOpener| and |connectionInformation| are the same object in
+    // some cases (SceneController). This retains the object while the block
+    // exists. Then this block is passed around and in some cases it ends up
+    // stored in BrowserViewController. This results in a memory leak that looks
+    // like this: SceneController -> BrowserViewWrangler -> BrowserCoordinator
+    // -> BrowserViewController -> SceneController
+    __weak id<ConnectionInformation> weakConnectionInfo = connectionInformation;
+
+    [tabOpener
+        dismissModalsAndOpenMultipleTabsInMode:mode
+                                          URLs:weakConnectionInfo
+                                                   .startupParameters.URLs
+                                dismissOmnibox:dismissOmnibox
+                                    completion:^{
+                                      weakConnectionInfo.startupParameters =
+                                          nil;
+                                    }];
+    return;
+  }
 
   GURL externalURL = connectionInformation.startupParameters.externalURL;
   // Check if it's an U2F call. If so, route it to correct tab.
diff --git a/ios/chrome/app/application_delegate/user_activity_handler_unittest.mm b/ios/chrome/app/application_delegate/user_activity_handler_unittest.mm
index 983bd20..ae811ad 100644
--- a/ios/chrome/app/application_delegate/user_activity_handler_unittest.mm
+++ b/ios/chrome/app/application_delegate/user_activity_handler_unittest.mm
@@ -20,6 +20,7 @@
 #include "ios/chrome/app/application_delegate/startup_information.h"
 #include "ios/chrome/app/application_delegate/tab_opening.h"
 #include "ios/chrome/app/application_mode.h"
+#import "ios/chrome/app/intents/OpenInChromeIncognitoIntent.h"
 #import "ios/chrome/app/intents/OpenInChromeIntent.h"
 #include "ios/chrome/app/main_controller.h"
 #include "ios/chrome/app/spotlight/actions_spotlight_manager.h"
@@ -451,6 +452,64 @@
   }
 }
 
+// Tests that Chrome responds to open in incognito intent in the background
+TEST_F(UserActivityHandlerTest, ContinueUserActivityIntentIncognitoBackground) {
+  NSURL* url1 = [[NSURL alloc] initWithString:@"http://www.google.com"];
+  NSURL* url2 = [[NSURL alloc] initWithString:@"http://www.apple.com"];
+  NSURL* url3 = [[NSURL alloc] initWithString:@"http://www.espn.com"];
+  NSArray<NSURL*>* urls = [NSArray arrayWithObjects:url1, url2, url3, nil];
+
+  NSUserActivity* userActivity = [[NSUserActivity alloc]
+      initWithActivityType:@"OpenInChromeIncognitoIntent"];
+
+  OpenInChromeIncognitoIntent* intent =
+      [[OpenInChromeIncognitoIntent alloc] init];
+
+  intent.urls = urls;
+
+  INInteraction* interaction = [[INInteraction alloc] initWithIntent:intent
+                                                            response:nil];
+
+  userActivity.interaction = interaction;
+
+  id startupInformationMock =
+      [OCMockObject niceMockForProtocol:@protocol(StartupInformation)];
+
+  id connectionInformationMock =
+      [OCMockObject niceMockForProtocol:@protocol(ConnectionInformation)];
+
+  [[connectionInformationMock expect]
+      setStartupParameters:[OCMArg checkWithBlock:^BOOL(id value) {
+        EXPECT_TRUE([value isKindOfClass:[AppStartupParameters class]] ||
+                    value == nil);
+
+        if (value != nil) {
+          AppStartupParameters* startupParameters =
+              (AppStartupParameters*)value;
+          const GURL calledURL = startupParameters.externalURL;
+          EXPECT_TRUE((int)[intent.urls count] == 3);
+          return [intent.urls containsObject:(net::NSURLWithGURL(calledURL))];
+        } else {
+          return YES;
+        }
+      }]];
+
+  [[[startupInformationMock stub] andReturnValue:@NO] isPresentingFirstRunUI];
+
+  MockTabOpener* tabOpener = [[MockTabOpener alloc] init];
+
+  // Action.
+  BOOL result =
+      [UserActivityHandler continueUserActivity:userActivity
+                            applicationIsActive:NO
+                                      tabOpener:tabOpener
+                          connectionInformation:connectionInformationMock
+                             startupInformation:startupInformationMock];
+
+  EXPECT_OCMOCK_VERIFY(startupInformationMock);
+  EXPECT_TRUE(result);
+}
+
 // Tests that Chrome responds to open intents in the background.
 TEST_F(UserActivityHandlerTest, ContinueUserActivityIntentBackground) {
   NSUserActivity* userActivity =
@@ -491,6 +550,65 @@
   EXPECT_TRUE(result);
 }
 
+// Test that Chrome respond to open in incognito intent in the foreground.
+TEST_F(UserActivityHandlerTest, ContinueUserActivityIntentIncognitoForeground) {
+  NSURL* url1 = [[NSURL alloc] initWithString:@"http://www.google.com"];
+  NSURL* url2 = [[NSURL alloc] initWithString:@"http://www.apple.com"];
+  NSURL* url3 = [[NSURL alloc] initWithString:@"http://www.espn.com"];
+  NSArray<NSURL*>* urls = [NSArray arrayWithObjects:url1, url2, url3, nil];
+
+  NSUserActivity* userActivity = [[NSUserActivity alloc]
+      initWithActivityType:@"OpenInChromeIncognitoIntent"];
+
+  OpenInChromeIncognitoIntent* intent =
+      [[OpenInChromeIncognitoIntent alloc] init];
+
+  intent.urls = urls;
+
+  INInteraction* interaction = [[INInteraction alloc] initWithIntent:intent
+                                                            response:nil];
+
+  userActivity.interaction = interaction;
+
+  id startupInformationMock =
+      [OCMockObject niceMockForProtocol:@protocol(StartupInformation)];
+
+  id connectionInformationMock =
+      [OCMockObject niceMockForProtocol:@protocol(ConnectionInformation)];
+
+  [[connectionInformationMock expect]
+      setStartupParameters:[OCMArg checkWithBlock:^BOOL(id value) {
+        EXPECT_TRUE([value isKindOfClass:[AppStartupParameters class]] ||
+                    value == nil);
+
+        if (value != nil) {
+          AppStartupParameters* startupParameters =
+              (AppStartupParameters*)value;
+          const GURL calledURL = startupParameters.externalURL;
+          EXPECT_TRUE((int)[intent.urls count] == 3);
+          return [intent.urls containsObject:(net::NSURLWithGURL(calledURL))];
+        } else {
+          return YES;
+        }
+      }]];
+
+  [[[startupInformationMock stub] andReturnValue:@NO] isPresentingFirstRunUI];
+
+  MockTabOpener* tabOpener = [[MockTabOpener alloc] init];
+
+  // Action.
+  BOOL result =
+      [UserActivityHandler continueUserActivity:userActivity
+                            applicationIsActive:YES
+                                      tabOpener:tabOpener
+                          connectionInformation:connectionInformationMock
+                             startupInformation:startupInformationMock];
+
+  // Test.
+  EXPECT_OCMOCK_VERIFY(startupInformationMock);
+  EXPECT_TRUE(result);
+}
+
 // Tests that Chrome responds to open intents in the foreground.
 TEST_F(UserActivityHandlerTest, ContinueUserActivityIntentForeground) {
   GURL gurl("http://www.google.com");
diff --git a/ios/chrome/app/intents/BUILD.gn b/ios/chrome/app/intents/BUILD.gn
index 8e5bfc6e..7f25c213 100644
--- a/ios/chrome/app/intents/BUILD.gn
+++ b/ios/chrome/app/intents/BUILD.gn
@@ -8,6 +8,7 @@
   intent_file = "Intents.intentdefinition"
   intent_names = [
     "OpenInChromeIntent",
+    "OpenInChromeIncognitoIntent",
     "SearchInChromeIntent",
   ]
 }
diff --git a/ios/chrome/app/intents/Intents.intentdefinition b/ios/chrome/app/intents/Intents.intentdefinition
index 40b38b05..653f181 100644
--- a/ios/chrome/app/intents/Intents.intentdefinition
+++ b/ios/chrome/app/intents/Intents.intentdefinition
@@ -203,6 +203,98 @@
 			<key>INIntentVerb</key>
 			<string>Open</string>
 		</dict>
+		<dict>
+			<key>INIntentCategory</key>
+			<string>information</string>
+			<key>INIntentConfigurable</key>
+			<true/>
+			<key>INIntentDescriptionID</key>
+			<string>dYRltF</string>
+			<key>INIntentEligibleForWidgets</key>
+			<true/>
+			<key>INIntentIneligibleForSuggestions</key>
+			<true/>
+			<key>INIntentInput</key>
+			<string>urls</string>
+			<key>INIntentLastParameterTag</key>
+			<integer>4</integer>
+			<key>INIntentManagedParameterCombinations</key>
+			<dict>
+				<key>urls</key>
+				<dict>
+					<key>INIntentParameterCombinationSupportsBackgroundExecution</key>
+					<true/>
+					<key>INIntentParameterCombinationTitle</key>
+					<string>Open ${urls} in Incognito</string>
+					<key>INIntentParameterCombinationTitleID</key>
+					<string>6TwrYB</string>
+					<key>INIntentParameterCombinationUpdatesLinked</key>
+					<true/>
+				</dict>
+			</dict>
+			<key>INIntentName</key>
+			<string>OpenInChromeIncognito</string>
+			<key>INIntentParameters</key>
+			<array>
+				<dict>
+					<key>INIntentParameterConfigurable</key>
+					<true/>
+					<key>INIntentParameterDisplayName</key>
+					<string>URLs</string>
+					<key>INIntentParameterDisplayNameID</key>
+					<string>C8MCpv</string>
+					<key>INIntentParameterDisplayPriority</key>
+					<integer>1</integer>
+					<key>INIntentParameterName</key>
+					<string>urls</string>
+					<key>INIntentParameterPromptDialogs</key>
+					<array>
+						<dict>
+							<key>INIntentParameterPromptDialogCustom</key>
+							<true/>
+							<key>INIntentParameterPromptDialogFormatString</key>
+							<string>What URLs?</string>
+							<key>INIntentParameterPromptDialogFormatStringID</key>
+							<string>sNz7Cu</string>
+							<key>INIntentParameterPromptDialogType</key>
+							<string>Primary</string>
+						</dict>
+					</array>
+					<key>INIntentParameterSupportsMultipleValues</key>
+					<true/>
+					<key>INIntentParameterSupportsResolution</key>
+					<true/>
+					<key>INIntentParameterTag</key>
+					<integer>4</integer>
+					<key>INIntentParameterType</key>
+					<string>URL</string>
+				</dict>
+			</array>
+			<key>INIntentResponse</key>
+			<dict>
+				<key>INIntentResponseCodes</key>
+				<array>
+					<dict>
+						<key>INIntentResponseCodeName</key>
+						<string>success</string>
+						<key>INIntentResponseCodeSuccess</key>
+						<true/>
+					</dict>
+					<dict>
+						<key>INIntentResponseCodeName</key>
+						<string>failure</string>
+					</dict>
+				</array>
+			</dict>
+			<key>INIntentTitle</key>
+			<string>Open URLs In Chrome Incognito</string>
+			<key>INIntentTitleID</key>
+			<string>LHJwpy</string>
+			<key>INIntentType</key>
+			<string>Custom</string>
+			<key>INIntentVerb</key>
+			<string>Open</string>
+		</dict>
 	</array>
 	<key>INTypes</key>
 	<array/>
diff --git a/ios/chrome/app/resources/Info.plist b/ios/chrome/app/resources/Info.plist
index f6d3921..79426e0 100644
--- a/ios/chrome/app/resources/Info.plist
+++ b/ios/chrome/app/resources/Info.plist
@@ -95,6 +95,7 @@
 		<string>${CHROMIUM_HANDOFF_ID}</string>
 		<string>OpenInChromeIntent</string>
 		<string>SearchInChromeIntent</string>
+        <string>OpenInChromeIncognitoIntent</string>
 	</array>
 	<key>UIBackgroundModes</key>
 	<array>
diff --git a/ios/chrome/app/resources/chrome_localize_strings_config.plist b/ios/chrome/app/resources/chrome_localize_strings_config.plist
index ecbdaa2..7de946931 100644
--- a/ios/chrome/app/resources/chrome_localize_strings_config.plist
+++ b/ios/chrome/app/resources/chrome_localize_strings_config.plist
@@ -30,6 +30,24 @@
 			<array>
 				<dict>
 					<key>input</key>
+					<string>IDS_IOS_INTENTS_OPEN_IN_CHROME_INCOGNITO_TITLE</string>
+					<key>output</key>
+					<string>LHJwpy</string>
+				</dict>
+				<dict>
+					<key>input</key>
+					<string>IDS_IOS_INTENTS_OPEN_IN_CHROME_INCOGNITO_DESCRIPTION</string>
+					<key>output</key>
+					<string>dYRltF</string>
+				</dict>
+				<dict>
+					<key>input</key>
+					<string>IDS_IOS_INTENTS_OPEN_IN_CHROME_INCOGNITO_PARAMETER_COMBINATION_TITLE</string>
+					<key>output</key>
+					<string>6TwrYB</string>
+				</dict>
+				<dict>
+					<key>input</key>
 					<string>IDS_IOS_INTENTS_SEARCH_IN_CHROME_DESCRIPTION</string>
 					<key>output</key>
 					<string>4nGj7v</string>
diff --git a/ios/chrome/app/strings/ios_strings.grd b/ios/chrome/app/strings/ios_strings.grd
index 9c48f4af..51aec9e 100644
--- a/ios/chrome/app/strings/ios_strings.grd
+++ b/ios/chrome/app/strings/ios_strings.grd
@@ -713,6 +713,12 @@
       <message name="IDS_IOS_DISCOVER_FEED_MENU_MANAGE_INTERESTS_ITEM" desc="The 'Manage Interests' action for the Discover feed header menu.">
         Manage Interests
       </message>
+      <message name="IDS_IOS_DISCOVER_FEED_MENU_TURN_OFF_ITEM" desc="The 'Turn Off' action for the Discover feed header menu.">
+        Turn Off
+      </message>
+      <message name="IDS_IOS_DISCOVER_FEED_MENU_TURN_ON_ITEM" desc="The 'Turn On' action for the Discover feed header menu.">
+        Turn On
+      </message>
       <message name="IDS_IOS_DOWNLOAD_MANAGER_CANNOT_DETERMINE_FILE_SIZE" desc="Message displayed when the size of the file to be downloaded is unknown. [Length: 30em] [iOS only]">
         Cannot determine file size.
       </message>
@@ -926,6 +932,15 @@
       <message name="IDS_IOS_INTENTS_OPEN_IN_CHROME_PARAMETER_COMBINATION_TITLE" desc="Apple Shortcuts.app format for displaying the URL input [iOS only].">
         Open ${url}
       </message>
+      <message name="IDS_IOS_INTENTS_OPEN_IN_CHROME_INCOGNITO_TITLE" desc="Shortcut name to open URL in Chrome in Incognito [iOS only].">
+        Open URLs in Chrome in Incognito
+      </message>
+      <message name="IDS_IOS_INTENTS_OPEN_IN_CHROME_INCOGNITO_DESCRIPTION" desc="Shortcut description to open URLs in Chrome in Incognito [iOS only].">
+        Opens the inputted URLs in Google Chrome in Incognito.
+      </message>
+      <message name="IDS_IOS_INTENTS_OPEN_IN_CHROME_INCOGNITO_PARAMETER_COMBINATION_TITLE" desc="Apple Shortcuts.app format for displaying the URL input [iOS only].">
+        Open ${urls} in Incognito
+      </message>
       <message name="IDS_IOS_INTENTS_SEARCH_IN_CHROME_DESCRIPTION" desc="Siri Shortcut for search in chrome description [iOS only].">
         Start a search in a new Chrome tab.
       </message>
@@ -1644,9 +1659,9 @@
       </message>
       <message name="IDS_IOS_CHECK_PASSWORDS_COMPROMISED_COUNT" desc="Displays the number of compromised passwords present in the database">
         {COUNT, plural,
-          =0 {No passwords compromised}
-          =1 {{COUNT} Password compromised}
-          other {{COUNT} Passwords compromised}}
+          =0 {No compromised passwords}
+          =1 {{COUNT} Compromised password}
+          other {{COUNT} Compromised passwords}}
       </message>
       <message name="IDS_IOS_CHECK_PASSWORDS_NOW_BUTTON" desc="Label of a button which starts Password Check [Length: 22em] [iOS only]" meaning="By clicking this button user will start checking all passwords for security issues.">
         Check Now
@@ -1658,7 +1673,7 @@
         The following accounts use passwords which were exposed in a third-party data breach or entered on a deceptive website. Change these passwords immediately to keep your accounts safe.
       </message>
       <message name="IDS_IOS_CHANGE_COMPROMISED_PASSWORD" desc="Button which is shown on Password Details Screen when password is compromised. [iOS only]" meaning="Telling user to change password on a website since it is compromised.">
-        Change password on Website
+        Change Password on Website
       </message>
       <message name="IDS_IOS_DELETE_COMPROMISED_PASSWORD_DESCRIPTION" desc="Message on top of the confirmation alert when the user tapped delete password button. [iOS only]" meaning="Explaining to the user that deleting password locally won't delete account on a website.">
         Deleting this password will not delete your account on <ph name="WEBSITE">$1<ex>twitter.com</ex></ph>. Change your password on <ph name="WEBSITE">$1<ex>twitter.com</ex></ph> to keep it safe from others.
diff --git a/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_CHANGE_COMPROMISED_PASSWORD.png.sha1 b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_CHANGE_COMPROMISED_PASSWORD.png.sha1
index c217b85..e6320e1 100644
--- a/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_CHANGE_COMPROMISED_PASSWORD.png.sha1
+++ b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_CHANGE_COMPROMISED_PASSWORD.png.sha1
@@ -1 +1 @@
-324e42d485c2b5ff652e154aa3ccac09e7b3aa09
\ No newline at end of file
+508a3df5a981a9511135c8f57d2698b559b82f84
\ No newline at end of file
diff --git a/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_CHECK_PASSWORDS_COMPROMISED_COUNT.png.sha1 b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_CHECK_PASSWORDS_COMPROMISED_COUNT.png.sha1
index 6627543..30e43b21 100644
--- a/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_CHECK_PASSWORDS_COMPROMISED_COUNT.png.sha1
+++ b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_CHECK_PASSWORDS_COMPROMISED_COUNT.png.sha1
@@ -1 +1 @@
-50f9da3214aa8cc48c5c1038753cb1aa27073713
\ No newline at end of file
+d1b5c43ab616e32688883221d4bffe29e4e5459f
\ No newline at end of file
diff --git a/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_DISCOVER_FEED_MENU_TURN_OFF_ITEM.png.sha1 b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_DISCOVER_FEED_MENU_TURN_OFF_ITEM.png.sha1
new file mode 100644
index 0000000..3833ceb4
--- /dev/null
+++ b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_DISCOVER_FEED_MENU_TURN_OFF_ITEM.png.sha1
@@ -0,0 +1 @@
+b446e394ffe02fdf7818bbfb93bcb64a81477e10
\ No newline at end of file
diff --git a/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_DISCOVER_FEED_MENU_TURN_ON_ITEM.png.sha1 b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_DISCOVER_FEED_MENU_TURN_ON_ITEM.png.sha1
new file mode 100644
index 0000000..cbdcb43
--- /dev/null
+++ b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_DISCOVER_FEED_MENU_TURN_ON_ITEM.png.sha1
@@ -0,0 +1 @@
+3d5ca14ee7f1293bfaf77ca7cb071e452c2f7690
\ No newline at end of file
diff --git a/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_INTENTS_OPEN_IN_CHROME_INCOGNITO_DESCRIPTION.png.sha1 b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_INTENTS_OPEN_IN_CHROME_INCOGNITO_DESCRIPTION.png.sha1
new file mode 100644
index 0000000..6eb8cebb
--- /dev/null
+++ b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_INTENTS_OPEN_IN_CHROME_INCOGNITO_DESCRIPTION.png.sha1
@@ -0,0 +1 @@
+8614ba451df59e9955225a99e030b9072e86701e
\ No newline at end of file
diff --git a/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_INTENTS_OPEN_IN_CHROME_INCOGNITO_PARAMETER_COMBINATION_TITLE.png.sha1 b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_INTENTS_OPEN_IN_CHROME_INCOGNITO_PARAMETER_COMBINATION_TITLE.png.sha1
new file mode 100644
index 0000000..61446bf
--- /dev/null
+++ b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_INTENTS_OPEN_IN_CHROME_INCOGNITO_PARAMETER_COMBINATION_TITLE.png.sha1
@@ -0,0 +1 @@
+6bd0e2112cf7b407cc5ea7adee64a55981d83751
\ No newline at end of file
diff --git a/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_INTENTS_OPEN_IN_CHROME_INCOGNITO_TITLE.png.sha1 b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_INTENTS_OPEN_IN_CHROME_INCOGNITO_TITLE.png.sha1
new file mode 100644
index 0000000..77374a07
--- /dev/null
+++ b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_INTENTS_OPEN_IN_CHROME_INCOGNITO_TITLE.png.sha1
@@ -0,0 +1 @@
+931014dc4c94334d3c6a37b6ddfb68cfc7ff7f7f
\ No newline at end of file
diff --git a/ios/chrome/browser/flags/about_flags.mm b/ios/chrome/browser/flags/about_flags.mm
index c53e865..31521a6 100644
--- a/ios/chrome/browser/flags/about_flags.mm
+++ b/ios/chrome/browser/flags/about_flags.mm
@@ -680,6 +680,9 @@
      flag_descriptions::kEnableFullPageScreenshotName,
      flag_descriptions::kEnableFullPageScreenshotDescription, flags_ui::kOsIos,
      FEATURE_VALUE_TYPE(kEnableFullPageScreenshot)},
+    {"scroll-to-text-ios", flag_descriptions::kScrollToTextIOSName,
+     flag_descriptions::kScrollToTextIOSDescription, flags_ui::kOsIos,
+     FEATURE_VALUE_TYPE(web::features::kScrollToTextIOS)},
 };
 
 bool SkipConditionalFeatureEntry(const flags_ui::FeatureEntry& entry) {
diff --git a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc
index 4ba8d47..abdc73e 100644
--- a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc
+++ b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc
@@ -421,6 +421,12 @@
 const char kSaveCardInfobarMessagesUIDescription[] =
     "When enabled, Save Card Infobar uses the new Messages UI.";
 
+const char kScrollToTextIOSName[] = "Enable Scroll to Text";
+const char kScrollToTextIOSDescription[] =
+    "When enabled, opening a URL with a text fragment (e.g., "
+    "example.com/#:~:text=examples) will cause matching text in the page to be "
+    "highlighted and scrolled into view.";
+
 const char kSendTabToSelfName[] = "Send tab to self";
 const char kSendTabToSelfDescription[] =
     "Allows users to receive tabs that were pushed from another of their "
@@ -438,9 +444,8 @@
 
 const char kSharedHighlightingIOSName[] = "Enable Shared Highlighting features";
 const char kSharedHighlightingIOSDescription[] =
-    "Enables support for Text Fragments (scroll-to-text based on URL "
-    "directive) and a Link to Text option in the Edit Menu which generates "
-    "these URLs.";
+    "Adds a Link to Text option in the Edit Menu which generates URLs with a "
+    "text fragment. Works best with the #scroll-to-text-ios flag.";
 
 const char kShowAutofillTypePredictionsName[] = "Show Autofill predictions";
 const char kShowAutofillTypePredictionsDescription[] =
diff --git a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h
index fa95ba8..c98e41b 100644
--- a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h
+++ b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h
@@ -366,6 +366,10 @@
 extern const char kSaveCardInfobarMessagesUIName[];
 extern const char kSaveCardInfobarMessagesUIDescription[];
 
+// Title and description for the flag to enable the Scroll to Text feature.
+extern const char kScrollToTextIOSName[];
+extern const char kScrollToTextIOSDescription[];
+
 // Title and description for the flag to enable the send tab to self receiving
 // feature.
 extern const char kSendTabToSelfName[];
@@ -381,7 +385,7 @@
 extern const char kSettingsRefreshDescription[];
 
 // Title and description for the flag to enable Shared Highlighting (Link to
-// Text Edit Menu option and Text Fragments web behavior).
+// Text Edit Menu option).
 extern const char kSharedHighlightingIOSName[];
 extern const char kSharedHighlightingIOSDescription[];
 
diff --git a/ios/chrome/browser/metrics/demographics_egtest.mm b/ios/chrome/browser/metrics/demographics_egtest.mm
index f06f84f..da2ad7f7 100644
--- a/ios/chrome/browser/metrics/demographics_egtest.mm
+++ b/ios/chrome/browser/metrics/demographics_egtest.mm
@@ -7,8 +7,8 @@
 #include "components/metrics/demographic_metrics_provider.h"
 #include "components/ukm/ukm_service.h"
 #import "ios/chrome/browser/metrics/metrics_app_interface.h"
+#import "ios/chrome/browser/ui/authentication/signin_earl_grey.h"
 #import "ios/chrome/browser/ui/authentication/signin_earl_grey_ui.h"
-#import "ios/chrome/browser/ui/authentication/signin_earlgrey_utils.h"
 #import "ios/chrome/test/earl_grey/chrome_earl_grey.h"
 #import "ios/chrome/test/earl_grey/chrome_test_case.h"
 #import "ios/testing/earl_grey/app_launch_configuration.h"
@@ -132,7 +132,7 @@
   // anonymized data collection is turned on as part of the flow to Sign in to
   // Chrome and Turn on sync. This matches the main user flow that enables
   // UKM.
-  [SigninEarlGreyUI signinWithFakeIdentity:[SigninEarlGreyUtils fakeIdentity1]];
+  [SigninEarlGreyUI signinWithFakeIdentity:[SigninEarlGrey fakeIdentity1]];
   [ChromeEarlGrey waitForSyncInitialized:YES
                              syncTimeout:syncher::kSyncUKMOperationsTimeout];
 }
diff --git a/ios/chrome/browser/metrics/ukm_egtest.mm b/ios/chrome/browser/metrics/ukm_egtest.mm
index 712594d..b8045ba4 100644
--- a/ios/chrome/browser/metrics/ukm_egtest.mm
+++ b/ios/chrome/browser/metrics/ukm_egtest.mm
@@ -5,9 +5,9 @@
 #include "base/ios/ios_util.h"
 #include "base/macros.h"
 #import "ios/chrome/browser/metrics/metrics_app_interface.h"
+#import "ios/chrome/browser/ui/authentication/signin_earl_grey.h"
+#import "ios/chrome/browser/ui/authentication/signin_earl_grey_app_interface.h"
 #import "ios/chrome/browser/ui/authentication/signin_earl_grey_ui.h"
-#import "ios/chrome/browser/ui/authentication/signin_earlgrey_utils.h"
-#import "ios/chrome/browser/ui/authentication/signin_earlgrey_utils_app_interface.h"
 #import "ios/chrome/test/earl_grey/chrome_actions.h"
 #import "ios/chrome/test/earl_grey/chrome_earl_grey.h"
 #import "ios/chrome/test/earl_grey/chrome_earl_grey_ui.h"
@@ -63,7 +63,7 @@
   // Note: URL-keyed anonymized data collection is turned on as part of the
   // flow to Sign in to Chrome and Turn sync on. This matches the main user
   // flow that enables UKM.
-  [SigninEarlGreyUI signinWithFakeIdentity:[SigninEarlGreyUtils fakeIdentity1]];
+  [SigninEarlGreyUI signinWithFakeIdentity:[SigninEarlGrey fakeIdentity1]];
   [ChromeEarlGrey waitForSyncInitialized:YES
                              syncTimeout:syncher::kSyncUKMOperationsTimeout];
 
@@ -93,8 +93,8 @@
   // Note: URL-keyed anonymized data collection is turned off as part of the
   // flow to Sign out of Chrome and Turn sync off. This matches the main user
   // flow that disables UKM.
-  if (![SigninEarlGreyUtilsAppInterface isSignedOut]) {
-    [SigninEarlGreyUtilsAppInterface signOut];
+  if (![SigninEarlGreyAppInterface isSignedOut]) {
+    [SigninEarlGreyAppInterface signOut];
   }
 
   [ChromeEarlGrey waitForSyncInitialized:NO
@@ -274,7 +274,7 @@
 // Corresponds to ConsentAddedButNoSyncCheck in //chrome/browser/metrics/
 // ukm_browsertest.cc.
 - (void)testConsentAddedButNoSync {
-  [SigninEarlGreyUtilsAppInterface signOut];
+  [SigninEarlGreyAppInterface signOut];
   [MetricsAppInterface setMetricsAndCrashReportingForTesting:NO];
   GREYAssert([MetricsAppInterface checkUKMRecordingEnabled:NO],
              @"Failed to assert that UKM was not enabled.");
@@ -283,7 +283,7 @@
   GREYAssert([MetricsAppInterface checkUKMRecordingEnabled:NO],
              @"Failed to assert that UKM was not enabled.");
 
-  [SigninEarlGreyUI signinWithFakeIdentity:[SigninEarlGreyUtils fakeIdentity1]];
+  [SigninEarlGreyUI signinWithFakeIdentity:[SigninEarlGrey fakeIdentity1]];
   GREYAssert([MetricsAppInterface checkUKMRecordingEnabled:YES],
              @"Failed to assert that UKM was enabled.");
 }
@@ -347,7 +347,7 @@
 #endif
   const uint64_t clientID1 = [MetricsAppInterface UKMClientID];
 
-  [SigninEarlGreyUtilsAppInterface signOut];
+  [SigninEarlGreyAppInterface signOut];
 
   GREYAssert([MetricsAppInterface checkUKMRecordingEnabled:NO],
              @"Failed to assert that UKM was not enabled.");
@@ -356,7 +356,7 @@
                      @"Client ID was not reset.");
 
   const uint64_t clientID2 = [MetricsAppInterface UKMClientID];
-  [SigninEarlGreyUI signinWithFakeIdentity:[SigninEarlGreyUtils fakeIdentity1]];
+  [SigninEarlGreyUI signinWithFakeIdentity:[SigninEarlGrey fakeIdentity1]];
 
   GREYAssert([MetricsAppInterface checkUKMRecordingEnabled:YES],
              @"Failed to assert that UKM was enabled.");
diff --git a/ios/chrome/browser/reading_list/offline_page_tab_helper.h b/ios/chrome/browser/reading_list/offline_page_tab_helper.h
index 5aeecde..d3b9f52 100644
--- a/ios/chrome/browser/reading_list/offline_page_tab_helper.h
+++ b/ios/chrome/browser/reading_list/offline_page_tab_helper.h
@@ -134,6 +134,11 @@
   bool navigation_is_renderer_initiated_ = false;
 
   WEB_STATE_USER_DATA_KEY_DECL();
+
+  // Member variables should appear before the WeakPtrFactory, to ensure
+  // that any WeakPtrs to OfflinePageTabHelper are invalidated before its
+  // members variable's destructors are executed, rendering them invalid.
+  base::WeakPtrFactory<OfflinePageTabHelper> weak_factory_{this};
 };
 
 #endif  // IOS_CHROME_BROWSER_READING_LIST_OFFLINE_PAGE_TAB_HELPER_H_
diff --git a/ios/chrome/browser/reading_list/offline_page_tab_helper.mm b/ios/chrome/browser/reading_list/offline_page_tab_helper.mm
index 837dc715..6962a17 100644
--- a/ios/chrome/browser/reading_list/offline_page_tab_helper.mm
+++ b/ios/chrome/browser/reading_list/offline_page_tab_helper.mm
@@ -289,9 +289,9 @@
       {base::MayBlock(), base::TaskPriority::USER_BLOCKING,
        base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN},
       base::BindOnce(&GetOfflineData, offline_root, offline_path),
-      base::BindOnce(&OfflinePageTabHelper::LoadData, base::Unretained(this),
-                     last_navigation_started_, entry_url,
-                     offline_path.Extension()));
+      base::BindOnce(&OfflinePageTabHelper::LoadData,
+                     weak_factory_.GetWeakPtr(), last_navigation_started_,
+                     entry_url, offline_path.Extension()));
 }
 
 bool OfflinePageTabHelper::HasDistilledVersionForOnlineUrl(
diff --git a/ios/chrome/browser/ui/authentication/BUILD.gn b/ios/chrome/browser/ui/authentication/BUILD.gn
index a7ff6f6..d9c54b1 100644
--- a/ios/chrome/browser/ui/authentication/BUILD.gn
+++ b/ios/chrome/browser/ui/authentication/BUILD.gn
@@ -144,8 +144,8 @@
   ]
   testonly = true
   sources = [
-    "signin_earlgrey_utils_app_interface.h",
-    "signin_earlgrey_utils_app_interface.mm",
+    "signin_earl_grey_app_interface.h",
+    "signin_earl_grey_app_interface.mm",
   ]
   deps = [
     ":authentication",
@@ -177,12 +177,12 @@
   ]
   testonly = true
   sources = [
+    "signin_earl_grey.h",
+    "signin_earl_grey.mm",
+    "signin_earl_grey_app_interface.h",
+    "signin_earl_grey_app_interface_stub.mm",
     "signin_earl_grey_ui.h",
     "signin_earl_grey_ui.mm",
-    "signin_earlgrey_utils.h",
-    "signin_earlgrey_utils.mm",
-    "signin_earlgrey_utils_app_interface.h",
-    "signin_earlgrey_utils_app_interface_stub.mm",
   ]
   deps = [
     "//base",
diff --git a/ios/chrome/browser/ui/authentication/signin/signin_coordinator_egtest.mm b/ios/chrome/browser/ui/authentication/signin/signin_coordinator_egtest.mm
index eab5ceed..09e9070 100644
--- a/ios/chrome/browser/ui/authentication/signin/signin_coordinator_egtest.mm
+++ b/ios/chrome/browser/ui/authentication/signin/signin_coordinator_egtest.mm
@@ -7,8 +7,8 @@
 #include "base/strings/sys_string_conversions.h"
 #import "base/test/ios/wait_util.h"
 #include "base/test/scoped_feature_list.h"
+#import "ios/chrome/browser/ui/authentication/signin_earl_grey.h"
 #import "ios/chrome/browser/ui/authentication/signin_earl_grey_ui.h"
-#import "ios/chrome/browser/ui/authentication/signin_earlgrey_utils.h"
 #import "ios/chrome/browser/ui/recent_tabs/recent_tabs_constants.h"
 #include "ios/chrome/grit/ios_strings.h"
 #import "ios/chrome/test/earl_grey/chrome_earl_grey.h"
@@ -75,10 +75,10 @@
 
 void ChooseImportOrKeepDataSepareteDialog(id<GREYMatcher> choiceButtonMatcher) {
   // Set up the fake identities.
-  FakeChromeIdentity* fakeIdentity1 = [SigninEarlGreyUtils fakeIdentity1];
-  FakeChromeIdentity* fakeIdentity2 = [SigninEarlGreyUtils fakeIdentity2];
-  [SigninEarlGreyUtils addFakeIdentity:fakeIdentity1];
-  [SigninEarlGreyUtils addFakeIdentity:fakeIdentity2];
+  FakeChromeIdentity* fakeIdentity1 = [SigninEarlGrey fakeIdentity1];
+  FakeChromeIdentity* fakeIdentity2 = [SigninEarlGrey fakeIdentity2];
+  [SigninEarlGrey addFakeIdentity:fakeIdentity1];
+  [SigninEarlGrey addFakeIdentity:fakeIdentity2];
 
   // Sign in to |fakeIdentity1|.
   [SigninEarlGreyUI signinWithFakeIdentity:fakeIdentity1];
@@ -101,7 +101,7 @@
       performAction:grey_tap()];
 
   // Check the signed-in user did change.
-  [SigninEarlGreyUtils checkSignedInWithFakeIdentity:fakeIdentity2];
+  [SigninEarlGrey checkSignedInWithFakeIdentity:fakeIdentity2];
 
   [[EarlGrey selectElementWithMatcher:SettingsDoneButton()]
       performAction:grey_tap()];
@@ -127,13 +127,13 @@
 // correctly when there is already an identity on the device.
 - (void)testSignInOneUser {
   // Set up a fake identity.
-  FakeChromeIdentity* fakeIdentity = [SigninEarlGreyUtils fakeIdentity1];
-  [SigninEarlGreyUtils addFakeIdentity:fakeIdentity];
+  FakeChromeIdentity* fakeIdentity = [SigninEarlGrey fakeIdentity1];
+  [SigninEarlGrey addFakeIdentity:fakeIdentity];
 
   [SigninEarlGreyUI signinWithFakeIdentity:fakeIdentity];
 
   // Check |fakeIdentity| is signed-in.
-  [SigninEarlGreyUtils checkSignedInWithFakeIdentity:fakeIdentity];
+  [SigninEarlGrey checkSignedInWithFakeIdentity:fakeIdentity];
 }
 
 // Tests signing in with one account, switching sync account to a second and
@@ -150,8 +150,8 @@
 
 // Tests that signing out from the Settings works correctly.
 - (void)testSignInDisconnectFromChrome {
-  FakeChromeIdentity* fakeIdentity = [SigninEarlGreyUtils fakeIdentity1];
-  [SigninEarlGreyUtils addFakeIdentity:fakeIdentity];
+  FakeChromeIdentity* fakeIdentity = [SigninEarlGrey fakeIdentity1];
+  [SigninEarlGrey addFakeIdentity:fakeIdentity];
 
   // Sign in to |fakeIdentity|.
   [SigninEarlGreyUI signinWithFakeIdentity:fakeIdentity];
@@ -164,7 +164,7 @@
 // Tests that signing out of a managed account from the Settings works
 // correctly.
 - (void)testSignInDisconnectFromChromeManaged {
-  FakeChromeIdentity* fakeIdentity = [SigninEarlGreyUtils fakeManagedIdentity];
+  FakeChromeIdentity* fakeIdentity = [SigninEarlGrey fakeManagedIdentity];
 
   // Sign-in with a managed account.
   [SigninEarlGreyUI signinWithFakeIdentity:fakeIdentity isManagedAccount:YES];
@@ -174,15 +174,15 @@
       signOutWithSignOutConfirmation:SignOutConfirmationManagedUser];
 
   // Check that there is no signed in user.
-  [SigninEarlGreyUtils checkSignedOut];
+  [SigninEarlGrey checkSignedOut];
 }
 
 // Tests that signing in, tapping the Settings link on the confirmation screen
 // and closing the advanced sign-in settings correctly leaves the user signed
 // in.
 - (void)testSignInOpenSettings {
-  FakeChromeIdentity* fakeIdentity = [SigninEarlGreyUtils fakeIdentity1];
-  [SigninEarlGreyUtils addFakeIdentity:fakeIdentity];
+  FakeChromeIdentity* fakeIdentity = [SigninEarlGrey fakeIdentity1];
+  [SigninEarlGrey addFakeIdentity:fakeIdentity];
 
   [self openSigninFromView:OpenSigninMethodFromSettings tapSettingsLink:YES];
 
@@ -196,7 +196,7 @@
   [[EarlGrey selectElementWithMatcher:settings_matcher]
       assertWithMatcher:grey_sufficientlyVisible()];
   // Test the user is signed in.
-  [SigninEarlGreyUtils checkSignedInWithFakeIdentity:fakeIdentity];
+  [SigninEarlGrey checkSignedInWithFakeIdentity:fakeIdentity];
 }
 
 // Opens the sign in screen and then cancel it by opening a new tab. Ensures
@@ -204,8 +204,8 @@
 - (void)testSignInCancelIdentityPicker {
   // Add an identity to avoid arriving on the Add Account screen when opening
   // sign-in.
-  FakeChromeIdentity* fakeIdentity = [SigninEarlGreyUtils fakeIdentity1];
-  [SigninEarlGreyUtils addFakeIdentity:fakeIdentity];
+  FakeChromeIdentity* fakeIdentity = [SigninEarlGrey fakeIdentity1];
+  [SigninEarlGrey addFakeIdentity:fakeIdentity];
 
   [ChromeEarlGreyUI openSettingsMenu];
   [ChromeEarlGreyUI tapSettingsMenuButton:SecondarySignInButton()];
@@ -241,10 +241,10 @@
 // crbug.com/462202
 - (void)testSignInCancelAuthenticationFlow {
   // Set up the fake identities.
-  FakeChromeIdentity* fakeIdentity1 = [SigninEarlGreyUtils fakeIdentity1];
-  FakeChromeIdentity* fakeIdentity2 = [SigninEarlGreyUtils fakeIdentity2];
-  [SigninEarlGreyUtils addFakeIdentity:fakeIdentity1];
-  [SigninEarlGreyUtils addFakeIdentity:fakeIdentity2];
+  FakeChromeIdentity* fakeIdentity1 = [SigninEarlGrey fakeIdentity1];
+  FakeChromeIdentity* fakeIdentity2 = [SigninEarlGrey fakeIdentity2];
+  [SigninEarlGrey addFakeIdentity:fakeIdentity1];
+  [SigninEarlGrey addFakeIdentity:fakeIdentity2];
 
   // This signs in |fakeIdentity2| first, ensuring that the "Clear Data Before
   // Syncing" dialog is shown during the second sign-in. This dialog will
@@ -290,15 +290,15 @@
       performAction:grey_tap()];
   [[EarlGrey selectElementWithMatcher:SettingsDoneButton()]
       performAction:grey_tap()];
-  [SigninEarlGreyUtils checkSignedOut];
+  [SigninEarlGrey checkSignedOut];
 }
 
 // Opens the sign in screen from the bookmarks and then cancel it by tapping on
 // done. Ensures that the sign in screen is correctly dismissed.
 // Regression test for crbug.com/596029.
 - (void)testSignInCancelFromBookmarks {
-  FakeChromeIdentity* fakeIdentity = [SigninEarlGreyUtils fakeIdentity1];
-  [SigninEarlGreyUtils addFakeIdentity:fakeIdentity];
+  FakeChromeIdentity* fakeIdentity = [SigninEarlGrey fakeIdentity1];
+  [SigninEarlGrey addFakeIdentity:fakeIdentity];
 
   // Open Bookmarks and tap on Sign In promo button.
   [ChromeEarlGreyUI openToolsMenu];
@@ -409,8 +409,8 @@
   if (!base::ios::IsRunningOnOrLater(13, 0, 0)) {
     EARL_GREY_TEST_SKIPPED(@"Test disabled on iOS 12 and lower.");
   }
-  FakeChromeIdentity* fakeIdentity = [SigninEarlGreyUtils fakeIdentity1];
-  [SigninEarlGreyUtils addFakeIdentity:fakeIdentity];
+  FakeChromeIdentity* fakeIdentity = [SigninEarlGrey fakeIdentity1];
+  [SigninEarlGrey addFakeIdentity:fakeIdentity];
   [self openSigninFromView:OpenSigninMethodFromSettings tapSettingsLink:YES];
 
   [[EarlGrey selectElementWithMatcher:GoogleServicesSettingsView()]
@@ -420,7 +420,7 @@
           ButtonWithAccessibilityLabel(GetNSString(
               IDS_IOS_ADVANCED_SIGNIN_SETTINGS_CANCEL_SYNC_ALERT_CANCEL_SYNC_BUTTON))]
       performAction:grey_tap()];
-  [SigninEarlGreyUtils checkSignedOut];
+  [SigninEarlGrey checkSignedOut];
 }
 
 #pragma mark - Utils
@@ -469,8 +469,8 @@
 // |tapSettingsLink| if YES, the setting link is tapped before opening the URL.
 - (void)assertOpenURLWhenSigninFromView:(OpenSigninMethod)openSigninMethod
                         tapSettingsLink:(BOOL)tapSettingsLink {
-  FakeChromeIdentity* fakeIdentity = [SigninEarlGreyUtils fakeIdentity1];
-  [SigninEarlGreyUtils addFakeIdentity:fakeIdentity];
+  FakeChromeIdentity* fakeIdentity = [SigninEarlGrey fakeIdentity1];
+  [SigninEarlGrey addFakeIdentity:fakeIdentity];
   [self openSigninFromView:openSigninMethod tapSettingsLink:tapSettingsLink];
   // Open the URL as if it was opened from another app.
   [ChromeEarlGrey simulateExternalAppURLOpening];
@@ -482,10 +482,10 @@
 
   if (tapSettingsLink) {
     // Should be signed in.
-    [SigninEarlGreyUtils checkSignedInWithFakeIdentity:fakeIdentity];
+    [SigninEarlGrey checkSignedInWithFakeIdentity:fakeIdentity];
   } else {
     // Should be not signed in.
-    [SigninEarlGreyUtils checkSignedOut];
+    [SigninEarlGrey checkSignedOut];
   }
 }
 
@@ -493,7 +493,7 @@
 // onscreen.
 - (void)assertFakeSSOScreenIsVisible {
   // Check for the fake SSO screen.
-  [SigninEarlGreyUtils
+  [SigninEarlGrey
       waitForMatcher:grey_accessibilityID(kFakeAddAccountViewIdentifier)];
   // Close the SSO view controller.
   id<GREYMatcher> matcher =
@@ -534,17 +534,17 @@
 // Tests to remove the last identity in the identity chooser.
 - (void)testRemoveLastAccount {
   // Set up a fake identity.
-  FakeChromeIdentity* fakeIdentity = [SigninEarlGreyUtils fakeIdentity1];
-  [SigninEarlGreyUtils addFakeIdentity:fakeIdentity];
+  FakeChromeIdentity* fakeIdentity = [SigninEarlGrey fakeIdentity1];
+  [SigninEarlGrey addFakeIdentity:fakeIdentity];
 
   // Open the identity chooser.
   [ChromeEarlGreyUI openSettingsMenu];
   [ChromeEarlGreyUI tapSettingsMenuButton:SecondarySignInButton()];
-  [SigninEarlGreyUtils waitForMatcher:identityChooserButtonMatcherWithEmail(
-                                          fakeIdentity.userEmail)];
+  [SigninEarlGrey waitForMatcher:identityChooserButtonMatcherWithEmail(
+                                     fakeIdentity.userEmail)];
 
   // Remove the fake identity.
-  [SigninEarlGreyUtils removeFakeIdentity:fakeIdentity];
+  [SigninEarlGrey removeFakeIdentity:fakeIdentity];
   [ChromeEarlGreyUI waitForAppToIdle];
 
   // Check that the identity has been removed.
@@ -558,8 +558,8 @@
 - (void)testSignInCancelAddAccount {
   // Add an identity to avoid arriving on the Add Account screen when opening
   // sign-in.
-  FakeChromeIdentity* fakeIdentity = [SigninEarlGreyUtils fakeIdentity1];
-  [SigninEarlGreyUtils addFakeIdentity:fakeIdentity];
+  FakeChromeIdentity* fakeIdentity = [SigninEarlGrey fakeIdentity1];
+  [SigninEarlGrey addFakeIdentity:fakeIdentity];
 
   [ChromeEarlGreyUI openSettingsMenu];
   [ChromeEarlGreyUI tapSettingsMenuButton:SecondarySignInButton()];
diff --git a/ios/chrome/browser/ui/authentication/signin_earlgrey_utils.h b/ios/chrome/browser/ui/authentication/signin_earl_grey.h
similarity index 78%
rename from ios/chrome/browser/ui/authentication/signin_earlgrey_utils.h
rename to ios/chrome/browser/ui/authentication/signin_earl_grey.h
index 2f5e43ec8..df6840d 100644
--- a/ios/chrome/browser/ui/authentication/signin_earlgrey_utils.h
+++ b/ios/chrome/browser/ui/authentication/signin_earl_grey.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 IOS_CHROME_BROWSER_UI_AUTHENTICATION_SIGNIN_EARLGREY_UTILS_H_
-#define IOS_CHROME_BROWSER_UI_AUTHENTICATION_SIGNIN_EARLGREY_UTILS_H_
+#ifndef IOS_CHROME_BROWSER_UI_AUTHENTICATION_SIGNIN_EARL_GREY_H_
+#define IOS_CHROME_BROWSER_UI_AUTHENTICATION_SIGNIN_EARL_GREY_H_
 
 #import <Foundation/Foundation.h>
 
@@ -13,12 +13,12 @@
 @protocol GREYMatcher;
 @class FakeChromeIdentity;
 
-#define SigninEarlGreyUtils \
-  [SigninEarlGreyUtilsImpl invokedFromFile:@"" __FILE__ lineNumber:__LINE__]
+#define SigninEarlGrey \
+  [SigninEarlGreyImpl invokedFromFile:@"" __FILE__ lineNumber:__LINE__]
 
 // Methods used for the EarlGrey tests.
 // TODO(crbug.com/974833): Consider moving these into ChromeEarlGrey.
-@interface SigninEarlGreyUtilsImpl : BaseEGTestHelperImpl
+@interface SigninEarlGreyImpl : BaseEGTestHelperImpl
 
 // Returns a fake identity.
 - (FakeChromeIdentity*)fakeIdentity1;
@@ -51,4 +51,4 @@
 
 @end
 
-#endif  // IOS_CHROME_BROWSER_UI_AUTHENTICATION_SIGNIN_EARLGREY_UTILS_H_
+#endif  // IOS_CHROME_BROWSER_UI_AUTHENTICATION_SIGNIN_EARL_GREY_H_
diff --git a/ios/chrome/browser/ui/authentication/signin_earlgrey_utils.mm b/ios/chrome/browser/ui/authentication/signin_earl_grey.mm
similarity index 83%
rename from ios/chrome/browser/ui/authentication/signin_earlgrey_utils.mm
rename to ios/chrome/browser/ui/authentication/signin_earl_grey.mm
index b4ed14a..060b40cc 100644
--- a/ios/chrome/browser/ui/authentication/signin_earlgrey_utils.mm
+++ b/ios/chrome/browser/ui/authentication/signin_earl_grey.mm
@@ -2,10 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "ios/chrome/browser/ui/authentication/signin_earlgrey_utils.h"
+#import "ios/chrome/browser/ui/authentication/signin_earl_grey.h"
 
 #import "base/test/ios/wait_util.h"
-#import "ios/chrome/browser/ui/authentication/signin_earlgrey_utils_app_interface.h"
+#import "ios/chrome/browser/ui/authentication/signin_earl_grey_app_interface.h"
 #import "ios/public/provider/chrome/browser/signin/fake_chrome_identity.h"
 #import "ios/testing/earl_grey/earl_grey_test.h"
 
@@ -13,7 +13,7 @@
 #error "This file requires ARC support."
 #endif
 
-@implementation SigninEarlGreyUtilsImpl
+@implementation SigninEarlGreyImpl
 
 - (FakeChromeIdentity*)fakeIdentity1 {
   return [FakeChromeIdentity identityWithEmail:@"foo1@gmail.com"
@@ -34,11 +34,11 @@
 }
 
 - (void)addFakeIdentity:(FakeChromeIdentity*)fakeIdentity {
-  [SigninEarlGreyUtilsAppInterface addFakeIdentity:fakeIdentity];
+  [SigninEarlGreyAppInterface addFakeIdentity:fakeIdentity];
 }
 
 - (void)forgetFakeIdentity:(FakeChromeIdentity*)fakeIdentity {
-  [SigninEarlGreyUtilsAppInterface forgetFakeIdentity:fakeIdentity];
+  [SigninEarlGreyAppInterface forgetFakeIdentity:fakeIdentity];
 }
 
 - (void)checkSignedInWithFakeIdentity:(FakeChromeIdentity*)fakeIdentity {
@@ -52,14 +52,14 @@
                  base::test::ios::kWaitForActionTimeout,
                  ^bool {
                    NSString* primaryAccountGaiaID =
-                       [SigninEarlGreyUtilsAppInterface primaryAccountGaiaID];
+                       [SigninEarlGreyAppInterface primaryAccountGaiaID];
                    return primaryAccountGaiaID.length > 0;
                  }),
              @"Sign in did not complete.");
   GREYWaitForAppToIdle(@"App failed to idle");
 
   NSString* primaryAccountGaiaID =
-      [SigninEarlGreyUtilsAppInterface primaryAccountGaiaID];
+      [SigninEarlGreyAppInterface primaryAccountGaiaID];
 
   NSString* errorStr = [NSString
       stringWithFormat:@"Unexpected Gaia ID of the signed in user [expected = "
@@ -75,12 +75,12 @@
   // the assert.
   GREYWaitForAppToIdle(@"App failed to idle");
 
-  EG_TEST_HELPER_ASSERT_TRUE([SigninEarlGreyUtilsAppInterface isSignedOut],
+  EG_TEST_HELPER_ASSERT_TRUE([SigninEarlGreyAppInterface isSignedOut],
                              @"Unexpected signed in user");
 }
 
 - (void)removeFakeIdentity:(FakeChromeIdentity*)fakeIdentity {
-  [SigninEarlGreyUtilsAppInterface removeFakeIdentity:fakeIdentity];
+  [SigninEarlGreyAppInterface removeFakeIdentity:fakeIdentity];
 }
 
 - (void)waitForMatcher:(id<GREYMatcher>)matcher {
diff --git a/ios/chrome/browser/ui/authentication/signin_earlgrey_utils_app_interface.h b/ios/chrome/browser/ui/authentication/signin_earl_grey_app_interface.h
similarity index 77%
rename from ios/chrome/browser/ui/authentication/signin_earlgrey_utils_app_interface.h
rename to ios/chrome/browser/ui/authentication/signin_earl_grey_app_interface.h
index 916c6b5..545fcd6 100644
--- a/ios/chrome/browser/ui/authentication/signin_earlgrey_utils_app_interface.h
+++ b/ios/chrome/browser/ui/authentication/signin_earl_grey_app_interface.h
@@ -2,18 +2,18 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef IOS_CHROME_BROWSER_UI_AUTHENTICATION_SIGNIN_EARLGREY_UTILS_APP_INTERFACE_H_
-#define IOS_CHROME_BROWSER_UI_AUTHENTICATION_SIGNIN_EARLGREY_UTILS_APP_INTERFACE_H_
+#ifndef IOS_CHROME_BROWSER_UI_AUTHENTICATION_SIGNIN_EARL_GREY_APP_INTERFACE_H_
+#define IOS_CHROME_BROWSER_UI_AUTHENTICATION_SIGNIN_EARL_GREY_APP_INTERFACE_H_
 
 #import <Foundation/Foundation.h>
 
 @class FakeChromeIdentity;
 @protocol GREYMatcher;
 
-// SigninEarlGreyUtilsAppInterface contains the app-side implementation for
+// SigninEarlGreyAppInterface contains the app-side implementation for
 // helpers that primarily work via direct model access. These helpers are
 // compiled into the app binary and can be called from either app or test code.
-@interface SigninEarlGreyUtilsAppInterface : NSObject
+@interface SigninEarlGreyAppInterface : NSObject
 
 // Returns a fake identity.
 + (FakeChromeIdentity*)fakeIdentity1;
@@ -48,4 +48,4 @@
 
 @end
 
-#endif  // IOS_CHROME_BROWSER_UI_AUTHENTICATION_SIGNIN_EARLGREY_UTILS_APP_INTERFACE_H_
+#endif  // IOS_CHROME_BROWSER_UI_AUTHENTICATION_SIGNIN_EARL_GREY_APP_INTERFACE_H_
diff --git a/ios/chrome/browser/ui/authentication/signin_earlgrey_utils_app_interface.mm b/ios/chrome/browser/ui/authentication/signin_earl_grey_app_interface.mm
similarity index 96%
rename from ios/chrome/browser/ui/authentication/signin_earlgrey_utils_app_interface.mm
rename to ios/chrome/browser/ui/authentication/signin_earl_grey_app_interface.mm
index 9d469a8..0f2416b 100644
--- a/ios/chrome/browser/ui/authentication/signin_earlgrey_utils_app_interface.mm
+++ b/ios/chrome/browser/ui/authentication/signin_earl_grey_app_interface.mm
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "ios/chrome/browser/ui/authentication/signin_earlgrey_utils_app_interface.h"
+#import "ios/chrome/browser/ui/authentication/signin_earl_grey_app_interface.h"
 
 #include "base/strings/sys_string_conversions.h"
 #include "components/bookmarks/browser/bookmark_model.h"
@@ -26,7 +26,7 @@
 #error "This file requires ARC support."
 #endif
 
-@implementation SigninEarlGreyUtilsAppInterface
+@implementation SigninEarlGreyAppInterface
 
 + (FakeChromeIdentity*)fakeIdentity1 {
   return [FakeChromeIdentity identityWithEmail:@"foo1@gmail.com"
diff --git a/ios/chrome/browser/ui/authentication/signin_earlgrey_utils_app_interface_stub.mm b/ios/chrome/browser/ui/authentication/signin_earl_grey_app_interface_stub.mm
similarity index 71%
rename from ios/chrome/browser/ui/authentication/signin_earlgrey_utils_app_interface_stub.mm
rename to ios/chrome/browser/ui/authentication/signin_earl_grey_app_interface_stub.mm
index c87d363..f1c6a193 100644
--- a/ios/chrome/browser/ui/authentication/signin_earlgrey_utils_app_interface_stub.mm
+++ b/ios/chrome/browser/ui/authentication/signin_earl_grey_app_interface_stub.mm
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "ios/chrome/browser/ui/authentication/signin_earlgrey_utils_app_interface.h"
+#import "ios/chrome/browser/ui/authentication/signin_earl_grey_app_interface.h"
 
 #import "ios/third_party/earl_grey2/src/CommonLib/DistantObject/GREYTestApplicationDistantObject.h"
 
@@ -10,4 +10,4 @@
 #error "This file requires ARC support."
 #endif
 
-GREY_STUB_CLASS_IN_APP_MAIN_QUEUE(SigninEarlGreyUtilsAppInterface)
+GREY_STUB_CLASS_IN_APP_MAIN_QUEUE(SigninEarlGreyAppInterface)
diff --git a/ios/chrome/browser/ui/authentication/signin_earl_grey_ui.mm b/ios/chrome/browser/ui/authentication/signin_earl_grey_ui.mm
index e514ec48..648ba391 100644
--- a/ios/chrome/browser/ui/authentication/signin_earl_grey_ui.mm
+++ b/ios/chrome/browser/ui/authentication/signin_earl_grey_ui.mm
@@ -6,8 +6,8 @@
 
 #include "base/mac/foundation_util.h"
 #import "ios/chrome/browser/ui/authentication/cells/signin_promo_view_constants.h"
-#import "ios/chrome/browser/ui/authentication/signin_earlgrey_utils.h"
-#import "ios/chrome/browser/ui/authentication/signin_earlgrey_utils_app_interface.h"
+#import "ios/chrome/browser/ui/authentication/signin_earl_grey.h"
+#import "ios/chrome/browser/ui/authentication/signin_earl_grey_app_interface.h"
 #import "ios/chrome/browser/ui/authentication/unified_consent/unified_consent_constants.h"
 #import "ios/chrome/browser/ui/settings/google_services/accounts_table_view_controller_constants.h"
 #include "ios/chrome/grit/ios_strings.h"
@@ -38,7 +38,7 @@
 
 + (void)signinWithFakeIdentity:(FakeChromeIdentity*)fakeIdentity
               isManagedAccount:(BOOL)isManagedAccount {
-  [SigninEarlGreyUtils addFakeIdentity:fakeIdentity];
+  [SigninEarlGrey addFakeIdentity:fakeIdentity];
   [ChromeEarlGreyUI openSettingsMenu];
   [ChromeEarlGreyUI
       tapSettingsMenuButton:chrome_test_util::SecondarySignInButton()];
@@ -49,13 +49,13 @@
   }
   [[EarlGrey selectElementWithMatcher:SettingsDoneButton()]
       performAction:grey_tap()];
-  [SigninEarlGreyUtils checkSignedInWithFakeIdentity:fakeIdentity];
+  [SigninEarlGrey checkSignedInWithFakeIdentity:fakeIdentity];
 }
 
 + (void)selectIdentityWithEmail:(NSString*)userEmail {
   // Assumes that the identity chooser is visible.
   [[EarlGrey
-      selectElementWithMatcher:[SigninEarlGreyUtilsAppInterface
+      selectElementWithMatcher:[SigninEarlGreyAppInterface
                                    identityCellMatcherForEmail:userEmail]]
       performAction:grey_tap()];
 }
@@ -74,7 +74,7 @@
   ScopedSynchronizationDisabler disabler;
   id<GREYMatcher> acceptButton = [ChromeMatchersAppInterface
       buttonWithAccessibilityLabelID:IDS_IOS_MANAGED_SIGNIN_ACCEPT_BUTTON];
-  [SigninEarlGreyUtils waitForMatcher:acceptButton];
+  [SigninEarlGrey waitForMatcher:acceptButton];
   [[EarlGrey selectElementWithMatcher:acceptButton] performAction:grey_tap()];
 }
 
@@ -224,7 +224,7 @@
   [ChromeEarlGreyUI waitForAppToIdle];
   [[EarlGrey selectElementWithMatcher:SettingsDoneButton()]
       performAction:grey_tap()];
-  [SigninEarlGreyUtils checkSignedOut];
+  [SigninEarlGrey checkSignedOut];
 }
 
 + (void)tapRemoveAccountFromDeviceWithFakeIdentity:
diff --git a/ios/chrome/browser/ui/bookmarks/bookmarks_promo_egtest.mm b/ios/chrome/browser/ui/bookmarks/bookmarks_promo_egtest.mm
index ebdebfe..5c5d22b 100644
--- a/ios/chrome/browser/ui/bookmarks/bookmarks_promo_egtest.mm
+++ b/ios/chrome/browser/ui/bookmarks/bookmarks_promo_egtest.mm
@@ -7,8 +7,8 @@
 
 #include "base/ios/ios_util.h"
 #import "ios/chrome/browser/ui/authentication/signin/signin_constants.h"
+#import "ios/chrome/browser/ui/authentication/signin_earl_grey.h"
 #import "ios/chrome/browser/ui/authentication/signin_earl_grey_ui.h"
-#import "ios/chrome/browser/ui/authentication/signin_earlgrey_utils.h"
 #import "ios/chrome/browser/ui/bookmarks/bookmark_earl_grey.h"
 #import "ios/chrome/browser/ui/bookmarks/bookmark_earl_grey_ui.h"
 #import "ios/chrome/browser/ui/bookmarks/bookmark_ui_constants.h"
@@ -150,7 +150,7 @@
   [BookmarkEarlGreyUI openBookmarks];
 
   // Set up a fake identity.
-  [SigninEarlGreyUtils addFakeIdentity:[SigninEarlGreyUtils fakeIdentity1]];
+  [SigninEarlGrey addFakeIdentity:[SigninEarlGrey fakeIdentity1]];
 
   // Check that promo is visible.
   [BookmarkEarlGrey verifyPromoAlreadySeen:NO];
@@ -182,8 +182,8 @@
   [BookmarkEarlGrey setupStandardBookmarks];
   [BookmarkEarlGreyUI openBookmarks];
 
-  FakeChromeIdentity* fakeIdentity = [SigninEarlGreyUtils fakeIdentity1];
-  [SigninEarlGreyUtils addFakeIdentity:fakeIdentity];
+  FakeChromeIdentity* fakeIdentity = [SigninEarlGrey fakeIdentity1];
+  [SigninEarlGrey addFakeIdentity:fakeIdentity];
 
   // Check that sign-in promo view are visible.
   [BookmarkEarlGrey verifyPromoAlreadySeen:NO];
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_coordinator.mm b/ios/chrome/browser/ui/content_suggestions/content_suggestions_coordinator.mm
index 9eda7d2..37988ac 100644
--- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_coordinator.mm
+++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_coordinator.mm
@@ -92,6 +92,7 @@
 // Redefined as readwrite.
 @property(nonatomic, strong, readwrite)
     ContentSuggestionsHeaderViewController* headerController;
+@property(nonatomic, strong) PrefBackedBoolean* contentSuggestionsVisible;
 
 @end
 
@@ -120,10 +121,11 @@
           ->GetPrefs();
   bool contentSuggestionsEnabled =
       prefs->GetBoolean(prefs::kArticlesForYouEnabled);
-  bool contentSuggestionsVisible =
-      prefs->GetBoolean(feed::prefs::kArticlesListVisible);
+  self.contentSuggestionsVisible = [[PrefBackedBoolean alloc]
+      initWithPrefService:prefs
+                 prefName:feed::prefs::kArticlesListVisible];
   if (contentSuggestionsEnabled) {
-    if (contentSuggestionsVisible) {
+    if ([self.contentSuggestionsVisible value]) {
       ntp_home::RecordNTPImpression(ntp_home::REMOTE_SUGGESTIONS);
     } else {
       ntp_home::RecordNTPImpression(ntp_home::REMOTE_COLLAPSED);
@@ -197,9 +199,7 @@
   self.contentSuggestionsMediator.commandHandler = self.NTPMediator;
   self.contentSuggestionsMediator.headerProvider = self.headerController;
   self.contentSuggestionsMediator.contentArticlesExpanded =
-      [[PrefBackedBoolean alloc]
-          initWithPrefService:prefs
-                     prefName:feed::prefs::kArticlesListVisible];
+      self.contentSuggestionsVisible;
 
   self.headerController.promoCanShow =
       [self.contentSuggestionsMediator notificationPromo]->CanShow();
@@ -365,6 +365,9 @@
 #pragma mark - DiscoverFeedMenuCommands
 
 - (void)openDiscoverFeedMenu:(UIView*)menuButton {
+  [self.alertCoordinator stop];
+  self.alertCoordinator = nil;
+
   self.alertCoordinator = [[ActionSheetCoordinator alloc]
       initWithBaseViewController:self.suggestionsViewController
                          browser:self.browser
@@ -373,6 +376,27 @@
                             rect:menuButton.frame
                             view:menuButton.superview];
   __weak ContentSuggestionsCoordinator* weakSelf = self;
+
+  if ([self.contentSuggestionsVisible value]) {
+    [self.alertCoordinator
+        addItemWithTitle:l10n_util::GetNSString(
+                             IDS_IOS_DISCOVER_FEED_MENU_TURN_OFF_ITEM)
+                  action:^{
+                    [weakSelf.contentSuggestionsVisible setValue:NO];
+                    [weakSelf.contentSuggestionsMediator reloadAllData];
+                  }
+                   style:UIAlertActionStyleDestructive];
+  } else {
+    [self.alertCoordinator
+        addItemWithTitle:l10n_util::GetNSString(
+                             IDS_IOS_DISCOVER_FEED_MENU_TURN_ON_ITEM)
+                  action:^{
+                    [weakSelf.contentSuggestionsVisible setValue:YES];
+                    [weakSelf.contentSuggestionsMediator reloadAllData];
+                  }
+                   style:UIAlertActionStyleDefault];
+  }
+
   [self.alertCoordinator
       addItemWithTitle:l10n_util::GetNSString(
                            IDS_IOS_DISCOVER_FEED_MENU_MANAGE_INTERESTS_ITEM)
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_mediator.h b/ios/chrome/browser/ui/content_suggestions/content_suggestions_mediator.h
index 577e40ea..4358af6 100644
--- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_mediator.h
+++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_mediator.h
@@ -78,6 +78,9 @@
 // Disconnects the mediator.
 - (void)disconnect;
 
+// Reloads content suggestions.
+- (void)reloadAllData;
+
 // The notification promo owned by this mediator.
 - (NotificationPromoWhatsNew*)notificationPromo;
 
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_mediator.mm b/ios/chrome/browser/ui/content_suggestions/content_suggestions_mediator.mm
index 2926810b..d8ba703 100644
--- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_mediator.mm
+++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_mediator.mm
@@ -199,6 +199,10 @@
   _prefObserverBridge.reset();
 }
 
+- (void)reloadAllData {
+  [self.dataSink reloadAllData];
+}
+
 - (void)blockMostVisitedURL:(GURL)URL {
   _mostVisitedSites->AddOrRemoveBlacklistedUrl(URL, true);
   [self useFreshMostVisited];
@@ -288,7 +292,9 @@
   } else if (sectionInfo == self.learnMoreSectionInfo) {
     [convertedSuggestions addObject:self.learnMoreItem];
   } else if (sectionInfo == self.discoverSectionInfo) {
-    [convertedSuggestions addObject:self.discoverItem];
+    if ([self.contentArticlesExpanded value]) {
+      [convertedSuggestions addObject:self.discoverItem];
+    }
   } else if (!IsDiscoverFeedEnabled()) {
     ntp_snippets::Category category =
         [[self categoryWrapperForSectionInfo:sectionInfo] category];
@@ -481,7 +487,7 @@
   dispatch_after(
       dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)),
       dispatch_get_main_queue(), ^{
-        [self.dataSink reloadAllData];
+        [self reloadAllData];
       });
 }
 
@@ -745,7 +751,7 @@
 
 - (void)onPreferenceChanged:(const std::string&)preferenceName {
   if (preferenceName == prefs::kArticlesForYouEnabled) {
-    [self.dataSink reloadAllData];
+    [self reloadAllData];
   }
 }
 
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller.mm b/ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller.mm
index 21400a2..cf157bae 100644
--- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller.mm
+++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller.mm
@@ -48,7 +48,7 @@
 using CSCollectionViewItem = CollectionViewItem<SuggestedContent>;
 const CGFloat kMostVisitedBottomMargin = 13;
 const CGFloat kCardBorderRadius = 11;
-
+const CGFloat kDiscoverFeedContentWith = 430;
 }
 
 NSString* const kContentSuggestionsMostVisitedAccessibilityIdentifierPrefix =
@@ -492,8 +492,7 @@
   UIEdgeInsets parentInset = [super collectionView:collectionView
                                             layout:collectionViewLayout
                             insetForSectionAtIndex:section];
-  if ([self.collectionUpdater isHeaderSection:section] ||
-      [self.collectionUpdater isDiscoverSection:section]) {
+  if ([self.collectionUpdater isHeaderSection:section]) {
     parentInset.top = 0;
     parentInset.left = 0;
     parentInset.right = 0;
@@ -506,6 +505,13 @@
     if ([self.collectionUpdater isMostVisitedSection:section]) {
       parentInset.bottom = kMostVisitedBottomMargin;
     }
+  } else if ([self.collectionUpdater isDiscoverSection:section]) {
+    // TODO(crbug.com/1085419): Get card width from Mulder.
+    CGFloat feedCardWidth = kDiscoverFeedContentWith;
+    CGFloat margin =
+        MAX(0, (collectionView.frame.size.width - feedCardWidth) / 2);
+    parentInset.left = margin;
+    parentInset.right = margin;
   } else if (self.styler.cellStyle == MDCCollectionViewCellStyleCard) {
     CGFloat collectionWidth = collectionView.bounds.size.width;
     CGFloat maxCardWidth =
diff --git a/ios/chrome/browser/ui/first_run/first_run_egtest.mm b/ios/chrome/browser/ui/first_run/first_run_egtest.mm
index 00895d0..9ef1049 100644
--- a/ios/chrome/browser/ui/first_run/first_run_egtest.mm
+++ b/ios/chrome/browser/ui/first_run/first_run_egtest.mm
@@ -5,8 +5,8 @@
 #include "base/strings/sys_string_conversions.h"
 #import "base/test/ios/wait_util.h"
 #import "ios/chrome/browser/ui/authentication/signin/signin_constants.h"
+#import "ios/chrome/browser/ui/authentication/signin_earl_grey.h"
 #import "ios/chrome/browser/ui/authentication/signin_earl_grey_ui.h"
-#import "ios/chrome/browser/ui/authentication/signin_earlgrey_utils.h"
 #import "ios/chrome/browser/ui/first_run/first_run_app_interface.h"
 #import "ios/chrome/browser/ui/first_run/first_run_constants.h"
 #include "ios/chrome/grit/ios_chromium_strings.h"
@@ -122,8 +122,8 @@
 
 // Signs in to an account and then taps the Advanced link to go to settings.
 - (void)testSignInAndTapSettingsLink {
-  FakeChromeIdentity* fakeIdentity = [SigninEarlGreyUtils fakeIdentity1];
-  [SigninEarlGreyUtils addFakeIdentity:fakeIdentity];
+  FakeChromeIdentity* fakeIdentity = [SigninEarlGrey fakeIdentity1];
+  [SigninEarlGrey addFakeIdentity:fakeIdentity];
 
   // Launch First Run and accept tems of services.
   [FirstRunAppInterface showFirstRunUI];
@@ -141,7 +141,7 @@
   [[EarlGrey selectElementWithMatcher:SyncSettingsConfirmButton()]
       performAction:grey_tap()];
 
-  [SigninEarlGreyUtils checkSignedInWithFakeIdentity:fakeIdentity];
+  [SigninEarlGrey checkSignedInWithFakeIdentity:fakeIdentity];
 
   GREYAssertTrue([FirstRunAppInterface isSyncFirstSetupComplete],
                  @"Sync should have finished its original setup");
diff --git a/ios/chrome/browser/ui/main/scene_controller.mm b/ios/chrome/browser/ui/main/scene_controller.mm
index c2e4c30..d7b121c 100644
--- a/ios/chrome/browser/ui/main/scene_controller.mm
+++ b/ios/chrome/browser/ui/main/scene_controller.mm
@@ -79,6 +79,7 @@
 #import "ios/public/provider/chrome/browser/user_feedback/user_feedback_provider.h"
 #include "ios/web/public/thread/web_task_traits.h"
 #import "ios/web/public/web_state.h"
+#import "net/base/mac/url_conversions.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
@@ -1353,6 +1354,55 @@
                          dismissOmnibox:dismissOmnibox];
 }
 
+- (void)dismissModalsAndOpenMultipleTabsInMode:
+            (ApplicationModeForTabOpening)targetMode
+                                          URLs:(const std::vector<GURL>&)URLs
+                                dismissOmnibox:(BOOL)dismissOmnibox
+                                    completion:(ProceduralBlock)completion {
+  __weak SceneController* weakSelf = self;
+
+  std::vector<GURL> copyURLs = URLs;
+
+  id<BrowserInterface> targetInterface =
+      [self extractInterfaceBaseOnMode:targetMode];
+
+  web::WebState* currentWebState =
+      targetInterface.browser->GetWebStateList()->GetActiveWebState();
+
+  if (currentWebState) {
+    web::NavigationManager* navigation_manager =
+        currentWebState->GetNavigationManager();
+    // Check if the current tab is in the procress of restoration and whether it
+    // is an NTP. If so, add the tabs-opening action to the
+    // RestoreCompletionCallback queue so that the tabs are opened only after
+    // the NTP finishes restoring. This is to avoid an edge where multiple tabs
+    // are trying to open in the middle of NTP restoration, as this will cause
+    // all tabs trying to load into the same NTP, causing a race condition that
+    // results in wrong behavior.
+    if (navigation_manager->IsRestoreSessionInProgress() &&
+        IsURLNtp(currentWebState->GetVisibleURL())) {
+      navigation_manager->AddRestoreCompletionCallback(base::BindOnce(^{
+        [self
+            dismissModalDialogsWithCompletion:^{
+              [weakSelf openMultipleTabsInMode:targetMode
+                                          URLs:copyURLs
+                                    completion:completion];
+            }
+                               dismissOmnibox:dismissOmnibox];
+      }));
+      return;
+    }
+  }
+
+  [self
+      dismissModalDialogsWithCompletion:^{
+        [weakSelf openMultipleTabsInMode:targetMode
+                                    URLs:copyURLs
+                              completion:completion];
+      }
+                         dismissOmnibox:dismissOmnibox];
+}
+
 - (void)openTabFromLaunchWithParams:(URLOpenerParams*)params
                  startupInformation:(id<StartupInformation>)startupInformation
                            appState:(AppState*)appState {
@@ -1508,6 +1558,61 @@
   ios::GetChromeBrowserProvider()->LogIfModalViewsArePresented();
 }
 
+- (void)openMultipleTabsInMode:
+            (ApplicationModeForTabOpening)tabOpeningTargetMode
+                          URLs:(const std::vector<GURL>&)URLs
+                    completion:(ProceduralBlock)completion {
+  [self recursiveOpenURLs:URLs
+                   inMode:tabOpeningTargetMode
+             currentIndex:0
+               totalCount:URLs.size()
+               completion:completion];
+}
+
+// Call |dismissModalsAndOpenSelectedTabInMode| recursively to open the list of
+// URLs contained in |URLs|. Achieved through chaining
+// |dismissModalsAndOpenSelectedTabInMode| in its completion handler.
+- (void)recursiveOpenURLs:(const std::vector<GURL>&)URLs
+                   inMode:(ApplicationModeForTabOpening)mode
+             currentIndex:(size_t)currentIndex
+               totalCount:(size_t)totalCount
+               completion:(ProceduralBlock)completion {
+  if (currentIndex >= totalCount) {
+    if (completion) {
+      completion();
+    }
+    return;
+  }
+
+  GURL webpageGURL = URLs.at(currentIndex);
+
+  __weak SceneController* weakSelf = self;
+
+  if (!webpageGURL.is_valid()) {
+    [self recursiveOpenURLs:URLs
+                     inMode:mode
+               currentIndex:(currentIndex + 1)
+                 totalCount:totalCount
+                 completion:completion];
+    return;
+  }
+
+  UrlLoadParams param = UrlLoadParams::InNewTab(webpageGURL, webpageGURL);
+  std::vector<GURL> copyURLs = URLs;
+
+  [self dismissModalsAndOpenSelectedTabInMode:mode
+                            withUrlLoadParams:param
+                               dismissOmnibox:YES
+                                   completion:^{
+                                     [weakSelf
+                                         recursiveOpenURLs:copyURLs
+                                                    inMode:mode
+                                              currentIndex:(currentIndex + 1)
+                                                totalCount:totalCount
+                                                completion:completion];
+                                   }];
+}
+
 // Opens a tab in the target BVC, and switches to it in a way that's appropriate
 // to the current UI, based on the |dismissModals| flag:
 // - If a modal dialog is showing and |dismissModals| is NO, the selected tab of
@@ -1548,7 +1653,6 @@
 
   // Commands are only allowed on NTP.
   DCHECK(IsURLNtp(urlLoadParams.web_params.url) || !startupCompletion);
-
   ProceduralBlock tabOpenedCompletion = nil;
   if (startupCompletion && completion) {
     tabOpenedCompletion = ^{
@@ -1697,7 +1801,6 @@
         ->Load(newTabParams);
     return;
   }
-
   // Otherwise, load |urlLoadParams| in the current tab.
   UrlLoadParams sameTabParams = urlLoadParams;
   sameTabParams.disposition = WindowOpenDisposition::CURRENT_TAB;
@@ -1973,6 +2076,28 @@
   }
 }
 
+- (id<BrowserInterface>)extractInterfaceBaseOnMode:
+    (ApplicationModeForTabOpening)targetMode {
+  ApplicationMode applicationMode;
+
+  if (targetMode == ApplicationModeForTabOpening::CURRENT) {
+    applicationMode = self.interfaceProvider.currentInterface.incognito
+                          ? ApplicationMode::INCOGNITO
+                          : ApplicationMode::NORMAL;
+  } else if (targetMode == ApplicationModeForTabOpening::NORMAL) {
+    applicationMode = ApplicationMode::NORMAL;
+  } else {
+    applicationMode = ApplicationMode::INCOGNITO;
+  }
+
+  id<BrowserInterface> targetInterface =
+      applicationMode == ApplicationMode::NORMAL
+          ? self.interfaceProvider.mainInterface
+          : self.interfaceProvider.incognitoInterface;
+
+  return targetInterface;
+}
+
 #pragma mark - Handling of destroying the incognito BrowserState
 
 // The incognito BrowserState should be closed when the last incognito tab is
diff --git a/ios/chrome/browser/ui/popup_menu/popup_menu_mediator.mm b/ios/chrome/browser/ui/popup_menu/popup_menu_mediator.mm
index a36800ce..63da8ca 100644
--- a/ios/chrome/browser/ui/popup_menu/popup_menu_mediator.mm
+++ b/ios/chrome/browser/ui/popup_menu/popup_menu_mediator.mm
@@ -771,34 +771,58 @@
 
 // Creates the menu items for the search menu.
 - (void)createSearchMenuItems {
-  NSMutableArray* items = [NSMutableArray array];
+  self.items = @[ [self searchMenuStaticItems] ];
 
-  // The consumer is expecting an array of arrays of items. Each sub array
-  // represent a section in the popup menu. Having one sub array means having
-  // all the items in the same section.
-  PopupMenuToolsItem* copiedContentItem = nil;
   ClipboardRecentContent* clipboardRecentContent =
       ClipboardRecentContent::GetInstance();
 
-  if (search_engines::SupportsSearchByImage(self.templateURLService) &&
-      clipboardRecentContent->HasRecentImageFromClipboard()) {
-    copiedContentItem = CreateTableViewItem(
-        IDS_IOS_TOOLS_MENU_SEARCH_COPIED_IMAGE,
-        PopupMenuActionSearchCopiedImage, @"popup_menu_paste_and_go",
-        kToolsMenuCopiedImageSearch);
-  } else if (clipboardRecentContent->GetRecentURLFromClipboard()) {
-    copiedContentItem = CreateTableViewItem(
-        IDS_IOS_TOOLS_MENU_VISIT_COPIED_LINK, PopupMenuActionPasteAndGo,
-        @"popup_menu_paste_and_go", kToolsMenuPasteAndGo);
-  } else if (clipboardRecentContent->GetRecentTextFromClipboard()) {
-    copiedContentItem = CreateTableViewItem(
-        IDS_IOS_TOOLS_MENU_SEARCH_COPIED_TEXT, PopupMenuActionPasteAndGo,
-        @"popup_menu_paste_and_go", kToolsMenuPasteAndGo);
-  }
-  if (copiedContentItem) {
-    [items addObject:@[ copiedContentItem ]];
-  }
+  std::set<ClipboardContentType> clipboard_types;
+  clipboard_types.insert(ClipboardContentType::URL);
+  clipboard_types.insert(ClipboardContentType::Text);
+  clipboard_types.insert(ClipboardContentType::Image);
 
+  clipboardRecentContent->HasRecentContentFromClipboard(
+      clipboard_types,
+      base::BindOnce(^(std::set<ClipboardContentType> matched_types) {
+        __block NSMutableArray* items = [NSMutableArray array];
+        // The consumer is expecting an array of arrays of items. Each sub array
+        // represent a section in the popup menu. Having one sub array means
+        // having all the items in the same section.
+        PopupMenuToolsItem* copiedContentItem = nil;
+        if (search_engines::SupportsSearchByImage(self.templateURLService) &&
+            matched_types.find(ClipboardContentType::Image) !=
+                matched_types.end()) {
+          copiedContentItem = CreateTableViewItem(
+              IDS_IOS_TOOLS_MENU_SEARCH_COPIED_IMAGE,
+              PopupMenuActionSearchCopiedImage, @"popup_menu_paste_and_go",
+              kToolsMenuCopiedImageSearch);
+        } else if (matched_types.find(ClipboardContentType::URL) !=
+                   matched_types.end()) {
+          copiedContentItem = CreateTableViewItem(
+              IDS_IOS_TOOLS_MENU_VISIT_COPIED_LINK, PopupMenuActionPasteAndGo,
+              @"popup_menu_paste_and_go", kToolsMenuPasteAndGo);
+        } else if (matched_types.find(ClipboardContentType::Text) !=
+                   matched_types.end()) {
+          copiedContentItem = CreateTableViewItem(
+              IDS_IOS_TOOLS_MENU_SEARCH_COPIED_TEXT, PopupMenuActionPasteAndGo,
+              @"popup_menu_paste_and_go", kToolsMenuPasteAndGo);
+        }
+        if (copiedContentItem) {
+          [items addObject:@[ copiedContentItem ]];
+        }
+
+        [items addObject:[self searchMenuStaticItems]];
+        self.items = items;
+
+        dispatch_async(dispatch_get_main_queue(), ^{
+          [self.popupMenu setPopupMenuItems:self.items];
+        });
+      }));
+}
+
+// Creates and returns the search menu items that are static (i.e. always in
+// the search menu).
+- (NSArray<TableViewItem<PopupMenuItem>*>*)searchMenuStaticItems {
   PopupMenuToolsItem* QRCodeSearch = CreateTableViewItem(
       IDS_IOS_TOOLS_MENU_QR_SCANNER, PopupMenuActionQRCodeSearch,
       @"popup_menu_qr_scanner", kToolsMenuQRCodeSearch);
@@ -812,10 +836,7 @@
       IDS_IOS_TOOLS_MENU_NEW_INCOGNITO_SEARCH, PopupMenuActionIncognitoSearch,
       @"popup_menu_new_incognito_tab", kToolsMenuIncognitoSearch);
 
-  [items
-      addObject:@[ newSearch, newIncognitoSearch, voiceSearch, QRCodeSearch ]];
-
-  self.items = items;
+  return @[ newSearch, newIncognitoSearch, voiceSearch, QRCodeSearch ];
 }
 
 // Creates the menu items for the tools menu.
diff --git a/ios/chrome/browser/ui/popup_menu/public/popup_menu_presenter.mm b/ios/chrome/browser/ui/popup_menu/public/popup_menu_presenter.mm
index 49e36dfd..176e9c8 100644
--- a/ios/chrome/browser/ui/popup_menu/public/popup_menu_presenter.mm
+++ b/ios/chrome/browser/ui/popup_menu/public/popup_menu_presenter.mm
@@ -33,6 +33,11 @@
 @property(nonatomic, strong) NSArray<NSLayoutConstraint*>* initialConstraints;
 // Constraints used for the positioning of the popup when presented.
 @property(nonatomic, strong) NSArray<NSLayoutConstraint*>* presentedConstraints;
+
+@property(nonatomic, strong)
+    NSLayoutConstraint* presentedViewControllerHeightConstraint;
+@property(nonatomic, strong)
+    NSLayoutConstraint* presentedViewControllerWidthConstraint;
 @end
 
 @implementation PopupMenuPresenter
@@ -67,35 +72,30 @@
   // itself.
   [self.presentedViewController.view setNeedsLayout];
   [self.presentedViewController.view layoutIfNeeded];
-  CGSize fittingSize = [self.presentedViewController.view
-      sizeThatFits:CGSizeMake(kMaxWidth, kMaxHeight)];
-  // Use preferredSize if it is set.
-  CGSize preferredSize = self.presentedViewController.preferredContentSize;
-  CGFloat width = fittingSize.width;
-  CGFloat height = fittingSize.height;
-  if (!CGSizeEqualToSize(preferredSize, CGSizeZero)) {
-    width = preferredSize.width;
-    height = preferredSize.height;
-  }
 
   // Set the sizing constraints, in case the UIViewController is using a
   // UIScrollView. The priority needs to be non-required to allow downsizing if
   // needed, and more than UILayoutPriorityDefaultHigh to take precedence on
   // compression resistance.
-  NSLayoutConstraint* widthConstraint =
+  self.presentedViewControllerWidthConstraint =
       [self.presentedViewController.view.widthAnchor
-          constraintEqualToConstant:width];
-  widthConstraint.priority = UILayoutPriorityDefaultHigh + 1;
+          constraintEqualToConstant:0];
+  self.presentedViewControllerWidthConstraint.priority =
+      UILayoutPriorityDefaultHigh + 1;
 
-  NSLayoutConstraint* heightConstraint =
+  self.presentedViewControllerHeightConstraint =
       [self.presentedViewController.view.heightAnchor
-          constraintEqualToConstant:height];
-  heightConstraint.priority = UILayoutPriorityDefaultHigh + 1;
+          constraintEqualToConstant:0];
+  self.presentedViewControllerHeightConstraint.priority =
+      UILayoutPriorityDefaultHigh + 1;
+
+  // Set the constraint constants to their correct intial values.
+  [self setPresentedViewControllerConstraintConstants];
 
   UIView* popup = self.popupViewController.contentContainer;
   [NSLayoutConstraint activateConstraints:@[
-    widthConstraint,
-    heightConstraint,
+    self.presentedViewControllerWidthConstraint,
+    self.presentedViewControllerHeightConstraint,
     [popup.heightAnchor constraintLessThanOrEqualToConstant:kMaxHeight],
     [popup.widthAnchor constraintLessThanOrEqualToConstant:kMaxWidth],
     [popup.widthAnchor constraintGreaterThanOrEqualToConstant:kMinWidth],
@@ -238,6 +238,23 @@
   ];
 }
 
+// Updates the constants for the constraints constraining the presented view
+// controller's height and width.
+- (void)setPresentedViewControllerConstraintConstants {
+  CGSize fittingSize = [self.presentedViewController.view
+      sizeThatFits:CGSizeMake(kMaxWidth, kMaxHeight)];
+  // Use preferredSize if it is set.
+  CGSize preferredSize = self.presentedViewController.preferredContentSize;
+  CGFloat width = fittingSize.width;
+  CGFloat height = fittingSize.height;
+  if (!CGSizeEqualToSize(preferredSize, CGSizeZero)) {
+    width = preferredSize.width;
+    height = preferredSize.height;
+  }
+  self.presentedViewControllerHeightConstraint.constant = height;
+  self.presentedViewControllerWidthConstraint.constant = width;
+}
+
 #pragma mark - PopupMenuViewControllerDelegate
 
 - (void)popupMenuViewControllerWillDismiss:
@@ -245,4 +262,19 @@
   [self.delegate popupMenuPresenterWillDismiss:self];
 }
 
+- (void)containedViewControllerContentSizeChangedForPopupMenuViewController:
+    (PopupMenuViewController*)viewController {
+  // Set the frame of the table view to the maximum width to have the label
+  // resizing correctly.
+  CGRect frame = self.presentedViewController.view.frame;
+  frame.size.width = kMaxWidth;
+  self.presentedViewController.view.frame = frame;
+  // It is necessary to do a first layout pass so the table view can size
+  // itself.
+  [self.presentedViewController.view setNeedsLayout];
+  [self.presentedViewController.view layoutIfNeeded];
+
+  [self setPresentedViewControllerConstraintConstants];
+}
+
 @end
diff --git a/ios/chrome/browser/ui/popup_menu/public/popup_menu_table_view_controller.mm b/ios/chrome/browser/ui/popup_menu/public/popup_menu_table_view_controller.mm
index 8c360e0..7ccb96ef 100644
--- a/ios/chrome/browser/ui/popup_menu/public/popup_menu_table_view_controller.mm
+++ b/ios/chrome/browser/ui/popup_menu/public/popup_menu_table_view_controller.mm
@@ -130,6 +130,7 @@
     }
   }
   [self.tableView reloadData];
+  self.preferredContentSize = [self calculatePreferredContentSize];
 }
 
 - (void)itemsHaveChanged:(NSArray<TableViewItem<PopupMenuItem>*>*)items {
@@ -166,7 +167,13 @@
   }
 }
 
-- (CGSize)preferredContentSize {
+- (void)viewDidLayoutSubviews {
+  [super viewDidLayoutSubviews];
+
+  self.preferredContentSize = [self calculatePreferredContentSize];
+}
+
+- (CGSize)calculatePreferredContentSize {
   CGFloat width = 0;
   CGFloat height = 0;
   for (NSInteger section = 0; section < [self.tableViewModel numberOfSections];
diff --git a/ios/chrome/browser/ui/popup_menu/public/popup_menu_view_controller.h b/ios/chrome/browser/ui/popup_menu/public/popup_menu_view_controller.h
index 3e0a269..2b230999 100644
--- a/ios/chrome/browser/ui/popup_menu/public/popup_menu_view_controller.h
+++ b/ios/chrome/browser/ui/popup_menu/public/popup_menu_view_controller.h
@@ -8,7 +8,6 @@
 #import <UIKit/UIKit.h>
 
 @protocol PopupMenuViewControllerDelegate;
-@protocol PopupMenuViewControllerDelegate;
 
 // ViewController displaying a popup for a menu. The view of this controller is
 // a transparent scrim, dismissing the popup if tapped.
diff --git a/ios/chrome/browser/ui/popup_menu/public/popup_menu_view_controller.mm b/ios/chrome/browser/ui/popup_menu/public/popup_menu_view_controller.mm
index 165beb7..3d811950 100644
--- a/ios/chrome/browser/ui/popup_menu/public/popup_menu_view_controller.mm
+++ b/ios/chrome/browser/ui/popup_menu/public/popup_menu_view_controller.mm
@@ -84,4 +84,10 @@
   [self.delegate popupMenuViewControllerWillDismiss:self];
 }
 
+- (void)preferredContentSizeDidChangeForChildContentContainer:
+    (id<UIContentContainer>)container {
+  [self.delegate
+      containedViewControllerContentSizeChangedForPopupMenuViewController:self];
+}
+
 @end
diff --git a/ios/chrome/browser/ui/popup_menu/public/popup_menu_view_controller_delegate.h b/ios/chrome/browser/ui/popup_menu/public/popup_menu_view_controller_delegate.h
index 61994689..8a3409e 100644
--- a/ios/chrome/browser/ui/popup_menu/public/popup_menu_view_controller_delegate.h
+++ b/ios/chrome/browser/ui/popup_menu/public/popup_menu_view_controller_delegate.h
@@ -16,6 +16,9 @@
 - (void)popupMenuViewControllerWillDismiss:
     (PopupMenuViewController*)viewController;
 
+- (void)containedViewControllerContentSizeChangedForPopupMenuViewController:
+    (PopupMenuViewController*)viewController;
+
 @end
 
 #endif  // IOS_CHROME_BROWSER_UI_POPUP_MENU_PUBLIC_POPUP_MENU_VIEW_CONTROLLER_DELEGATE_H_
diff --git a/ios/chrome/browser/ui/recent_tabs/recent_tabs_egtest.mm b/ios/chrome/browser/ui/recent_tabs/recent_tabs_egtest.mm
index 1d90351..a06e18bd 100644
--- a/ios/chrome/browser/ui/recent_tabs/recent_tabs_egtest.mm
+++ b/ios/chrome/browser/ui/recent_tabs/recent_tabs_egtest.mm
@@ -8,8 +8,8 @@
 #include "base/ios/ios_util.h"
 #include "components/strings/grit/components_strings.h"
 #import "ios/chrome/browser/ui/authentication/cells/signin_promo_view_constants.h"
+#import "ios/chrome/browser/ui/authentication/signin_earl_grey.h"
 #import "ios/chrome/browser/ui/authentication/signin_earl_grey_ui.h"
-#import "ios/chrome/browser/ui/authentication/signin_earlgrey_utils.h"
 #import "ios/chrome/browser/ui/history/history_ui_constants.h"
 #import "ios/chrome/browser/ui/list_model/list_model.h"
 #import "ios/chrome/browser/ui/recent_tabs/recent_tabs_constants.h"
@@ -162,13 +162,13 @@
   // Sign-in promo should be visible with cold state.
   [SigninEarlGreyUI checkSigninPromoVisibleWithMode:SigninPromoViewModeColdState
                                         closeButton:NO];
-  FakeChromeIdentity* fakeIdentity = [SigninEarlGreyUtils fakeIdentity1];
-  [SigninEarlGreyUtils addFakeIdentity:fakeIdentity];
+  FakeChromeIdentity* fakeIdentity = [SigninEarlGrey fakeIdentity1];
+  [SigninEarlGrey addFakeIdentity:fakeIdentity];
   // Sign-in promo should be visible with warm state.
   [SigninEarlGreyUI checkSigninPromoVisibleWithMode:SigninPromoViewModeWarmState
                                         closeButton:NO];
   [self closeRecentTabs];
-  [SigninEarlGreyUtils removeFakeIdentity:fakeIdentity];
+  [SigninEarlGrey removeFakeIdentity:fakeIdentity];
 }
 
 // Tests that the sign-in promo can be reloaded correctly while being hidden.
@@ -189,8 +189,8 @@
   [SigninEarlGreyUI checkSigninPromoNotVisible];
 
   // Add an account.
-  FakeChromeIdentity* fakeIdentity = [SigninEarlGreyUtils fakeIdentity1];
-  [SigninEarlGreyUtils addFakeIdentity:fakeIdentity];
+  FakeChromeIdentity* fakeIdentity = [SigninEarlGrey fakeIdentity1];
+  [SigninEarlGrey addFakeIdentity:fakeIdentity];
 
   // Tap on "Other Devices", to show the sign-in promo.
   [[EarlGrey selectElementWithMatcher:otherDevicesMatcher]
@@ -198,7 +198,7 @@
   [SigninEarlGreyUI checkSigninPromoVisibleWithMode:SigninPromoViewModeWarmState
                                         closeButton:NO];
   [self closeRecentTabs];
-  [SigninEarlGreyUtils removeFakeIdentity:fakeIdentity];
+  [SigninEarlGrey removeFakeIdentity:fakeIdentity];
 }
 
 // Tests that the VC can be dismissed by swiping down.
@@ -234,7 +234,7 @@
 // Tests that the Recent Tabs can be opened while signed in (prevent regression
 // for https://crbug.com/1056613).
 - (void)testOpenWhileSignedIn {
-  FakeChromeIdentity* fakeIdentity = [SigninEarlGreyUtils fakeIdentity1];
+  FakeChromeIdentity* fakeIdentity = [SigninEarlGrey fakeIdentity1];
   [SigninEarlGreyUI signinWithFakeIdentity:fakeIdentity];
 
   OpenRecentTabsPanel();
diff --git a/ios/chrome/browser/ui/settings/google_services/accounts_table_egtest.mm b/ios/chrome/browser/ui/settings/google_services/accounts_table_egtest.mm
index ce9e3813..d774aea7 100644
--- a/ios/chrome/browser/ui/settings/google_services/accounts_table_egtest.mm
+++ b/ios/chrome/browser/ui/settings/google_services/accounts_table_egtest.mm
@@ -5,8 +5,8 @@
 #import <UIKit/UIKit.h>
 
 #import "base/test/scoped_feature_list.h"
+#import "ios/chrome/browser/ui/authentication/signin_earl_grey.h"
 #import "ios/chrome/browser/ui/authentication/signin_earl_grey_ui.h"
-#import "ios/chrome/browser/ui/authentication/signin_earlgrey_utils.h"
 #import "ios/chrome/browser/ui/bookmarks/bookmark_earl_grey.h"
 #import "ios/chrome/browser/ui/bookmarks/bookmark_earl_grey_ui.h"
 #import "ios/chrome/browser/ui/settings/google_services/accounts_table_view_controller_constants.h"
@@ -76,7 +76,7 @@
 // Tests that the Sync and Account Settings screen are correctly popped if the
 // signed in account is removed.
 - (void)testSignInPopUpAccountOnSyncSettings {
-  FakeChromeIdentity* fakeIdentity = [SigninEarlGreyUtils fakeIdentity1];
+  FakeChromeIdentity* fakeIdentity = [SigninEarlGrey fakeIdentity1];
 
   // Sign In |identity|, then open the Sync Settings.
   [SigninEarlGreyUI signinWithFakeIdentity:fakeIdentity];
@@ -85,11 +85,11 @@
 
   // Forget |fakeIdentity|, screens should be popped back to the Main Settings.
   [ChromeEarlGreyUI waitForAppToIdle];
-  [SigninEarlGreyUtils forgetFakeIdentity:fakeIdentity];
+  [SigninEarlGrey forgetFakeIdentity:fakeIdentity];
 
   [[EarlGrey selectElementWithMatcher:PrimarySignInButton()]
       assertWithMatcher:grey_sufficientlyVisible()];
-  [SigninEarlGreyUtils checkSignedOut];
+  [SigninEarlGrey checkSignedOut];
 
   [[EarlGrey selectElementWithMatcher:SettingsDoneButton()]
       performAction:grey_tap()];
@@ -98,8 +98,8 @@
 // Tests that the Account Settings screen is correctly popped if the signed in
 // account is removed while the "Disconnect Account" dialog is up.
 - (void)testSignInPopUpAccountOnDisconnectAccount {
-  FakeChromeIdentity* fakeIdentity = [SigninEarlGreyUtils fakeIdentity1];
-  [SigninEarlGreyUtils addFakeIdentity:fakeIdentity];
+  FakeChromeIdentity* fakeIdentity = [SigninEarlGrey fakeIdentity1];
+  [SigninEarlGrey addFakeIdentity:fakeIdentity];
 
   // Sign In |fakeIdentity|, then open the Account Settings.
   [SigninEarlGreyUI signinWithFakeIdentity:fakeIdentity];
@@ -109,11 +109,11 @@
 
   // Forget |fakeIdentity|, screens should be popped back to the Main Settings.
   [ChromeEarlGreyUI waitForAppToIdle];
-  [SigninEarlGreyUtils forgetFakeIdentity:fakeIdentity];
+  [SigninEarlGrey forgetFakeIdentity:fakeIdentity];
 
   [[EarlGrey selectElementWithMatcher:PrimarySignInButton()]
       assertWithMatcher:grey_sufficientlyVisible()];
-  [SigninEarlGreyUtils checkSignedOut];
+  [SigninEarlGrey checkSignedOut];
 
   [[EarlGrey selectElementWithMatcher:SettingsDoneButton()]
       performAction:grey_tap()];
@@ -122,9 +122,9 @@
 // Tests that the Account Settings screen is correctly reloaded when one of
 // the non-primary account is removed.
 - (void)testSignInReloadOnRemoveAccount {
-  FakeChromeIdentity* fakeIdentity1 = [SigninEarlGreyUtils fakeIdentity1];
-  FakeChromeIdentity* fakeIdentity2 = [SigninEarlGreyUtils fakeIdentity2];
-  [SigninEarlGreyUtils addFakeIdentity:fakeIdentity2];
+  FakeChromeIdentity* fakeIdentity1 = [SigninEarlGrey fakeIdentity1];
+  FakeChromeIdentity* fakeIdentity2 = [SigninEarlGrey fakeIdentity2];
+  [SigninEarlGrey addFakeIdentity:fakeIdentity2];
 
   // Sign In |fakeIdentity|, then open the Account Settings.
   [SigninEarlGreyUI signinWithFakeIdentity:fakeIdentity1];
@@ -148,7 +148,7 @@
                                               fakeIdentity2.userEmail),
                                           grey_sufficientlyVisible(), nil)]
       assertWithMatcher:grey_nil()];
-  [SigninEarlGreyUtils checkSignedInWithFakeIdentity:fakeIdentity1];
+  [SigninEarlGrey checkSignedInWithFakeIdentity:fakeIdentity1];
 
   [[EarlGrey selectElementWithMatcher:SettingsDoneButton()]
       performAction:grey_tap()];
@@ -157,7 +157,7 @@
 // Tests that the Account Settings screen is popped and the user signed out
 // when the account is removed.
 - (void)testSignOutOnRemoveAccount {
-  FakeChromeIdentity* fakeIdentity = [SigninEarlGreyUtils fakeIdentity1];
+  FakeChromeIdentity* fakeIdentity = [SigninEarlGrey fakeIdentity1];
 
   // Sign In |fakeIdentity|, then open the Account Settings.
   [SigninEarlGreyUI signinWithFakeIdentity:fakeIdentity];
@@ -177,7 +177,7 @@
   // Check that the user is signed out and the Main Settings screen is shown.
   [[EarlGrey selectElementWithMatcher:PrimarySignInButton()]
       assertWithMatcher:grey_sufficientlyVisible()];
-  [SigninEarlGreyUtils checkSignedOut];
+  [SigninEarlGrey checkSignedOut];
 
   [[EarlGrey selectElementWithMatcher:SettingsDoneButton()]
       performAction:grey_tap()];
@@ -186,7 +186,7 @@
 // Tests that selecting sign-out from a non-managed account keeps the user's
 // synced data.
 - (void)testSignOutFromNonManagedAccountKeepsData {
-  FakeChromeIdentity* fakeIdentity = [SigninEarlGreyUtils fakeIdentity1];
+  FakeChromeIdentity* fakeIdentity = [SigninEarlGrey fakeIdentity1];
 
   // Sign In |fakeIdentity|.
   [SigninEarlGreyUI signinWithFakeIdentity:fakeIdentity];
@@ -212,7 +212,7 @@
 // Tests that selecting sign-out and clear data from a non-managed user account
 // clears the user's synced data.
 - (void)testSignOutAndClearDataFromNonManagedAccountClearsData {
-  FakeChromeIdentity* fakeIdentity = [SigninEarlGreyUtils fakeIdentity1];
+  FakeChromeIdentity* fakeIdentity = [SigninEarlGrey fakeIdentity1];
 
   // Sign In |fakeIdentity|.
   [SigninEarlGreyUI signinWithFakeIdentity:fakeIdentity];
@@ -237,7 +237,7 @@
 
 // Tests that signing out from a managed user account clears the user's data.
 - (void)testsSignOutFromManagedAccount {
-  FakeChromeIdentity* fakeIdentity = [SigninEarlGreyUtils fakeManagedIdentity];
+  FakeChromeIdentity* fakeIdentity = [SigninEarlGrey fakeManagedIdentity];
 
   // Sign In |fakeIdentity|.
   [SigninEarlGreyUI signinWithFakeIdentity:fakeIdentity isManagedAccount:YES];
@@ -270,7 +270,7 @@
 #define MAYBE_testSignInDisconnectCancelled testSignInDisconnectCancelled
 #endif
 - (void)MAYBE_testSignInDisconnectCancelled {
-  FakeChromeIdentity* fakeIdentity = [SigninEarlGreyUtils fakeIdentity1];
+  FakeChromeIdentity* fakeIdentity = [SigninEarlGrey fakeIdentity1];
 
   // Sign In |fakeIdentity|, then open the Account Settings.
   [SigninEarlGreyUI signinWithFakeIdentity:fakeIdentity];
@@ -290,7 +290,7 @@
   [[EarlGrey selectElementWithMatcher:chrome_test_util::
                                           SettingsAccountsCollectionView()]
       assertWithMatcher:grey_sufficientlyVisible()];
-  [SigninEarlGreyUtils checkSignedInWithFakeIdentity:fakeIdentity];
+  [SigninEarlGrey checkSignedInWithFakeIdentity:fakeIdentity];
 
   [[EarlGrey selectElementWithMatcher:SettingsDoneButton()]
       performAction:grey_tap()];
diff --git a/ios/chrome/browser/ui/settings/google_services/google_services_settings_egtest.mm b/ios/chrome/browser/ui/settings/google_services/google_services_settings_egtest.mm
index eba276b..bdf384a80 100644
--- a/ios/chrome/browser/ui/settings/google_services/google_services_settings_egtest.mm
+++ b/ios/chrome/browser/ui/settings/google_services/google_services_settings_egtest.mm
@@ -5,8 +5,8 @@
 #include "components/password_manager/core/common/password_manager_pref_names.h"
 #include "components/safe_browsing/core/common/safe_browsing_prefs.h"
 #include "components/safe_browsing/core/features.h"
+#import "ios/chrome/browser/ui/authentication/signin_earl_grey.h"
 #import "ios/chrome/browser/ui/authentication/signin_earl_grey_ui.h"
-#import "ios/chrome/browser/ui/authentication/signin_earlgrey_utils.h"
 #import "ios/chrome/browser/ui/settings/google_services/google_services_settings_app_interface.h"
 #import "ios/chrome/browser/ui/settings/google_services/google_services_settings_constants.h"
 #import "ios/chrome/browser/ui/settings/google_services/manage_sync_settings_constants.h"
@@ -91,12 +91,12 @@
 // Regression test for crbug.com/1033901
 - (void)testRemovePrimaryAccount {
   // Signin.
-  FakeChromeIdentity* fakeIdentity = [SigninEarlGreyUtils fakeIdentity1];
+  FakeChromeIdentity* fakeIdentity = [SigninEarlGrey fakeIdentity1];
   [SigninEarlGreyUI signinWithFakeIdentity:fakeIdentity];
   // Open "Google Services" settings.
   [self openGoogleServicesSettings];
   // Remove the primary account.
-  [SigninEarlGreyUtils forgetFakeIdentity:fakeIdentity];
+  [SigninEarlGrey forgetFakeIdentity:fakeIdentity];
   // Assert the UI has been reloaded by testing for the signin cell being
   // visible.
   id<GREYMatcher> signinCellMatcher =
@@ -122,8 +122,8 @@
     [GoogleServicesSettingsAppInterface
         unblockAllNavigationRequestsForCurrentWebState];
   }];
-  FakeChromeIdentity* fakeIdentity = [SigninEarlGreyUtils fakeIdentity1];
-  [SigninEarlGreyUtils addFakeIdentity:fakeIdentity];
+  FakeChromeIdentity* fakeIdentity = [SigninEarlGrey fakeIdentity1];
+  [SigninEarlGrey addFakeIdentity:fakeIdentity];
   // Open "Google Services" settings.
   [self openGoogleServicesSettings];
   // Open sign-in.
@@ -163,7 +163,7 @@
 // See: crbug.com/1076843
 - (void)testOpenSSOAddAccount {
   // Signin.
-  FakeChromeIdentity* fakeIdentity = [SigninEarlGreyUtils fakeIdentity1];
+  FakeChromeIdentity* fakeIdentity = [SigninEarlGrey fakeIdentity1];
   [SigninEarlGreyUI signinWithFakeIdentity:fakeIdentity];
   // Open "Google Services" settings.
   [self openGoogleServicesSettings];
@@ -234,7 +234,7 @@
        forUserPref:password_manager::prefs::kPasswordLeakDetectionEnabled];
 
   // Sign in.
-  FakeChromeIdentity* fakeIdentity = [SigninEarlGreyUtils fakeIdentity1];
+  FakeChromeIdentity* fakeIdentity = [SigninEarlGrey fakeIdentity1];
   [SigninEarlGreyUI signinWithFakeIdentity:fakeIdentity];
   // Open "Google Services" settings.
   [self openGoogleServicesSettings];
@@ -294,7 +294,7 @@
        forUserPref:password_manager::prefs::kPasswordLeakDetectionEnabled];
 
   // Sign in.
-  FakeChromeIdentity* fakeIdentity = [SigninEarlGreyUtils fakeIdentity1];
+  FakeChromeIdentity* fakeIdentity = [SigninEarlGrey fakeIdentity1];
   [SigninEarlGreyUI signinWithFakeIdentity:fakeIdentity];
   // Open "Google Services" settings.
   [self openGoogleServicesSettings];
diff --git a/ios/chrome/browser/ui/settings/password/password_details/password_details_view_controller_unittest.mm b/ios/chrome/browser/ui/settings/password/password_details/password_details_view_controller_unittest.mm
index afde54e6..7d91e77 100644
--- a/ios/chrome/browser/ui/settings/password/password_details/password_details_view_controller_unittest.mm
+++ b/ios/chrome/browser/ui/settings/password/password_details/password_details_view_controller_unittest.mm
@@ -185,19 +185,15 @@
 
 // Tests that password is shown/hidden.
 TEST_F(PasswordDetailsViewControllerTest, TestShowHidePassword) {
-  // TODO(crbug.com/1111183): Investigate why this is failing on iOS14.
-  if (base::ios::IsRunningOnIOS14OrLater()) {
-    return;
-  }
   SetPassword();
-
   CheckEditCellText(kMaskedPassword, 0, 2);
 
   NSIndexPath* indexOfPassword = [NSIndexPath indexPathForRow:2 inSection:0];
   TableViewTextEditCell* textFieldCell =
-      base::mac::ObjCCastStrict<TableViewTextEditCell>(
-          [controller().tableView cellForRowAtIndexPath:indexOfPassword]);
-  ASSERT_TRUE(textFieldCell != nil);
+      base::mac::ObjCCastStrict<TableViewTextEditCell>([controller()
+                      tableView:controller().tableView
+          cellForRowAtIndexPath:indexOfPassword]);
+  EXPECT_TRUE(textFieldCell);
   [textFieldCell.identifyingIconButton
       sendActionsForControlEvents:UIControlEventTouchUpInside];
 
@@ -213,11 +209,6 @@
 
 // Tests that passwords was not shown in case reauth failed.
 TEST_F(PasswordDetailsViewControllerTest, TestShowPasswordReauthFailed) {
-  // TODO(crbug.com/1111183): Investigate why this is failing on iOS14.
-  if (base::ios::IsRunningOnIOS14OrLater()) {
-    return;
-  }
-
   SetPassword();
 
   CheckEditCellText(kMaskedPassword, 0, 2);
@@ -225,9 +216,10 @@
   reauth().expectedResult = ReauthenticationResult::kFailure;
   NSIndexPath* indexOfPassword = [NSIndexPath indexPathForRow:2 inSection:0];
   TableViewTextEditCell* textFieldCell =
-      base::mac::ObjCCastStrict<TableViewTextEditCell>(
-          [controller().tableView cellForRowAtIndexPath:indexOfPassword]);
-  ASSERT_TRUE(textFieldCell != nil);
+      base::mac::ObjCCastStrict<TableViewTextEditCell>([controller()
+                      tableView:controller().tableView
+          cellForRowAtIndexPath:indexOfPassword]);
+  EXPECT_TRUE(textFieldCell);
   [textFieldCell.identifyingIconButton
       sendActionsForControlEvents:UIControlEventTouchUpInside];
 
diff --git a/ios/chrome/browser/ui/settings/password/passwords_mediator.mm b/ios/chrome/browser/ui/settings/password/passwords_mediator.mm
index d9e9a04..522aec4 100644
--- a/ios/chrome/browser/ui/settings/password/passwords_mediator.mm
+++ b/ios/chrome/browser/ui/settings/password/passwords_mediator.mm
@@ -160,6 +160,11 @@
 - (void)compromisedCredentialsDidChange:
     (password_manager::CompromisedCredentialsManager::CredentialsView)
         credentials {
+  // Compromised passwords changes has no effect on UI while check is running.
+  if (_passwordCheckManager->GetPasswordCheckState() ==
+      PasswordCheckState::kRunning)
+    return;
+
   DCHECK(self.consumer);
   [self.consumer setPasswordCheckUIState:
                      [self computePasswordCheckUIStateWith:_currentState]];
diff --git a/ios/chrome/browser/ui/settings/password/passwords_table_view_controller.mm b/ios/chrome/browser/ui/settings/password/passwords_table_view_controller.mm
index 920b9a7..3f9b81d 100644
--- a/ios/chrome/browser/ui/settings/password/passwords_table_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/password/passwords_table_view_controller.mm
@@ -259,6 +259,9 @@
 // The loading spinner background which appears when loading passwords.
 @property(nonatomic, strong) HomeWaitingView* spinnerView;
 
+// Current state of the Password Check.
+@property(nonatomic, assign) PasswordCheckUIState passwordCheckState;
+
 @end
 
 @implementation PasswordsTableViewController
@@ -439,16 +442,16 @@
           password_manager::features::kPasswordCheck)) {
     // Password check.
     [model addSectionWithIdentifier:SectionIdentifierPasswordCheck];
-    if (!_passwordProblemsItem) {
-      _passwordProblemsItem = [self passwordProblemsItem];
-    }
+    _passwordProblemsItem = [self passwordProblemsItem];
+
     [model addItem:_passwordProblemsItem
         toSectionWithIdentifier:SectionIdentifierPasswordCheck];
-    if (!_checkForProblemsItem) {
-      _checkForProblemsItem = [self checkForProblemsItem];
-    }
+    _checkForProblemsItem = [self checkForProblemsItem];
+
     [model addItem:_checkForProblemsItem
         toSectionWithIdentifier:SectionIdentifierPasswordCheck];
+    [self updatePasswordCheckButtonWithState:_passwordCheckState];
+    [self updatePasswordCheckStatusLabelWithState:_passwordCheckState];
   }
 
   // Saved passwords.
@@ -700,6 +703,7 @@
 #pragma mark - PasswordsConsumer
 
 - (void)setPasswordCheckUIState:(PasswordCheckUIState)state {
+  _passwordCheckState = state;
   [self updatePasswordCheckButtonWithState:state];
   [self updatePasswordCheckStatusLabelWithState:state];
 }
diff --git a/ios/chrome/browser/ui/settings/signin_settings_egtest.mm b/ios/chrome/browser/ui/settings/signin_settings_egtest.mm
index 624ab85..95a46c7 100644
--- a/ios/chrome/browser/ui/settings/signin_settings_egtest.mm
+++ b/ios/chrome/browser/ui/settings/signin_settings_egtest.mm
@@ -4,8 +4,8 @@
 
 #import "ios/chrome/browser/ui/authentication/cells/signin_promo_view_constants.h"
 #import "ios/chrome/browser/ui/authentication/signin/signin_constants.h"
+#import "ios/chrome/browser/ui/authentication/signin_earl_grey.h"
 #import "ios/chrome/browser/ui/authentication/signin_earl_grey_ui.h"
-#import "ios/chrome/browser/ui/authentication/signin_earlgrey_utils.h"
 #import "ios/chrome/browser/ui/settings/settings_table_view_controller_constants.h"
 #import "ios/chrome/browser/ui/settings/signin_settings_app_interface.h"
 #include "ios/chrome/grit/ios_strings.h"
@@ -55,8 +55,8 @@
 
 // Tests signing in, using the primary button with a warm state.
 - (void)testSignInPromoWithWarmStateUsingPrimaryButton {
-  FakeChromeIdentity* fakeIdentity = [SigninEarlGreyUtils fakeIdentity1];
-  [SigninEarlGreyUtils addFakeIdentity:fakeIdentity];
+  FakeChromeIdentity* fakeIdentity = [SigninEarlGrey fakeIdentity1];
+  [SigninEarlGrey addFakeIdentity:fakeIdentity];
 
   [ChromeEarlGreyUI openSettingsMenu];
   [SigninEarlGreyUI
@@ -65,7 +65,7 @@
   [SigninEarlGreyUI confirmSigninConfirmationDialog];
 
   // User signed in.
-  [SigninEarlGreyUtils checkSignedInWithFakeIdentity:fakeIdentity];
+  [SigninEarlGrey checkSignedInWithFakeIdentity:fakeIdentity];
   [SigninEarlGreyUI checkSigninPromoNotVisible];
   [[EarlGrey selectElementWithMatcher:SettingsAccountButton()]
       assertWithMatcher:grey_interactable()];
@@ -73,8 +73,8 @@
 
 // Tests signing in, using the secondary button with a warm state.
 - (void)testSignInPromoWithWarmStateUsingSecondaryButton {
-  FakeChromeIdentity* fakeIdentity = [SigninEarlGreyUtils fakeIdentity1];
-  [SigninEarlGreyUtils addFakeIdentity:fakeIdentity];
+  FakeChromeIdentity* fakeIdentity = [SigninEarlGrey fakeIdentity1];
+  [SigninEarlGrey addFakeIdentity:fakeIdentity];
 
   [ChromeEarlGreyUI openSettingsMenu];
   [SigninEarlGreyUI
@@ -84,7 +84,7 @@
   [SigninEarlGreyUI confirmSigninConfirmationDialog];
 
   // User signed in.
-  [SigninEarlGreyUtils checkSignedInWithFakeIdentity:fakeIdentity];
+  [SigninEarlGrey checkSignedInWithFakeIdentity:fakeIdentity];
   [SigninEarlGreyUI checkSigninPromoNotVisible];
   [[EarlGrey selectElementWithMatcher:SettingsAccountButton()]
       assertWithMatcher:grey_interactable()];
diff --git a/ios/chrome/browser/ui/settings/sync/sync_encryption_passphrase_table_view_controller_egtest.mm b/ios/chrome/browser/ui/settings/sync/sync_encryption_passphrase_table_view_controller_egtest.mm
index 0f21000..a320b72 100644
--- a/ios/chrome/browser/ui/settings/sync/sync_encryption_passphrase_table_view_controller_egtest.mm
+++ b/ios/chrome/browser/ui/settings/sync/sync_encryption_passphrase_table_view_controller_egtest.mm
@@ -3,8 +3,8 @@
 // found in the LICENSE file.
 
 #import <UIKit/UIKit.h>
+#import "ios/chrome/browser/ui/authentication/signin_earl_grey.h"
 #import "ios/chrome/browser/ui/authentication/signin_earl_grey_ui.h"
-#import "ios/chrome/browser/ui/authentication/signin_earlgrey_utils.h"
 #import "ios/chrome/grit/ios_strings.h"
 #import "ios/chrome/test/earl_grey/chrome_earl_grey.h"
 #import "ios/chrome/test/earl_grey/chrome_earl_grey_ui.h"
@@ -28,7 +28,7 @@
 - (void)testShowSyncPassphraseAndDismiss {
   [ChromeEarlGrey addBookmarkWithSyncPassphrase:@"hello"];
   // Signin.
-  FakeChromeIdentity* fakeIdentity = [SigninEarlGreyUtils fakeIdentity1];
+  FakeChromeIdentity* fakeIdentity = [SigninEarlGrey fakeIdentity1];
   [SigninEarlGreyUI signinWithFakeIdentity:fakeIdentity];
   [ChromeEarlGrey openNewTab];
   [[EarlGrey selectElementWithMatcher:ButtonWithAccessibilityLabelId(
diff --git a/ios/chrome/browser/ui/settings/sync/utils/sync_fake_server_egtest.mm b/ios/chrome/browser/ui/settings/sync/utils/sync_fake_server_egtest.mm
index 34184c9..7763918 100644
--- a/ios/chrome/browser/ui/settings/sync/utils/sync_fake_server_egtest.mm
+++ b/ios/chrome/browser/ui/settings/sync/utils/sync_fake_server_egtest.mm
@@ -4,8 +4,8 @@
 
 #include "base/strings/sys_string_conversions.h"
 #import "base/test/ios/wait_util.h"
+#import "ios/chrome/browser/ui/authentication/signin_earl_grey_app_interface.h"
 #import "ios/chrome/browser/ui/authentication/signin_earl_grey_ui.h"
-#import "ios/chrome/browser/ui/authentication/signin_earlgrey_utils_app_interface.h"
 #import "ios/chrome/browser/ui/bookmarks/bookmark_earl_grey.h"
 #import "ios/chrome/test/earl_grey/chrome_earl_grey.h"
 #import "ios/chrome/test/earl_grey/chrome_matchers.h"
@@ -69,9 +69,8 @@
   [BookmarkEarlGrey addBookmarkWithTitle:@"foo" URL:@"https://www.foo.com"];
 
   // Sign in to sync, after a bookmark has been added.
-  FakeChromeIdentity* fakeIdentity =
-      [SigninEarlGreyUtilsAppInterface fakeIdentity1];
-  [SigninEarlGreyUtilsAppInterface addFakeIdentity:fakeIdentity];
+  FakeChromeIdentity* fakeIdentity = [SigninEarlGreyAppInterface fakeIdentity1];
+  [SigninEarlGreyAppInterface addFakeIdentity:fakeIdentity];
   [SigninEarlGreyUI signinWithFakeIdentity:fakeIdentity];
 
   // Assert that the correct number of bookmarks have been synced.
@@ -81,9 +80,8 @@
 
 // Tests that a bookmark added on the client is uploaded to the Sync server.
 - (void)testSyncUploadBookmark {
-  FakeChromeIdentity* fakeIdentity =
-      [SigninEarlGreyUtilsAppInterface fakeIdentity1];
-  [SigninEarlGreyUtilsAppInterface addFakeIdentity:fakeIdentity];
+  FakeChromeIdentity* fakeIdentity = [SigninEarlGreyAppInterface fakeIdentity1];
+  [SigninEarlGreyAppInterface addFakeIdentity:fakeIdentity];
   [SigninEarlGreyUI signinWithFakeIdentity:fakeIdentity];
 
   // Add a bookmark after sync is initialized.
@@ -100,9 +98,8 @@
   [ChromeEarlGrey addFakeSyncServerBookmarkWithURL:URL title:"hoo"];
 
   // Sign in to sync, after a bookmark has been injected in the sync server.
-  FakeChromeIdentity* fakeIdentity =
-      [SigninEarlGreyUtilsAppInterface fakeIdentity1];
-  [SigninEarlGreyUtilsAppInterface addFakeIdentity:fakeIdentity];
+  FakeChromeIdentity* fakeIdentity = [SigninEarlGreyAppInterface fakeIdentity1];
+  [SigninEarlGreyAppInterface addFakeIdentity:fakeIdentity];
   [SigninEarlGreyUI signinWithFakeIdentity:fakeIdentity];
   [ChromeEarlGrey waitForSyncInitialized:YES syncTimeout:kSyncOperationTimeout];
   [BookmarkEarlGrey verifyBookmarksWithTitle:@"hoo" expectedCount:1];
@@ -111,9 +108,8 @@
 // Tests that the local cache guid does not change when sync is restarted.
 - (void)testSyncCheckSameCacheGuid_SyncRestarted {
   // Sign in the fake identity.
-  FakeChromeIdentity* fakeIdentity =
-      [SigninEarlGreyUtilsAppInterface fakeIdentity1];
-  [SigninEarlGreyUtilsAppInterface addFakeIdentity:fakeIdentity];
+  FakeChromeIdentity* fakeIdentity = [SigninEarlGreyAppInterface fakeIdentity1];
+  [SigninEarlGreyAppInterface addFakeIdentity:fakeIdentity];
   [SigninEarlGreyUI signinWithFakeIdentity:fakeIdentity];
   [ChromeEarlGrey waitForSyncInitialized:YES syncTimeout:kSyncOperationTimeout];
 
@@ -133,16 +129,15 @@
 // signs back in with the same account.
 - (void)testSyncCheckDifferentCacheGuid_SignOutAndSignIn {
   // Sign in a fake identity, and store the initial sync guid.
-  FakeChromeIdentity* fakeIdentity =
-      [SigninEarlGreyUtilsAppInterface fakeIdentity1];
-  [SigninEarlGreyUtilsAppInterface addFakeIdentity:fakeIdentity];
+  FakeChromeIdentity* fakeIdentity = [SigninEarlGreyAppInterface fakeIdentity1];
+  [SigninEarlGreyAppInterface addFakeIdentity:fakeIdentity];
   [SigninEarlGreyUI signinWithFakeIdentity:fakeIdentity];
   [ChromeEarlGrey waitForSyncInitialized:YES syncTimeout:kSyncOperationTimeout];
   std::string original_guid = [ChromeEarlGrey syncCacheGUID];
 
-  GREYAssert([SigninEarlGreyUtilsAppInterface isAuthenticated],
+  GREYAssert([SigninEarlGreyAppInterface isAuthenticated],
              @"User is not signed in.");
-  [SigninEarlGreyUtilsAppInterface signOut];
+  [SigninEarlGreyAppInterface signOut];
   [ChromeEarlGrey waitForSyncInitialized:NO syncTimeout:kSyncOperationTimeout];
 
   // Sign the user back in, and verify the guid has changed.
@@ -158,15 +153,14 @@
 // Test for http://crbug.com/413611 .
 - (void)testSyncCheckSameCacheGuid_SyncRestartedAfterSignOutAndSignIn {
   // Sign in a fake idenitty.
-  FakeChromeIdentity* fakeIdentity =
-      [SigninEarlGreyUtilsAppInterface fakeIdentity1];
-  [SigninEarlGreyUtilsAppInterface addFakeIdentity:fakeIdentity];
+  FakeChromeIdentity* fakeIdentity = [SigninEarlGreyAppInterface fakeIdentity1];
+  [SigninEarlGreyAppInterface addFakeIdentity:fakeIdentity];
   [SigninEarlGreyUI signinWithFakeIdentity:fakeIdentity];
   [ChromeEarlGrey waitForSyncInitialized:YES syncTimeout:kSyncOperationTimeout];
 
-  GREYAssert([SigninEarlGreyUtilsAppInterface isAuthenticated],
+  GREYAssert([SigninEarlGreyAppInterface isAuthenticated],
              @"User is not signed in.");
-  [SigninEarlGreyUtilsAppInterface signOut];
+  [SigninEarlGreyAppInterface signOut];
   [ChromeEarlGrey waitForSyncInitialized:NO syncTimeout:kSyncOperationTimeout];
 
   // Sign the user back in.
@@ -199,9 +193,8 @@
   }];
 
   // Sign in to sync.
-  FakeChromeIdentity* fakeIdentity =
-      [SigninEarlGreyUtilsAppInterface fakeIdentity1];
-  [SigninEarlGreyUtilsAppInterface addFakeIdentity:fakeIdentity];
+  FakeChromeIdentity* fakeIdentity = [SigninEarlGreyAppInterface fakeIdentity1];
+  [SigninEarlGreyAppInterface addFakeIdentity:fakeIdentity];
   [SigninEarlGreyUI signinWithFakeIdentity:fakeIdentity];
 
   // Verify that the autofill profile has been downloaded.
@@ -228,9 +221,8 @@
   }];
 
   // Sign in to sync.
-  FakeChromeIdentity* fakeIdentity =
-      [SigninEarlGreyUtilsAppInterface fakeIdentity1];
-  [SigninEarlGreyUtilsAppInterface addFakeIdentity:fakeIdentity];
+  FakeChromeIdentity* fakeIdentity = [SigninEarlGreyAppInterface fakeIdentity1];
+  [SigninEarlGreyAppInterface addFakeIdentity:fakeIdentity];
   [SigninEarlGreyUI signinWithFakeIdentity:fakeIdentity];
 
   // Verify that the autofill profile has been downloaded.
@@ -274,9 +266,8 @@
   }];
 
   // Sign in to sync.
-  FakeChromeIdentity* fakeIdentity =
-      [SigninEarlGreyUtilsAppInterface fakeIdentity1];
-  [SigninEarlGreyUtilsAppInterface addFakeIdentity:fakeIdentity];
+  FakeChromeIdentity* fakeIdentity = [SigninEarlGreyAppInterface fakeIdentity1];
+  [SigninEarlGreyAppInterface addFakeIdentity:fakeIdentity];
   [SigninEarlGreyUI signinWithFakeIdentity:fakeIdentity];
 
   // Verify that the autofill profile has been downloaded
@@ -315,9 +306,8 @@
   [ChromeEarlGrey loadURL:URL2];
 
   // Sign in to sync, after opening two tabs.
-  FakeChromeIdentity* fakeIdentity =
-      [SigninEarlGreyUtilsAppInterface fakeIdentity1];
-  [SigninEarlGreyUtilsAppInterface addFakeIdentity:fakeIdentity];
+  FakeChromeIdentity* fakeIdentity = [SigninEarlGreyAppInterface fakeIdentity1];
+  [SigninEarlGreyAppInterface addFakeIdentity:fakeIdentity];
   [SigninEarlGreyUI signinWithFakeIdentity:fakeIdentity];
 
   // Verify the sessions on the sync server.
@@ -343,9 +333,8 @@
   [ChromeEarlGrey addHistoryServiceTypedURL:mockURL];
 
   // Sign in to sync.
-  FakeChromeIdentity* fakeIdentity =
-      [SigninEarlGreyUtilsAppInterface fakeIdentity1];
-  [SigninEarlGreyUtilsAppInterface addFakeIdentity:fakeIdentity];
+  FakeChromeIdentity* fakeIdentity = [SigninEarlGreyAppInterface fakeIdentity1];
+  [SigninEarlGreyAppInterface addFakeIdentity:fakeIdentity];
   [SigninEarlGreyUI signinWithFakeIdentity:fakeIdentity];
 
   [ChromeEarlGrey waitForSyncInitialized:YES syncTimeout:kSyncOperationTimeout];
@@ -371,9 +360,8 @@
   [ChromeEarlGrey addFakeSyncServerTypedURL:mockURL];
 
   // Sign in to sync.
-  FakeChromeIdentity* fakeIdentity =
-      [SigninEarlGreyUtilsAppInterface fakeIdentity1];
-  [SigninEarlGreyUtilsAppInterface addFakeIdentity:fakeIdentity];
+  FakeChromeIdentity* fakeIdentity = [SigninEarlGreyAppInterface fakeIdentity1];
+  [SigninEarlGreyAppInterface addFakeIdentity:fakeIdentity];
   [SigninEarlGreyUI signinWithFakeIdentity:fakeIdentity];
 
   [ChromeEarlGrey waitForSyncInitialized:YES syncTimeout:kSyncOperationTimeout];
@@ -398,9 +386,8 @@
   [ChromeEarlGrey addFakeSyncServerTypedURL:mockURL];
 
   // Sign in to sync.
-  FakeChromeIdentity* fakeIdentity =
-      [SigninEarlGreyUtilsAppInterface fakeIdentity1];
-  [SigninEarlGreyUtilsAppInterface addFakeIdentity:fakeIdentity];
+  FakeChromeIdentity* fakeIdentity = [SigninEarlGreyAppInterface fakeIdentity1];
+  [SigninEarlGreyAppInterface addFakeIdentity:fakeIdentity];
   [SigninEarlGreyUI signinWithFakeIdentity:fakeIdentity];
 
   [ChromeEarlGrey waitForSyncInitialized:YES syncTimeout:kSyncOperationTimeout];
@@ -433,9 +420,8 @@
   [ChromeEarlGrey addHistoryServiceTypedURL:mockURL];
 
   // Sign in to sync.
-  FakeChromeIdentity* fakeIdentity =
-      [SigninEarlGreyUtilsAppInterface fakeIdentity1];
-  [SigninEarlGreyUtilsAppInterface addFakeIdentity:fakeIdentity];
+  FakeChromeIdentity* fakeIdentity = [SigninEarlGreyAppInterface fakeIdentity1];
+  [SigninEarlGreyAppInterface addFakeIdentity:fakeIdentity];
   [SigninEarlGreyUI signinWithFakeIdentity:fakeIdentity];
 
   [ChromeEarlGrey waitForSyncInitialized:YES syncTimeout:kSyncOperationTimeout];
@@ -476,9 +462,8 @@
                    originator_client_item_id:"1"];
 
   // Sign in to sync.
-  FakeChromeIdentity* fakeIdentity =
-      [SigninEarlGreyUtilsAppInterface fakeIdentity1];
-  [SigninEarlGreyUtilsAppInterface addFakeIdentity:fakeIdentity];
+  FakeChromeIdentity* fakeIdentity = [SigninEarlGreyAppInterface fakeIdentity1];
+  [SigninEarlGreyAppInterface addFakeIdentity:fakeIdentity];
   [SigninEarlGreyUI signinWithFakeIdentity:fakeIdentity];
 
   [ChromeEarlGrey waitForSyncInitialized:YES syncTimeout:kSyncOperationTimeout];
diff --git a/ios/chrome/browser/ui/ui_feature_flags.h b/ios/chrome/browser/ui/ui_feature_flags.h
index 43015dc..0e3a27897 100644
--- a/ios/chrome/browser/ui/ui_feature_flags.h
+++ b/ios/chrome/browser/ui/ui_feature_flags.h
@@ -65,8 +65,8 @@
 // Feature flag to enable new illustrations and UI on empty states.
 extern const base::Feature kIllustratedEmptyStates;
 
-// Feature flag to enable Shared Highlighting (Text Fragments/scroll-to-text
-// and Link to Text features).
+// Feature flag to enable Shared Highlighting (Link to Text). Also enable
+// kScrollToTextIOS to successfully open these links.
 extern const base::Feature kSharedHighlightingIOS;
 
 // Feature flag that enables taking fullpage screenshots of a webpage.
diff --git a/ios/chrome/browser/url_loading/scene_url_loading_service.h b/ios/chrome/browser/url_loading/scene_url_loading_service.h
index 40e80da..c244b56 100644
--- a/ios/chrome/browser/url_loading/scene_url_loading_service.h
+++ b/ios/chrome/browser/url_loading/scene_url_loading_service.h
@@ -37,6 +37,14 @@
             withUrlLoadParams:(const UrlLoadParams&)urlLoadParams
                    completion:(ProceduralBlock)completion;
 
+// Open the list of URLs contained in |URLs| in mode specified by
+// |tabOpeningTargetMode|. |completion| is executed after all the tabs are
+// opened.
+- (void)openMultipleTabsInMode:
+            (ApplicationModeForTabOpening)tabOpeningTargetMode
+                          URLs:(const std::vector<GURL>&)URLs
+                    completion:(ProceduralBlock)completion;
+
 // Opens a new tab as if originating from |originPoint| and |focusOmnibox|.
 - (void)openNewTabFromOriginPoint:(CGPoint)originPoint
                      focusOmnibox:(BOOL)focusOmnibox;
diff --git a/ios/chrome/browser/web/error_page_util.mm b/ios/chrome/browser/web/error_page_util.mm
index ab4bcac..1c2e46d63a 100644
--- a/ios/chrome/browser/web/error_page_util.mm
+++ b/ios/chrome/browser/web/error_page_util.mm
@@ -12,7 +12,6 @@
 #include "base/strings/sys_string_conversions.h"
 #include "base/values.h"
 #include "components/error_page/common/error.h"
-#include "components/error_page/common/error_page_params.h"
 #include "components/error_page/common/localized_error.h"
 #include "components/grit/components_resources.h"
 #include "ios/chrome/browser/application_context.h"
@@ -57,8 +56,7 @@
           /*offline_content_feature_enabled=*/false,
           /*auto_fetch_feature_enabled=*/false,
           /*is_kiosk_mode=*/false,
-          GetApplicationContext()->GetApplicationLocale(),
-          /*params=*/nullptr);
+          GetApplicationContext()->GetApplicationLocale());
 
   ui::ScaleFactor scale_factor =
       ui::ResourceBundle::GetSharedInstance().GetMaxScaleFactor();
diff --git a/ios/chrome/search_widget_extension/copied_content_view.h b/ios/chrome/search_widget_extension/copied_content_view.h
index 53cb519..51e0ed0e 100644
--- a/ios/chrome/search_widget_extension/copied_content_view.h
+++ b/ios/chrome/search_widget_extension/copied_content_view.h
@@ -30,8 +30,7 @@
 - (instancetype)initWithFrame:(CGRect)frame NS_UNAVAILABLE;
 - (instancetype)initWithCoder:(NSCoder*)aDecoder NS_UNAVAILABLE;
 
-- (void)setCopiedContentType:(CopiedContentType)type
-                  copiedText:(NSString*)copiedText;
+- (void)setCopiedContentType:(CopiedContentType)type;
 
 @end
 
diff --git a/ios/chrome/search_widget_extension/copied_content_view.mm b/ios/chrome/search_widget_extension/copied_content_view.mm
index 426a05b6..b2188bd4 100644
--- a/ios/chrome/search_widget_extension/copied_content_view.mm
+++ b/ios/chrome/search_widget_extension/copied_content_view.mm
@@ -24,8 +24,6 @@
 
 // The type of the copied content
 @property(nonatomic) CopiedContentType type;
-// The copied text to be displayed if the type supports showing the string.
-@property(nonatomic, copy) NSString* copiedText;
 // The copied URL label containing the URL or a placeholder text.
 @property(nonatomic, strong) UILabel* copiedContentLabel;
 // The copied URL title label containing the title of the copied URL button.
@@ -38,7 +36,7 @@
 @property(nonatomic, strong) UIVisualEffectView* primaryEffectView;
 @property(nonatomic, strong) UIVisualEffectView* secondaryEffectView;
 
-// Updates the view to show the currently set |type| and |copiedText|.
+// Updates the view to show the currently set |type|.
 - (void)updateUI;
 
 @end
@@ -173,7 +171,7 @@
       [_copiedContentLabel.trailingAnchor
           constraintEqualToAnchor:_openCopiedContentTitleLabel.trailingAnchor],
     ]];
-    [self setCopiedContentType:CopiedContentTypeNone copiedText:nil];
+    [self setCopiedContentType:CopiedContentTypeNone];
     self.highlightableViews = @[
       _hairlineView, _copiedButtonView, _openCopiedContentTitleLabel,
       _copiedContentLabel
@@ -182,10 +180,8 @@
   return self;
 }
 
-- (void)setCopiedContentType:(CopiedContentType)type
-                  copiedText:(NSString*)copiedText {
+- (void)setCopiedContentType:(CopiedContentType)type {
   self.type = type;
-  self.copiedText = copiedText;
   [self updateUI];
 }
 
@@ -224,12 +220,10 @@
     }
     case CopiedContentTypeURL: {
       titleText = NSLocalizedString(@"IDS_IOS_OPEN_COPIED_LINK", nil);
-      contentText = self.copiedText;
       break;
     }
     case CopiedContentTypeString: {
       titleText = NSLocalizedString(@"IDS_IOS_OPEN_COPIED_TEXT", nil);
-      contentText = self.copiedText;
       break;
     }
     case CopiedContentTypeImage: {
diff --git a/ios/chrome/search_widget_extension/search_widget_view.h b/ios/chrome/search_widget_extension/search_widget_view.h
index e1b8faf..80f99e1 100644
--- a/ios/chrome/search_widget_extension/search_widget_view.h
+++ b/ios/chrome/search_widget_extension/search_widget_view.h
@@ -44,10 +44,8 @@
 // Gets the height of the widget.
 - (CGFloat)widgetHeight;
 
-// Sets the copied content type. |copiedText| should be provided if the content
-// type requires textual data, otherwise it should be nil.
-- (void)setCopiedContentType:(CopiedContentType)type
-                  copiedText:(NSString*)copiedText;
+// Sets the copied content type.
+- (void)setCopiedContentType:(CopiedContentType)type;
 
 @end
 
diff --git a/ios/chrome/search_widget_extension/search_widget_view.mm b/ios/chrome/search_widget_extension/search_widget_view.mm
index ae61caf..49d9991 100644
--- a/ios/chrome/search_widget_extension/search_widget_view.mm
+++ b/ios/chrome/search_widget_extension/search_widget_view.mm
@@ -210,9 +210,8 @@
   return [self actionContentHeight] + [self copiedURLSectionHeight];
 }
 
-- (void)setCopiedContentType:(CopiedContentType)type
-                  copiedText:(NSString*)copiedText {
-  [self.copiedURLSection setCopiedContentType:type copiedText:copiedText];
+- (void)setCopiedContentType:(CopiedContentType)type {
+  [self.copiedURLSection setCopiedContentType:type];
 }
 
 @end
diff --git a/ios/chrome/search_widget_extension/search_widget_view_controller.mm b/ios/chrome/search_widget_extension/search_widget_view_controller.mm
index 3f33f01..cbf9c36 100644
--- a/ios/chrome/search_widget_extension/search_widget_view_controller.mm
+++ b/ios/chrome/search_widget_extension/search_widget_view_controller.mm
@@ -23,8 +23,6 @@
 
 @interface SearchWidgetViewController ()<SearchWidgetViewActionTarget>
 @property(nonatomic, weak) SearchWidgetView* widgetView;
-@property(nonatomic, strong, nullable) NSString* copiedText;
-@property(nonatomic, strong, nullable) UIImage* copiedImage;
 @property(nonatomic) CopiedContentType copiedContentType;
 @property(nonatomic, strong)
     ClipboardRecentContentImplIOS* clipboardRecentContent;
@@ -96,13 +94,26 @@
 
 - (void)widgetPerformUpdateWithCompletionHandler:
     (void (^)(NCUpdateResult))completionHandler {
-  completionHandler([self updateWidget] ? NCUpdateResultNewData
-                                        : NCUpdateResultNoData);
+  [self updateWidgetWithCompletionHandler:^(BOOL updates) {
+    completionHandler(updates ? NCUpdateResultNewData : NCUpdateResultNoData);
+  }];
 }
 
-// Updates the widget with latest data from the clipboard. Returns whether any
-// visual updates occurred.
-- (BOOL)updateWidget {
+- (void)updateWidget {
+  [self updateWidgetWithCompletionHandler:^(BOOL updates) {
+    if (updates && self.extensionContext.widgetActiveDisplayMode ==
+                       NCWidgetDisplayModeExpanded) {
+      CGSize maxSize = [self.extensionContext
+          widgetMaximumSizeForDisplayMode:NCWidgetDisplayModeExpanded];
+      self.preferredContentSize =
+          CGSizeMake(maxSize.width, [self.widgetView widgetHeight]);
+    }
+  }];
+}
+
+// Updates the widget with latest data from the clipboard. Calls completion
+// handler with whether any updates occured..
+- (void)updateWidgetWithCompletionHandler:(void (^)(BOOL))completionHandler {
   NSUserDefaults* sharedDefaults = app_group::GetGroupUserDefaults();
   NSString* fieldTrialKey =
       base::SysUTF8ToNSString(app_group::kChromeExtensionFieldTrialPreference);
@@ -113,35 +124,25 @@
   self.supportsSearchByImage =
       [sharedDefaults boolForKey:supportsSearchByImageKey];
 
-  NSString* copiedText;
-  UIImage* copiedImage;
-  CopiedContentType type = CopiedContentTypeNone;
+  NSSet* wantedTypes = [NSSet
+      setWithArray:@[ ContentTypeURL, ContentTypeText, ContentTypeImage ]];
 
-  if (UIImage* image = [self getCopiedImageFromClipboard]) {
-    copiedImage = image;
-    type = CopiedContentTypeImage;
-  } else if (NSURL* url =
-                 [self.clipboardRecentContent recentURLFromClipboard]) {
-    copiedText = url.absoluteString;
-    type = CopiedContentTypeURL;
-  } else if (NSString* text =
-                 [self.clipboardRecentContent recentTextFromClipboard]) {
-    copiedText = text;
-    type = CopiedContentTypeString;
-  }
-
-  return [self setCopiedContentType:type
-                         copiedText:copiedText
-                        copiedImage:copiedImage];
-}
-
-// Helper method to encapsulate checking whether the current search engine
-// supports search-by-image and getting the copied image.
-- (UIImage*)getCopiedImageFromClipboard {
-  if (!self.supportsSearchByImage) {
-    return nil;
-  }
-  return [self.clipboardRecentContent recentImageFromClipboard];
+  [self.clipboardRecentContent
+      hasContentMatchingTypes:wantedTypes
+            completionHandler:^(NSSet<ContentType>* matchedTypes) {
+              CopiedContentType newType = CopiedContentTypeNone;
+              if (self.supportsSearchByImage &&
+                  [matchedTypes containsObject:ContentTypeImage]) {
+                newType = CopiedContentTypeImage;
+              } else if ([matchedTypes containsObject:ContentTypeURL]) {
+                newType = CopiedContentTypeURL;
+              } else if ([matchedTypes containsObject:ContentTypeText]) {
+                newType = CopiedContentTypeString;
+              }
+              dispatch_async(dispatch_get_main_queue(), ^{
+                completionHandler([self updateCopiedContentType:newType]);
+              });
+            }];
 }
 
 - (void)viewWillTransitionToSize:(CGSize)size
@@ -201,25 +202,47 @@
 }
 
 - (void)openCopiedContent:(id)sender {
-  DCHECK([self verifyCopiedContentType]);
   switch (self.copiedContentType) {
-    case CopiedContentTypeURL:
-      [self.command prepareToOpenURL:[NSURL URLWithString:self.copiedText]];
+    case CopiedContentTypeURL: {
+      [self.clipboardRecentContent
+          recentURLFromClipboardAsync:^(NSURL* copiedURL) {
+            if (!copiedURL) {
+              return;
+            }
+            [self.command prepareToOpenURL:copiedURL];
+            [self.command executeInApp];
+          }];
       break;
-    case CopiedContentTypeString:
-      [self.command prepareToSearchText:self.copiedText];
+    }
+    case CopiedContentTypeString: {
+      [self.clipboardRecentContent
+          recentTextFromClipboardAsync:^(NSString* copiedText) {
+            if (!copiedText) {
+              return;
+            }
+            [self.command prepareToSearchText:copiedText];
+            [self.command executeInApp];
+          }];
       break;
+    }
     case CopiedContentTypeImage: {
-      // Resize image before converting to NSData so we can store less data.
-      UIImage* resizedImage = ResizeImageForSearchByImage(self.copiedImage);
-      [self.command prepareToSearchImage:resizedImage];
+      [self.clipboardRecentContent
+          recentImageFromClipboardAsync:^(UIImage* copiedImage) {
+            if (!copiedImage) {
+              return;
+            }
+            // Resize image before converting to NSData so we can store less
+            // data.
+            UIImage* resizedImage = ResizeImageForSearchByImage(copiedImage);
+            [self.command prepareToSearchImage:resizedImage];
+            [self.command executeInApp];
+          }];
       break;
     }
     case CopiedContentTypeNone:
       NOTREACHED();
       return;
   }
-  [self.command executeInApp];
 }
 
 #pragma mark - internal
@@ -241,38 +264,15 @@
                       forKey:app_group::kSearchExtensionDisplayCount];
 }
 
-// Sets the copied content type. |copiedText| should be provided if the content
-// type requires textual data, otherwise it should be nil. Likewise,
-// |copiedImage| should be provided if the content type requires image data.
-// Also saves the data and returns YES if the screen needs updating and NO
-// otherwise.
-- (BOOL)setCopiedContentType:(CopiedContentType)type
-                  copiedText:(NSString*)copiedText
-                 copiedImage:(UIImage*)copiedImage {
-  if (self.copiedContentType == type &&
-      [self.copiedText isEqualToString:copiedText] &&
-      [self.copiedImage isEqual:copiedImage]) {
+// Sets the copied content type returns YES if the screen needs updating and NO
+// otherwise. This must only be called on the main thread.
+- (BOOL)updateCopiedContentType:(CopiedContentType)type {
+  if (self.copiedContentType == type) {
     return NO;
   }
   self.copiedContentType = type;
-  self.copiedText = copiedText;
-  self.copiedImage = copiedImage;
-  [self.widgetView setCopiedContentType:self.copiedContentType
-                             copiedText:self.copiedText];
+  [self.widgetView setCopiedContentType:self.copiedContentType];
   return YES;
 }
 
-// Verifies that the current copied content type has the required data with it.
-- (BOOL)verifyCopiedContentType {
-  switch (self.copiedContentType) {
-    case CopiedContentTypeString:
-    case CopiedContentTypeURL:
-      return self.copiedText;
-    case CopiedContentTypeImage:
-      return self.copiedImage;
-    case CopiedContentTypeNone:
-      return true;
-  }
-}
-
 @end
diff --git a/ios/web/common/features.h b/ios/web/common/features.h
index b3e4360..d7e550d 100644
--- a/ios/web/common/features.h
+++ b/ios/web/common/features.h
@@ -57,6 +57,12 @@
 // the URL.
 extern const base::Feature kAddWebContentDropInteraction;
 
+// When enabled, opening a URL with a text fragment (e.g.,
+// example.com/#:~:text=examples) will cause matching text in the page to be
+// highlighted and scrolled into view.
+// See also: https://wicg.github.io/scroll-to-text-fragment/
+extern const base::Feature kScrollToTextIOS;
+
 // When true, for each navigation, the default user agent is chosen by the
 // WebClient GetDefaultUserAgent() method. If it is false, the mobile version
 // is requested by default.
diff --git a/ios/web/common/features.mm b/ios/web/common/features.mm
index 688740c..df6b531 100644
--- a/ios/web/common/features.mm
+++ b/ios/web/common/features.mm
@@ -48,6 +48,9 @@
 const base::Feature kAddWebContentDropInteraction{
     "AddWebContentDropInteraction", base::FEATURE_DISABLED_BY_DEFAULT};
 
+const base::Feature kScrollToTextIOS{"ScrollToTextIOS",
+                                     base::FEATURE_DISABLED_BY_DEFAULT};
+
 bool UseWebClientDefaultUserAgent() {
   if (@available(iOS 13, *)) {
     return base::FeatureList::IsEnabled(kUseDefaultUserAgentInWebClient);
diff --git a/ios/web/navigation/session_storage_builder.mm b/ios/web/navigation/session_storage_builder.mm
index a78731f..11817a0 100644
--- a/ios/web/navigation/session_storage_builder.mm
+++ b/ios/web/navigation/session_storage_builder.mm
@@ -8,6 +8,7 @@
 
 #include "base/check_op.h"
 #include "base/mac/foundation_util.h"
+#include "ios/web/common/features.h"
 #import "ios/web/navigation/navigation_item_impl.h"
 #import "ios/web/navigation/navigation_item_storage_builder.h"
 #include "ios/web/navigation/navigation_manager_impl.h"
@@ -113,7 +114,12 @@
   web_state->certificate_policy_cache_ = std::move(cert_policy_cache);
   web::SerializableUserDataManager::FromWebState(web_state)
       ->AddSerializableUserData(storage.userData);
-  web_state->SetUserAgent(storage.userAgentType);
+  UserAgentType user_agent_type = storage.userAgentType;
+  if (user_agent_type == UserAgentType::AUTOMATIC &&
+      !features::UseWebClientDefaultUserAgent()) {
+    user_agent_type = UserAgentType::MOBILE;
+  }
+  web_state->SetUserAgent(user_agent_type);
 }
 
 }  // namespace web
diff --git a/ios/web/session/crw_session_storage.mm b/ios/web/session/crw_session_storage.mm
index 18401f6..6891deb1 100644
--- a/ios/web/session/crw_session_storage.mm
+++ b/ios/web/session/crw_session_storage.mm
@@ -110,8 +110,13 @@
                forKey:kCertificatePolicyCacheStorageKey];
   if (_userData)
     _userData->Encode(coder);
+  web::UserAgentType userAgentType = _userAgentType;
+  if (userAgentType == web::UserAgentType::AUTOMATIC &&
+      !web::features::UseWebClientDefaultUserAgent()) {
+    userAgentType = web::UserAgentType::MOBILE;
+  }
   web::nscoder_util::EncodeString(
-      coder, kUserAgentKey, web::GetUserAgentTypeDescription(_userAgentType));
+      coder, kUserAgentKey, web::GetUserAgentTypeDescription(userAgentType));
 }
 
 @end
diff --git a/mojo/BUILD.gn b/mojo/BUILD.gn
index b0d08da..ea1cc00 100644
--- a/mojo/BUILD.gn
+++ b/mojo/BUILD.gn
@@ -10,7 +10,7 @@
   testonly = true
   deps = [ ":tests" ]
 
-  if (!(is_linux && current_cpu == "x86")) {
+  if (!((is_linux || is_chromeos) && current_cpu == "x86")) {
     deps += [ "//mojo/public" ]
   }
 
diff --git a/mojo/core/test/BUILD.gn b/mojo/core/test/BUILD.gn
index 1abadfc..1eab16f 100644
--- a/mojo/core/test/BUILD.gn
+++ b/mojo/core/test/BUILD.gn
@@ -47,7 +47,7 @@
     "//testing/gtest",
   ]
 
-  if (is_linux && !is_component_build) {
+  if ((is_linux || is_chromeos) && !is_component_build) {
     public_configs = [ "//build/config/gcc:rpath_for_built_shared_libraries" ]
   }
 }
@@ -65,7 +65,7 @@
 
   sources = [ "run_all_perftests.cc" ]
 
-  if (is_linux && !is_component_build) {
+  if ((is_linux || is_chromeos) && !is_component_build) {
     public_configs = [ "//build/config/gcc:rpath_for_built_shared_libraries" ]
   }
 }
diff --git a/native_client_sdk/src/BUILD.gn b/native_client_sdk/src/BUILD.gn
index ff31576..27b24d1 100644
--- a/native_client_sdk/src/BUILD.gn
+++ b/native_client_sdk/src/BUILD.gn
@@ -7,7 +7,7 @@
 declare_args() {
   # Set to true if cross compiling trusted (e.g. building sel_ldr_arm on x86)
   # binaries is supported.
-  enable_cross_trusted = is_linux
+  enable_cross_trusted = is_linux || is_chromeos
 
   # Build the nacl SDK untrusted components.  This is disabled by default since
   # not all NaCl untrusted compilers are in goma (e.g arm-nacl-glibc)
@@ -28,7 +28,7 @@
         "//native_client/src/trusted/service_runtime:sel_ldr",
         "//native_client/src/trusted/validator/driver:ncval_new",
       ]
-      if (is_linux) {
+      if (is_linux || is_chromeos) {
         deps += [ "//native_client/src/nonsfi/loader:nonsfi_loader" ]
       }
     }
diff --git a/net/BUILD.gn b/net/BUILD.gn
index 497313b..45688e85 100644
--- a/net/BUILD.gn
+++ b/net/BUILD.gn
@@ -99,7 +99,7 @@
   "//build/config/compiler:wexit_time_destructors",
 ]
 
-if (is_linux) {
+if (is_linux || is_chromeos) {
   net_configs += [ "//build/config/linux:libresolv" ]
 }
 
@@ -166,6 +166,8 @@
     "base/sockaddr_storage.cc",
     "base/sockaddr_storage.h",
     "base/sys_addrinfo.h",
+    "base/transport_info.cc",
+    "base/transport_info.h",
     "base/url_util.cc",
     "base/url_util.h",
     "cert/asn1_util.cc",
@@ -1237,7 +1239,7 @@
       ]
     }
 
-    if (is_linux) {
+    if (is_linux && !is_chromeos) {
       sources += [
         "base/network_change_notifier_linux.cc",
         "base/network_change_notifier_linux.h",
@@ -1246,7 +1248,7 @@
       ]
     }
 
-    if (is_linux || is_android) {
+    if (is_linux || is_chromeos || is_android) {
       sources += [
         "base/address_tracker_linux.cc",
         "base/address_tracker_linux.h",
@@ -1425,7 +1427,7 @@
     }
 
     # Use getifaddrs() on POSIX platforms, except Linux.
-    if (is_posix && !is_linux) {
+    if (is_posix && !is_linux && !is_chromeos) {
       sources += [
         "base/network_interfaces_getifaddrs.cc",
         "base/network_interfaces_getifaddrs.h",
@@ -2455,7 +2457,7 @@
   }
 }
 
-if (is_linux || is_mac) {
+if (is_linux || is_chromeos || is_mac) {
   executable("cachetool") {
     testonly = true
     sources = [ "tools/cachetool/cachetool.cc" ]
@@ -2482,7 +2484,7 @@
   }
 }
 
-if (is_linux) {
+if (is_linux || is_chromeos) {
   static_library("epoll_server") {
     sources = [
       "tools/epoll_server/platform/impl/epoll_bug_impl.h",
@@ -2558,7 +2560,7 @@
   }
 }
 
-if (is_android || is_linux) {
+if (is_android || is_linux || is_chromeos) {
   executable("disk_cache_memory_test") {
     testonly = true
     sources = [ "tools/disk_cache_memory_test/disk_cache_memory_test.cc" ]
@@ -4472,7 +4474,7 @@
     ]
   }
 
-  if (is_linux) {
+  if (is_linux || is_chromeos) {
     sources += [
       "base/address_tracker_linux_unittest.cc",
       "base/network_interfaces_linux_unittest.cc",
@@ -4583,7 +4585,7 @@
     "//testing/buildbot/filters:net_unittests_filters",
   ]
 
-  if (is_linux || is_mac || is_win || is_fuchsia) {
+  if (is_linux || is_chromeos || is_mac || is_win || is_fuchsia) {
     deps += [
       "//third_party/pywebsocket3/",
       "//third_party/tlslite/",
@@ -4599,7 +4601,7 @@
     ]
   }
 
-  if (is_linux) {
+  if (is_linux || is_chromeos) {
     sources += [
       "quic/platform/impl/quic_epoll_clock_test.cc",
       "quic/platform/impl/quic_flags_test.cc",
@@ -4760,7 +4762,7 @@
   }
 
   # Use getifaddrs() on POSIX platforms, except Linux and Android.
-  if (is_posix && !is_linux && !is_android) {
+  if (is_posix && !is_linux && !is_chromeos && !is_android) {
     sources += [ "base/network_interfaces_getifaddrs_unittest.cc" ]
   }
 
@@ -5564,7 +5566,7 @@
   dict = "data/fuzzer_dictionaries/net_uri_template_fuzzer.dict"
 }
 
-if (is_linux) {
+if (is_linux || is_chromeos) {
   fuzzer_test("net_base_address_tracker_linux_fuzzer") {
     sources = [ "base/address_tracker_linux_fuzzer.cc" ]
     deps = [
diff --git a/net/base/transport_info.cc b/net/base/transport_info.cc
new file mode 100644
index 0000000..3861e83
--- /dev/null
+++ b/net/base/transport_info.cc
@@ -0,0 +1,61 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/base/transport_info.h"
+
+#include <ostream>
+#include <utility>
+
+#include "base/strings/strcat.h"
+
+namespace net {
+
+base::StringPiece TransportTypeToString(TransportType type) {
+  switch (type) {
+    case TransportType::kDirect:
+      return "TransportType::kDirect";
+    case TransportType::kProxied:
+      return "TransportType::kProxied";
+  }
+
+  // We define this here instead of as a `default` clause above so as to force
+  // a compiler error if a new value is added to the enum and this method is
+  // not updated to reflect it.
+  return "<invalid transport type>";
+}
+
+TransportInfo::TransportInfo() = default;
+
+TransportInfo::TransportInfo(TransportType type_arg, IPEndPoint endpoint_arg)
+    : type(type_arg), endpoint(std::move(endpoint_arg)) {}
+
+TransportInfo::~TransportInfo() = default;
+
+bool TransportInfo::operator==(const TransportInfo& other) const {
+  return type == other.type && endpoint == other.endpoint;
+}
+
+bool TransportInfo::operator!=(const TransportInfo& other) const {
+  return !(*this == other);
+}
+
+std::string TransportInfo::ToString() const {
+  return base::StrCat({
+      "TransportInfo{ type = ",
+      TransportTypeToString(type),
+      ", endpoint = ",
+      endpoint.ToString(),
+      " }",
+  });
+}
+
+std::ostream& operator<<(std::ostream& out, TransportType type) {
+  return out << TransportTypeToString(type);
+}
+
+std::ostream& operator<<(std::ostream& out, const TransportInfo& info) {
+  return out << info.ToString();
+}
+
+}  // namespace net
diff --git a/net/base/transport_info.h b/net/base/transport_info.h
new file mode 100644
index 0000000..6f0dde3
--- /dev/null
+++ b/net/base/transport_info.h
@@ -0,0 +1,57 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_BASE_TRANSPORT_INFO_H_
+#define NET_BASE_TRANSPORT_INFO_H_
+
+#include <iosfwd>
+#include <string>
+
+#include "base/strings/string_piece.h"
+#include "net/base/ip_endpoint.h"
+#include "net/base/net_export.h"
+
+namespace net {
+
+// Specifies the type of a network transport.
+enum class TransportType {
+  // The transport was established directly to a peer.
+  kDirect,
+  // The transport was established to a proxy of some kind.
+  kProxied,
+};
+
+// Returns a string representation of the given transport type.
+// The returned StringPiece is static, has no lifetime restrictions.
+NET_EXPORT base::StringPiece TransportTypeToString(TransportType type);
+
+// Describes a network transport.
+struct NET_EXPORT TransportInfo {
+  TransportInfo();
+  TransportInfo(TransportType type_arg, IPEndPoint endpoint_arg);
+  ~TransportInfo();
+
+  // Instances of this type are comparable for equality.
+  bool operator==(const TransportInfo& other) const;
+  bool operator!=(const TransportInfo& other) const;
+
+  // Returns a string representation of this struct, suitable for debugging.
+  std::string ToString() const;
+
+  // The type of the transport.
+  TransportType type = TransportType::kDirect;
+
+  // If |type| is kDirect, then this identifies the peer endpoint.
+  // If |type| is kProxied, then this identifies the proxy endpoint.
+  IPEndPoint endpoint;
+};
+
+// Instances of these types are streamable for easier debugging.
+NET_EXPORT std::ostream& operator<<(std::ostream& out, TransportType type);
+NET_EXPORT std::ostream& operator<<(std::ostream& out,
+                                    const TransportInfo& info);
+
+}  // namespace net
+
+#endif  // NET_BASE_TRANSPORT_INFO_H_
diff --git a/net/cookies/cookie_monster_perftest.cc b/net/cookies/cookie_monster_perftest.cc
index 7ca45d7..10c79a1 100644
--- a/net/cookies/cookie_monster_perftest.cc
+++ b/net/cookies/cookie_monster_perftest.cc
@@ -28,8 +28,7 @@
 namespace {
 
 const int kNumCookies = 20000;
-const char kCookieLine[] = "A  = \"b=;\\\"\"  ;secure;;;";
-const char kGoogleURL[] = "http://www.foo.com";
+const char kCookieLine[] = "A  = \"b=;\\\"\"  ;secure;;; samesite=none";
 
 static constexpr char kMetricPrefixParsedCookie[] = "ParsedCookie.";
 static constexpr char kMetricPrefixCookieMonster[] = "CookieMonster.";
@@ -106,7 +105,8 @@
 
  private:
   void Run(CookieAccessResult result) {
-    EXPECT_TRUE(result.status.IsInclude());
+    EXPECT_TRUE(result.status.IsInclude())
+        << "result.status: " << result.status.GetDebugString();
     CookieTestCallback::Run();
   }
   CookieOptions options_;
@@ -178,7 +178,7 @@
   auto cm = std::make_unique<CookieMonster>(nullptr, nullptr);
   std::vector<std::string> cookies;
   for (int i = 0; i < kNumCookies; i++) {
-    cookies.push_back(base::StringPrintf("a%03d=b", i));
+    cookies.push_back(base::StringPrintf("a%03d=b; SameSite=None; Secure", i));
   }
 
   SetCookieCallback setCookieCallback;
@@ -187,9 +187,10 @@
   auto reporter = SetUpCookieMonsterReporter("single_host");
   base::ElapsedTimer add_timer;
 
+  const GURL kGoogleURL = GURL("https://www.foo.com");
   for (std::vector<std::string>::const_iterator it = cookies.begin();
        it != cookies.end(); ++it) {
-    setCookieCallback.SetCookie(cm.get(), GURL(kGoogleURL), *it);
+    setCookieCallback.SetCookie(cm.get(), kGoogleURL, *it);
   }
   reporter.AddResult(kMetricAddTimeMs, add_timer.Elapsed().InMillisecondsF());
 
@@ -198,7 +199,7 @@
   base::ElapsedTimer query_timer;
   for (std::vector<std::string>::const_iterator it = cookies.begin();
        it != cookies.end(); ++it) {
-    getCookieListCallback.GetCookieList(cm.get(), GURL(kGoogleURL));
+    getCookieListCallback.GetCookieList(cm.get(), kGoogleURL);
   }
   reporter.AddResult(kMetricQueryTimeMs,
                      query_timer.Elapsed().InMillisecondsF());
@@ -250,7 +251,8 @@
   auto cm = std::make_unique<CookieMonster>(nullptr, nullptr);
   GetCookieListCallback getCookieListCallback;
   SetCookieCallback setCookieCallback;
-  const char domain_cookie_format_tree[] = "a=b; domain=%s";
+  const char domain_cookie_format_tree[] =
+      "a=b; domain=%s; samesite=none; secure";
   const std::string domain_base("top.com");
 
   std::vector<std::string> domain_list;
@@ -323,7 +325,8 @@
   domain_list.push_back("b.a.b.a.top.com");
   EXPECT_EQ(4u, domain_list.size());
 
-  const char domain_cookie_format_line[] = "a%03d=b; domain=%s";
+  const char domain_cookie_format_line[] =
+      "a%03d=b; domain=%s; samesite=none; secure";
   for (int i = 0; i < 8; i++) {
     for (std::vector<std::string>::const_iterator it = domain_list.begin();
          it != domain_list.end(); it++) {
@@ -445,8 +448,8 @@
         test_case.num_cookies, test_case.num_old_cookies, 0, 0,
         CookieMonster::kSafeFromGlobalPurgeDays * 2);
 
-    GURL gurl("http://foo.com");
-    std::string cookie_line("z=3");
+    GURL gurl("https://foo.com");
+    std::string cookie_line("z=3; samesite=none; secure");
     // Trigger the Garbage collection we're allowed.
     setCookieCallback.SetCookie(cm.get(), gurl, cookie_line);
 
diff --git a/net/features.gni b/net/features.gni
index d545845..25729120 100644
--- a/net/features.gni
+++ b/net/features.gni
@@ -23,7 +23,7 @@
   disable_brotli_filter = false
 
   # Multicast DNS.
-  enable_mdns = is_win || is_linux || is_fuchsia || is_apple
+  enable_mdns = is_win || is_linux || is_chromeos || is_fuchsia || is_apple
 
   # Reporting not used on iOS.
   enable_reporting = !is_ios
diff --git a/net/http/http_cache_lookup_manager_unittest.cc b/net/http/http_cache_lookup_manager_unittest.cc
index 5298584..73e760b 100644
--- a/net/http/http_cache_lookup_manager_unittest.cc
+++ b/net/http/http_cache_lookup_manager_unittest.cc
@@ -52,12 +52,25 @@
 
 std::unique_ptr<MockTransaction> CreateMockTransaction(const GURL& url) {
   MockTransaction mock_trans = {
-      url.spec().c_str(), "GET", base::Time(), "", LOAD_NORMAL,
+      url.spec().c_str(),
+      "GET",
+      base::Time(),
+      "",
+      LOAD_NORMAL,
+      DefaultTransportInfo(),
       "HTTP/1.1 200 OK",
       "Date: Wed, 28 Nov 2007 09:40:09 GMT\n"
       "Last-Modified: Wed, 28 Nov 2007 00:40:09 GMT\n",
-      base::Time(), "<html><body>Google Blah Blah</body></html>",
-      TEST_MODE_NORMAL, nullptr, nullptr, nullptr, 0, 0, OK};
+      base::Time(),
+      "<html><body>Google Blah Blah</body></html>",
+      TEST_MODE_NORMAL,
+      nullptr,
+      nullptr,
+      nullptr,
+      0,
+      0,
+      OK,
+  };
   return std::make_unique<MockTransaction>(mock_trans);
 }
 
diff --git a/net/http/http_cache_unittest.cc b/net/http/http_cache_unittest.cc
index c410380..c7a7a9b 100644
--- a/net/http/http_cache_unittest.cc
+++ b/net/http/http_cache_unittest.cc
@@ -35,6 +35,7 @@
 #include "net/base/elements_upload_data_stream.h"
 #include "net/base/features.h"
 #include "net/base/host_port_pair.h"
+#include "net/base/ip_address.h"
 #include "net/base/ip_endpoint.h"
 #include "net/base/load_flags.h"
 #include "net/base/load_timing_info.h"
@@ -75,9 +76,11 @@
 using testing::AllOf;
 using testing::ByRef;
 using testing::Contains;
+using testing::ElementsAre;
 using testing::Eq;
 using testing::Field;
 using testing::Gt;
+using testing::IsEmpty;
 using testing::NotNull;
 
 using base::Time;
@@ -371,6 +374,7 @@
     base::Time(),
     "",
     LOAD_VALIDATE_CACHE,
+    DefaultTransportInfo(),
     "HTTP/1.1 200 OK",
     "Cache-Control: max-age=10000\n",
     base::Time(),
@@ -381,7 +385,8 @@
     nullptr,
     0,
     0,
-    OK};
+    OK,
+};
 
 // This class provides a handler for kRangeGET_TransactionOK so that the range
 // request can be served on demand.
@@ -565,6 +570,7 @@
     base::Time(),
     "Range: bytes = 40-49\r\n" EXTRA_HEADER,
     LOAD_NORMAL,
+    DefaultTransportInfo(),
     "HTTP/1.1 206 Partial Content",
     "Last-Modified: Sat, 18 Apr 2007 01:10:43 GMT\n"
     "ETag: \"foo\"\n"
@@ -578,7 +584,8 @@
     nullptr,
     0,
     0,
-    OK};
+    OK,
+};
 
 const char kFullRangeData[] =
     "rg: 00-09 rg: 10-19 rg: 20-29 rg: 30-39 "
@@ -720,6 +727,13 @@
   return !log.GetEntriesWithType(expected).empty();
 }
 
+// Returns a TransportInfo distinct from the default for mock transactions.
+TransportInfo TestTransportInfo() {
+  TransportInfo result;
+  result.endpoint = IPEndPoint(IPAddress(42, 0, 1, 2), 1337);
+  return result;
+}
+
 }  // namespace
 
 using HttpCacheTest = TestWithTaskEnvironment;
@@ -826,7 +840,9 @@
 
   ConnectedHandler connected_handler;
 
-  MockHttpRequest request(kSimpleGET_Transaction);
+  ScopedMockTransaction mock_transaction(kSimpleGET_Transaction);
+  mock_transaction.transport_info = TestTransportInfo();
+  MockHttpRequest request(mock_transaction);
 
   std::unique_ptr<HttpTransaction> transaction;
   EXPECT_THAT(cache.CreateTransaction(&transaction), IsOk());
@@ -840,7 +856,7 @@
       IsError(ERR_IO_PENDING));
   EXPECT_THAT(callback.WaitForResult(), IsOk());
 
-  EXPECT_EQ(1, connected_handler.call_count());
+  EXPECT_THAT(connected_handler.transports(), ElementsAre(TestTransportInfo()));
 }
 
 // This test verifies that when the callback passed to SetConnectedCallback()
@@ -904,8 +920,9 @@
   EXPECT_EQ(1, cache.network_layer()->transaction_count());
 
   // TODO(crbug.com/986744): Expect 1 call once HttpCache::Transaction is
-  // modified to call the callback on cache hits.
-  EXPECT_EQ(0, connected_handler.call_count());
+  // modified to call the callback on cache hits, expect that the endpoint is
+  // the one from which the cached data was downloaded.
+  EXPECT_THAT(connected_handler.transports(), IsEmpty());
 }
 
 class HttpCacheTest_SplitCacheFeature
@@ -2024,6 +2041,7 @@
     ScopedMockTransaction mock_transaction(kRangeGET_TransactionOK);
     mock_transaction.request_headers = "Range: bytes = 10-39\r\n" EXTRA_HEADER;
     mock_transaction.data = "rg: 10-19 rg: 20-29 rg: 30-39 ";
+    mock_transaction.transport_info = TestTransportInfo();
     MockHttpRequest request(mock_transaction);
 
     ConnectedHandler connected_handler;
@@ -2041,14 +2059,29 @@
     EXPECT_THAT(callback.WaitForResult(), IsOk());
 
     // 1 call for the first range's network transaction.
-    EXPECT_EQ(1, connected_handler.call_count());
+    EXPECT_THAT(connected_handler.transports(),
+                ElementsAre(TestTransportInfo()));
+
+    // Switch the endpoint for the next network transaction to observe.
+    // For ease, we just switch the port number.
+    //
+    // NOTE: This works because only the mock transaction struct's address is
+    // registered with the mocking framework - the pointee data is consulted
+    // each time it is read.
+    auto new_transport_info = TestTransportInfo();
+    new_transport_info.endpoint =
+        IPEndPoint(new_transport_info.endpoint.address(), 123);
+    mock_transaction.transport_info = new_transport_info;
 
     ReadAndVerifyTransaction(transaction.get(), mock_transaction);
 
     // A second call for the last range's network transaction.
     // TODO(crbug.com/986744): Expect 3 calls once the callback is notified on
     // cache hits as well as network connections.
-    EXPECT_EQ(2, connected_handler.call_count());
+    // TODO(crbug.com/986744): Test that the cached data is served as coming
+    // from the endpoint whence it was originally downloaded.
+    EXPECT_THAT(connected_handler.transports(),
+                ElementsAre(TestTransportInfo(), new_transport_info));
   }
 }
 
@@ -2095,7 +2128,8 @@
     EXPECT_THAT(callback.WaitForResult(), IsOk());
 
     // 1 call for the first range's network transaction.
-    EXPECT_EQ(1, connected_handler.call_count());
+    EXPECT_THAT(connected_handler.transports(),
+                ElementsAre(DefaultTransportInfo()));
 
     // Set the callback to return an error the next time it is called. The exact
     // error code is irrelevant, what matters is that it is reflected in the
@@ -2107,7 +2141,8 @@
                 IsError(ERR_NOT_IMPLEMENTED));
 
     // A second call that failed.
-    EXPECT_EQ(2, connected_handler.call_count());
+    EXPECT_THAT(connected_handler.transports(),
+                ElementsAre(DefaultTransportInfo(), DefaultTransportInfo()));
   }
 }
 
@@ -9283,8 +9318,7 @@
 
 // Tests basic pickling/unpickling of HttpResponseInfo.
 TEST_F(HttpCacheTest, PersistHttpResponseInfo) {
-  const IPEndPoint expected_endpoint =
-      IPEndPoint(net::IPAddress(1, 2, 3, 4), 80);
+  const IPEndPoint expected_endpoint = IPEndPoint(IPAddress(1, 2, 3, 4), 80);
   // Set some fields (add more if needed.)
   HttpResponseInfo response1;
   response1.was_cached = false;
diff --git a/net/http/http_network_transaction.cc b/net/http/http_network_transaction.cc
index 4aa3c63..69b55e23 100644
--- a/net/http/http_network_transaction.cc
+++ b/net/http/http_network_transaction.cc
@@ -30,6 +30,7 @@
 #include "net/base/load_timing_info.h"
 #include "net/base/net_errors.h"
 #include "net/base/proxy_server.h"
+#include "net/base/transport_info.h"
 #include "net/base/upload_data_stream.h"
 #include "net/base/url_util.h"
 #include "net/cert/cert_status_flags.h"
@@ -876,7 +877,11 @@
 
   // Fire off notification that we have successfully connected.
   if (!connected_callback_.is_null()) {
-    result = connected_callback_.Run();
+    TransportType type = TransportType::kDirect;
+    if (!proxy_info_.is_direct()) {
+      type = TransportType::kProxied;
+    }
+    result = connected_callback_.Run(TransportInfo(type, remote_endpoint_));
     DCHECK_NE(result, ERR_IO_PENDING);
   }
 
diff --git a/net/http/http_network_transaction_unittest.cc b/net/http/http_network_transaction_unittest.cc
index 755df4ac..26d16a20 100644
--- a/net/http/http_network_transaction_unittest.cc
+++ b/net/http/http_network_transaction_unittest.cc
@@ -43,6 +43,7 @@
 #include "net/base/elements_upload_data_stream.h"
 #include "net/base/features.h"
 #include "net/base/host_port_pair.h"
+#include "net/base/ip_address.h"
 #include "net/base/ip_endpoint.h"
 #include "net/base/load_timing_info.h"
 #include "net/base/load_timing_info_test_util.h"
@@ -141,6 +142,8 @@
 using base::ASCIIToUTF16;
 
 using testing::AnyOf;
+using testing::ElementsAre;
+using testing::IsEmpty;
 
 //-----------------------------------------------------------------------------
 
@@ -356,15 +359,23 @@
   }
 };
 
+// A default minimal HttpRequestInfo for use in tests, targeting HTTP.
 HttpRequestInfo DefaultRequestInfo() {
   HttpRequestInfo info;
   info.method = "GET";
-  info.url = GURL("http://www.example.org");
+  info.url = GURL("http://foo.test");
   info.traffic_annotation =
       net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS);
   return info;
 }
 
+// The default info for transports to the embedded HTTP server.
+TransportInfo EmbeddedHttpServerTransportInfo() {
+  TransportInfo info;
+  info.endpoint = IPEndPoint(IPAddress::IPv4Localhost(), 80);
+  return info;
+}
+
 }  // namespace
 
 class HttpNetworkTransactionTest : public PlatformTest,
@@ -885,12 +896,15 @@
 // remote endpoint, the ConnectedCallback is never called.
 TEST_F(HttpNetworkTransactionTest, ConnectedCallbackNeverCalled) {
   auto resolver = std::make_unique<MockHostResolver>();
-  resolver->rules()->AddSimulatedTimeoutFailure("www.example.org");
+  resolver->rules()->AddSimulatedTimeoutFailure("bar.test");
   session_deps_.host_resolver = std::move(resolver);
 
   ConnectedHandler connected_handler;
   std::unique_ptr<HttpNetworkSession> session = CreateSession(&session_deps_);
+
   auto request = DefaultRequestInfo();
+  request.url = GURL("http://bar.test");
+
   HttpNetworkTransaction transaction(DEFAULT_PRIORITY, session.get());
   transaction.SetConnectedCallback(connected_handler.Callback());
 
@@ -898,7 +912,7 @@
   transaction.Start(&request, callback.callback(), NetLogWithSource());
   callback.WaitForResult();
 
-  EXPECT_EQ(connected_handler.call_count(), 0);
+  EXPECT_THAT(connected_handler.transports(), IsEmpty());
 }
 
 // This test verifies that if the ConnectedCallback returns an error, the
@@ -924,7 +938,8 @@
       IsError(ERR_IO_PENDING));
   EXPECT_THAT(callback.WaitForResult(), IsError(ERR_NOT_IMPLEMENTED));
 
-  EXPECT_EQ(connected_handler.call_count(), 1);
+  EXPECT_THAT(connected_handler.transports(),
+              ElementsAre(EmbeddedHttpServerTransportInfo()));
 }
 
 // This test verifies that the ConnectedCallback is called once in the case of
@@ -949,7 +964,8 @@
       IsError(ERR_IO_PENDING));
   EXPECT_THAT(callback.WaitForResult(), IsOk());
 
-  EXPECT_EQ(connected_handler.call_count(), 1);
+  EXPECT_THAT(connected_handler.transports(),
+              ElementsAre(EmbeddedHttpServerTransportInfo()));
 }
 
 // This test verifies that the ConnectedCallback is called once more per
@@ -985,7 +1001,8 @@
       IsError(ERR_IO_PENDING));
   EXPECT_THAT(callback1.WaitForResult(), IsOk());
 
-  EXPECT_EQ(connected_handler.call_count(), 1);
+  EXPECT_THAT(connected_handler.transports(),
+              ElementsAre(EmbeddedHttpServerTransportInfo()));
 
   // Second request, connects again.
   TestCompletionCallback callback2;
@@ -994,7 +1011,9 @@
               IsError(ERR_IO_PENDING));
   EXPECT_THAT(callback2.WaitForResult(), IsOk());
 
-  EXPECT_EQ(connected_handler.call_count(), 2);
+  EXPECT_THAT(connected_handler.transports(),
+              ElementsAre(EmbeddedHttpServerTransportInfo(),
+                          EmbeddedHttpServerTransportInfo()));
 }
 
 // This test verifies that the ConnectedCallback is called once more per retry.
@@ -1026,7 +1045,9 @@
       IsError(ERR_IO_PENDING));
   EXPECT_THAT(callback1.WaitForResult(), IsOk());
 
-  EXPECT_EQ(connected_handler.call_count(), 2);
+  EXPECT_THAT(connected_handler.transports(),
+              ElementsAre(EmbeddedHttpServerTransportInfo(),
+                          EmbeddedHttpServerTransportInfo()));
 }
 
 // Allow up to 4 bytes of junk to precede status line.
@@ -1379,7 +1400,8 @@
   EXPECT_EQ(1234, response->headers->GetContentLength());
   EXPECT_EQ("HTTP/1.1 404 Not Found", response->headers->GetStatusLine());
   EXPECT_TRUE(response->proxy_server.is_direct());
-  EXPECT_EQ(connected_handler.call_count(), 1);
+  EXPECT_THAT(connected_handler.transports(),
+              ElementsAre(EmbeddedHttpServerTransportInfo()));
 
   std::string server_header;
   size_t iter = 0;
@@ -3445,10 +3467,13 @@
   session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl);
 
   TestCompletionCallback callback1;
+  ConnectedHandler connected_handler;
 
   auto trans =
       std::make_unique<HttpNetworkTransaction>(DEFAULT_PRIORITY, session.get());
 
+  trans->SetConnectedCallback(connected_handler.Callback());
+
   int rv = trans->Start(&request, callback1.callback(), log.bound());
   EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
 
@@ -3463,6 +3488,12 @@
       NetLogEventType::HTTP_TRANSACTION_READ_TUNNEL_RESPONSE_HEADERS,
       NetLogEventPhase::NONE);
 
+  // TODO(crbug.com/986744): Fix handling of OnConnected() when proxy
+  // authentication is required. We should notify the callback that a connection
+  // was established, even though the stream might not be ready for us to send
+  // data through it.
+  EXPECT_THAT(connected_handler.transports(), IsEmpty());
+
   const HttpResponseInfo* response = trans->GetResponseInfo();
   ASSERT_TRUE(response);
   EXPECT_FALSE(response->headers->IsKeepAlive());
@@ -3493,6 +3524,11 @@
   EXPECT_EQ(5, response->headers->GetContentLength());
   EXPECT_TRUE(HttpVersion(1, 1) == response->headers->GetHttpVersion());
 
+  TransportInfo expected_transport;
+  expected_transport.type = TransportType::kProxied;
+  expected_transport.endpoint = IPEndPoint(IPAddress::IPv4Localhost(), 70);
+  EXPECT_THAT(connected_handler.transports(), ElementsAre(expected_transport));
+
   // Check that credentials were successfully cached, with the right target.
   HttpAuthCache::Entry* entry = session->http_auth_cache()->Lookup(
       GURL("http://myproxy:70"), HttpAuth::AUTH_PROXY, "MyRealm1",
@@ -3576,11 +3612,14 @@
   SSLSocketDataProvider ssl(ASYNC, OK);
   session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl);
 
+  ConnectedHandler connected_handler;
   TestCompletionCallback callback1;
 
   auto trans =
       std::make_unique<HttpNetworkTransaction>(DEFAULT_PRIORITY, session.get());
 
+  trans->SetConnectedCallback(connected_handler.Callback());
+
   int rv = trans->Start(&request, callback1.callback(), log.bound());
   EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
 
@@ -3603,6 +3642,12 @@
   EXPECT_TRUE(HttpVersion(1, 1) == response->headers->GetHttpVersion());
   EXPECT_TRUE(CheckBasicProxyAuth(response->auth_challenge));
 
+  // TODO(crbug.com/986744): Fix handling of OnConnected() when proxy
+  // authentication is required. We should notify the callback that a connection
+  // was established, even though the stream might not be ready for us to send
+  // data through it.
+  EXPECT_THAT(connected_handler.transports(), IsEmpty());
+
   LoadTimingInfo load_timing_info;
   // CONNECT requests and responses are handled at the connect job level, so
   // the transaction does not yet have a connection.
@@ -3625,6 +3670,11 @@
   EXPECT_EQ(5, response->headers->GetContentLength());
   EXPECT_TRUE(HttpVersion(1, 1) == response->headers->GetHttpVersion());
 
+  TransportInfo expected_transport;
+  expected_transport.type = TransportType::kProxied;
+  expected_transport.endpoint = IPEndPoint(IPAddress::IPv4Localhost(), 70);
+  EXPECT_THAT(connected_handler.transports(), ElementsAre(expected_transport));
+
   // The password prompt info should not be set.
   EXPECT_FALSE(response->auth_challenge.has_value());
 
@@ -5672,10 +5722,14 @@
   SameProxyWithDifferentSchemesProxyResolver() {}
   ~SameProxyWithDifferentSchemesProxyResolver() override {}
 
-  static std::string ProxyHostPortPairAsString() { return "proxy.test:10000"; }
+  static constexpr uint16_t kProxyPort = 10000;
 
   static HostPortPair ProxyHostPortPair() {
-    return HostPortPair::FromString(ProxyHostPortPairAsString());
+    return HostPortPair("proxy.test", kProxyPort);
+  }
+
+  static std::string ProxyHostPortPairAsString() {
+    return ProxyHostPortPair().ToString();
   }
 
   // ProxyResolver implementation.
@@ -5870,29 +5924,44 @@
   };
 
   for (const auto& test_case : kTestCases) {
+    SCOPED_TRACE(test_case.url);
+
     HttpRequestInfo request;
     request.method = "GET";
     request.url = test_case.url;
     request.traffic_annotation =
         net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS);
-    std::unique_ptr<HttpNetworkTransaction> trans =
-        std::make_unique<HttpNetworkTransaction>(DEFAULT_PRIORITY,
-                                                 session.get());
+    ConnectedHandler connected_handler;
+
+    auto transaction = std::make_unique<HttpNetworkTransaction>(
+        DEFAULT_PRIORITY, session.get());
+
+    transaction->SetConnectedCallback(connected_handler.Callback());
+
     TestCompletionCallback callback;
-    int rv = trans->Start(&request, callback.callback(), NetLogWithSource());
+    int rv =
+        transaction->Start(&request, callback.callback(), NetLogWithSource());
     EXPECT_THAT(callback.GetResult(rv), IsOk());
 
-    const HttpResponseInfo* response = trans->GetResponseInfo();
+    const HttpResponseInfo* response = transaction->GetResponseInfo();
     ASSERT_TRUE(response);
     ASSERT_TRUE(response->headers);
     EXPECT_EQ(200, response->headers->response_code());
     std::string response_data;
-    EXPECT_THAT(ReadTransaction(trans.get(), &response_data), IsOk());
+    EXPECT_THAT(ReadTransaction(transaction.get(), &response_data), IsOk());
     EXPECT_EQ(test_case.expected_response, response_data);
 
+    TransportInfo expected_transport;
+    expected_transport.type = TransportType::kProxied;
+    expected_transport.endpoint =
+        IPEndPoint(IPAddress::IPv4Localhost(),
+                   SameProxyWithDifferentSchemesProxyResolver::kProxyPort);
+    EXPECT_THAT(connected_handler.transports(),
+                ElementsAre(expected_transport));
+
     // Return the socket to the socket pool, so can make sure it's not used for
     // the next requests.
-    trans.reset();
+    transaction.reset();
     base::RunLoop().RunUntilIdle();
 
     // Check the number of idle sockets in the pool, to make sure that used
@@ -6256,10 +6325,13 @@
   SSLSocketDataProvider ssl(ASYNC, OK);
   session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl);
 
+  ConnectedHandler connected_handler;
   TestCompletionCallback callback1;
 
   HttpNetworkTransaction trans(DEFAULT_PRIORITY, session.get());
 
+  trans.SetConnectedCallback(connected_handler.Callback());
+
   int rv = trans.Start(&request, callback1.callback(), log.bound());
   EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
 
@@ -6280,6 +6352,11 @@
   EXPECT_EQ(100, response->headers->GetContentLength());
   EXPECT_TRUE(HttpVersion(1, 1) == response->headers->GetHttpVersion());
 
+  TransportInfo expected_transport;
+  expected_transport.type = TransportType::kProxied;
+  expected_transport.endpoint = IPEndPoint(IPAddress::IPv4Localhost(), 70);
+  EXPECT_THAT(connected_handler.transports(), ElementsAre(expected_transport));
+
   // The password prompt info should not be set.
   EXPECT_FALSE(response->auth_challenge.has_value());
 }
@@ -6319,10 +6396,13 @@
   ssl.next_proto = kProtoHTTP2;
   session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl);
 
+  ConnectedHandler connected_handler;
   TestCompletionCallback callback1;
 
   HttpNetworkTransaction trans(DEFAULT_PRIORITY, session.get());
 
+  trans.SetConnectedCallback(connected_handler.Callback());
+
   int rv = trans.Start(&request, callback1.callback(), log.bound());
   EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
 
@@ -6340,6 +6420,11 @@
   ASSERT_TRUE(response->headers);
   EXPECT_EQ("HTTP/1.1 200", response->headers->GetStatusLine());
 
+  TransportInfo expected_transport;
+  expected_transport.type = TransportType::kProxied;
+  expected_transport.endpoint = IPEndPoint(IPAddress::IPv4Localhost(), 70);
+  EXPECT_THAT(connected_handler.transports(), ElementsAre(expected_transport));
+
   std::string response_data;
   ASSERT_THAT(ReadTransaction(&trans, &response_data), IsOk());
   EXPECT_EQ(kUploadData, response_data);
@@ -16015,9 +16100,13 @@
   EXPECT_EQ(ProxyServer(ProxyServer::SCHEME_HTTP,
                         HostPortPair::FromString("myproxy:70")),
             response->proxy_server);
-  EXPECT_EQ(1, connected_handler.call_count());
   EXPECT_TRUE(HttpVersion(1, 1) == response->headers->GetHttpVersion());
 
+  TransportInfo expected_transport;
+  expected_transport.type = TransportType::kProxied;
+  expected_transport.endpoint = IPEndPoint(IPAddress::IPv4Localhost(), 70);
+  EXPECT_THAT(connected_handler.transports(), ElementsAre(expected_transport));
+
   LoadTimingInfo load_timing_info;
   EXPECT_TRUE(trans.GetLoadTimingInfo(&load_timing_info));
   TestLoadTimingNotReusedWithPac(load_timing_info,
@@ -16095,7 +16184,11 @@
   EXPECT_EQ(ProxyServer(ProxyServer::SCHEME_HTTP,
                         HostPortPair::FromString("myproxy:70")),
             response->proxy_server);
-  EXPECT_EQ(1, connected_handler.call_count());
+
+  TransportInfo expected_transport;
+  expected_transport.type = TransportType::kProxied;
+  expected_transport.endpoint = IPEndPoint(IPAddress::IPv4Localhost(), 70);
+  EXPECT_THAT(connected_handler.transports(), ElementsAre(expected_transport));
 
   LoadTimingInfo load_timing_info;
   EXPECT_TRUE(trans.GetLoadTimingInfo(&load_timing_info));
diff --git a/net/http/http_response_info.cc b/net/http/http_response_info.cc
index f1adaf1..65daba7 100644
--- a/net/http/http_response_info.cc
+++ b/net/http/http_response_info.cc
@@ -164,6 +164,7 @@
     case CONNECTION_INFO_QUIC_DRAFT_27:
     case CONNECTION_INFO_QUIC_DRAFT_28:
     case CONNECTION_INFO_QUIC_DRAFT_29:
+    case CONNECTION_INFO_QUIC_T051:
       return CONNECTION_INFO_COARSE_QUIC;
 
     case CONNECTION_INFO_UNKNOWN:
@@ -501,6 +502,7 @@
     case CONNECTION_INFO_QUIC_DRAFT_27:
     case CONNECTION_INFO_QUIC_DRAFT_28:
     case CONNECTION_INFO_QUIC_DRAFT_29:
+    case CONNECTION_INFO_QUIC_T051:
       return true;
     case NUM_OF_CONNECTION_INFOS:
       NOTREACHED();
@@ -595,6 +597,8 @@
       return "http/1.0";
     case CONNECTION_INFO_QUIC_999:
       return "http2+quic/999";
+    case CONNECTION_INFO_QUIC_T051:
+      return "h3-T051";
     case NUM_OF_CONNECTION_INFOS:
       break;
   }
diff --git a/net/http/http_response_info.h b/net/http/http_response_info.h
index f878369a..e47c31f 100644
--- a/net/http/http_response_info.h
+++ b/net/http/http_response_info.h
@@ -75,6 +75,7 @@
     CONNECTION_INFO_QUIC_DRAFT_27 = 36,
     CONNECTION_INFO_QUIC_DRAFT_28 = 37,
     CONNECTION_INFO_QUIC_DRAFT_29 = 38,
+    CONNECTION_INFO_QUIC_T051 = 39,
     NUM_OF_CONNECTION_INFOS,
   };
 
diff --git a/net/http/http_transaction.h b/net/http/http_transaction.h
index 4e26bdb..19e4d4d 100644
--- a/net/http/http_transaction.h
+++ b/net/http/http_transaction.h
@@ -24,6 +24,7 @@
 struct HttpRequestInfo;
 class HttpResponseInfo;
 class IOBuffer;
+struct TransportInfo;
 struct LoadTimingInfo;
 class NetLogWithSource;
 class QuicServerInfo;
@@ -37,24 +38,29 @@
  public:
   // If |*defer| is set to true, the transaction will wait until
   // ResumeNetworkStart is called before establishing a connection.
-  typedef base::OnceCallback<void(bool* defer)> BeforeNetworkStartCallback;
+  using BeforeNetworkStartCallback = base::OnceCallback<void(bool* defer)>;
 
   // Called each time a connection is obtained, before any data is sent.
   //
+  // |info| describes the newly-obtained connection.
+  //
   // This can be called multiple times for a single transaction, in the case of
   // retries, auth challenges, and split range requests.
   //
-  // The callee can call GetRemoteEndpoint() on the caller transaction to
-  // determine where the latest connection terminates.
-  //
   // If this callback returns an error, the transaction fails with that error.
   // Otherwise the transaction continues unimpeded.
   // Must not return ERR_IO_PENDING.
   //
+  // TODO(crbug.com/986744): Fix handling of OnConnected() when proxy
+  // authentication is required. We should notify this callback that a
+  // connection was established, even though the stream might not be ready for
+  // us to send data through it.
+  //
   // TODO(crbug.com/591068): Allow ERR_IO_PENDING, add a new state machine state
   // to wait on a callback (either passed to this callback or a new explicit
   // method like ResumeNetworkStart()) to be called before continuing.
-  typedef base::RepeatingCallback<int()> ConnectedCallback;
+  using ConnectedCallback =
+      base::RepeatingCallback<int(const TransportInfo& info)>;
 
   // Stops any pending IO and destroys the transaction object.
   virtual ~HttpTransaction() {}
diff --git a/net/http/http_transaction_test_util.cc b/net/http/http_transaction_test_util.cc
index 580a358..90235d2 100644
--- a/net/http/http_transaction_test_util.cc
+++ b/net/http/http_transaction_test_util.cc
@@ -17,6 +17,8 @@
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/time/clock.h"
 #include "base/time/time.h"
+#include "net/base/ip_address.h"
+#include "net/base/ip_endpoint.h"
 #include "net/base/load_flags.h"
 #include "net/base/load_timing_info.h"
 #include "net/base/net_errors.h"
@@ -40,6 +42,11 @@
 static MockTransactionMap mock_transactions;
 }  // namespace
 
+TransportInfo DefaultTransportInfo() {
+  return TransportInfo(TransportType::kDirect,
+                       IPEndPoint(IPAddress::IPv4Localhost(), 80));
+}
+
 //-----------------------------------------------------------------------------
 // mock transaction data
 
@@ -49,6 +56,7 @@
     base::Time(),
     "",
     LOAD_NORMAL,
+    DefaultTransportInfo(),
     "HTTP/1.1 200 OK",
     "Cache-Control: max-age=10000\n",
     base::Time(),
@@ -60,7 +68,8 @@
     0,
     0,
     OK,
-    OK};
+    OK,
+};
 
 const MockTransaction kSimplePOST_Transaction = {
     "http://bugdatabase.com/edit",
@@ -68,6 +77,7 @@
     base::Time(),
     "",
     LOAD_NORMAL,
+    DefaultTransportInfo(),
     "HTTP/1.1 200 OK",
     "",
     base::Time(),
@@ -79,7 +89,8 @@
     0,
     0,
     OK,
-    OK};
+    OK,
+};
 
 const MockTransaction kTypicalGET_Transaction = {
     "http://www.example.com/~foo/bar.html",
@@ -87,6 +98,7 @@
     base::Time(),
     "",
     LOAD_NORMAL,
+    DefaultTransportInfo(),
     "HTTP/1.1 200 OK",
     "Date: Wed, 28 Nov 2007 09:40:09 GMT\n"
     "Last-Modified: Wed, 28 Nov 2007 00:40:09 GMT\n",
@@ -99,7 +111,8 @@
     0,
     0,
     OK,
-    OK};
+    OK,
+};
 
 const MockTransaction kETagGET_Transaction = {
     "http://www.google.com/foopy",
@@ -107,6 +120,7 @@
     base::Time(),
     "",
     LOAD_NORMAL,
+    DefaultTransportInfo(),
     "HTTP/1.1 200 OK",
     "Cache-Control: max-age=10000\n"
     "Etag: \"foopy\"\n",
@@ -119,7 +133,8 @@
     0,
     0,
     OK,
-    OK};
+    OK,
+};
 
 const MockTransaction kRangeGET_Transaction = {
     "http://www.google.com/",
@@ -127,6 +142,7 @@
     base::Time(),
     "Range: 0-100\r\n",
     LOAD_NORMAL,
+    DefaultTransportInfo(),
     "HTTP/1.1 200 OK",
     "Cache-Control: max-age=10000\n",
     base::Time(),
@@ -138,7 +154,8 @@
     0,
     0,
     OK,
-    OK};
+    OK,
+};
 
 static const MockTransaction* const kBuiltinMockTransactions[] = {
   &kSimpleGET_Transaction,
@@ -525,7 +542,7 @@
 
   int result = OK;
   if (!connected_callback_.is_null()) {
-    result = connected_callback_.Run();
+    result = connected_callback_.Run(t->transport_info);
   }
 
   CallbackLater(std::move(callback), result);
@@ -646,4 +663,21 @@
   return OK;
 }
 
+//-----------------------------------------------------------------------------
+// connected callback handler
+
+ConnectedHandler::ConnectedHandler() = default;
+ConnectedHandler::~ConnectedHandler() = default;
+
+ConnectedHandler::ConnectedHandler(const ConnectedHandler&) = default;
+ConnectedHandler& ConnectedHandler::operator=(const ConnectedHandler&) =
+    default;
+ConnectedHandler::ConnectedHandler(ConnectedHandler&&) = default;
+ConnectedHandler& ConnectedHandler::operator=(ConnectedHandler&&) = default;
+
+int ConnectedHandler::OnConnected(const TransportInfo& info) {
+  transports_.push_back(info);
+  return result_;
+}
+
 }  // namespace net
diff --git a/net/http/http_transaction_test_util.h b/net/http/http_transaction_test_util.h
index 8f7df63..89a5826 100644
--- a/net/http/http_transaction_test_util.h
+++ b/net/http/http_transaction_test_util.h
@@ -10,6 +10,7 @@
 #include <stdint.h>
 
 #include <string>
+#include <vector>
 
 #include "base/callback.h"
 #include "base/compiler_specific.h"
@@ -22,6 +23,7 @@
 #include "net/base/net_errors.h"
 #include "net/base/request_priority.h"
 #include "net/base/test_completion_callback.h"
+#include "net/base/transport_info.h"
 #include "net/disk_cache/disk_cache.h"
 #include "net/http/http_cache.h"
 #include "net/http/http_request_info.h"
@@ -63,6 +65,10 @@
                                         std::string* response_headers,
                                         std::string* response_data);
 
+// Default TransportInfo suitable for most MockTransactions.
+// Describes a direct connection to (127.0.0.1, 80).
+TransportInfo DefaultTransportInfo();
+
 struct MockTransaction {
   const char* url;
   const char* method;
@@ -70,6 +76,8 @@
   base::Time request_time;
   const char* request_headers;
   int load_flags;
+  // Connection info passed to ConnectedCallback(), if any.
+  TransportInfo transport_info = DefaultTransportInfo();
   const char* status;
   const char* response_headers;
   // If |response_time| is unspecified, the current time will be used.
@@ -367,12 +375,15 @@
 // Used for injecting ConnectedCallback instances in HttpTransaction.
 class ConnectedHandler {
  public:
-  ConnectedHandler() = default;
+  ConnectedHandler();
+  ~ConnectedHandler();
 
-  ConnectedHandler(const ConnectedHandler&) = default;
-  ConnectedHandler& operator=(const ConnectedHandler&) = default;
-  ConnectedHandler(ConnectedHandler&&) = default;
-  ConnectedHandler& operator=(ConnectedHandler&&) = default;
+  // Instances of this class are copyable and efficiently movable.
+  // WARNING: Do not move an instance to which a callback is bound.
+  ConnectedHandler(const ConnectedHandler&);
+  ConnectedHandler& operator=(const ConnectedHandler&);
+  ConnectedHandler(ConnectedHandler&&);
+  ConnectedHandler& operator=(ConnectedHandler&&);
 
   // Returns a callback bound to this->OnConnected().
   // The returned callback must not outlive this instance.
@@ -382,19 +393,18 @@
   }
 
   // Compatible with HttpTransaction::ConnectedCallback.
-  int OnConnected() {
-    call_count_++;
-    return result_;
-  }
+  // Returns the last value passed to set_result(), if any, OK otherwise.
+  int OnConnected(const TransportInfo& info);
 
-  // Returns the number of times OnConnected() was called.
-  int call_count() const { return call_count_; }
+  // Returns the list of arguments with which OnConnected() was called.
+  // The arguments are listed in the same order as the calls were received.
+  const std::vector<TransportInfo>& transports() const { return transports_; }
 
   // Sets the value to be returned by subsequent calls to OnConnected().
   void set_result(int result) { result_ = result; }
 
  private:
-  int call_count_ = 0;
+  std::vector<TransportInfo> transports_;
   int result_ = OK;
 };
 
diff --git a/net/quic/quic_flags_list.h b/net/quic/quic_flags_list.h
index 3834c10c..398b0e5 100644
--- a/net/quic/quic_flags_list.h
+++ b/net/quic/quic_flags_list.h
@@ -447,3 +447,6 @@
 QUIC_FLAG(bool,
           FLAGS_quic_reloadable_flag_quic_enable_overshooting_detection,
           false)
+
+// If true, enable QUIC version h3-T051.
+QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_enable_version_t051, true)
diff --git a/net/quic/quic_http_stream.cc b/net/quic/quic_http_stream.cc
index 1b03ac6..6318e43 100644
--- a/net/quic/quic_http_stream.cc
+++ b/net/quic/quic_http_stream.cc
@@ -108,6 +108,8 @@
       return HttpResponseInfo::CONNECTION_INFO_QUIC_DRAFT_29;
     case quic::QUIC_VERSION_RESERVED_FOR_NEGOTIATION:
       return HttpResponseInfo::CONNECTION_INFO_QUIC_999;
+    case quic::QUIC_VERSION_51:
+      return HttpResponseInfo::CONNECTION_INFO_QUIC_T051;
   }
   NOTREACHED();
   return HttpResponseInfo::CONNECTION_INFO_QUIC_UNKNOWN_VERSION;
diff --git a/net/third_party/quiche/BUILD.gn b/net/third_party/quiche/BUILD.gn
index a715ea8..836245d 100644
--- a/net/third_party/quiche/BUILD.gn
+++ b/net/third_party/quiche/BUILD.gn
@@ -652,7 +652,7 @@
   extra_configs = [ "//build/config/compiler:wexit_time_destructors" ]
 }
 
-if (is_linux) {
+if (is_linux || is_chromeos) {
   source_set("epoll_server_core") {
     sources = [
       "src/epoll_server/platform/api/epoll_bug.h",
@@ -984,7 +984,7 @@
     "//third_party/quic_trace:quic_trace_proto",
   ]
 
-  if (is_linux) {
+  if (is_linux || is_chromeos) {
     sources += [
       "src/epoll_server/fake_simple_epoll_server.cc",
       "src/epoll_server/fake_simple_epoll_server.h",
@@ -1450,7 +1450,7 @@
   if (is_desktop_linux) {
     public_deps += [ "//net:epoll_quic_tools" ]
   }
-  if (is_linux) {
+  if (is_linux || is_chromeos) {
     sources += [
       "src/epoll_server/simple_epoll_server_test.cc",
       "src/quic/core/chlo_extractor_test.cc",
diff --git a/net/url_request/url_request.cc b/net/url_request/url_request.cc
index bc4646f..d96b342 100644
--- a/net/url_request/url_request.cc
+++ b/net/url_request/url_request.cc
@@ -124,7 +124,8 @@
 ///////////////////////////////////////////////////////////////////////////////
 // URLRequest::Delegate
 
-int URLRequest::Delegate::OnConnected(URLRequest* request) {
+int URLRequest::Delegate::OnConnected(URLRequest* request,
+                                      const TransportInfo& info) {
   return OK;
 }
 
@@ -773,8 +774,8 @@
   return (status_ != OK && status_ != ERR_IO_PENDING);
 }
 
-int URLRequest::NotifyConnected() {
-  return delegate_->OnConnected(this);
+int URLRequest::NotifyConnected(const TransportInfo& info) {
+  return delegate_->OnConnected(this, info);
 }
 
 void URLRequest::NotifyReceivedRedirect(const RedirectInfo& redirect_info,
diff --git a/net/url_request/url_request.h b/net/url_request/url_request.h
index 765ecc2..b139941 100644
--- a/net/url_request/url_request.h
+++ b/net/url_request/url_request.h
@@ -60,6 +60,7 @@
 class SSLCertRequestInfo;
 class SSLInfo;
 class SSLPrivateKey;
+struct TransportInfo;
 class UploadDataStream;
 class URLRequestContext;
 class URLRequestJob;
@@ -116,21 +117,22 @@
    public:
     // Called each time a connection is obtained, before any data is sent.
     //
+    // |request| is never nullptr. Caller retains ownership.
+    //
+    // |info| describes the newly-obtained connection.
+    //
     // This may be called several times if the request creates multiple HTTP
     // transactions, e.g. if the request is redirected. It may also be called
     // several times per transaction, e.g. if the connection is retried, after
     // each HTTP auth challenge, or for split HTTP range requests.
     //
-    // The delegate may call request->GetTransactionRemoteEndpoint() to
-    // determine where the latest connection terminates.
-    //
     // If this returns an error, the request fails with the given error.
     // Otherwise the request continues unimpeded.
     // Must not return ERR_IO_PENDING.
     //
     // TODO(crbug.com/591068): Allow ERR_IO_PENDING for a potentially-slow
     // CORS-RFC1918 preflight check.
-    virtual int OnConnected(URLRequest* request);
+    virtual int OnConnected(URLRequest* request, const TransportInfo& info);
 
     // Called upon receiving a redirect.  The delegate may call the request's
     // Cancel method to prevent the redirect from being followed.  Since there
@@ -789,7 +791,7 @@
 
   // These functions delegate to |delegate_|.  See URLRequest::Delegate for the
   // meaning of these functions.
-  int NotifyConnected();
+  int NotifyConnected(const TransportInfo& info);
   void NotifyAuthRequired(std::unique_ptr<AuthChallengeInfo> auth_info);
   void NotifyCertificateRequested(SSLCertRequestInfo* cert_request_info);
   void NotifySSLCertificateError(int net_error,
diff --git a/net/url_request/url_request_http_job.cc b/net/url_request/url_request_http_job.cc
index c69bc7c9..0f66b6e 100644
--- a/net/url_request/url_request_http_job.cc
+++ b/net/url_request/url_request_http_job.cc
@@ -362,8 +362,8 @@
     out->clear();
 }
 
-int URLRequestHttpJob::NotifyConnectedCallback() {
-  return URLRequestJob::NotifyConnected();
+int URLRequestHttpJob::NotifyConnectedCallback(const TransportInfo& info) {
+  return URLRequestJob::NotifyConnected(info);
 }
 
 void URLRequestHttpJob::NotifyHeadersComplete() {
diff --git a/net/url_request/url_request_http_job.h b/net/url_request/url_request_http_job.h
index 8ddb724..ef89c70 100644
--- a/net/url_request/url_request_http_job.h
+++ b/net/url_request/url_request_http_job.h
@@ -36,6 +36,7 @@
 class HttpTransaction;
 class HttpUserAgentSettings;
 class SSLPrivateKey;
+struct TransportInfo;
 class UploadDataStream;
 
 // A URLRequestJob subclass that is built on top of HttpTransaction. It
@@ -122,7 +123,7 @@
   // This just forwards the call to URLRequestJob::NotifyConnected().
   // We need it because that method is protected and cannot be bound in a
   // callback in this class.
-  int NotifyConnectedCallback();
+  int NotifyConnectedCallback(const TransportInfo& info);
 
   void RestartTransactionWithAuth(const AuthCredentials& credentials);
 
diff --git a/net/url_request/url_request_job.cc b/net/url_request/url_request_job.cc
index e1e4d408..88d9a007 100644
--- a/net/url_request/url_request_job.cc
+++ b/net/url_request/url_request_job.cc
@@ -385,8 +385,8 @@
   return GURL();
 }
 
-int URLRequestJob::NotifyConnected() {
-  return request_->NotifyConnected();
+int URLRequestJob::NotifyConnected(const TransportInfo& info) {
+  return request_->NotifyConnected(info);
 }
 
 void URLRequestJob::NotifyCertificateRequested(
diff --git a/net/url_request/url_request_job.h b/net/url_request/url_request_job.h
index 7e34fff..b3f9216 100644
--- a/net/url_request/url_request_job.h
+++ b/net/url_request/url_request_job.h
@@ -45,6 +45,7 @@
 class SSLCertRequestInfo;
 class SSLInfo;
 class SSLPrivateKey;
+struct TransportInfo;
 class UploadDataStream;
 class X509Certificate;
 
@@ -257,7 +258,7 @@
 
  protected:
   // Notifies the job that we are connected.
-  int NotifyConnected();
+  int NotifyConnected(const TransportInfo& info);
 
   // Notifies the job that a certificate is requested.
   void NotifyCertificateRequested(SSLCertRequestInfo* cert_request_info);
diff --git a/net/url_request/url_request_job_unittest.cc b/net/url_request/url_request_job_unittest.cc
index 7db8a73..d4b985c 100644
--- a/net/url_request/url_request_job_unittest.cc
+++ b/net/url_request/url_request_job_unittest.cc
@@ -107,6 +107,7 @@
     base::Time(),
     "",
     LOAD_NORMAL,
+    DefaultTransportInfo(),
     "HTTP/1.1 200 OK",
     "Cache-Control: max-age=10000\n"
     "Content-Length: 30\n",  // Intentionally wrong.
@@ -127,6 +128,7 @@
     base::Time(),
     "",
     LOAD_NORMAL,
+    DefaultTransportInfo(),
     "HTTP/1.1 200 OK",
     "Cache-Control: max-age=10000\n"
     "Content-Length: +30\n",  // Invalid
@@ -147,6 +149,7 @@
     base::Time(),
     "",
     LOAD_NORMAL,
+    DefaultTransportInfo(),
     "HTTP/1.1 200 OK",
     "Cache-Control: max-age=10000\n"
     "Content-Encoding: gzip\n"
@@ -169,6 +172,7 @@
     base::Time(),
     "",
     LOAD_NORMAL,
+    DefaultTransportInfo(),
     "HTTP/1.1 200 OK",
     "Cache-Control: max-age=10000\n"
     "Content-Encoding: gzip\n",
@@ -190,6 +194,7 @@
     base::Time(),
     "",
     LOAD_NORMAL,
+    DefaultTransportInfo(),
     "HTTP/1.1 302 Found",
     "Cache-Control: max-age=10000\n"
     "Location: http://www.google.com/destination\n"
@@ -212,6 +217,7 @@
     base::Time(),
     "",
     LOAD_NORMAL,
+    DefaultTransportInfo(),
     "HTTP/1.1 200 OK",
     "Content-Encoding: gzip\n",
     base::Time(),
@@ -232,6 +238,7 @@
     base::Time(),
     "",
     LOAD_NORMAL,
+    DefaultTransportInfo(),
     "HTTP/1.1 200 OK",
     "Content-Encoding: gzip\n"
     "Content-Length: 21\n",
@@ -253,6 +260,7 @@
     base::Time(),
     "",
     LOAD_NORMAL,
+    DefaultTransportInfo(),
     "HTTP/1.1 200 OK",
     "Cache-Control: max-age=10000\n"
     "Content-Encoding: br\n"
diff --git a/net/url_request/url_request_test_util.cc b/net/url_request/url_request_test_util.cc
index 5b2da3c..beaf7acc 100644
--- a/net/url_request/url_request_test_util.cc
+++ b/net/url_request/url_request_test_util.cc
@@ -247,8 +247,8 @@
   run_loop.Run();
 }
 
-int TestDelegate::OnConnected(URLRequest* request) {
-  connected_count_++;
+int TestDelegate::OnConnected(URLRequest* request, const TransportInfo& info) {
+  transports_.push_back(info);
   return on_connected_result_;
 }
 
diff --git a/net/url_request/url_request_test_util.h b/net/url_request/url_request_test_util.h
index 932b3e0f..2b4d68fe 100644
--- a/net/url_request/url_request_test_util.h
+++ b/net/url_request/url_request_test_util.h
@@ -12,6 +12,7 @@
 #include <memory>
 #include <string>
 #include <utility>
+#include <vector>
 
 #include "base/compiler_specific.h"
 #include "base/memory/ref_counted.h"
@@ -27,6 +28,7 @@
 #include "net/base/net_errors.h"
 #include "net/base/network_delegate_impl.h"
 #include "net/base/request_priority.h"
+#include "net/base/transport_info.h"
 #include "net/cert/cert_verifier.h"
 #include "net/cert/ct_policy_enforcer.h"
 #include "net/cookies/cookie_monster.h"
@@ -167,7 +169,9 @@
     on_complete_ = std::move(on_complete);
   }
 
+  // Sets the result returned by subsequent calls to OnConnected().
   void set_on_connected_result(int result) { on_connected_result_ = result; }
+
   void set_cancel_in_received_redirect(bool val) { cancel_in_rr_ = val; }
   void set_cancel_in_response_started(bool val) { cancel_in_rs_ = val; }
   void set_cancel_in_received_data(bool val) { cancel_in_rd_ = val; }
@@ -182,8 +186,11 @@
     credentials_ = credentials;
   }
 
+  // Returns the list of arguments with which OnConnected() was called.
+  // The arguments are listed in the same order as the calls were received.
+  const std::vector<TransportInfo>& transports() const { return transports_; }
+
   // query state
-  int connected_count() const { return connected_count_; }
   const std::string& data_received() const { return data_received_; }
   int bytes_received() const { return static_cast<int>(data_received_.size()); }
   int response_started_count() const { return response_started_count_; }
@@ -204,7 +211,7 @@
   int request_status() const { return request_status_; }
 
   // URLRequest::Delegate:
-  int OnConnected(URLRequest*) override;
+  int OnConnected(URLRequest* request, const TransportInfo& info) override;
   void OnReceivedRedirect(URLRequest* request,
                           const RedirectInfo& redirect_info,
                           bool* defer_redirect) override;
@@ -244,7 +251,7 @@
   base::OnceClosure on_auth_required_;
 
   // tracks status of callbacks
-  int connected_count_ = 0;
+  std::vector<TransportInfo> transports_;
   int response_started_count_ = 0;
   int received_bytes_count_ = 0;
   int received_redirect_count_ = 0;
diff --git a/net/url_request/url_request_unittest.cc b/net/url_request/url_request_unittest.cc
index e7426b40..0e83d22d 100644
--- a/net/url_request/url_request_unittest.cc
+++ b/net/url_request/url_request_unittest.cc
@@ -57,6 +57,8 @@
 #include "net/base/escape.h"
 #include "net/base/features.h"
 #include "net/base/hash_value.h"
+#include "net/base/ip_address.h"
+#include "net/base/ip_endpoint.h"
 #include "net/base/isolation_info.h"
 #include "net/base/load_flags.h"
 #include "net/base/load_timing_info.h"
@@ -66,6 +68,7 @@
 #include "net/base/proxy_server.h"
 #include "net/base/request_priority.h"
 #include "net/base/test_completion_callback.h"
+#include "net/base/transport_info.h"
 #include "net/base/upload_bytes_element_reader.h"
 #include "net/base/upload_data_stream.h"
 #include "net/base/upload_file_element_reader.h"
@@ -166,6 +169,8 @@
 using net::test::IsOk;
 using net::test_server::RegisterDefaultHandlers;
 using testing::AnyOf;
+using testing::ElementsAre;
+using testing::IsEmpty;
 
 using base::ASCIIToUTF16;
 using base::Time;
@@ -1485,7 +1490,7 @@
   request->Start();
   delegate.RunUntilComplete();
 
-  EXPECT_EQ(delegate.connected_count(), 0);
+  EXPECT_THAT(delegate.transports(), IsEmpty());
 }
 
 // This test verifies that URLRequest::Delegate's OnConnected() callback
@@ -1503,7 +1508,10 @@
   request->Start();
   delegate.RunUntilComplete();
 
-  EXPECT_EQ(delegate.connected_count(), 1);
+  TransportInfo expected_transport;
+  expected_transport.endpoint =
+      IPEndPoint(IPAddress::IPv4Localhost(), test_server.port());
+  EXPECT_THAT(delegate.transports(), ElementsAre(expected_transport));
 }
 
 // This test verifies that URLRequest::Delegate's OnConnected() callback is
@@ -1523,13 +1531,17 @@
   request->Start();
   delegate.RunUntilRedirect();
 
-  EXPECT_EQ(delegate.connected_count(), 1);
+  TransportInfo expected_transport;
+  expected_transport.endpoint =
+      IPEndPoint(IPAddress::IPv4Localhost(), test_server.port());
+  EXPECT_THAT(delegate.transports(), ElementsAre(expected_transport));
 
   request->FollowDeferredRedirect(/*removed_headers=*/{},
                                   /*modified_headers=*/{});
   delegate.RunUntilComplete();
 
-  EXPECT_EQ(delegate.connected_count(), 2);
+  EXPECT_THAT(delegate.transports(),
+              ElementsAre(expected_transport, expected_transport));
 }
 
 // This test verifies that when the URLRequest Delegate returns an error from
@@ -1548,7 +1560,11 @@
   request->Start();
   delegate.RunUntilComplete();
 
-  EXPECT_EQ(delegate.connected_count(), 1);
+  TransportInfo expected_transport;
+  expected_transport.endpoint =
+      IPEndPoint(IPAddress::IPv4Localhost(), test_server.port());
+  EXPECT_THAT(delegate.transports(), ElementsAre(expected_transport));
+
   EXPECT_TRUE(delegate.request_failed());
   EXPECT_THAT(delegate.request_status(), IsError(ERR_NOT_IMPLEMENTED));
 }
diff --git a/pdf/BUILD.gn b/pdf/BUILD.gn
index abbe76a..04fffbb 100644
--- a/pdf/BUILD.gn
+++ b/pdf/BUILD.gn
@@ -149,7 +149,7 @@
       "//ui/gfx/range",
     ]
 
-    if (is_linux) {
+    if (is_linux || is_chromeos) {
       sources += [
         "pdfium/pdfium_font_linux.cc",
         "pdfium/pdfium_font_linux.h",
diff --git a/ppapi/BUILD.gn b/ppapi/BUILD.gn
index ad1525f1..8d0cac67 100644
--- a/ppapi/BUILD.gn
+++ b/ppapi/BUILD.gn
@@ -457,7 +457,7 @@
           [ ":ppapi_nacl_tests(//build/toolchain/nacl:glibc_${target_cpu})" ]
     }
 
-    if (is_linux && enable_nacl_nonsfi) {
+    if ((is_linux || is_chromeos) && enable_nacl_nonsfi) {
       data_deps +=
           [ ":ppapi_nacl_tests(//build/toolchain/nacl:newlib_pnacl_nonsfi)" ]
     }
diff --git a/ppapi/tests/extensions/BUILD.gn b/ppapi/tests/extensions/BUILD.gn
index 2a6342f1..1abf250 100644
--- a/ppapi/tests/extensions/BUILD.gn
+++ b/ppapi/tests/extensions/BUILD.gn
@@ -19,7 +19,7 @@
     ":ppapi_tests_extensions_popup($newlib)",
     ":ppapi_tests_extensions_socket_permissions($newlib)",
   ]
-  if ((target_cpu == "x86" || target_cpu == "x64") && is_linux &&
+  if ((target_cpu == "x86" || target_cpu == "x64") && (is_linux || is_chromeos) &&
       enable_nacl_nonsfi) {
     nonsfi = "//build/toolchain/nacl:newlib_pnacl_nonsfi"
     data_deps += [ ":ppapi_tests_extensions_packaged_app($nonsfi)" ]
diff --git a/printing/BUILD.gn b/printing/BUILD.gn
index 446773a..766501a 100644
--- a/printing/BUILD.gn
+++ b/printing/BUILD.gn
@@ -206,7 +206,7 @@
   if (use_cups) {
     configs += [ ":cups" ]
 
-    if (is_linux) {
+    if (is_linux || is_chromeos) {
       # CUPS 1.6 deprecated the PPD APIs, but we will stay with this API
       # for now as the suitability of the replacement is unclear.
       # More info: crbug.com/226176
@@ -312,7 +312,7 @@
   if (is_fuchsia) {
     sources += [ "image_fuchsia.cc" ]
   }
-  if (is_linux) {
+  if (is_linux || is_chromeos) {
     sources += [ "image_linux.cc" ]
   }
   if (is_mac) {
diff --git a/printing/buildflags/buildflags.gni b/printing/buildflags/buildflags.gni
index ed471184..bb9dabfc 100644
--- a/printing/buildflags/buildflags.gni
+++ b/printing/buildflags/buildflags.gni
@@ -20,7 +20,7 @@
   # Enable exporting to tagged PDF.
   enable_tagged_pdf = !is_android && !is_chromecast && !is_ios && !is_fuchsia
 
-  if (use_fuzzing_engine && is_linux) {
+  if (use_fuzzing_engine && (is_linux || is_chromeos)) {
     # For fuzzing, just restrict to chromeos and linux.
     use_cups = true
   } else {
diff --git a/remoting/client/display/BUILD.gn b/remoting/client/display/BUILD.gn
index 0897284..a0c8de3 100644
--- a/remoting/client/display/BUILD.gn
+++ b/remoting/client/display/BUILD.gn
@@ -43,7 +43,7 @@
     libs = [ "GLESv2" ]
   }
 
-  if (is_linux) {
+  if (is_linux || is_chromeos) {
     libs = [ "GL" ]
   }
 
diff --git a/remoting/codec/BUILD.gn b/remoting/codec/BUILD.gn
index f6ff1ab..b1a76d5 100644
--- a/remoting/codec/BUILD.gn
+++ b/remoting/codec/BUILD.gn
@@ -41,7 +41,7 @@
 
   # Currently, building WebrtcVideoEncoderGpu is only supported on Windows and
   # Linux, and encoding with WebrtcVideoEncoderGpu is only supported on Windows.
-  if (is_win || is_linux) {
+  if (is_win || is_linux || is_chromeos) {
     public_configs = [ "//skia:skia_config" ]
     sources += [
       "webrtc_video_encoder_gpu.cc",
diff --git a/remoting/protocol/BUILD.gn b/remoting/protocol/BUILD.gn
index 5fd981cc..fdd9be64 100644
--- a/remoting/protocol/BUILD.gn
+++ b/remoting/protocol/BUILD.gn
@@ -237,7 +237,7 @@
   defines = []
 
   # Must match condition in //remoting/codec/BUILD.gn
-  if (is_win || is_linux) {
+  if (is_win || is_linux || is_chromeos) {
     defines += [ "USE_H264_ENCODER" ]
     deps += [
       "//media",
diff --git a/remoting/remoting_options.gni b/remoting/remoting_options.gni
index 7cd564c..b7e9fa6 100644
--- a/remoting/remoting_options.gni
+++ b/remoting/remoting_options.gni
@@ -6,7 +6,7 @@
 import("//build/config/ui.gni")
 
 enable_remoting_host =
-    is_win || (is_linux && (is_chromeos || use_x11)) || is_mac
+    is_win || (is_chromeos || (is_linux && use_x11)) || is_mac
 enable_me2me_host = is_win || (is_linux && !is_chromeos && use_x11) || is_mac
 
 # Enable the multi-process host on Windows by default.
diff --git a/services/tracing/perfetto/perfetto_integration_unittest.cc b/services/tracing/perfetto/perfetto_integration_unittest.cc
index a6e6518b..c5b00b31 100644
--- a/services/tracing/perfetto/perfetto_integration_unittest.cc
+++ b/services/tracing/perfetto/perfetto_integration_unittest.cc
@@ -42,9 +42,21 @@
   return base::StrCat({mojom::kPerfettoProducerNamePrefix, "123"});
 }
 
+RebindableTaskRunner* GetPerfettoTaskRunner() {
+  static base::NoDestructor<RebindableTaskRunner> task_runner;
+  return task_runner.get();
+}
+
 class PerfettoIntegrationTest : public testing::Test {
  public:
   void SetUp() override {
+    auto* perfetto_task_runner = GetPerfettoTaskRunner();
+    auto* perfetto_platform =
+        PerfettoTracedProcess::Get()->perfetto_platform_for_testing();
+    if (!perfetto_platform->did_start_task_runner())
+      perfetto_platform->StartTaskRunner(perfetto_task_runner);
+    perfetto_task_runner->set_task_runner(base::ThreadTaskRunnerHandle::Get());
+
     PerfettoTracedProcess::ResetTaskRunnerForTesting();
     PerfettoTracedProcess::Get()->ClearDataSourcesForTesting();
     data_source_ = TestDataSource::CreateAndRegisterDataSource(
diff --git a/services/tracing/perfetto/test_utils.cc b/services/tracing/perfetto/test_utils.cc
index a8366d5..074b557d 100644
--- a/services/tracing/perfetto/test_utils.cc
+++ b/services/tracing/perfetto/test_utils.cc
@@ -410,6 +410,9 @@
 
 MockProducer::~MockProducer() = default;
 
+RebindableTaskRunner::RebindableTaskRunner() = default;
+RebindableTaskRunner::~RebindableTaskRunner() = default;
+
 void MockProducer::WritePacketBigly(base::OnceClosure on_write_complete) {
   PerfettoTracedProcess::Get()
       ->GetTaskRunner()
@@ -420,4 +423,22 @@
                          std::move(on_write_complete));
 }
 
+bool RebindableTaskRunner::PostDelayedTask(const base::Location& from_here,
+                                           base::OnceClosure task,
+                                           base::TimeDelta delay) {
+  return task_runner_->PostDelayedTask(from_here, std::move(task), delay);
+}
+
+bool RebindableTaskRunner::PostNonNestableDelayedTask(
+    const base::Location& from_here,
+    base::OnceClosure task,
+    base::TimeDelta delay) {
+  return task_runner_->PostNonNestableDelayedTask(from_here, std::move(task),
+                                                  delay);
+}
+
+bool RebindableTaskRunner::RunsTasksInCurrentSequence() const {
+  return task_runner_->RunsTasksInCurrentSequence();
+}
+
 }  // namespace tracing
diff --git a/services/tracing/perfetto/test_utils.h b/services/tracing/perfetto/test_utils.h
index c33d41c7..7963a76 100644
--- a/services/tracing/perfetto/test_utils.h
+++ b/services/tracing/perfetto/test_utils.h
@@ -226,6 +226,31 @@
   std::unique_ptr<MockProducerHost> producer_host_;
 };
 
+// A proxy task runner which can be dynamically pointed to route tasks into a
+// different task runner.
+class RebindableTaskRunner : public base::SequencedTaskRunner {
+ public:
+  RebindableTaskRunner();
+
+  void set_task_runner(scoped_refptr<base::SequencedTaskRunner> task_runner) {
+    task_runner_ = task_runner;
+  }
+
+  // base::SequecedTaskRunner implementation.
+  bool PostDelayedTask(const base::Location& from_here,
+                       base::OnceClosure task,
+                       base::TimeDelta delay) override;
+  bool PostNonNestableDelayedTask(const base::Location& from_here,
+                                  base::OnceClosure task,
+                                  base::TimeDelta delay) override;
+  bool RunsTasksInCurrentSequence() const override;
+
+ private:
+  ~RebindableTaskRunner() override;
+
+  scoped_refptr<base::SequencedTaskRunner> task_runner_;
+};
+
 }  // namespace tracing
 
 #endif  // SERVICES_TRACING_PERFETTO_TEST_UTILS_H_
diff --git a/services/tracing/tracing_service_unittest.cc b/services/tracing/tracing_service_unittest.cc
index 07f7729..6e5cb97 100644
--- a/services/tracing/tracing_service_unittest.cc
+++ b/services/tracing/tracing_service_unittest.cc
@@ -41,40 +41,8 @@
 namespace tracing {
 namespace {
 
-// A task runner which can be dynamically redirected to a different task runner.
-class ProxyTaskRunner : public base::SequencedTaskRunner {
- public:
-  ProxyTaskRunner() = default;
-
-  void set_task_runner(scoped_refptr<base::SequencedTaskRunner> task_runner) {
-    task_runner_ = task_runner;
-  }
-
-  bool PostDelayedTask(const base::Location& from_here,
-                       base::OnceClosure task,
-                       base::TimeDelta delay) override {
-    return task_runner_->PostDelayedTask(from_here, std::move(task), delay);
-  }
-
-  bool PostNonNestableDelayedTask(const base::Location& from_here,
-                                  base::OnceClosure task,
-                                  base::TimeDelta delay) override {
-    return task_runner_->PostNonNestableDelayedTask(from_here, std::move(task),
-                                                    delay);
-  }
-
-  bool RunsTasksInCurrentSequence() const override {
-    return task_runner_->RunsTasksInCurrentSequence();
-  }
-
- private:
-  ~ProxyTaskRunner() override = default;
-
-  scoped_refptr<base::SequencedTaskRunner> task_runner_;
-};
-
-ProxyTaskRunner* GetProxyTaskRunner() {
-  static base::NoDestructor<ProxyTaskRunner> task_runner;
+RebindableTaskRunner* GetPerfettoTaskRunner() {
+  static base::NoDestructor<RebindableTaskRunner> task_runner;
   return task_runner.get();
 }
 
@@ -86,12 +54,12 @@
     // Since Perfetto's platform backend can only be initialized once in a
     // process, we give it a task runner that can outlive the per-test task
     // environment.
-    auto* proxy_task_runner = GetProxyTaskRunner();
+    auto* perfetto_task_runner = GetPerfettoTaskRunner();
     auto* perfetto_platform =
         PerfettoTracedProcess::Get()->perfetto_platform_for_testing();
     if (!perfetto_platform->did_start_task_runner())
-      perfetto_platform->StartTaskRunner(proxy_task_runner);
-    proxy_task_runner->set_task_runner(base::ThreadTaskRunnerHandle::Get());
+      perfetto_platform->StartTaskRunner(perfetto_task_runner);
+    perfetto_task_runner->set_task_runner(base::ThreadTaskRunnerHandle::Get());
 
     // Also tell PerfettoTracedProcess to use the current task environment.
     PerfettoTracedProcess::ResetTaskRunnerForTesting(
diff --git a/styleguide/c++/c++11.html b/styleguide/c++/c++11.html
index 6a91c4f..633c649 100644
--- a/styleguide/c++/c++11.html
+++ b/styleguide/c++/c++11.html
@@ -1,4 +1,4 @@
-<!DOCTYPE html>
+<!DOCTYPE html>
 <!--
 Copyright 2014 The Chromium Authors. All rights reserved.
 Use of this source code is governed by a BSD-style license that can be
@@ -51,7 +51,7 @@
 <li><b>C++14:</b> <i>Default allowed; see banned features below</i></li>
 <li><b>C++17:</b> <i>Not yet supported in Chromium, unlikely before mid-2021; <a href="http://crbug.com/752720">tracking bug</a></i></li>
 <li><b>C++20:</b> <i>Not yet standardized</i></li>
-<li><b>Abseil:</b> Initially supported July 27, 2020; see allowed/banned/TBD features below</li>
+<li><b>Abseil:</b> Initially supported July 31, 2020; see allowed/banned/TBD features below</li>
 </ul></p>
 
 
@@ -290,7 +290,11 @@
 </tr>
 
 <tr>
-<td colspan="5" style="text-align:center">TBD</td>
+<td>Variant</td>
+<td><code>absl::variant</code></td>
+<td>Early adaptation of C++17 std::variant.</td>
+<td><a href="https://en.cppreference.com/w/cpp/utility/variant">std::variant</a></td>
+<td><a href="https://groups.google.com/a/chromium.org/g/cxx/c/DqvG-TpvMyU">Discussion thread</a></td>
 </tr>
 
 </tbody>
@@ -469,14 +473,6 @@
 <td>Overlaps with <code>Time</code> APIs in <code>base/</code>.</td>
 </tr>
 
-<tr>
-<td>Variant</td>
-<td><code>absl::variant</code></td>
-<td>Early adaptation of C++17 std::variant.</td>
-<td><a href="https://en.cppreference.com/w/cpp/utility/variant">std::variant</a></td>
-<td></td>
-</tr>
-
 </tbody>
 </table>
 
diff --git a/testing/scripts/representative_perf_test_data/representatives_frame_times_upper_limit.json b/testing/scripts/representative_perf_test_data/representatives_frame_times_upper_limit.json
index 3f09038..91466a0 100644
--- a/testing/scripts/representative_perf_test_data/representatives_frame_times_upper_limit.json
+++ b/testing/scripts/representative_perf_test_data/representatives_frame_times_upper_limit.json
@@ -7,10 +7,8 @@
     },
     "filter_terrain_svg": {
       "ci_095": 0.574,
-      "_comment": "crbug.com/1053614",
       "avg": 33.202,
-      "cpu_wall_time_ratio": 0.368,
-      "experimental": true
+      "cpu_wall_time_ratio": 0.368
     },
     "web_animation_value_type_transform_complex": {
       "ci_095": 1.704,
@@ -70,10 +68,8 @@
     },
     "css_value_type_shadow": {
       "ci_095": 1.482,
-      "_comment": "crbug.com/1093313",
       "avg": 56.704,
-      "cpu_wall_time_ratio": 0.414,
-      "experimental": true
+      "cpu_wall_time_ratio": 0.414
     },
     "nvidia_vertex_buffer_object": {
       "ci_095": 3.605,
@@ -109,10 +105,8 @@
     },
     "css_value_type_shadow": {
       "ci_095": 18.3,
-      "_comment": "crbug.com/1093313",
       "avg": 65.502,
-      "cpu_wall_time_ratio": 0.585,
-      "experimental": true
+      "cpu_wall_time_ratio": 0.585
     },
     "animometer_webgl_attrib_arrays": {
       "ci_095": 0.44,
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 8a26f83..e21867f 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -1225,6 +1225,46 @@
             ]
         }
     ],
+    "BrowserVerifiedUserActivationKeyboardTrigger": [
+        {
+            "platforms": [
+                "windows",
+                "mac",
+                "chromeos",
+                "linux",
+                "android",
+                "android_weblayer"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "BrowserVerifiedUserActivationKeyboard"
+                    ]
+                }
+            ]
+        }
+    ],
+    "BrowserVerifiedUserActivationMouseTrigger": [
+        {
+            "platforms": [
+                "windows",
+                "mac",
+                "chromeos",
+                "linux",
+                "android",
+                "android_weblayer"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "BrowserVerifiedUserActivationMouse"
+                    ]
+                }
+            ]
+        }
+    ],
     "CSSMatchedPropertiesCacheDependencies": [
         {
             "platforms": [
@@ -1389,6 +1429,21 @@
             ]
         }
     ],
+    "CertVerifierBuiltin": [
+        {
+            "platforms": [
+                "mac"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "CertVerifierBuiltin"
+                    ]
+                }
+            ]
+        }
+    ],
     "ChromeCleanupDistribution": [
         {
             "platforms": [
diff --git a/third_party/abseil-cpp/BUILD.gn b/third_party/abseil-cpp/BUILD.gn
index 0373bff..dffe05f3 100644
--- a/third_party/abseil-cpp/BUILD.gn
+++ b/third_party/abseil-cpp/BUILD.gn
@@ -53,14 +53,6 @@
       }
     }
   }
-
-  # Usage of Abseil in Chromium is guarded by an explicit opt-in list, before
-  # adding projects to this list please reach out to cxx@chromium.org and CC:
-  # - https://cs.chromium.org/chromium/src/third_party/abseil-cpp/OWNERS
-  #
-  # More information can be found at:
-  # https://docs.google.com/document/d/1DgS1-A3rzboTLjpf4m1sqkJgWjnY_Ru2dokk1X1vBDU
-  visibility = absl_visibility
 }
 
 group("absl_component_deps") {
@@ -69,8 +61,8 @@
     "//third_party/abseil-cpp/absl/base",
     "//third_party/abseil-cpp/absl/base:config",
     "//third_party/abseil-cpp/absl/base:core_headers",
-    "//third_party/abseil-cpp/absl/container:flat_hash_map",
     "//third_party/abseil-cpp/absl/container:fixed_array",
+    "//third_party/abseil-cpp/absl/container:flat_hash_map",
     "//third_party/abseil-cpp/absl/container:flat_hash_set",
     "//third_party/abseil-cpp/absl/container:inlined_vector",
     "//third_party/abseil-cpp/absl/debugging:failure_signal_handler",
@@ -181,12 +173,8 @@
       "absl/strings:match_test",
       "absl/strings:str_replace_test",
       "absl/strings:string_view_test",
-
-      # TODO(mbonadei): On iOS, gtest doesn't support death tests. Fix upstream
-      # Abseil to use EXPECT_DEATH_IF_SUPPORTED instead of EXPECT_DEATH.
-      # "absl/types:optional_test",
-      # "absl/types:variant_test",
-
+      "absl/types:optional_test",
+      "absl/types:variant_test",
       "//third_party/googletest:gtest_main",
     ]
   }
@@ -203,6 +191,7 @@
     "//third_party/abseil-cpp/absl/strings",
     "//third_party/abseil-cpp/absl/types:optional",
     "//third_party/abseil-cpp/absl/types:span",
+    "//third_party/abseil-cpp/absl/types:variant",
     "//third_party/googletest:gtest",
   ]
 }
diff --git a/third_party/abseil-cpp/README.chromium b/third_party/abseil-cpp/README.chromium
index 37dbe88..cb4beff 100644
--- a/third_party/abseil-cpp/README.chromium
+++ b/third_party/abseil-cpp/README.chromium
@@ -9,8 +9,10 @@
 
 Description:
 This directory contains the source code of Abseil for C++. This can be used by
-Chromium's dependencies, but shouldn't be used by Chromium itself.
-See: https://goo.gl/TgnJb8.
+Chromium, subject to the guidance at https://chromium-cpp.appspot.com/; it can
+be used without restriction by Chromium's dependencies, except that objects
+compiled into Chromium itself cannot use anything relying on
+absl::base_internal::FastTypeId (see https://crbug.com/1096380).
 
 How to update Abseil:
 
diff --git a/third_party/abseil-cpp/absl.gni b/third_party/abseil-cpp/absl.gni
index 88ce3aa..dc87e2a6 100644
--- a/third_party/abseil-cpp/absl.gni
+++ b/third_party/abseil-cpp/absl.gni
@@ -7,9 +7,6 @@
 # This template will correctly set "configs" and "public_configs" in order
 # to correctly compile abseil in Chromium.
 #
-# Targets that set visibility should set it to something more restrictive than
-# `absl_visibility` (defined below).
-#
 # Usage:
 # Most of the times its usage will be similar to the example below but all
 # the arguments avilable in source_set are also available for absl_source_set.
@@ -22,35 +19,6 @@
 
 import("//build_overrides/build.gni")
 
-# Usage of Abseil in Chromium is guarded by an explicit opt-in list, before
-# adding projects to this list please reach out to cxx@chromium.org and CC:
-# - https://cs.chromium.org/chromium/src/third_party/abseil-cpp/OWNERS
-#
-# More information can be found at:
-# https://docs.google.com/document/d/1DgS1-A3rzboTLjpf4m1sqkJgWjnY_Ru2dokk1X1vBDU
-declare_args() {
-  # Additional targets that can depend on absl.
-  additional_absl_clients = []
-}
-
-_chromium_absl_clients = [
-  "//chrome/services/sharing/nearby/platform_v2/*",
-  "//third_party/blink/renderer/modules/peerconnection",
-  "//third_party/blink/renderer/platform",
-  "//third_party/googletest:gtest",
-  "//third_party/openscreen/src/third_party/abseil/*",
-  "//third_party/private_membership/*",
-  "//third_party/shell-encryption/*",
-  "//third_party/webrtc/*",
-  "//third_party/nearby/*",
-]
-
-# When adding a new package to `absl_visibility`, please check that all the
-# Abseil dependencies that are required are listed in the dependencies of
-# //third_party/abseil-cpp:absl. If not, please add them and run:
-# //third_party/abseil-cpp/generate_def_file.py.
-absl_visibility = _chromium_absl_clients + additional_absl_clients
-
 template("absl_source_set") {
   source_set(target_name) {
     forward_variables_from(invoker, "*")
@@ -75,8 +43,14 @@
     public_configs += [ "//third_party/abseil-cpp:absl_include_config" ]
 
     if (!defined(visibility)) {
+      # Within Chromium builds, restrict direct visibility of Abseil sources, so
+      # users must depend on //third_party/abseil-cpp:absl. This prevents use of
+      # banned targets like absl/types:any. A few targets require exceptions.
+      # TODO(crbug.com/1096380): Consider replacing build_with_chromium with
+      # is_component_build for a narrower, more accurate condition.
       if (build_with_chromium) {
         visibility = [
+          # Abseil itself.
           "//third_party/abseil-cpp/*",
 
           # WebRTC binary to run PSNR and SSIM video quality analysis. It
@@ -85,7 +59,7 @@
           # component because it uses absl/flags.
           "//third_party/webrtc/rtc_tools:frame_analyzer",
 
-          # WebRTC binaries used by //:chromium_builder_asan, they both
+          # WebRTC binaries used by //:chromium_builder_asan. They both
           # statically link absl (because they depend on absl/flags) and are
           # used by Chromium only when is_component_build=false.
           "//third_party/webrtc/rtc_tools:rtp_generator",
diff --git a/third_party/abseil-cpp/absl/types/BUILD.gn b/third_party/abseil-cpp/absl/types/BUILD.gn
index 40b9d9e6..f9f1515c 100644
--- a/third_party/abseil-cpp/absl/types/BUILD.gn
+++ b/third_party/abseil-cpp/absl/types/BUILD.gn
@@ -98,36 +98,34 @@
     "//third_party/abseil-cpp/absl/meta:type_traits",
   ]
 }
-# TODO(mbonadei): On iOS, gtest doesn't support death tests. Fix upstream
-# Abseil to use EXPECT_DEATH_IF_SUPPORTED instead of EXPECT_DEATH.
-# absl_source_set("optional_test") {
-#   testonly = true
-#   sources = [ "optional_test.cc" ]
-#   deps = [
-#     ":optional",
-#     "//third_party/abseil-cpp/absl/base:config",
-#     "//third_party/abseil-cpp/absl/base:raw_logging_internal",
-#     "//third_party/abseil-cpp/absl/meta:type_traits",
-#     "//third_party/abseil-cpp/absl/strings",
-#     "//third_party/googletest:gtest",
-#   ]
-#   visibility = [ "*" ]
-# }
-#
-# absl_source_set("variant_test") {
-#   testonly = true
-#   sources = [ "variant_test.cc" ]
-#   if (is_clang) {
-#     cflags_cc = [ "-Wno-unused-function" ]
-#   }
-#   deps = [
-#     ":variant",
-#     "//third_party/abseil-cpp/absl/base:config",
-#     "//third_party/abseil-cpp/absl/base:core_headers",
-#     "//third_party/abseil-cpp/absl/memory",
-#     "//third_party/abseil-cpp/absl/meta:type_traits",
-#     "//third_party/abseil-cpp/absl/strings",
-#     "//third_party/googletest:gtest",
-#     "//third_party/googletest:gmock",
-#   ]
-# }
+
+absl_source_set("optional_test") {
+  testonly = true
+  sources = [ "optional_test.cc" ]
+  deps = [
+    ":optional",
+    "//third_party/abseil-cpp/absl/base:config",
+    "//third_party/abseil-cpp/absl/base:raw_logging_internal",
+    "//third_party/abseil-cpp/absl/meta:type_traits",
+    "//third_party/abseil-cpp/absl/strings",
+    "//third_party/googletest:gtest",
+  ]
+}
+
+absl_source_set("variant_test") {
+  testonly = true
+  sources = [ "variant_test.cc" ]
+  if (is_clang) {
+    cflags_cc = [ "-Wno-unused-function" ]
+  }
+  deps = [
+    ":variant",
+    "//third_party/abseil-cpp/absl/base:config",
+    "//third_party/abseil-cpp/absl/base:core_headers",
+    "//third_party/abseil-cpp/absl/memory",
+    "//third_party/abseil-cpp/absl/meta:type_traits",
+    "//third_party/abseil-cpp/absl/strings",
+    "//third_party/googletest:gtest",
+    "//third_party/googletest:gmock",
+  ]
+}
diff --git a/third_party/abseil-cpp/absl_hardening_test.cc b/third_party/abseil-cpp/absl_hardening_test.cc
index 4b280986..cbe8532 100644
--- a/third_party/abseil-cpp/absl_hardening_test.cc
+++ b/third_party/abseil-cpp/absl_hardening_test.cc
@@ -12,6 +12,7 @@
 #include "third_party/abseil-cpp/absl/strings/string_view.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/abseil-cpp/absl/types/span.h"
+#include "third_party/abseil-cpp/absl/types/variant.h"
 
 namespace {
 
@@ -70,4 +71,10 @@
   EXPECT_DEATH_IF_SUPPORTED(absl::MakeConstSpan(&v1[2], &v[0]), "");
 }
 
+TEST(AbslHardeningTest, Variant) {
+  absl::variant<int, std::string> variant = 5;
+  EXPECT_DEATH_IF_SUPPORTED(absl::get<std::string>(variant), "");
+  EXPECT_DEATH_IF_SUPPORTED(absl::get<1>(variant), "");
+}
+
 }  // namespace
diff --git a/third_party/blink/common/features.cc b/third_party/blink/common/features.cc
index 0f3f1bf..7f0ea90 100644
--- a/third_party/blink/common/features.cc
+++ b/third_party/blink/common/features.cc
@@ -228,6 +228,10 @@
 const base::Feature kIntensiveWakeUpThrottling{
     "IntensiveWakeUpThrottling", base::FEATURE_DISABLED_BY_DEFAULT};
 
+// When enabled, timers with timeout=0 are not throttled.
+const base::Feature kOptOutZeroTimeoutTimersFromThrottling{
+    "OptOutZeroTimeoutTimersFromThrottling", base::FEATURE_DISABLED_BY_DEFAULT};
+
 // When enabled, no throttling is applied to a page when it uses WebRTC.
 //
 // This allows a page to use a timer to do video processing on frames. An
diff --git a/third_party/blink/public/common/BUILD.gn b/third_party/blink/public/common/BUILD.gn
index cd0090d..9401ecb 100644
--- a/third_party/blink/public/common/BUILD.gn
+++ b/third_party/blink/public/common/BUILD.gn
@@ -168,13 +168,9 @@
     "sms/sms_receiver_destroyed_reason.h",
     "sms/sms_receiver_outcome.h",
     "switches.h",
-    "tokens/frame_token.h",
-    "tokens/frame_token_mojom_traits.h",
-    "tokens/portal_token.h",
-    "tokens/portal_token_mojom_traits.h",
     "tokens/token_mojom_traits_helper.h",
-    "tokens/worker_tokens.h",
-    "tokens/worker_tokens_mojom_traits.h",
+    "tokens/tokens.h",
+    "tokens/tokens_mojom_traits.h",
     "user_agent/user_agent_metadata.h",
     "web_cache/web_cache_resource_type_stats.h",
     "web_package/signed_exchange_consts.h",
diff --git a/third_party/blink/public/common/features.h b/third_party/blink/public/common/features.h
index 0e033c8..1e2fc23 100644
--- a/third_party/blink/public/common/features.h
+++ b/third_party/blink/public/common/features.h
@@ -61,6 +61,8 @@
 BLINK_COMMON_EXPORT extern const char
     kIntensiveWakeUpThrottling_GracePeriodSeconds_Name[];
 
+BLINK_COMMON_EXPORT extern const base::Feature
+    kOptOutZeroTimeoutTimersFromThrottling;
 BLINK_COMMON_EXPORT extern const base::Feature kOptOutWebRTCFromAllThrottling;
 
 #if BUILDFLAG(RTC_USE_H264) && BUILDFLAG(ENABLE_FFMPEG_VIDEO_DECODERS)
diff --git a/third_party/blink/public/common/tokens/README.md b/third_party/blink/public/common/tokens/README.md
index b847885..c8bd024 100644
--- a/third_party/blink/public/common/tokens/README.md
+++ b/third_party/blink/public/common/tokens/README.md
@@ -12,22 +12,18 @@
 
 ## Adding a new token
 
-Suppose you want to add a new token type, `FooToken`. You would do the following:
+Suppose you want to add a new token type. You would do the following:
 
- - Create a new `foo_token.h` header in [`/third_party/blink/public/common/tokens`](/third_party/blink/public/common/tokens)
-   and define the token using [`util::TokenType<...>`](/base/util/type_safety/token_type.h).
-   See [`/third_party/blink/public/common/tokens/worker_tokens.h`](/third_party/blink/public/common/tokens/worker_tokens.h)
-   for an example.
- - Create a new `foo_token.mojom` defining a Mojo struct for the token type in
-   [`/third_party/blink/public/mojom/tokens`](/third_party/blink/public/mojom/tokens).
-   Be sure to follow the convention that the struct contain a single
-   `base.mojom.UnguessableToken` member named `value`. See
-   [`worker_tokens.mojom`](/third_party/blink/public/mojom/tokens/worker_tokens.mojom) for an example.
- - Create a new `foo_token_mojom_traits.h` header in [`/third_party/blink/public/common/tokens`](/third_party/blink/public/common/tokens)
-   that implements `mojo::StructTraits` serialization. Use the templated
-   [`TokenMojomTraitsHelper<...>`](/third_party/blink/public/common/token_mojom_traits_helper.h)
-   helper class. See [`worker_tokens_mojom_traits.h`](/third_party/blink/public/common/tokens/worker_tokens_mojom_traits.h) for an example.
+ - Add a new C++ token type to
+   [`/third_party/blink/public/common/tokens/tokens.h`](/third_party/blink/public/common/tokens/tokens.h).
+ - Add an equivalent Mojom token type to
+   [`/third_party/blink/public/mojom/tokens/tokens.mojom`](/third_party/blink/public/mojom/tokens/tokens.mojom).
+   Be sure to follow the convention that the struct contains a single
+   `base.mojom.UnguessableToken` member named `value`.
+ - Create a new Mojom traits declaration to
+   [`/third_party/blink/public/common/tokens/tokens_mojom_traits.h`](/third_party/blink/public/common/tokens/tokens_mojom_traits.h).
+   Use the templated [`TokenMojomTraitsHelper<...>`](/third_party/blink/public/common/token_mojom_traits_helper.h) helper class.
  - Update [`mojom/tokens/BUILD.gn`](third_party/blink/public/mojom/tokens/BUILD.gn) and add a new
    typemap definition for the token to the `shared_cpp_typemaps` section.
  - If your token needs to be sent via legacy IPC as well, add the appropriate
-   definition to [`/content/common/content_param_traits.h`](/content/common/content_param_traits.h).
\ No newline at end of file
+   definition to [`/content/common/content_param_traits.h`](/content/common/content_param_traits.h).
diff --git a/third_party/blink/public/common/tokens/frame_token.h b/third_party/blink/public/common/tokens/frame_token.h
deleted file mode 100644
index 70f3023..0000000
--- a/third_party/blink/public/common/tokens/frame_token.h
+++ /dev/null
@@ -1,19 +0,0 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef THIRD_PARTY_BLINK_PUBLIC_COMMON_TOKENS_FRAME_TOKEN_H_
-#define THIRD_PARTY_BLINK_PUBLIC_COMMON_TOKENS_FRAME_TOKEN_H_
-
-#include "base/util/type_safety/token_type.h"
-
-namespace blink {
-
-// Frame token type. Note that this token is type-mapped to its mojom equivalent
-// (defined in third_party/blink/public/mojom/tokens/frame_token.mojom). See
-// frame_token_mojom_traits.h for the StructTraits.
-using FrameToken = util::TokenType<class FrameTokenTypeMarker>;
-
-}  // namespace blink
-
-#endif  // THIRD_PARTY_BLINK_PUBLIC_COMMON_TOKENS_FRAME_TOKEN_H_
diff --git a/third_party/blink/public/common/tokens/frame_token_mojom_traits.h b/third_party/blink/public/common/tokens/frame_token_mojom_traits.h
deleted file mode 100644
index a9974a8..0000000
--- a/third_party/blink/public/common/tokens/frame_token_mojom_traits.h
+++ /dev/null
@@ -1,21 +0,0 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef THIRD_PARTY_BLINK_PUBLIC_COMMON_TOKENS_FRAME_TOKEN_MOJOM_TRAITS_H_
-#define THIRD_PARTY_BLINK_PUBLIC_COMMON_TOKENS_FRAME_TOKEN_MOJOM_TRAITS_H_
-
-#include "third_party/blink/public/common/tokens/frame_token.h"
-#include "third_party/blink/public/common/tokens/token_mojom_traits_helper.h"
-#include "third_party/blink/public/mojom/tokens/frame_token.mojom-shared.h"
-
-namespace mojo {
-
-template <>
-struct StructTraits<blink::mojom::FrameTokenDataView, blink::FrameToken>
-    : public blink::TokenMojomTraitsHelper<blink::mojom::FrameTokenDataView,
-                                           blink::FrameToken> {};
-
-}  // namespace mojo
-
-#endif  // THIRD_PARTY_BLINK_PUBLIC_COMMON_TOKENS_FRAME_TOKEN_MOJOM_TRAITS_H_
diff --git a/third_party/blink/public/common/tokens/portal_token.h b/third_party/blink/public/common/tokens/portal_token.h
deleted file mode 100644
index 53ccda2..0000000
--- a/third_party/blink/public/common/tokens/portal_token.h
+++ /dev/null
@@ -1,17 +0,0 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef THIRD_PARTY_BLINK_PUBLIC_COMMON_TOKENS_PORTAL_TOKEN_H_
-#define THIRD_PARTY_BLINK_PUBLIC_COMMON_TOKENS_PORTAL_TOKEN_H_
-
-#include "base/util/type_safety/token_type.h"
-
-namespace blink {
-
-// Typemapped to blink::mojom::PortalToken (see portal_token_mojom_traits.h).
-using PortalToken = util::TokenType<class PortalTokenTypeMarker>;
-
-}  // namespace blink
-
-#endif  // THIRD_PARTY_BLINK_PUBLIC_COMMON_TOKENS_PORTAL_TOKEN_H_
diff --git a/third_party/blink/public/common/tokens/portal_token_mojom_traits.h b/third_party/blink/public/common/tokens/portal_token_mojom_traits.h
deleted file mode 100644
index c97fc59..0000000
--- a/third_party/blink/public/common/tokens/portal_token_mojom_traits.h
+++ /dev/null
@@ -1,21 +0,0 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef THIRD_PARTY_BLINK_PUBLIC_COMMON_TOKENS_PORTAL_TOKEN_MOJOM_TRAITS_H_
-#define THIRD_PARTY_BLINK_PUBLIC_COMMON_TOKENS_PORTAL_TOKEN_MOJOM_TRAITS_H_
-
-#include "third_party/blink/public/common/tokens/portal_token.h"
-#include "third_party/blink/public/common/tokens/token_mojom_traits_helper.h"
-#include "third_party/blink/public/mojom/tokens/portal_token.mojom-shared.h"
-
-namespace mojo {
-
-template <>
-struct StructTraits<blink::mojom::PortalTokenDataView, blink::PortalToken>
-    : public blink::TokenMojomTraitsHelper<blink::mojom::PortalTokenDataView,
-                                           blink::PortalToken> {};
-
-}  // namespace mojo
-
-#endif  // THIRD_PARTY_BLINK_PUBLIC_COMMON_TOKENS_PORTAL_TOKEN_MOJOM_TRAITS_H_
diff --git a/third_party/blink/public/common/tokens/tokens.h b/third_party/blink/public/common/tokens/tokens.h
new file mode 100644
index 0000000..988f326
--- /dev/null
+++ b/third_party/blink/public/common/tokens/tokens.h
@@ -0,0 +1,68 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_PUBLIC_COMMON_TOKENS_TOKENS_H_
+#define THIRD_PARTY_BLINK_PUBLIC_COMMON_TOKENS_TOKENS_H_
+
+#include "base/util/type_safety/token_type.h"
+
+namespace blink {
+
+// Various token types. These are used as cross-layer and cross-process
+// identifiers for objects that exist in blink, but which have representations
+// in the browser process. They should not be used to identify objects in
+// browser-to-renderer control messages; rather, such messages should exist as
+// methods on the interface bound to the object itself. They are fine to use
+// for informational messages that cross over other interfaces, in both
+// directions.
+//
+// See README.md for more details.
+
+////////////////////////////////////////////////////////////////////////////////
+// FRAME TOKENS
+
+// Uniquely identifies a blink::LocalFrame / blink::WebLocalFrame /
+// content::RenderFrame in a renderer process, and its content::RenderFrameHost
+// counterpart in the browser.
+using LocalFrameToken = util::TokenType<class LocalFrameTokenTypeMarker>;
+
+// Uniquely identifies an blink::RemoteFrame / blink::WebRemoteFrame /
+// content::RenderFrameProxy in a renderer process, and its
+// ontent::RenderFrameProxyHost counterpart in the browser. There can be
+// multiple RemoteFrames corresponding to a single LocalFrame, and each token
+// will be distinct.
+using RemoteFrameToken = util::TokenType<class RemoteFrameTokenTypeMarker>;
+
+////////////////////////////////////////////////////////////////////////////////
+// WORKER TOKENS
+
+// Identifies a blink::DedicatedWorkerGlobalScope in the renderer and a
+// content::DedicatedWorkerHost in the browser.
+using DedicatedWorkerToken =
+    util::TokenType<class DedicatedWorkerTokenTypeMarker>;
+
+// Identifies a blink::SharedWorkerGlobalScope in the renderer and a
+// content::SharedWorkerHost in the browser.
+using SharedWorkerToken = util::TokenType<class SharedWorkerTokenTypeMarker>;
+
+// Identifies a blink::ServiceWorkerGlobalScope in the renderer and a
+// content::ServiceWorkerVersion in the browser.
+using ServiceWorkerToken = util::TokenType<class ServiceWorkerTokenTypeMarker>;
+
+////////////////////////////////////////////////////////////////////////////////
+// OTHER TOKENS
+//
+// Keep this section last.
+//
+// If you have multiple tokens that make a thematic group, please lift them to
+// their own section, in alphabetical order. If adding a new token here, please
+// keep the following list in alphabetic order.
+
+// Identifies a blink::PortalContents / blink::HTMLPortalElement in the
+// renderer process, and a content::Portal in the browser process.
+using PortalToken = util::TokenType<class PortalTokenTypeMarker>;
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_PUBLIC_COMMON_TOKENS_TOKENS_H_
diff --git a/third_party/blink/public/common/tokens/tokens_mojom_traits.h b/third_party/blink/public/common/tokens/tokens_mojom_traits.h
new file mode 100644
index 0000000..af23712
--- /dev/null
+++ b/third_party/blink/public/common/tokens/tokens_mojom_traits.h
@@ -0,0 +1,74 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_PUBLIC_COMMON_TOKENS_TOKENS_MOJOM_TRAITS_H_
+#define THIRD_PARTY_BLINK_PUBLIC_COMMON_TOKENS_TOKENS_MOJOM_TRAITS_H_
+
+#include "third_party/blink/public/common/tokens/token_mojom_traits_helper.h"
+#include "third_party/blink/public/common/tokens/tokens.h"
+#include "third_party/blink/public/mojom/tokens/tokens.mojom-shared.h"
+
+namespace mojo {
+
+// Mojom traits for the various token types.
+// See third_party/blink/public/common/tokens/tokens.h for more details.
+
+////////////////////////////////////////////////////////////////////////////////
+// FRAME TOKENS
+
+template <>
+struct StructTraits<blink::mojom::LocalFrameTokenDataView,
+                    blink::LocalFrameToken>
+    : public blink::TokenMojomTraitsHelper<
+          blink::mojom::LocalFrameTokenDataView,
+          blink::LocalFrameToken> {};
+
+template <>
+struct StructTraits<blink::mojom::RemoteFrameTokenDataView,
+                    blink::RemoteFrameToken>
+    : public blink::TokenMojomTraitsHelper<
+          blink::mojom::RemoteFrameTokenDataView,
+          blink::RemoteFrameToken> {};
+
+////////////////////////////////////////////////////////////////////////////////
+// WORKER TOKENS
+
+template <>
+struct StructTraits<blink::mojom::DedicatedWorkerTokenDataView,
+                    blink::DedicatedWorkerToken>
+    : public blink::TokenMojomTraitsHelper<
+          blink::mojom::DedicatedWorkerTokenDataView,
+          blink::DedicatedWorkerToken> {};
+
+template <>
+struct StructTraits<blink::mojom::ServiceWorkerTokenDataView,
+                    blink::ServiceWorkerToken>
+    : public blink::TokenMojomTraitsHelper<
+          blink::mojom::ServiceWorkerTokenDataView,
+          blink::ServiceWorkerToken> {};
+
+template <>
+struct StructTraits<blink::mojom::SharedWorkerTokenDataView,
+                    blink::SharedWorkerToken>
+    : public blink::TokenMojomTraitsHelper<
+          blink::mojom::SharedWorkerTokenDataView,
+          blink::SharedWorkerToken> {};
+
+////////////////////////////////////////////////////////////////////////////////
+// OTHER TOKENS
+//
+// Keep this section last.
+//
+// If you have multiple tokens that make a thematic group, please lift them to
+// their own section, in alphabetical order. If adding a new token here, please
+// keep the following list in alphabetic order.
+
+template <>
+struct StructTraits<blink::mojom::PortalTokenDataView, blink::PortalToken>
+    : public blink::TokenMojomTraitsHelper<blink::mojom::PortalTokenDataView,
+                                           blink::PortalToken> {};
+
+}  // namespace mojo
+
+#endif  // THIRD_PARTY_BLINK_PUBLIC_COMMON_TOKENS_TOKENS_MOJOM_TRAITS_H_
diff --git a/third_party/blink/public/common/tokens/worker_tokens.h b/third_party/blink/public/common/tokens/worker_tokens.h
deleted file mode 100644
index 42d9ca1..0000000
--- a/third_party/blink/public/common/tokens/worker_tokens.h
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Definitions of all Blink token types. Each is of these has distinct mojo
-// type maps for both Blink and non-Blink variants. See the various
-// *_mojom_traits.h files in this directory.
-
-#ifndef THIRD_PARTY_BLINK_PUBLIC_COMMON_TOKENS_WORKER_TOKENS_H_
-#define THIRD_PARTY_BLINK_PUBLIC_COMMON_TOKENS_WORKER_TOKENS_H_
-
-#include "base/util/type_safety/token_type.h"
-
-namespace blink {
-
-// Worker token types. Note that these token types are type-mapped to their
-// mojom equivalents (defined in mojom/worker_tokens.mojom). See
-// worker_tokens_mojom_traits for the StructTraits.
-using DedicatedWorkerToken =
-    util::TokenType<class DedicatedWorkerTokenTypeMarker>;
-using SharedWorkerToken = util::TokenType<class SharedWorkerTokenTypeMarker>;
-using ServiceWorkerToken = util::TokenType<class ServiceWorkerTokenTypeMarker>;
-
-}  // namespace blink
-
-#endif  // THIRD_PARTY_BLINK_PUBLIC_COMMON_TOKENS_WORKER_TOKENS_H_
diff --git a/third_party/blink/public/common/tokens/worker_tokens_mojom_traits.h b/third_party/blink/public/common/tokens/worker_tokens_mojom_traits.h
deleted file mode 100644
index 6cfbcba..0000000
--- a/third_party/blink/public/common/tokens/worker_tokens_mojom_traits.h
+++ /dev/null
@@ -1,37 +0,0 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef THIRD_PARTY_BLINK_PUBLIC_COMMON_TOKENS_WORKER_TOKENS_MOJOM_TRAITS_H_
-#define THIRD_PARTY_BLINK_PUBLIC_COMMON_TOKENS_WORKER_TOKENS_MOJOM_TRAITS_H_
-
-#include "third_party/blink/public/common/tokens/token_mojom_traits_helper.h"
-#include "third_party/blink/public/common/tokens/worker_tokens.h"
-#include "third_party/blink/public/mojom/tokens/worker_tokens.mojom-shared.h"
-
-namespace mojo {
-
-template <>
-struct StructTraits<blink::mojom::DedicatedWorkerTokenDataView,
-                    blink::DedicatedWorkerToken>
-    : public blink::TokenMojomTraitsHelper<
-          blink::mojom::DedicatedWorkerTokenDataView,
-          blink::DedicatedWorkerToken> {};
-
-template <>
-struct StructTraits<blink::mojom::ServiceWorkerTokenDataView,
-                    blink::ServiceWorkerToken>
-    : public blink::TokenMojomTraitsHelper<
-          blink::mojom::ServiceWorkerTokenDataView,
-          blink::ServiceWorkerToken> {};
-
-template <>
-struct StructTraits<blink::mojom::SharedWorkerTokenDataView,
-                    blink::SharedWorkerToken>
-    : public blink::TokenMojomTraitsHelper<
-          blink::mojom::SharedWorkerTokenDataView,
-          blink::SharedWorkerToken> {};
-
-}  // namespace mojo
-
-#endif  // THIRD_PARTY_BLINK_PUBLIC_COMMON_TOKENS_WORKER_TOKENS_MOJOM_TRAITS_H_
diff --git a/third_party/blink/public/mojom/page/widget.mojom b/third_party/blink/public/mojom/page/widget.mojom
index 2010444..5a68c0c8 100644
--- a/third_party/blink/public/mojom/page/widget.mojom
+++ b/third_party/blink/public/mojom/page/widget.mojom
@@ -91,6 +91,9 @@
 
   // Disables device emulator.
   DisableDeviceEmulation();
+
+  // Binds an WidgetCompositor interface.
+  BindWidgetCompositor(pending_receiver<WidgetCompositor> host);
 };
 
 // Implemented in Browser, this interface defines frame-widget-specific methods that
@@ -186,3 +189,10 @@
              mojo_base.mojom.TextDirection focus_dir,
              bool is_anchor_first);
 };
+
+// This interface is bound on the compositor thread.
+interface WidgetCompositor {
+  // Requests that the RenderWidget sends back a response after the next main
+  // frame is generated and presented in the display compositor.
+  VisualStateRequest() => ();
+};
diff --git a/third_party/blink/public/mojom/tokens/BUILD.gn b/third_party/blink/public/mojom/tokens/BUILD.gn
index 4b2c60c..d71335db 100644
--- a/third_party/blink/public/mojom/tokens/BUILD.gn
+++ b/third_party/blink/public/mojom/tokens/BUILD.gn
@@ -9,24 +9,27 @@
   output_prefix = "tokens_mojom"
   macro_prefix = "TOKENS_MOJOM"
 
-  sources = [
-    "frame_token.mojom",
-    "portal_token.mojom",
-    "worker_tokens.mojom",
-  ]
+  sources = [ "tokens.mojom" ]
 
   shared_cpp_typemaps = [
     {
       types = [
+        # FRAME TOKENS
+        {
+          mojom = "blink.mojom.LocalFrameToken"
+          cpp = "::blink::LocalFrameToken"
+        },
+        {
+          mojom = "blink.mojom.RemoteFrameToken"
+          cpp = "::blink::RemoteFrameToken"
+        },
+
+        # WORKER TOKENS
         {
           mojom = "blink.mojom.DedicatedWorkerToken"
           cpp = "::blink::DedicatedWorkerToken"
         },
         {
-          mojom = "blink.mojom.PortalToken"
-          cpp = "::blink::PortalToken"
-        },
-        {
           mojom = "blink.mojom.ServiceWorkerToken"
           cpp = "::blink::ServiceWorkerToken"
         },
@@ -34,22 +37,21 @@
           mojom = "blink.mojom.SharedWorkerToken"
           cpp = "::blink::SharedWorkerToken"
         },
-      ]
-      traits_headers = [
-        "//third_party/blink/public/common/tokens/portal_token_mojom_traits.h",
-        "//third_party/blink/public/common/tokens/worker_tokens_mojom_traits.h",
-      ]
-    },
-    {
-      types = [
+
+        # OTHER TOKENS
+        #
+        # Keep this section last.
+        #
+        # If you have multiple tokens that make a thematic group, please lift
+        # them to their own section, in alphabetical order. If adding a new
+        # token here, please keep the following list in alphabetic order.
         {
-          mojom = "blink.mojom.FrameToken"
-          cpp = "::blink::FrameToken"
+          mojom = "blink.mojom.PortalToken"
+          cpp = "::blink::PortalToken"
         },
       ]
-      traits_headers = [
-        "//third_party/blink/public/common/tokens/frame_token_mojom_traits.h",
-      ]
+      traits_headers =
+          [ "//third_party/blink/public/common/tokens/tokens_mojom_traits.h" ]
     },
   ]
 
diff --git a/third_party/blink/public/mojom/tokens/frame_token.mojom b/third_party/blink/public/mojom/tokens/frame_token.mojom
deleted file mode 100644
index 36d5e52..0000000
--- a/third_party/blink/public/mojom/tokens/frame_token.mojom
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-module blink.mojom;
-
-import "mojo/public/mojom/base/unguessable_token.mojom";
-
-// This identifier represents the stable identifier between a
-// blink::LocalFrame <--> content::RenderFrameHostImpl or a
-// blink::RemoteFrame <--> content::RenderFrameProxyHost in the browser process.
-// Note that this identifier is unique per render process and
-// browser relationship. ie. If this is a LocalFrame, RemoteFrames that
-// represent this frame node in other processes will *not* have the same
-// identifier. Similarly, if this is a RemoteFrame, the LocalFrame and
-// other RemoteFrames that represent this frame node in other processes
-// will *not* have the same identifier. This is different than the
-// |devtools_frame_token_| in which all representations of this frame node
-// have the same value in all processes. This is intended to be transferred
-// between browser and renderer processes to uniquely identify a frame in the
-// context of that browser/renderer relationship.
-// See third_party/blink/public/common/tokens for helper classes.
-struct FrameToken {
-  mojo_base.mojom.UnguessableToken value;
-};
\ No newline at end of file
diff --git a/third_party/blink/public/mojom/tokens/portal_token.mojom b/third_party/blink/public/mojom/tokens/portal_token.mojom
deleted file mode 100644
index 6a218e7..0000000
--- a/third_party/blink/public/mojom/tokens/portal_token.mojom
+++ /dev/null
@@ -1,14 +0,0 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-module blink.mojom;
-
-import "mojo/public/mojom/base/unguessable_token.mojom";
-
-// Identifier sent from the browser process to renderer processes, that is used
-// by the renderer to uniquely identify and refer to a portal object created in
-// the browser process.
-struct PortalToken {
-  mojo_base.mojom.UnguessableToken value;
-};
diff --git a/third_party/blink/public/mojom/tokens/tokens.mojom b/third_party/blink/public/mojom/tokens/tokens.mojom
new file mode 100644
index 0000000..75a7d685
--- /dev/null
+++ b/third_party/blink/public/mojom/tokens/tokens.mojom
@@ -0,0 +1,56 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module blink.mojom;
+
+import "mojo/public/mojom/base/unguessable_token.mojom";
+
+// Various token types. These are used as cross-layer and cross-process
+// identifiers for objects that exist in blink, but which have representations
+// in the browser process. They should not be used to identify objects in
+// browser-to-renderer control messages; rather, such messages should exist as
+// methods on the interface bound to the object itself. They are fine to use
+// for informational messages that cross over other interfaces, in both
+// directions.
+//
+// See third_party/blink/public/common/tokens/tokens.h for more details.
+
+////////////////////////////////////////////////////////////////////////////////
+// FRAME TOKENS
+
+struct LocalFrameToken {
+  mojo_base.mojom.UnguessableToken value;
+};
+
+struct RemoteFrameToken {
+  mojo_base.mojom.UnguessableToken value;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// WORKER TOKENS
+
+struct DedicatedWorkerToken {
+  mojo_base.mojom.UnguessableToken value;
+};
+
+struct SharedWorkerToken {
+  mojo_base.mojom.UnguessableToken value;
+};
+
+struct ServiceWorkerToken {
+  mojo_base.mojom.UnguessableToken value;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// OTHER TOKENS
+//
+// Keep this section last.
+//
+// If you have multiple tokens that make a thematic group, please lift them to
+// their own section, in alphabetical order. If adding a new token here, please
+// keep the following list in alphabetic order.
+
+struct PortalToken {
+  mojo_base.mojom.UnguessableToken value;
+};
diff --git a/third_party/blink/public/mojom/tokens/worker_tokens.mojom b/third_party/blink/public/mojom/tokens/worker_tokens.mojom
deleted file mode 100644
index fd79521..0000000
--- a/third_party/blink/public/mojom/tokens/worker_tokens.mojom
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-module blink.mojom;
-
-import "mojo/public/mojom/base/unguessable_token.mojom";
-
-// These identifiers uniquely represent workers (dedicated/shared/service)
-// across the content and blink layers. It is only intended to be transferred
-// between the browser process and the renderer process hosting the worker.
-// This is a simple wrapper that provides type safety.
-
-struct DedicatedWorkerToken {
-  mojo_base.mojom.UnguessableToken value;
-};
-
-struct SharedWorkerToken {
-  mojo_base.mojom.UnguessableToken value;
-};
-
-struct ServiceWorkerToken {
-  mojo_base.mojom.UnguessableToken value;
-};
diff --git a/third_party/blink/public/mojom/worker/dedicated_worker_host_factory.mojom b/third_party/blink/public/mojom/worker/dedicated_worker_host_factory.mojom
index 3d20f54..b5fe48e 100644
--- a/third_party/blink/public/mojom/worker/dedicated_worker_host_factory.mojom
+++ b/third_party/blink/public/mojom/worker/dedicated_worker_host_factory.mojom
@@ -14,7 +14,7 @@
 import "third_party/blink/public/mojom/worker/worker_main_script_load_params.mojom";
 import "third_party/blink/public/mojom/service_worker/controller_service_worker.mojom";
 import "third_party/blink/public/mojom/service_worker/service_worker_container.mojom";
-import "third_party/blink/public/mojom/tokens/worker_tokens.mojom";
+import "third_party/blink/public/mojom/tokens/tokens.mojom";
 import "url/mojom/url.mojom";
 
 // The name of the InterfaceProviderSpec in service manifests used by the
diff --git a/third_party/blink/public/mojom/worker/shared_worker_factory.mojom b/third_party/blink/public/mojom/worker/shared_worker_factory.mojom
index 123a42f..dcf0850 100644
--- a/third_party/blink/public/mojom/worker/shared_worker_factory.mojom
+++ b/third_party/blink/public/mojom/worker/shared_worker_factory.mojom
@@ -12,7 +12,7 @@
 import "third_party/blink/public/mojom/renderer_preferences.mojom";
 import "third_party/blink/public/mojom/service_worker/controller_service_worker.mojom";
 import "third_party/blink/public/mojom/service_worker/service_worker_container.mojom";
-import "third_party/blink/public/mojom/tokens/worker_tokens.mojom";
+import "third_party/blink/public/mojom/tokens/tokens.mojom";
 import "third_party/blink/public/mojom/worker/shared_worker.mojom";
 import "third_party/blink/public/mojom/worker/shared_worker_host.mojom";
 import "third_party/blink/public/mojom/worker/shared_worker_info.mojom";
diff --git a/third_party/blink/public/platform/TaskTypes.md b/third_party/blink/public/platform/TaskTypes.md
index 76c519a..3a3d28c4 100644
--- a/third_party/blink/public/platform/TaskTypes.md
+++ b/third_party/blink/public/platform/TaskTypes.md
@@ -19,7 +19,8 @@
 | MediaElementEvent            | No          | No         | Yes       | Yes      | Yes          |
 | CanvasBlobSerialization      | No          | Yes        | Yes       | Yes      | Yes          |
 | Microtask                    | No          | Yes        | Yes       | Yes      | Yes          |
-| JavascriptTimer              | Yes         | Yes        | Yes       | Yes      | Yes          |
+| JavascriptTimerDelayed       | Yes         | Yes        | Yes       | Yes      | Yes          |
+| JavascriptTimerImmediate [1] | No          | Yes        | Yes       | Yes      | Yes          |
 | RemoteEvent                  | No          | Yes        | Yes       | Yes      | Yes          |
 | WebSocket                    | No          | Yes        | Yes       | Yes      | Yes          |
 | PostedMessage                | No          | No         | Yes       | Yes      | Yes          |
@@ -56,3 +57,5 @@
 | InternalContinueScriptLoadin | No          | No         | Yes       | Yes      | Yes          |
 
 Internal Translation queue supports concept of it running only in the foreground. It is disabled if the page that owns it goes in background.
+
+[1] Assuming that the "OptOutZeroTimeoutTimersFromThrottling" feature is enabled.
diff --git a/third_party/blink/public/platform/platform.h b/third_party/blink/public/platform/platform.h
index 688088b..abfcd20b 100644
--- a/third_party/blink/public/platform/platform.h
+++ b/third_party/blink/public/platform/platform.h
@@ -72,6 +72,10 @@
 class SingleThreadTaskRunner;
 }
 
+namespace gfx {
+class ColorSpace;
+}
+
 namespace gpu {
 class GpuMemoryBufferManager;
 }
@@ -679,6 +683,8 @@
     return nullptr;
   }
 
+  virtual void SetRenderingColorSpace(const gfx::ColorSpace& color_space) {}
+
   // Renderer Memory Metrics ----------------------------------------------
 
   virtual void RecordMetricsForBackgroundedRendererPurge() {}
diff --git a/third_party/blink/public/platform/task_type.h b/third_party/blink/public/platform/task_type.h
index 35d9d12..9d2ac8d7 100644
--- a/third_party/blink/public/platform/task_type.h
+++ b/third_party/blink/public/platform/task_type.h
@@ -73,9 +73,14 @@
   kMicrotask = 9,
 
   // https://html.spec.whatwg.org/multipage/webappapis.html#timers
-  // This task source is used to queue tasks queued by setInterval() and similar
-  // APIs.
-  kJavascriptTimer = 10,
+  // For tasks queued by setInterval() and similar APIs. A different type is
+  // used depending on whether the timeout is zero or non-zero. Tasks with
+  // a zero timeout and a nesting level <= 5 will be associated with task
+  // queues that are not throttlable. This complies with the spec since it
+  // does not reduce the timeout to less than zero or bypass the timeout
+  // extension triggered on nesting level >= 5.
+  kJavascriptTimerDelayed = 10,
+  kJavascriptTimerImmediate = 72,
 
   // https://html.spec.whatwg.org/multipage/comms.html#sse-processing-model
   // This task source is used for any tasks that are queued by EventSource
@@ -264,7 +269,7 @@
   kWorkerThreadTaskQueueV8 = 47,
   kWorkerThreadTaskQueueCompositor = 48,
 
-  kCount = 72,
+  kCount = 73,
 };
 
 }  // namespace blink
diff --git a/third_party/blink/public/platform/web_dedicated_worker_host_factory_client.h b/third_party/blink/public/platform/web_dedicated_worker_host_factory_client.h
index 4a2eed0..d0230c9 100644
--- a/third_party/blink/public/platform/web_dedicated_worker_host_factory_client.h
+++ b/third_party/blink/public/platform/web_dedicated_worker_host_factory_client.h
@@ -10,7 +10,7 @@
 #include "mojo/public/cpp/system/message_pipe.h"
 #include "services/network/public/mojom/fetch_api.mojom-shared.h"
 #include "services/network/public/mojom/referrer_policy.mojom-shared.h"
-#include "third_party/blink/public/common/tokens/worker_tokens.h"
+#include "third_party/blink/public/common/tokens/tokens.h"
 #include "third_party/blink/public/mojom/blob/blob_url_store.mojom-shared.h"
 #include "third_party/blink/public/mojom/frame/lifecycle.mojom-shared.h"
 #include "third_party/blink/public/platform/cross_variant_mojo_util.h"
diff --git a/third_party/blink/public/web/web_local_frame.h b/third_party/blink/public/web/web_local_frame.h
index 2f369cc2..699eb37 100644
--- a/third_party/blink/public/web/web_local_frame.h
+++ b/third_party/blink/public/web/web_local_frame.h
@@ -18,7 +18,7 @@
 #include "third_party/blink/public/common/feature_policy/feature_policy_features.h"
 #include "third_party/blink/public/common/frame/user_activation_update_source.h"
 #include "third_party/blink/public/common/messaging/transferable_message.h"
-#include "third_party/blink/public/common/tokens/portal_token.h"
+#include "third_party/blink/public/common/tokens/tokens.h"
 #include "third_party/blink/public/mojom/ad_tagging/ad_frame.mojom-shared.h"
 #include "third_party/blink/public/mojom/blob/blob_url_store.mojom-shared.h"
 #include "third_party/blink/public/mojom/commit_result/commit_result.mojom-shared.h"
diff --git a/third_party/blink/public/web/web_local_frame_client.h b/third_party/blink/public/web/web_local_frame_client.h
index 7a54209..cc7c11d6 100644
--- a/third_party/blink/public/web/web_local_frame_client.h
+++ b/third_party/blink/public/web/web_local_frame_client.h
@@ -43,7 +43,7 @@
 #include "third_party/blink/public/common/loader/loading_behavior_flag.h"
 #include "third_party/blink/public/common/loader/url_loader_factory_bundle.h"
 #include "third_party/blink/public/common/navigation/triggering_event_info.h"
-#include "third_party/blink/public/common/tokens/portal_token.h"
+#include "third_party/blink/public/common/tokens/tokens.h"
 #include "third_party/blink/public/common/user_agent/user_agent_metadata.h"
 #include "third_party/blink/public/mojom/frame/blocked_navigation_types.mojom-shared.h"
 #include "third_party/blink/public/mojom/frame/frame_owner_element_type.mojom-shared.h"
diff --git a/third_party/blink/public/web/web_shared_worker.h b/third_party/blink/public/web/web_shared_worker.h
index 4360fea..d744c1bb 100644
--- a/third_party/blink/public/web/web_shared_worker.h
+++ b/third_party/blink/public/web/web_shared_worker.h
@@ -38,7 +38,7 @@
 #include "services/network/public/mojom/content_security_policy.mojom-shared.h"
 #include "services/network/public/mojom/fetch_api.mojom-shared.h"
 #include "services/network/public/mojom/ip_address_space.mojom-shared.h"
-#include "third_party/blink/public/common/tokens/worker_tokens.h"
+#include "third_party/blink/public/common/tokens/tokens.h"
 #include "third_party/blink/public/common/user_agent/user_agent_metadata.h"
 #include "third_party/blink/public/mojom/browser_interface_broker.mojom-shared.h"
 #include "third_party/blink/public/mojom/script/script_type.mojom-shared.h"
diff --git a/third_party/blink/renderer/controller/BUILD.gn b/third_party/blink/renderer/controller/BUILD.gn
index 7791766..fbfd5591 100644
--- a/third_party/blink/renderer/controller/BUILD.gn
+++ b/third_party/blink/renderer/controller/BUILD.gn
@@ -45,7 +45,7 @@
     "memory_usage_monitor.h",
   ]
 
-  if (is_linux) {
+  if (is_linux || is_chromeos) {
     sources += [
       "memory_usage_monitor_posix.cc",
       "memory_usage_monitor_posix.h",
@@ -81,7 +81,7 @@
 
   # HighestPmfReporter depends on MemoryUsageMonitor and MemoryUsageMonitor
   # depends on platform specific code. Explicitly specify supported platforms.
-  if (is_linux || is_win || is_android || is_mac) {
+  if (is_linux || is_chromeos || is_win || is_android || is_mac) {
     sources += [
       "highest_pmf_reporter.cc",
       "highest_pmf_reporter.h",
@@ -165,7 +165,7 @@
 
   sources = [ "tests/run_all_tests.cc" ]
   sources += bindings_unittest_files
-  if (is_linux) {
+  if (is_linux || is_chromeos) {
     sources += [ "memory_usage_monitor_posix_test.cc" ]
   }
   if (is_android) {
@@ -176,7 +176,7 @@
     ]
   }
 
-  if (is_linux || is_android || is_mac || is_win) {
+  if (is_linux || is_chromeos || is_android || is_mac || is_win) {
     sources += [
       "highest_pmf_reporter_test.cc",
       "memory_usage_monitor_test.cc",
diff --git a/third_party/blink/renderer/core/frame/dom_timer.cc b/third_party/blink/renderer/core/frame/dom_timer.cc
index 30dfd23..a287ba4 100644
--- a/third_party/blink/renderer/core/frame/dom_timer.cc
+++ b/third_party/blink/renderer/core/frame/dom_timer.cc
@@ -27,6 +27,7 @@
 #include "third_party/blink/renderer/core/frame/dom_timer.h"
 
 #include "base/single_thread_task_runner.h"
+#include "base/time/time.h"
 #include "third_party/blink/public/platform/task_type.h"
 #include "third_party/blink/renderer/bindings/core/v8/scheduled_action.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
@@ -36,15 +37,32 @@
 
 namespace blink {
 
-static const int kMaxTimerNestingLevel = 5;
-// Chromium uses a minimum timer interval of 4ms. We'd like to go
-// lower; however, there are poorly coded websites out there which do
-// create CPU-spinning loops.  Using 4ms prevents the CPU from
-// spinning too busily and provides a balance between CPU spinning and
-// the smallest possible interval timer.
-static constexpr base::TimeDelta kMinimumInterval =
+namespace {
+
+// Step 11 of the algorithm at
+// https://html.spec.whatwg.org/multipage/timers-and-user-prompts.html requires
+// that a timeout less than 4ms is increased to 4ms when the nesting level is
+// greater than 5.
+constexpr int kMaxTimerNestingLevel = 5;
+constexpr base::TimeDelta kMinimumInterval =
     base::TimeDelta::FromMilliseconds(4);
 
+// Computes the timeout of a timer according to the algorithm at
+// https://html.spec.whatwg.org/multipage/timers-and-user-prompts.html.
+base::TimeDelta ComputeTimeout(base::TimeDelta timeout, int nesting_level) {
+  // Step 10:
+  if (timeout < base::TimeDelta())
+    timeout = base::TimeDelta();
+
+  // Step 11 (the implementation is not spec-compliant crbug.com/1108877):
+  if (nesting_level + 1 >= kMaxTimerNestingLevel && timeout < kMinimumInterval)
+    timeout = kMinimumInterval;
+
+  return timeout;
+}
+
+}  // namespace
+
 int DOMTimer::Install(ExecutionContext* context,
                       ScheduledAction* action,
                       base::TimeDelta timeout,
@@ -63,33 +81,39 @@
   if (timer)
     timer->SetExecutionContext(nullptr);
 }
-
 DOMTimer::DOMTimer(ExecutionContext* context,
                    ScheduledAction* action,
-                   base::TimeDelta interval,
+                   base::TimeDelta timeout,
                    bool single_shot,
                    int timeout_id)
     : ExecutionContextLifecycleObserver(context),
-      TimerBase(context->GetTaskRunner(TaskType::kJavascriptTimer)),
+      TimerBase(context->GetTaskRunner(
+          ComputeTimeout(timeout, context->Timers()->TimerNestingLevel())
+                  .is_zero()
+              ? TaskType::kJavascriptTimerImmediate
+              : TaskType::kJavascriptTimerDelayed)),
       timeout_id_(timeout_id),
-      nesting_level_(context->Timers()->TimerNestingLevel() + 1),
+      nesting_level_(context->Timers()->TimerNestingLevel()),
       action_(action) {
   DCHECK_GT(timeout_id, 0);
 
-  base::TimeDelta interval_milliseconds =
-      std::max(base::TimeDelta::FromMilliseconds(1), interval);
-  if (interval_milliseconds < kMinimumInterval &&
-      nesting_level_ >= kMaxTimerNestingLevel)
-    interval_milliseconds = kMinimumInterval;
+  // Steps 10 and 11, and rounding up to 1 ms for historical reasons
+  // crbug.com/402694.
+  timeout = std::max(base::TimeDelta::FromMilliseconds(1),
+                     ComputeTimeout(timeout, nesting_level_));
+
+  // Step 12 and 13:
+  ++nesting_level_;
+
   if (single_shot)
-    StartOneShot(interval_milliseconds, FROM_HERE);
+    StartOneShot(timeout, FROM_HERE);
   else
-    StartRepeating(interval_milliseconds, FROM_HERE);
+    StartRepeating(timeout, FROM_HERE);
 
   TRACE_EVENT_INSTANT1("devtools.timeline", "TimerInstall",
                        TRACE_EVENT_SCOPE_THREAD, "data",
                        inspector_timer_install_event::Data(
-                           context, timeout_id, interval, single_shot));
+                           context, timeout_id, timeout, single_shot));
   probe::AsyncTaskScheduledBreakable(
       context, single_shot ? "setTimeout" : "setInterval", &async_task_id_);
 }
@@ -142,8 +166,11 @@
   if (IsActive()) {
     if (is_interval && RepeatInterval() < kMinimumInterval) {
       nesting_level_++;
-      if (nesting_level_ >= kMaxTimerNestingLevel)
+      if (nesting_level_ >= kMaxTimerNestingLevel) {
+        MoveToNewTaskRunner(
+            context->GetTaskRunner(TaskType::kJavascriptTimerDelayed));
         AugmentRepeatInterval(kMinimumInterval - RepeatInterval());
+      }
     }
 
     // No access to member variables after this point, it can delete the timer.
diff --git a/third_party/blink/renderer/core/frame/dom_timer.h b/third_party/blink/renderer/core/frame/dom_timer.h
index 669db99..460577a 100644
--- a/third_party/blink/renderer/core/frame/dom_timer.h
+++ b/third_party/blink/renderer/core/frame/dom_timer.h
@@ -57,7 +57,7 @@
 
   DOMTimer(ExecutionContext*,
            ScheduledAction*,
-           base::TimeDelta interval,
+           base::TimeDelta timeout,
            bool single_shot,
            int timeout_id);
   ~DOMTimer() override;
diff --git a/third_party/blink/renderer/core/frame/local_frame_client.h b/third_party/blink/renderer/core/frame/local_frame_client.h
index f31cae1..96bf50bf 100644
--- a/third_party/blink/renderer/core/frame/local_frame_client.h
+++ b/third_party/blink/renderer/core/frame/local_frame_client.h
@@ -42,7 +42,7 @@
 #include "third_party/blink/public/common/feature_policy/feature_policy.h"
 #include "third_party/blink/public/common/loader/loading_behavior_flag.h"
 #include "third_party/blink/public/common/navigation/triggering_event_info.h"
-#include "third_party/blink/public/common/tokens/portal_token.h"
+#include "third_party/blink/public/common/tokens/tokens.h"
 #include "third_party/blink/public/common/user_agent/user_agent_metadata.h"
 #include "third_party/blink/public/mojom/frame/navigation_initiator.mojom-blink-forward.h"
 #include "third_party/blink/public/mojom/portal/portal.mojom-blink-forward.h"
diff --git a/third_party/blink/renderer/core/frame/pausable_script_executor.cc b/third_party/blink/renderer/core/frame/pausable_script_executor.cc
index 4ad3f53..bc8a98e 100644
--- a/third_party/blink/renderer/core/frame/pausable_script_executor.cc
+++ b/third_party/blink/renderer/core/frame/pausable_script_executor.cc
@@ -209,10 +209,7 @@
     ExecuteAndDestroySelf();
     return;
   }
-  task_handle_ = PostCancellableTask(
-      *context->GetTaskRunner(TaskType::kJavascriptTimer), FROM_HERE,
-      WTF::Bind(&PausableScriptExecutor::ExecuteAndDestroySelf,
-                WrapPersistent(this)));
+  PostExecuteAndDestroySelf(context);
 }
 
 void PausableScriptExecutor::RunAsync(BlockingOption blocking) {
@@ -222,8 +219,13 @@
   if (blocking_option_ == kOnloadBlocking)
     To<LocalDOMWindow>(context)->document()->IncrementLoadEventDelayCount();
 
+  PostExecuteAndDestroySelf(context);
+}
+
+void PausableScriptExecutor::PostExecuteAndDestroySelf(
+    ExecutionContext* context) {
   task_handle_ = PostCancellableTask(
-      *context->GetTaskRunner(TaskType::kJavascriptTimer), FROM_HERE,
+      *context->GetTaskRunner(TaskType::kJavascriptTimerImmediate), FROM_HERE,
       WTF::Bind(&PausableScriptExecutor::ExecuteAndDestroySelf,
                 WrapPersistent(this)));
 }
diff --git a/third_party/blink/renderer/core/frame/pausable_script_executor.h b/third_party/blink/renderer/core/frame/pausable_script_executor.h
index 99f2a8d..8eb168e7 100644
--- a/third_party/blink/renderer/core/frame/pausable_script_executor.h
+++ b/third_party/blink/renderer/core/frame/pausable_script_executor.h
@@ -63,7 +63,7 @@
   void Trace(Visitor*) const override;
 
  private:
-
+  void PostExecuteAndDestroySelf(ExecutionContext* context);
   void ExecuteAndDestroySelf();
   void Dispose();
 
diff --git a/third_party/blink/renderer/core/frame/web_frame_widget_base.cc b/third_party/blink/renderer/core/frame/web_frame_widget_base.cc
index a3b74a6..4387d2f2 100644
--- a/third_party/blink/renderer/core/frame/web_frame_widget_base.cc
+++ b/third_party/blink/renderer/core/frame/web_frame_widget_base.cc
@@ -353,6 +353,11 @@
 }
 #endif
 
+void WebFrameWidgetBase::BindWidgetCompositor(
+    mojo::PendingReceiver<mojom::blink::WidgetCompositor> receiver) {
+  widget_base_->BindWidgetCompositor(std::move(receiver));
+}
+
 void WebFrameWidgetBase::CancelDrag() {
   // It's possible for this to be called while we're not doing a drag if
   // it's from a previous page that got unloaded.
diff --git a/third_party/blink/renderer/core/frame/web_frame_widget_base.h b/third_party/blink/renderer/core/frame/web_frame_widget_base.h
index d2f9f984c..944a5a8 100644
--- a/third_party/blink/renderer/core/frame/web_frame_widget_base.h
+++ b/third_party/blink/renderer/core/frame/web_frame_widget_base.h
@@ -385,6 +385,9 @@
 
   base::Optional<gfx::Point> GetAndResetContextMenuLocation();
 
+  void BindWidgetCompositor(
+      mojo::PendingReceiver<mojom::blink::WidgetCompositor> receiver) override;
+
   // Called when the FrameView for this Widget's local root is created.
   virtual void DidCreateLocalRootView() {}
 
diff --git a/third_party/blink/renderer/core/html/forms/resources/calendarPicker.js b/third_party/blink/renderer/core/html/forms/resources/calendarPicker.js
index 6380d46..ab4faac 100644
--- a/third_party/blink/renderer/core/html/forms/resources/calendarPicker.js
+++ b/third_party/blink/renderer/core/html/forms/resources/calendarPicker.js
@@ -3479,6 +3479,26 @@
 };
 
 /**
+ * @param {!Day} day
+ * @param {!Day} minDay
+ * @param {!Day} maxDay
+ * @return {boolean}
+ */
+function isDayOutsideOfRange(day, minDay, maxDay) {
+  return day < minDay || maxDay < day;
+}
+
+/**
+ * @param {!Week} week
+ * @param {!Week} minWeek
+ * @param {!Week} maxWeek
+ * @return {boolean}
+ */
+function isWeekOutsideOfRange(week, minWeek, maxWeek) {
+  return week < minWeek || maxWeek < week;
+}
+
+/**
  * @constructor
  * @extends View
  * @param {!CalendarPicker} calendarPicker
@@ -3532,10 +3552,15 @@
         this.onNavigationButtonClick);
     this._todayButton.element.classList.add(
         CalendarHeaderView.GetClassNameTodayButton());
-    var monthContainingToday = Month.createFromToday();
-    this._todayButton.setDisabled(
-        monthContainingToday < this.calendarPicker.minimumMonth ||
-        monthContainingToday > this.calendarPicker.maximumMonth);
+    if (this.calendarPicker.type === 'week') {
+      this._todayButton.setDisabled(isWeekOutsideOfRange(
+          Week.createFromToday(), this.calendarPicker.config.minimum,
+          this.calendarPicker.config.maximum));
+    } else {
+      this._todayButton.setDisabled(isDayOutsideOfRange(
+          Day.createFromToday(), this.calendarPicker.config.minimum,
+          this.calendarPicker.config.maximum));
+    }
     this._todayButton.element.setAttribute(
         'aria-label', global.params.todayLabel);
   }
@@ -3660,11 +3685,17 @@
       this.disabled ||
       this.calendarPicker.currentMonth() >= this.calendarPicker.maximumMonth);
   if (this._todayButton) {
-    var monthContainingToday = Month.createFromToday();
-    this._todayButton.setDisabled(
-        this.disabled ||
-        monthContainingToday < this.calendarPicker.minimumMonth ||
-        monthContainingToday > this.calendarPicker.maximumMonth);
+    if (this.disabled) {
+      this._todayButton.setDisabled(true);
+    } else if (this.calendarPicker.type === 'week') {
+      this._todayButton.setDisabled(isWeekOutsideOfRange(
+          Week.createFromToday(), this.calendarPicker.config.minimum,
+          this.calendarPicker.config.maximum));
+    } else {
+      this._todayButton.setDisabled(isDayOutsideOfRange(
+          Day.createFromToday(), this.calendarPicker.config.minimum,
+          this.calendarPicker.config.maximum));
+    }
   }
 };
 
@@ -4041,10 +4072,15 @@
     todayButton.element.textContent = global.params.todayLabel;
     todayButton.element.classList.add(
         CalendarHeaderView.GetClassNameTodayButton());
-    var monthContainingToday = Month.createFromToday();
-    todayButton.setDisabled(
-        monthContainingToday < this.calendarPicker.minimumMonth ||
-        monthContainingToday > this.calendarPicker.maximumMonth);
+    if (this.calendarPicker.type === 'week') {
+      todayButton.setDisabled(isWeekOutsideOfRange(
+          Week.createFromToday(), this.calendarPicker.config.minimum,
+          this.calendarPicker.config.maximum));
+    } else {
+      todayButton.setDisabled(isDayOutsideOfRange(
+          Day.createFromToday(), this.calendarPicker.config.minimum,
+          this.calendarPicker.config.maximum));
+    }
     todayButton.element.setAttribute('aria-label', global.params.todayLabel);
   }
 
diff --git a/third_party/blink/renderer/core/html/portal/html_portal_element.h b/third_party/blink/renderer/core/html/portal/html_portal_element.h
index 4d4244d..9311cc7 100644
--- a/third_party/blink/renderer/core/html/portal/html_portal_element.h
+++ b/third_party/blink/renderer/core/html/portal/html_portal_element.h
@@ -9,7 +9,7 @@
 #include "mojo/public/cpp/bindings/associated_receiver.h"
 #include "mojo/public/cpp/bindings/associated_remote.h"
 #include "mojo/public/cpp/bindings/pending_associated_receiver.h"
-#include "third_party/blink/public/common/tokens/portal_token.h"
+#include "third_party/blink/public/common/tokens/tokens.h"
 #include "third_party/blink/public/mojom/portal/portal.mojom-blink-forward.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_promise.h"
 #include "third_party/blink/renderer/core/core_export.h"
diff --git a/third_party/blink/renderer/core/html/portal/portal_activate_event.h b/third_party/blink/renderer/core/html/portal/portal_activate_event.h
index 8d18425a..c0576bc 100644
--- a/third_party/blink/renderer/core/html/portal/portal_activate_event.h
+++ b/third_party/blink/renderer/core/html/portal/portal_activate_event.h
@@ -9,7 +9,7 @@
 #include "mojo/public/cpp/bindings/associated_remote.h"
 #include "mojo/public/cpp/bindings/pending_associated_receiver.h"
 #include "mojo/public/cpp/bindings/pending_associated_remote.h"
-#include "third_party/blink/public/common/tokens/portal_token.h"
+#include "third_party/blink/public/common/tokens/tokens.h"
 #include "third_party/blink/public/mojom/portal/portal.mojom-blink-forward.h"
 #include "third_party/blink/renderer/bindings/core/v8/serialization/serialized_script_value.h"
 #include "third_party/blink/renderer/bindings/core/v8/serialization/unpacked_serialized_script_value.h"
diff --git a/third_party/blink/renderer/core/html/portal/portal_contents.h b/third_party/blink/renderer/core/html/portal/portal_contents.h
index b97903a..a33b60f8 100644
--- a/third_party/blink/renderer/core/html/portal/portal_contents.h
+++ b/third_party/blink/renderer/core/html/portal/portal_contents.h
@@ -12,7 +12,7 @@
 #include "mojo/public/cpp/bindings/pending_associated_receiver.h"
 #include "mojo/public/cpp/bindings/pending_associated_remote.h"
 #include "services/network/public/mojom/referrer_policy.mojom-shared.h"
-#include "third_party/blink/public/common/tokens/portal_token.h"
+#include "third_party/blink/public/common/tokens/tokens.h"
 #include "third_party/blink/public/mojom/portal/portal.mojom-blink.h"
 #include "third_party/blink/renderer/platform/heap/garbage_collected.h"
 
diff --git a/third_party/blink/renderer/core/inspector/inspector_highlight.cc b/third_party/blink/renderer/core/inspector/inspector_highlight.cc
index e93ec7ccf..657543a 100644
--- a/third_party/blink/renderer/core/inspector/inspector_highlight.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_highlight.cc
@@ -486,7 +486,6 @@
   size_t first_explicit_index =
       layout_grid->ExplicitGridStartForDirection(direction);
 
-  LayoutUnit first_offset = track_positions.front();
   // Go line by line, calculating the offset to fall in the middle of gaps
   // if needed.
   for (size_t i = first_explicit_index; i < track_positions.size(); ++i) {
@@ -496,8 +495,8 @@
     if (grid_gap == 0 || i == 0 || i == track_positions.size() - 1) {
       gapOffset = LayoutUnit();
     }
-    PhysicalOffset number_position(
-        track_positions.at(i) - gapOffset - first_offset, alt_axis_pos);
+    PhysicalOffset number_position(track_positions.at(i) - gapOffset,
+                                   alt_axis_pos);
     if (direction == kForRows)
       number_position = Transpose(number_position);
     number_positions->pushValue(BuildPosition(
@@ -545,8 +544,8 @@
                           i == trackPositions.size() - 1)) {
       gapOffset = LayoutUnit();
     }
-    PhysicalOffset number_position(
-        trackPositions.at(i) - gapOffset - first_offset, alt_axis_pos);
+    PhysicalOffset number_position(trackPositions.at(i) - gapOffset,
+                                   alt_axis_pos);
     if (direction == kForRows)
       number_position = Transpose(number_position);
     number_positions->pushValue(BuildPosition(
@@ -699,7 +698,7 @@
       LayoutUnit gap_offset =
           index > 0 && index < tracks.size() - 1 ? gap / 2 : LayoutUnit();
 
-      LayoutUnit main_axis_pos = track - gap_offset - first_offset;
+      LayoutUnit main_axis_pos = track - gap_offset;
       PhysicalOffset line_name_pos(main_axis_pos, alt_axis_pos);
 
       if (direction == kForRows)
@@ -711,8 +710,8 @@
       line->setString("name", name);
       // TODO (alexrudenko): offset should be removed once the frontend starts
       // using absolute positions.
-      line->setValue("offset",
-                     protocol::FundamentalValue::create(main_axis_pos * scale));
+      line->setValue("offset", protocol::FundamentalValue::create(
+                                   (main_axis_pos - first_offset) * scale));
 
       lines->pushValue(std::move(line));
     }
diff --git a/third_party/blink/renderer/core/layout/BUILD.gn b/third_party/blink/renderer/core/layout/BUILD.gn
index 1b9e856..74381bb 100644
--- a/third_party/blink/renderer/core/layout/BUILD.gn
+++ b/third_party/blink/renderer/core/layout/BUILD.gn
@@ -627,7 +627,7 @@
     sources += [ "layout_theme_font_provider_default.cc" ]
   }
 
-  if (is_linux) {
+  if (is_linux || is_chromeos) {
     sources += [
       "layout_theme_linux.cc",
       "layout_theme_linux.h",
diff --git a/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.cc b/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.cc
index 8757357..8433a3fe 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_container_fragment_builder.cc
@@ -73,12 +73,17 @@
         if (!containing_block_fragment)
           containing_block_fragment = fragment;
 
+        LogicalOffset containing_block_offset =
+            descendant.containing_block_offset.ConvertToLogical(
+                GetWritingMode(), Direction(), child.Size(), PhysicalSize());
+        containing_block_offset += child_offset;
+
         NGLogicalStaticPosition static_position =
             descendant.static_position.ConvertToLogical(
                 GetWritingMode(), Direction(), PhysicalSize());
         oof_positioned_fragmentainer_descendants_.emplace_back(
             descendant.node, static_position, descendant.inline_container,
-            /* needs_block_offset_adjustment */ false,
+            /* needs_block_offset_adjustment */ false, containing_block_offset,
             containing_block_fragment);
       }
     }
diff --git a/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_positioned_node.h b/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_positioned_node.h
index 6b332b1..7d4a2509 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_positioned_node.h
+++ b/third_party/blink/renderer/core/layout/ng/ng_out_of_flow_positioned_node.h
@@ -37,17 +37,20 @@
   NGPhysicalStaticPosition static_position;
   // Continuation root of the optional inline container.
   const LayoutInline* inline_container;
+  PhysicalOffset containing_block_offset;
   scoped_refptr<const NGPhysicalContainerFragment> containing_block_fragment;
 
   NGPhysicalOutOfFlowPositionedNode(
       NGBlockNode node,
       NGPhysicalStaticPosition static_position,
       const LayoutInline* inline_container = nullptr,
+      PhysicalOffset containing_block_offset = PhysicalOffset(),
       scoped_refptr<const NGPhysicalContainerFragment>
           containing_block_fragment = nullptr)
       : node(node),
         static_position(static_position),
         inline_container(inline_container),
+        containing_block_offset(containing_block_offset),
         containing_block_fragment(std::move(containing_block_fragment)) {
     DCHECK(!inline_container ||
            inline_container == inline_container->ContinuationRoot());
@@ -66,6 +69,7 @@
   // Continuation root of the optional inline container.
   const LayoutInline* inline_container;
   bool needs_block_offset_adjustment;
+  LogicalOffset containing_block_offset;
   scoped_refptr<const NGPhysicalContainerFragment> containing_block_fragment;
 
   NGLogicalOutOfFlowPositionedNode(
@@ -73,12 +77,14 @@
       NGLogicalStaticPosition static_position,
       const LayoutInline* inline_container = nullptr,
       bool needs_block_offset_adjustment = false,
+      LogicalOffset containing_block_offset = LogicalOffset(),
       scoped_refptr<const NGPhysicalContainerFragment>
           containing_block_fragment = nullptr)
       : node(node),
         static_position(static_position),
         inline_container(inline_container),
         needs_block_offset_adjustment(needs_block_offset_adjustment),
+        containing_block_offset(containing_block_offset),
         containing_block_fragment(std::move(containing_block_fragment)) {
     DCHECK(!inline_container ||
            inline_container == inline_container->ContinuationRoot());
diff --git a/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.cc b/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.cc
index 61b61767..2cfed6f 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.cc
@@ -163,7 +163,13 @@
         descendant.node,
         descendant.static_position.ConvertToPhysical(
             builder->Style().GetWritingMode(), builder->Direction(), size),
-        descendant.inline_container, descendant.containing_block_fragment);
+        descendant.inline_container,
+        descendant.containing_block_offset.ConvertToPhysical(
+            builder->Style().GetWritingDirection(), size,
+            descendant.containing_block_fragment
+                ? descendant.containing_block_fragment->Size()
+                : PhysicalSize()),
+        descendant.containing_block_fragment);
   }
 }
 
diff --git a/third_party/blink/renderer/core/layout/scroll_anchor.cc b/third_party/blink/renderer/core/layout/scroll_anchor.cc
index 117a16a..ad15ffa 100644
--- a/third_party/blink/renderer/core/layout/scroll_anchor.cc
+++ b/third_party/blink/renderer/core/layout/scroll_anchor.cc
@@ -597,9 +597,13 @@
     // and attempt to re-find the anchor. The user-visible effect should end up
     // roughly the same.
     ScrollOffset current_offset = scroller_->GetScrollOffset();
-    FloatPoint desired_point =
-        anchor_object->AbsoluteBoundingBoxFloatRect().Location() +
-        current_offset;
+    FloatRect bounding_box = anchor_object->AbsoluteBoundingBoxFloatRect();
+    FloatPoint location_point =
+        anchor_object->Style()->IsFlippedBlocksWritingMode()
+            ? bounding_box.MaxXMinYCorner()
+            : bounding_box.Location();
+    FloatPoint desired_point = location_point + current_offset;
+
     ScrollOffset desired_offset =
         ScrollOffset(desired_point.X(), desired_point.Y());
     ScrollOffset delta =
@@ -647,6 +651,7 @@
   SerializedAnchor new_anchor(
       ComputeUniqueSelector(anchor_object_->GetNode()),
       ComputeRelativeOffset(anchor_object_, scroller_, corner_));
+
   if (new_anchor.IsValid()) {
     saved_selector_ = new_anchor.selector;
   }
diff --git a/third_party/blink/renderer/core/layout/scroll_anchor_test.cc b/third_party/blink/renderer/core/layout/scroll_anchor_test.cc
index df6bb25..e53134f 100644
--- a/third_party/blink/renderer/core/layout/scroll_anchor_test.cc
+++ b/third_party/blink/renderer/core/layout/scroll_anchor_test.cc
@@ -751,6 +751,30 @@
   ValidateSerializedAnchor("html>body>.barbaz", LayoutPoint(-50, 0));
 }
 
+TEST_P(ScrollAnchorTest, RestoreAnchorVerticalRlWritingMode) {
+  SetBodyInnerHTML(R"HTML(
+      <style>
+      body {
+          height: 100px;
+          margin: 0;
+          writing-mode:
+          vertical-rl;
+        }
+        div.big { width: 800px; }
+        div { width: 100px; height: 100px; }
+      </style>
+      <div class='big'></div>
+      <div id='last'></div>
+      )HTML");
+
+  SerializedAnchor serialized_anchor("#last", LayoutPoint(0, 0));
+
+  EXPECT_TRUE(
+      GetScrollAnchor(LayoutViewport()).RestoreAnchor(serialized_anchor));
+  EXPECT_EQ(LayoutViewport()->ScrollOffsetInt().Width(), 0);
+  EXPECT_EQ(LayoutViewport()->ScrollOffsetInt().Height(), 0);
+}
+
 TEST_P(ScrollAnchorTest, SerializeAnchorQualifiedTagName) {
   SetBodyInnerHTML(R"HTML(
       <style>
diff --git a/third_party/blink/renderer/core/paint/ng/ng_box_fragment_painter_test.cc b/third_party/blink/renderer/core/paint/ng/ng_box_fragment_painter_test.cc
index bc7e917..43b17aa 100644
--- a/third_party/blink/renderer/core/paint/ng/ng_box_fragment_painter_test.cc
+++ b/third_party/blink/renderer/core/paint/ng/ng_box_fragment_painter_test.cc
@@ -19,6 +19,24 @@
 
 namespace blink {
 
+namespace {
+
+void ExtractLinks(const cc::PaintOpBuffer* buffer, std::vector<GURL>* links) {
+  for (cc::PaintOpBuffer::Iterator it(buffer); it; ++it) {
+    if (it->GetType() == cc::PaintOpType::Annotate) {
+      auto* annotate_op = static_cast<cc::AnnotateOp*>(*it);
+      links->push_back(GURL(
+          std::string(reinterpret_cast<const char*>(annotate_op->data->data()),
+                      annotate_op->data->size())));
+    } else if (it->GetType() == cc::PaintOpType::DrawRecord) {
+      auto* record_op = static_cast<cc::DrawRecordOp*>(*it);
+      ExtractLinks(record_op->record.get(), links);
+    }
+  }
+}
+
+}  // namespace
+
 class NGBoxFragmentPainterTest : public PaintControllerPaintTest,
                                  private ScopedLayoutNGForTest {
  public:
@@ -126,10 +144,12 @@
           kGlobalPaintFlattenCompositingLayers,
       CullRect::Infinite());
 
-  builder.EndRecording();
-  ASSERT_EQ(tracker.GetLinks().size(), 2U);
-  EXPECT_EQ(tracker.GetLinks()[0]->url, "https://www.chromium.org/");
-  EXPECT_EQ(tracker.GetLinks()[1]->url, "https://www.wikipedia.org/");
+  auto record = builder.EndRecording();
+  std::vector<GURL> links;
+  ExtractLinks(record.get(), &links);
+  ASSERT_EQ(links.size(), 2U);
+  EXPECT_EQ(links[0].spec(), "https://www.chromium.org/");
+  EXPECT_EQ(links[1].spec(), "https://www.wikipedia.org/");
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/paint/video_painter.cc b/third_party/blink/renderer/core/paint/video_painter.cc
index 89ee4c2..e892ef5d 100644
--- a/third_party/blink/renderer/core/paint/video_painter.cc
+++ b/third_party/blink/renderer/core/paint/video_painter.cc
@@ -51,6 +51,9 @@
   content_box_rect.Move(paint_offset);
 
   if (context.IsPaintingPreview()) {
+    // Create a canvas and draw a URL rect to it for the paint preview.
+    BoxDrawingRecorder recorder(context, layout_video_, paint_info.phase,
+                                paint_offset);
     context.SetURLForRect(layout_video_.GetDocument().Url(),
                           snapped_replaced_rect);
   }
diff --git a/third_party/blink/renderer/core/paint/video_painter_test.cc b/third_party/blink/renderer/core/paint/video_painter_test.cc
index a9c0aae8..b9d3264c 100644
--- a/third_party/blink/renderer/core/paint/video_painter_test.cc
+++ b/third_party/blink/renderer/core/paint/video_painter_test.cc
@@ -22,6 +22,23 @@
 namespace blink {
 namespace {
 
+void ExtractLinks(const cc::PaintOpBuffer* buffer,
+                  std::vector<std::pair<GURL, SkRect>>* links) {
+  for (cc::PaintOpBuffer::Iterator it(buffer); it; ++it) {
+    if (it->GetType() == cc::PaintOpType::Annotate) {
+      auto* annotate_op = static_cast<cc::AnnotateOp*>(*it);
+      links->push_back(std::make_pair(
+          GURL(std::string(
+              reinterpret_cast<const char*>(annotate_op->data->data()),
+              annotate_op->data->size())),
+          annotate_op->rect));
+    } else if (it->GetType() == cc::PaintOpType::DrawRecord) {
+      auto* record_op = static_cast<cc::DrawRecordOp*>(*it);
+      ExtractLinks(record_op->record.get(), links);
+    }
+  }
+}
+
 class StubWebMediaPlayer : public EmptyWebMediaPlayer {
  public:
   StubWebMediaPlayer(WebMediaPlayerClient* client) : client_(client) {}
@@ -164,13 +181,15 @@
       recorder.beginRecording(bounds().width(), bounds().height());
   canvas->SetPaintPreviewTracker(&tracker);
 
-  EXPECT_EQ(0lu, tracker.GetLinks().size());
   GetLocalMainFrame().CapturePaintPreview(WebRect(bounds()), canvas,
                                           /*include_linked_destinations=*/true);
+  auto record = recorder.finishRecordingAsPicture();
+  std::vector<std::pair<GURL, SkRect>> links;
+  ExtractLinks(record.get(), &links);
 
-  ASSERT_EQ(1lu, tracker.GetLinks().size());
-  EXPECT_EQ("http://test.com/", tracker.GetLinks()[0]->url);
-  EXPECT_EQ(gfx::Rect(300, 300), tracker.GetLinks()[0]->rect);
+  ASSERT_EQ(1lu, links.size());
+  EXPECT_EQ("http://test.com/", links[0].first);
+  EXPECT_EQ(SkRect::MakeWH(300, 300), links[0].second);
 }
 
 }  // namespace
diff --git a/third_party/blink/renderer/core/scheduler_integration_tests/throttling_test.cc b/third_party/blink/renderer/core/scheduler_integration_tests/throttling_test.cc
index 3bff32d..5079751 100644
--- a/third_party/blink/renderer/core/scheduler_integration_tests/throttling_test.cc
+++ b/third_party/blink/renderer/core/scheduler_integration_tests/throttling_test.cc
@@ -27,13 +27,29 @@
 constexpr auto kDefaultThrottledWakeUpInterval =
     base::TimeDelta::FromSeconds(1);
 
+// A SimTest with mock time.
+class ThrottlingTestBase : public SimTest {
+ public:
+  ThrottlingTestBase() {
+    platform_->SetAutoAdvanceNowToPendingTasks(false);
+
+    // Align the time on a 1-minute interval, to simplify expectations.
+    platform_->AdvanceClock(
+        platform_->NowTicks().SnappedToNextTick(
+            base::TimeTicks(), base::TimeDelta::FromMinutes(1)) -
+        platform_->NowTicks());
+  }
+
+  ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler>
+      platform_;
+};
+
 class DisableBackgroundThrottlingIsRespectedTest
-    : public SimTest,
+    : public ThrottlingTestBase,
       private ScopedTimerThrottlingForBackgroundTabsForTest {
  public:
   DisableBackgroundThrottlingIsRespectedTest()
       : ScopedTimerThrottlingForBackgroundTabsForTest(false) {}
-  void SetUp() override { SimTest::SetUp(); }
 };
 
 TEST_F(DisableBackgroundThrottlingIsRespectedTest,
@@ -56,15 +72,15 @@
 
   // Run delayed tasks for 1 second. All tasks should be completed
   // with throttling disabled.
-  test::RunDelayedTasks(base::TimeDelta::FromSeconds(1));
+  platform_->RunForPeriod(base::TimeDelta::FromSeconds(1));
 
   EXPECT_THAT(ConsoleMessages(), ElementsAre("called f", "called f", "called f",
                                              "called f", "called f"));
 }
 
-class BackgroundPageThrottlingTest : public SimTest {};
+class BackgroundPageThrottlingTest : public ThrottlingTestBase {};
 
-TEST_F(BackgroundPageThrottlingTest, BackgroundPagesAreThrottled) {
+TEST_F(BackgroundPageThrottlingTest, TimersThrottledInBackgroundPage) {
   SimRequest main_resource("https://example.com/", "text/html");
 
   LoadURL("https://example.com/");
@@ -82,28 +98,155 @@
   GetDocument().GetPage()->GetPageScheduler()->SetPageVisible(false);
 
   // Make sure that we run no more than one task a second.
-  test::RunDelayedTasks(base::TimeDelta::FromMilliseconds(3000));
-  EXPECT_THAT(
-      ConsoleMessages(),
-      AnyOf(ElementsAre("called f", "called f", "called f"),
-            ElementsAre("called f", "called f", "called f", "called f")));
+  platform_->RunForPeriod(base::TimeDelta::FromSeconds(3));
+  EXPECT_THAT(ConsoleMessages(),
+              ElementsAre("called f", "called f", "called f"));
 }
 
-class IntensiveWakeUpThrottlingTest : public SimTest {
+// Same test as above, but using timeout=0.
+TEST_F(BackgroundPageThrottlingTest,
+       ZeroTimeoutTimersThrottledInBackgroundPage) {
+  SimRequest main_resource("https://example.com/", "text/html");
+
+  LoadURL("https://example.com/");
+
+  main_resource.Complete(
+      "(<script>"
+      "  function f(repetitions) {"
+      "     if (repetitions == 0) return;"
+      "     console.log('called f');"
+      "     setTimeout(f, 0, repetitions - 1);"
+      "  }"
+      "  setTimeout(f, 0, 50);"
+      "</script>)");
+
+  GetDocument().GetPage()->GetPageScheduler()->SetPageVisible(false);
+
+  // 0ms timeouts are rounded up to 1ms (https://crbug.com/402694). When the
+  // nesting level is 5, they are rounded up to 4 ms. The duration of a
+  // throttled wake up is 3ms. Therefore, at the 2 first wake ups, the timer
+  // runs twice. At the third wake up, it runs once.
+  platform_->RunForPeriod(base::TimeDelta::FromSeconds(3));
+  EXPECT_THAT(ConsoleMessages(), ElementsAre("called f", "called f", "called f",
+                                             "called f", "called f"));
+}
+
+namespace {
+
+class OptOutZeroTimeoutFromThrottlingTest : public ThrottlingTestBase {
+ public:
+  OptOutZeroTimeoutFromThrottlingTest() {
+    scoped_feature_list_.InitAndEnableFeature(
+        features::kOptOutZeroTimeoutTimersFromThrottling);
+  }
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+}  // namespace
+
+// Verify that in a hidden page, when the kOptOutZeroTimeoutTimersFromThrottling
+// feature is enabled:
+// - setTimeout(..., 0) and setTimeout(..., -1) schedule their callback after
+//   1ms. The 1 ms delay exists for historical reasons crbug.com/402694.
+// - setTimeout(..., 5) schedules its callback at the next aligned time
+TEST_F(OptOutZeroTimeoutFromThrottlingTest, WithoutNesting) {
+  SimRequest main_resource("https://example.com/", "text/html");
+  LoadURL("https://example.com/");
+  main_resource.Complete(
+      "<script>"
+      "  setTimeout(function() {"
+      "    setTimeout(function() { console.log('setTimeout 0'); }, 0);"
+      "    setTimeout(function() { console.log('setTimeout -1'); }, -1);"
+      "    setTimeout(function() { console.log('setTimeout 5'); }, 5);"
+      "  }, 1000);"
+      "</script>");
+  GetDocument().GetPage()->GetPageScheduler()->SetPageVisible(false);
+
+  platform_->RunForPeriod(base::TimeDelta::FromMilliseconds(1001));
+  EXPECT_THAT(ConsoleMessages(), ElementsAre("setTimeout 0", "setTimeout -1"));
+
+  platform_->RunForPeriod(base::TimeDelta::FromMilliseconds(998));
+  EXPECT_THAT(ConsoleMessages(), ElementsAre("setTimeout 0", "setTimeout -1"));
+
+  platform_->RunForPeriod(base::TimeDelta::FromMilliseconds(1));
+  EXPECT_THAT(ConsoleMessages(),
+              ElementsAre("setTimeout 0", "setTimeout -1", "setTimeout 5"));
+}
+
+// Verify that in a hidden page, when the kOptOutZeroTimeoutTimersFromThrottling
+// feature is enabled, a timer created with setTimeout(..., 0) is throttled
+// after 5 nesting levels.
+TEST_F(OptOutZeroTimeoutFromThrottlingTest, SetTimeoutNesting) {
+  SimRequest main_resource("https://example.com/", "text/html");
+  LoadURL("https://example.com/");
+  main_resource.Complete(
+      "<script>"
+      "  function f(repetitions) {"
+      "    if (repetitions == 0) return;"
+      "    console.log('called f');"
+      "    setTimeout(f, 0, repetitions - 1);"
+      "  }"
+      "  setTimeout(f, 0, 50);"
+      "</script>");
+  GetDocument().GetPage()->GetPageScheduler()->SetPageVisible(false);
+
+  platform_->RunForPeriod(base::TimeDelta::FromMilliseconds(1));
+  EXPECT_THAT(ConsoleMessages(), Vector<String>(1, "called f"));
+  platform_->RunForPeriod(base::TimeDelta::FromMilliseconds(1));
+  EXPECT_THAT(ConsoleMessages(), Vector<String>(2, "called f"));
+  platform_->RunForPeriod(base::TimeDelta::FromMilliseconds(1));
+  EXPECT_THAT(ConsoleMessages(), Vector<String>(3, "called f"));
+  platform_->RunForPeriod(base::TimeDelta::FromMilliseconds(1));
+  EXPECT_THAT(ConsoleMessages(), Vector<String>(4, "called f"));
+  platform_->RunForPeriod(base::TimeDelta::FromMilliseconds(995));
+  EXPECT_THAT(ConsoleMessages(), Vector<String>(4, "called f"));
+  platform_->RunForPeriod(base::TimeDelta::FromMilliseconds(1));
+  EXPECT_THAT(ConsoleMessages(), Vector<String>(5, "called f"));
+}
+
+// Verify that in a hidden page, when the kOptOutZeroTimeoutTimersFromThrottling
+// feature is enabled, a timer created with setInterval(..., 0) is throttled
+// after 5 nesting levels.
+TEST_F(OptOutZeroTimeoutFromThrottlingTest, SetIntervalNesting) {
+  SimRequest main_resource("https://example.com/", "text/html");
+  LoadURL("https://example.com/");
+  main_resource.Complete(
+      "<script>"
+      "  function f() {"
+      "    if (repetitions == 0) clearInterval(interval_id);"
+      "    console.log('called f');"
+      "    repetitions = repetitions - 1;"
+      "  }"
+      "  var repetitions = 50;"
+      "  var interval_id = setInterval(f, 0);"
+      "</script>");
+  GetDocument().GetPage()->GetPageScheduler()->SetPageVisible(false);
+
+  platform_->RunForPeriod(base::TimeDelta::FromMilliseconds(1));
+  EXPECT_THAT(ConsoleMessages(), Vector<String>(1, "called f"));
+  platform_->RunForPeriod(base::TimeDelta::FromMilliseconds(1));
+  EXPECT_THAT(ConsoleMessages(), Vector<String>(2, "called f"));
+  platform_->RunForPeriod(base::TimeDelta::FromMilliseconds(1));
+  EXPECT_THAT(ConsoleMessages(), Vector<String>(3, "called f"));
+  platform_->RunForPeriod(base::TimeDelta::FromMilliseconds(1));
+  EXPECT_THAT(ConsoleMessages(), Vector<String>(4, "called f"));
+  platform_->RunForPeriod(base::TimeDelta::FromMilliseconds(995));
+  EXPECT_THAT(ConsoleMessages(), Vector<String>(4, "called f"));
+  platform_->RunForPeriod(base::TimeDelta::FromMilliseconds(1));
+  EXPECT_THAT(ConsoleMessages(), Vector<String>(5, "called f"));
+}
+
+namespace {
+
+class IntensiveWakeUpThrottlingTest : public ThrottlingTestBase {
  public:
   IntensiveWakeUpThrottlingTest() {
     scoped_feature_list_.InitWithFeatures(
         {features::kIntensiveWakeUpThrottling},
         // Disable freezing because it hides the effect of intensive throttling.
         {features::kStopInBackground});
-
-    platform_->SetAutoAdvanceNowToPendingTasks(false);
-
-    // Align the time on a 1-minute interval, to simplify expectations.
-    platform_->AdvanceClock(
-        platform_->NowTicks().SnappedToNextTick(
-            base::TimeTicks(), base::TimeDelta::FromMinutes(1)) -
-        platform_->NowTicks());
   }
 
   void TestNoIntensiveThrotlingOnTitleOrFaviconUpdate() {
@@ -133,20 +276,15 @@
     EXPECT_THAT(ConsoleMessages(), expected_ouput);
   }
 
-  ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler>
-      platform_;
-
  private:
   base::test::ScopedFeatureList scoped_feature_list_;
 };
 
-namespace {
-
 // Use to install a function that does not actually communicate with the user.
 constexpr char kCommunicationNop[] =
     "<script>"
-    "  function maybeCommunicateInBackground() {       "
-    "    return;                      "
+    "  function maybeCommunicateInBackground() {"
+    "    return;"
     "  }"
     "</script>";
 
@@ -154,7 +292,7 @@
 // update.
 constexpr char kCommunicateThroughTitleScript[] =
     "<script>"
-    "  function maybeCommunicateInBackground() {       "
+    "  function maybeCommunicateInBackground() {"
     "    document.title += \"A\";"
     "  }"
     "</script>";
@@ -163,7 +301,7 @@
 // update.
 constexpr char kCommunicateThroughFavisonScript[] =
     "<script>"
-    "  function maybeCommunicateInBackground() {       "
+    "  function maybeCommunicateInBackground() {"
     "  document.querySelector(\"link[rel*='icon']\").href = \"favicon.ico\";"
     "  }"
     "</script>";
diff --git a/third_party/blink/renderer/core/workers/dedicated_worker.h b/third_party/blink/renderer/core/workers/dedicated_worker.h
index c5f64f7..f310727 100644
--- a/third_party/blink/renderer/core/workers/dedicated_worker.h
+++ b/third_party/blink/renderer/core/workers/dedicated_worker.h
@@ -10,7 +10,7 @@
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "services/network/public/mojom/url_loader_factory.mojom-blink.h"
 #include "third_party/blink/public/common/loader/worker_main_script_load_parameters.h"
-#include "third_party/blink/public/common/tokens/worker_tokens.h"
+#include "third_party/blink/public/common/tokens/tokens.h"
 #include "third_party/blink/public/mojom/browser_interface_broker.mojom-blink-forward.h"
 #include "third_party/blink/public/platform/web_dedicated_worker.h"
 #include "third_party/blink/public/platform/web_dedicated_worker_host_factory_client.h"
diff --git a/third_party/blink/renderer/core/workers/dedicated_worker_global_scope.h b/third_party/blink/renderer/core/workers/dedicated_worker_global_scope.h
index 258ff1ff..50ea000 100644
--- a/third_party/blink/renderer/core/workers/dedicated_worker_global_scope.h
+++ b/third_party/blink/renderer/core/workers/dedicated_worker_global_scope.h
@@ -32,7 +32,7 @@
 #define THIRD_PARTY_BLINK_RENDERER_CORE_WORKERS_DEDICATED_WORKER_GLOBAL_SCOPE_H_
 
 #include <memory>
-#include "third_party/blink/public/common/tokens/worker_tokens.h"
+#include "third_party/blink/public/common/tokens/tokens.h"
 #include "third_party/blink/renderer/core/animation_frame/worker_animation_frame_provider.h"
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/messaging/message_port.h"
diff --git a/third_party/blink/renderer/core/workers/dedicated_worker_messaging_proxy.h b/third_party/blink/renderer/core/workers/dedicated_worker_messaging_proxy.h
index a73e37a7..cfc9757 100644
--- a/third_party/blink/renderer/core/workers/dedicated_worker_messaging_proxy.h
+++ b/third_party/blink/renderer/core/workers/dedicated_worker_messaging_proxy.h
@@ -10,7 +10,7 @@
 #include "base/memory/scoped_refptr.h"
 #include "base/optional.h"
 #include "services/network/public/mojom/referrer_policy.mojom-blink-forward.h"
-#include "third_party/blink/public/common/tokens/worker_tokens.h"
+#include "third_party/blink/public/common/tokens/tokens.h"
 #include "third_party/blink/public/mojom/messaging/transferable_message.mojom-blink-forward.h"
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/messaging/message_port.h"
diff --git a/third_party/blink/renderer/core/workers/dedicated_worker_object_proxy.h b/third_party/blink/renderer/core/workers/dedicated_worker_object_proxy.h
index 08206d0..f8b0b92a 100644
--- a/third_party/blink/renderer/core/workers/dedicated_worker_object_proxy.h
+++ b/third_party/blink/renderer/core/workers/dedicated_worker_object_proxy.h
@@ -34,7 +34,7 @@
 #include <memory>
 #include "base/macros.h"
 #include "base/memory/scoped_refptr.h"
-#include "third_party/blink/public/common/tokens/worker_tokens.h"
+#include "third_party/blink/public/common/tokens/tokens.h"
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/messaging/message_port.h"
 #include "third_party/blink/renderer/core/workers/threaded_object_proxy_base.h"
diff --git a/third_party/blink/renderer/core/workers/shared_worker_global_scope.h b/third_party/blink/renderer/core/workers/shared_worker_global_scope.h
index 9f729d9..d6ae45c 100644
--- a/third_party/blink/renderer/core/workers/shared_worker_global_scope.h
+++ b/third_party/blink/renderer/core/workers/shared_worker_global_scope.h
@@ -34,7 +34,7 @@
 #include <memory>
 #include "services/metrics/public/cpp/ukm_source_id.h"
 #include "third_party/blink/public/common/messaging/message_port_channel.h"
-#include "third_party/blink/public/common/tokens/worker_tokens.h"
+#include "third_party/blink/public/common/tokens/tokens.h"
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/workers/global_scope_creation_params.h"
 #include "third_party/blink/renderer/core/workers/worker_global_scope.h"
diff --git a/third_party/blink/renderer/core/workers/shared_worker_thread.h b/third_party/blink/renderer/core/workers/shared_worker_thread.h
index ac16212..a400165 100644
--- a/third_party/blink/renderer/core/workers/shared_worker_thread.h
+++ b/third_party/blink/renderer/core/workers/shared_worker_thread.h
@@ -32,7 +32,7 @@
 
 #include <memory>
 #include "services/metrics/public/cpp/ukm_source_id.h"
-#include "third_party/blink/public/common/tokens/worker_tokens.h"
+#include "third_party/blink/public/common/tokens/tokens.h"
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/workers/worker_thread.h"
 
diff --git a/third_party/blink/renderer/modules/indexeddb/idb_cursor.cc b/third_party/blink/renderer/modules/indexeddb/idb_cursor.cc
index 76d204b..9c5145c 100644
--- a/third_party/blink/renderer/modules/indexeddb/idb_cursor.cc
+++ b/third_party/blink/renderer/modules/indexeddb/idb_cursor.cc
@@ -123,7 +123,9 @@
   IDBObjectStore* object_store = EffectiveObjectStore();
   return object_store->DoPut(script_state, mojom::IDBPutMode::CursorUpdate,
                              IDBRequest::Source::FromIDBCursor(this), value,
-                             IdbPrimaryKey(), exception_state);
+                             IdbPrimaryKey(), exception_state,
+                             /*optional_custom_callback=*/nullptr,
+                             /*blob_handles_out=*/nullptr);
 }
 
 void IDBCursor::advance(unsigned count, ExceptionState& exception_state) {
diff --git a/third_party/blink/renderer/modules/indexeddb/idb_object_store.cc b/third_party/blink/renderer/modules/indexeddb/idb_object_store.cc
index 231d68c..ac684b1c 100644
--- a/third_party/blink/renderer/modules/indexeddb/idb_object_store.cc
+++ b/third_party/blink/renderer/modules/indexeddb/idb_object_store.cc
@@ -47,6 +47,7 @@
 #include "third_party/blink/renderer/modules/indexeddb/idb_key_path.h"
 #include "third_party/blink/renderer/modules/indexeddb/idb_tracing.h"
 #include "third_party/blink/renderer/modules/indexeddb/idb_value_wrapping.h"
+#include "third_party/blink/renderer/modules/indexeddb/web_idb_callbacks_impl.h"
 #include "third_party/blink/renderer/modules/indexeddb/web_idb_database.h"
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
 #include "third_party/blink/renderer/platform/bindings/script_state.h"
@@ -353,7 +354,9 @@
   IDB_TRACE1("IDBObjectStore::addRequestSetup", "store_name",
              metadata_->name.Utf8());
   return DoPut(script_state, mojom::IDBPutMode::AddOnly, value, key,
-               exception_state);
+               exception_state,
+               /*optional_custom_callback=*/nullptr,
+               /*blob_handles_out=*/nullptr);
 }
 
 IDBRequest* IDBObjectStore::put(ScriptState* script_state,
@@ -364,20 +367,171 @@
              exception_state);
 }
 
-HeapVector<Member<IDBRequest>> IDBObjectStore::putAll(
-    ScriptState* script_state,
-    const HeapVector<ScriptValue>& values,
-    ExceptionState& exception_state) {
+namespace {
+
+class PutAllWebCallbacksAccumulationImpl
+    : public base::RefCounted<PutAllWebCallbacksAccumulationImpl> {
+ public:
+  PutAllWebCallbacksAccumulationImpl(
+      int num_custom_callbacks,
+      std::unique_ptr<WebIDBCallbacks> request_callbacks)
+      : request_callbacks_(std::move(request_callbacks)),
+        num_custom_callbacks_(num_custom_callbacks) {}
+
+  void HandleCustomCallbackSuccess() {
+    DCHECK_GT(num_custom_callbacks_, 0);
+    num_custom_callbacks_--;
+    if (num_custom_callbacks_ == 0)
+      CallRequestCallbacks();
+  }
+
+  void HandleCustomCallbackError(mojom::blink::IDBException code,
+                                 const String& message) {
+    DCHECK_GT(num_custom_callbacks_, 0);
+    num_custom_callbacks_--;
+    received_error_ = true;
+    latest_error_code = code;
+    latest_error_message = message;
+    if (num_custom_callbacks_ == 0)
+      CallRequestCallbacks();
+  }
+
+  void CallRequestCallbacks() {
+    if (!received_error_) {
+      request_callbacks_->Success();
+    } else {
+      request_callbacks_->Error(latest_error_code, latest_error_message);
+    }
+  }
+
+ private:
+  friend class base::RefCounted<PutAllWebCallbacksAccumulationImpl>;
+  ~PutAllWebCallbacksAccumulationImpl() = default;
+
+  std::unique_ptr<WebIDBCallbacks> request_callbacks_;
+  int num_custom_callbacks_;
+  bool received_error_ = false;
+  mojom::blink::IDBException latest_error_code;
+  String latest_error_message;
+};
+
+class PutAllWebCallbacksImpl : public WebIDBCallbacks {
+ public:
+  explicit PutAllWebCallbacksImpl(
+      scoped_refptr<PutAllWebCallbacksAccumulationImpl> callback_accumulator)
+      : callback_accumulator_(callback_accumulator) {}
+
+  void Success() override { NOTREACHED(); }
+
+  void Error(mojom::blink::IDBException code, const String& message) override {
+    callback_accumulator_->HandleCustomCallbackError(code, message);
+  }
+
+  void SetState(base::WeakPtr<WebIDBCursorImpl> cursor,
+                int64_t transaction_id) override {}
+
+  void SuccessNamesAndVersionsList(
+      Vector<mojom::blink::IDBNameAndVersionPtr> names_and_versions) override {
+    NOTREACHED();
+  }
+
+  void SuccessStringList(const Vector<String>&) override { NOTREACHED(); }
+
+  void SuccessCursor(
+      mojo::PendingAssociatedRemote<mojom::blink::IDBCursor> cursor_info,
+      std::unique_ptr<IDBKey> key,
+      std::unique_ptr<IDBKey> primary_key,
+      base::Optional<std::unique_ptr<IDBValue>> optional_value) override {
+    NOTREACHED();
+  }
+
+  void SuccessCursorPrefetch(
+      Vector<std::unique_ptr<IDBKey>> keys,
+      Vector<std::unique_ptr<IDBKey>> primary_keys,
+      Vector<std::unique_ptr<IDBValue>> values) override {
+    NOTREACHED();
+  }
+
+  void SuccessDatabase(
+      mojo::PendingAssociatedRemote<mojom::blink::IDBDatabase> pending_backend,
+      const IDBDatabaseMetadata& metadata) override {
+    NOTREACHED();
+  }
+
+  void SuccessKey(std::unique_ptr<IDBKey> key) override {
+    callback_accumulator_->HandleCustomCallbackSuccess();
+  }
+
+  void SuccessValue(mojom::blink::IDBReturnValuePtr return_value) override {
+    NOTREACHED();
+  }
+
+  void SuccessArray(Vector<mojom::blink::IDBReturnValuePtr> values) override {
+    NOTREACHED();
+  }
+
+  void SuccessInteger(int64_t value) override { NOTREACHED(); }
+
+  void SuccessCursorContinue(
+      std::unique_ptr<IDBKey> key,
+      std::unique_ptr<IDBKey> primary_key,
+      base::Optional<std::unique_ptr<IDBValue>> value) override {
+    NOTREACHED();
+  }
+
+  void Blocked(int64_t old_version) override { NOTREACHED(); }
+
+  void UpgradeNeeded(
+      mojo::PendingAssociatedRemote<mojom::blink::IDBDatabase> pending_database,
+      int64_t old_version,
+      mojom::IDBDataLoss data_loss,
+      const String& data_loss_message,
+      const IDBDatabaseMetadata& metadata) override {
+    NOTREACHED();
+  }
+
+  void DetachRequestFromCallback() override { NOTREACHED(); }
+
+  void ReceiveGetAllResults(
+      bool key_only,
+      mojo::PendingReceiver<mojom::blink::IDBDatabaseGetAllResultSink> receiver)
+      override {
+    NOTREACHED();
+  }
+
+ private:
+  scoped_refptr<PutAllWebCallbacksAccumulationImpl> callback_accumulator_;
+};
+
+}  // namespace
+
+IDBRequest* IDBObjectStore::putAll(ScriptState* script_state,
+                                   const HeapVector<ScriptValue>& values,
+                                   ExceptionState& exception_state) {
   v8::Isolate* isolate = script_state->GetIsolate();
   const ScriptValue& v8_undefined =
       ScriptValue(isolate, v8::Undefined(isolate));
-  HeapVector<Member<IDBRequest>> requests;
+  IDBRequest::AsyncTraceState metrics("IDBObjectStore::putAll");
+  IDBRequest* request = IDBRequest::Create(
+      script_state, this, transaction_.Get(), std::move(metrics));
+  std::unique_ptr<WebIDBCallbacks> request_callbacks =
+      request->CreateWebCallbacks();
+  scoped_refptr<PutAllWebCallbacksAccumulationImpl> callback_accumulator =
+      base::MakeRefCounted<PutAllWebCallbacksAccumulationImpl>(
+          values.size(), std::move(request_callbacks));
   for (const auto& value : values) {
+    std::unique_ptr<WebIDBCallbacks> custom_callback =
+        std::make_unique<PutAllWebCallbacksImpl>(callback_accumulator);
+    Vector<scoped_refptr<BlobDataHandle>> blob_handles_out;
     IDBRequest* result =
-        put(script_state, value, v8_undefined, exception_state);
-    requests.push_back(*result);
+        DoPut(script_state, mojom::IDBPutMode::AddOrUpdate, value, v8_undefined,
+              exception_state, std::move(custom_callback), &blob_handles_out);
+    for (const auto& blob_handle : blob_handles_out) {
+      request->transit_blob_handles().push_back(blob_handle);
+    }
+    DCHECK(result == nullptr);
   }
-  return requests;
+  return request;
 }
 
 IDBRequest* IDBObjectStore::put(ScriptState* script_state,
@@ -387,14 +541,19 @@
   IDB_TRACE1("IDBObjectStore::putRequestSetup", "store_name",
              metadata_->name.Utf8());
   return DoPut(script_state, mojom::IDBPutMode::AddOrUpdate, value, key,
-               exception_state);
+               exception_state,
+               /*optional_custom_callback=*/nullptr,
+               /*blob_handles_out=*/nullptr);
 }
 
-IDBRequest* IDBObjectStore::DoPut(ScriptState* script_state,
-                                  mojom::IDBPutMode put_mode,
-                                  const ScriptValue& value,
-                                  const ScriptValue& key_value,
-                                  ExceptionState& exception_state) {
+IDBRequest* IDBObjectStore::DoPut(
+    ScriptState* script_state,
+    mojom::IDBPutMode put_mode,
+    const ScriptValue& value,
+    const ScriptValue& key_value,
+    ExceptionState& exception_state,
+    std::unique_ptr<WebIDBCallbacks> optional_custom_callback,
+    Vector<scoped_refptr<BlobDataHandle>>* blob_handles_out) {
   std::unique_ptr<IDBKey> key =
       key_value.IsUndefined()
           ? nullptr
@@ -402,17 +561,22 @@
                 script_state->GetIsolate(), key_value, exception_state);
   if (exception_state.HadException())
     return nullptr;
+
   return DoPut(script_state, put_mode,
                IDBRequest::Source::FromIDBObjectStore(this), value, key.get(),
-               exception_state);
+               exception_state, std::move(optional_custom_callback),
+               blob_handles_out);
 }
 
-IDBRequest* IDBObjectStore::DoPut(ScriptState* script_state,
-                                  mojom::IDBPutMode put_mode,
-                                  const IDBRequest::Source& source,
-                                  const ScriptValue& value,
-                                  const IDBKey* key,
-                                  ExceptionState& exception_state) {
+IDBRequest* IDBObjectStore::DoPut(
+    ScriptState* script_state,
+    mojom::IDBPutMode put_mode,
+    const IDBRequest::Source& source,
+    const ScriptValue& value,
+    const IDBKey* key,
+    ExceptionState& exception_state,
+    std::unique_ptr<WebIDBCallbacks> optional_custom_callback,
+    Vector<scoped_refptr<BlobDataHandle>>* blob_handles_out) {
   const char* tracing_name = nullptr;
   switch (put_mode) {
     case mojom::IDBPutMode::AddOrUpdate:
@@ -596,8 +760,11 @@
       base::saturated_cast<base::HistogramBase::Sample>(
           value_wrapper.DataLengthBeforeWrapInBytes() / 1024));
 
-  IDBRequest* request = IDBRequest::Create(
-      script_state, source, transaction_.Get(), std::move(metrics));
+  IDBRequest* request = nullptr;
+  if (!optional_custom_callback) {
+    request = IDBRequest::Create(script_state, source, transaction_.Get(),
+                                 std::move(metrics));
+  }
 
   value_wrapper.DoneCloning();
 
@@ -607,11 +774,18 @@
       value_wrapper.TakeWireBytes(), value_wrapper.TakeBlobInfo(),
       value_wrapper.TakeNativeFileSystemTransferTokens());
 
-  request->transit_blob_handles() = value_wrapper.TakeBlobDataHandles();
+  std::unique_ptr<WebIDBCallbacks> callbacks;
+  if (optional_custom_callback) {
+    *blob_handles_out = value_wrapper.TakeBlobDataHandles();
+    callbacks = std::move(optional_custom_callback);
+  } else {
+    request->transit_blob_handles() = value_wrapper.TakeBlobDataHandles();
+    callbacks = request->CreateWebCallbacks();
+  }
+
   transaction_->transaction_backend()->Put(
       Id(), std::move(idb_value), IDBKey::Clone(key), put_mode,
-      base::WrapUnique(request->CreateWebCallbacks().release()),
-      std::move(index_keys));
+      std::move(callbacks), std::move(index_keys));
 
   return request;
 }
diff --git a/third_party/blink/renderer/modules/indexeddb/idb_object_store.h b/third_party/blink/renderer/modules/indexeddb/idb_object_store.h
index 58353ab..ca85f63 100644
--- a/third_party/blink/renderer/modules/indexeddb/idb_object_store.h
+++ b/third_party/blink/renderer/modules/indexeddb/idb_object_store.h
@@ -102,9 +102,9 @@
                   const ScriptValue& key,
                   ExceptionState&);
   IDBRequest* put(ScriptState*, const ScriptValue& value, ExceptionState&);
-  HeapVector<Member<IDBRequest>> putAll(ScriptState*,
-                                        const HeapVector<ScriptValue>& values,
-                                        ExceptionState&);
+  IDBRequest* putAll(ScriptState*,
+                     const HeapVector<ScriptValue>& values,
+                     ExceptionState&);
   IDBRequest* put(ScriptState*,
                   const ScriptValue& value,
                   const ScriptValue& key,
@@ -131,7 +131,9 @@
                     const IDBRequest::Source&,
                     const ScriptValue&,
                     const IDBKey*,
-                    ExceptionState&);
+                    ExceptionState&,
+                    std::unique_ptr<WebIDBCallbacks> optional_custom_callback,
+                    Vector<scoped_refptr<BlobDataHandle>>* blob_handles_out);
 
   // Used internally and by InspectorIndexedDBAgent:
   IDBRequest* openCursor(
@@ -207,7 +209,9 @@
                     mojom::IDBPutMode,
                     const ScriptValue&,
                     const ScriptValue& key_value,
-                    ExceptionState&);
+                    ExceptionState&,
+                    std::unique_ptr<WebIDBCallbacks> optional_custom_callback,
+                    Vector<scoped_refptr<BlobDataHandle>>* blob_handles_out);
 
   int64_t FindIndexId(const String& name) const;
 
diff --git a/third_party/blink/renderer/modules/indexeddb/idb_object_store.idl b/third_party/blink/renderer/modules/indexeddb/idb_object_store.idl
index 275241c..844774e 100644
--- a/third_party/blink/renderer/modules/indexeddb/idb_object_store.idl
+++ b/third_party/blink/renderer/modules/indexeddb/idb_object_store.idl
@@ -42,7 +42,7 @@
       MeasureAs=IndexedDBWrite,
       RaisesException,
       RuntimeEnabled=IDBPutAll
-    ] sequence<IDBRequest> putAll(sequence<any> values);
+    ] IDBRequest putAll(sequence<any> values);
 
     [CallWith=ScriptState, MeasureAs=IndexedDBWrite, NewObject, RaisesException]
     IDBRequest add(any value, optional any key);
diff --git a/third_party/blink/renderer/modules/plugins/navigator_plugins.cc b/third_party/blink/renderer/modules/plugins/navigator_plugins.cc
index 58a61076..6ea9da60 100644
--- a/third_party/blink/renderer/modules/plugins/navigator_plugins.cc
+++ b/third_party/blink/renderer/modules/plugins/navigator_plugins.cc
@@ -11,6 +11,7 @@
 #include "third_party/blink/renderer/core/frame/settings.h"
 #include "third_party/blink/renderer/modules/plugins/dom_mime_type_array.h"
 #include "third_party/blink/renderer/modules/plugins/dom_plugin_array.h"
+#include "third_party/blink/renderer/platform/privacy_budget/identifiability_digest_helpers.h"
 
 namespace blink {
 
@@ -65,14 +66,14 @@
   IdentifiableTokenBuilder builder;
   for (unsigned i = 0; i < result->length(); i++) {
     DOMPlugin* plugin = result->item(i);
-    builder.AddAtomic(StringToBytesSafe(plugin->name()))
-        .AddAtomic(StringToBytesSafe(plugin->description()))
-        .AddAtomic(StringToBytesSafe(plugin->filename()));
+    builder.AddToken(IdentifiabilityBenignStringToken(plugin->name()))
+        .AddToken(IdentifiabilityBenignStringToken(plugin->description()))
+        .AddToken(IdentifiabilityBenignStringToken(plugin->filename()));
     for (unsigned j = 0; j < plugin->length(); j++) {
       DOMMimeType* mimeType = plugin->item(j);
-      builder.AddAtomic(StringToBytesSafe(mimeType->type()))
-          .AddAtomic(StringToBytesSafe(mimeType->description()))
-          .AddAtomic(StringToBytesSafe(mimeType->suffixes()));
+      builder.AddToken(IdentifiabilityBenignStringToken(mimeType->type()))
+          .AddToken(IdentifiabilityBenignStringToken(mimeType->description()))
+          .AddToken(IdentifiabilityBenignStringToken(mimeType->suffixes()));
     }
   }
   // ...and report to UKM.
@@ -83,10 +84,6 @@
   return result;
 }
 
-base::span<const uint8_t> NavigatorPlugins::StringToBytesSafe(String str) const {
-  return str.Is8Bit() ? str.Span8() : as_bytes(str.Span16());
-}
-
 DOMMimeTypeArray* NavigatorPlugins::mimeTypes(LocalFrame* frame) const {
   if (!mime_types_)
     mime_types_ = MakeGarbageCollected<DOMMimeTypeArray>(frame);
diff --git a/third_party/blink/renderer/modules/plugins/navigator_plugins.h b/third_party/blink/renderer/modules/plugins/navigator_plugins.h
index 15b4b5d9..7f424a4 100644
--- a/third_party/blink/renderer/modules/plugins/navigator_plugins.h
+++ b/third_party/blink/renderer/modules/plugins/navigator_plugins.h
@@ -37,8 +37,6 @@
 
   mutable Member<DOMPluginArray> plugins_;
   mutable Member<DOMMimeTypeArray> mime_types_;
-
-  base::span<const uint8_t> StringToBytesSafe(String str) const;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/BUILD.gn b/third_party/blink/renderer/platform/BUILD.gn
index 2bfd450..ccdfd97 100644
--- a/third_party/blink/renderer/platform/BUILD.gn
+++ b/third_party/blink/renderer/platform/BUILD.gn
@@ -1469,6 +1469,12 @@
     "widget/compositing/layer_tree_view.cc",
     "widget/compositing/layer_tree_view.h",
     "widget/compositing/layer_tree_view_delegate.h",
+    "widget/compositing/queue_report_time_swap_promise.cc",
+    "widget/compositing/queue_report_time_swap_promise.h",
+    "widget/compositing/widget_compositor.cc",
+    "widget/compositing/widget_compositor.h",
+    "widget/compositing/widget_swap_queue.cc",
+    "widget/compositing/widget_swap_queue.h",
     "widget/frame_widget.cc",
     "widget/frame_widget.h",
     "widget/input/compositor_thread_event_queue.cc",
@@ -2030,6 +2036,7 @@
     "widget/compositing/layer_tree_settings_unittest.cc",
     "widget/compositing/layer_tree_view_unittest.cc",
     "widget/compositing/test/stub_layer_tree_view_delegate.h",
+    "widget/compositing/widget_compositor_unittest.cc",
     "widget/input/elastic_overscroll_controller_bezier_unittest.cc",
     "widget/input/elastic_overscroll_controller_exponential_unittest.cc",
     "widget/input/input_event_prediction_unittest.cc",
@@ -2223,7 +2230,7 @@
 
   sources = [ "testing/run_all_tests.cc" ]
 
-  if (is_linux) {
+  if (is_linux || is_chromeos) {
     deps += [
       "//third_party/blink/renderer/platform/scheduler:scheduler_fuzzer_tests",
     ]
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 13ad534..e16fcda 100644
--- a/third_party/blink/renderer/platform/graphics/canvas_resource_provider.cc
+++ b/third_party/blink/renderer/platform/graphics/canvas_resource_provider.cc
@@ -32,6 +32,42 @@
 #include "third_party/skia/include/gpu/GrDirectContext.h"
 
 namespace blink {
+
+class FlushForImageListener {
+  // With deferred rendering it's possible for a drawImage operation on a canvas
+  // to trigger a copy-on-write if another canvas has a read reference to it.
+  // This can cause serious regressions due to extra allocations:
+  // crbug.com/1030108. FlushForImageListener keeps a list of all active 2d
+  // contexts on a thread and notifies them when one is attempting copy-on
+  // write. If the notified context has a read reference to the canvas
+  // attempting a copy-on-write it then flushes so as to make the copy-on-write
+  // unnecessary.
+ public:
+  static FlushForImageListener* GetFlushForImageListener();
+  void AddObserver(CanvasResourceProvider* observer) {
+    observers_.AddObserver(observer);
+  }
+
+  void RemoveObserver(CanvasResourceProvider* observer) {
+    observers_.RemoveObserver(observer);
+  }
+
+  void NotifyFlushForImage(cc::PaintImage::ContentId content_id) {
+    for (CanvasResourceProvider& obs : observers_)
+      obs.OnFlushForImage(content_id);
+  }
+
+ private:
+  friend class WTF::ThreadSpecific<FlushForImageListener>;
+  base::ObserverList<CanvasResourceProvider> observers_;
+};
+
+static FlushForImageListener* GetFlushForImageListener() {
+  DEFINE_THREAD_SAFE_STATIC_LOCAL(ThreadSpecific<FlushForImageListener>,
+                                  flush_for_image_listener, ());
+  return flush_for_image_listener;
+}
+
 namespace {
 
 bool IsGMBAllowed(IntSize size,
@@ -206,11 +242,15 @@
                                                      ->GetCapabilities()
                                                      .supports_oop_raster) {
     resource_ = NewOrRecycledResource();
+    GetFlushForImageListener()->AddObserver(this);
+
     if (resource_)
       EnsureWriteAccess();
   }
 
-  ~CanvasResourceProviderSharedImage() override {}
+  ~CanvasResourceProviderSharedImage() override {
+    GetFlushForImageListener()->RemoveObserver(this);
+  }
 
   bool IsAccelerated() const final { return is_accelerated_; }
   bool SupportsDirectCompositing() const override { return true; }
@@ -354,6 +394,16 @@
     if (IsGpuContextLost())
       return;
 
+    // Because the cached snapshot is about to be cleared we'll record its
+    // content_id to be used by the FlushForImageListener.
+    // ShouldReplaceTargetBuffer needs this ID in order to let other contexts
+    // know to flush to avoid unnecessary copy-on-writes.
+    PaintImage::ContentId content_id = PaintImage::kInvalidContentId;
+    if (cached_snapshot_) {
+      content_id =
+          cached_snapshot_->PaintImageForCurrentFrame().GetContentIdForFrame(
+              0u);
+    }
     // Since the resource will be updated, the cached snapshot is no longer
     // valid. Note that it is important to release this reference here to not
     // trigger copy-on-write below from the resource ref in the snapshot.
@@ -365,7 +415,7 @@
     // We don't need to do copy-on-write for the resource here since writes to
     // the GMB are deferred until it needs to be dispatched to the display
     // compositor via ProduceCanvasResource.
-    if (is_accelerated_ && ShouldReplaceTargetBuffer()) {
+    if (is_accelerated_ && ShouldReplaceTargetBuffer(content_id)) {
       DCHECK(!current_resource_has_write_access_)
           << "Write access must be released before sharing the resource";
 
@@ -454,7 +504,8 @@
     ri->EndRasterCHROMIUM();
   }
 
-  bool ShouldReplaceTargetBuffer() {
+  bool ShouldReplaceTargetBuffer(
+      PaintImage::ContentId content_id = PaintImage::kInvalidContentId) {
     // If the canvas is single buffered, concurrent read/writes to the resource
     // are allowed. Note that we ignore the resource lost case as well since
     // that only indicates that we did not get a sync token for read/write
@@ -474,8 +525,12 @@
     // Its possible to have deferred work in skia which uses this resource. Try
     // flushing once to see if that releases the read refs. We can avoid a copy
     // by queuing this work before writing to this resource.
-    if (is_accelerated_)
+    if (is_accelerated_) {
+      // Another context may have a read reference to this resource. Flush the
+      // deferred queue in that context so that we don't need to copy.
+      GetFlushForImageListener()->NotifyFlushForImage(content_id);
       surface_->flushAndSubmit();
+    }
 
     return !resource_->HasOneRef();
   }
@@ -1090,6 +1145,15 @@
   }
 }
 
+void CanvasResourceProvider::OnFlushForImage(PaintImage::ContentId content_id) {
+  if (Canvas()) {
+    MemoryManagedPaintCanvas* canvas =
+        static_cast<MemoryManagedPaintCanvas*>(Canvas());
+    if (canvas->IsCachingImage(content_id))
+      this->FlushCanvas();
+  }
+}
+
 void CanvasResourceProvider::ReleaseLockedImages() {
   if (canvas_image_provider_)
     canvas_image_provider_->ReleaseLockedImages();
diff --git a/third_party/blink/renderer/platform/graphics/canvas_resource_provider.h b/third_party/blink/renderer/platform/graphics/canvas_resource_provider.h
index b8ed7f9..6cd6cf6d 100644
--- a/third_party/blink/renderer/platform/graphics/canvas_resource_provider.h
+++ b/third_party/blink/renderer/platform/graphics/canvas_resource_provider.h
@@ -11,8 +11,10 @@
 #include "third_party/blink/renderer/platform/graphics/canvas_resource.h"
 #include "third_party/blink/renderer/platform/graphics/identifiability_paint_op_digest.h"
 #include "third_party/blink/renderer/platform/graphics/image_orientation.h"
+#include "third_party/blink/renderer/platform/graphics/memory_managed_paint_recorder.h"
 #include "third_party/blink/renderer/platform/graphics/paint/paint_recorder.h"
 #include "third_party/blink/renderer/platform/instrumentation/canvas_memory_dump_provider.h"
+#include "third_party/blink/renderer/platform/wtf/thread_specific.h"
 #include "third_party/skia/include/core/SkSurface.h"
 
 class GrDirectContext;
@@ -58,6 +60,7 @@
 
 class PLATFORM_EXPORT CanvasResourceProvider
     : public WebGraphicsContext3DProviderWrapper::DestructionObserver,
+      public base::CheckedObserver,
       public CanvasMemoryDumpClient {
  public:
   // These values are persisted to logs. Entries should not be renumbered and
@@ -255,6 +258,8 @@
   SkSurface::ContentChangeMode mode_ = SkSurface::kRetain_ContentChangeMode;
 
  private:
+  friend class FlushForImageListener;
+  void OnFlushForImage(cc::PaintImage::ContentId content_id);
   virtual sk_sp<SkSurface> CreateSkSurface() const = 0;
   virtual scoped_refptr<CanvasResource> CreateResource();
   virtual bool UseOopRasterization() { return false; }
@@ -282,7 +287,7 @@
   const bool is_origin_top_left_;
   std::unique_ptr<CanvasImageProvider> canvas_image_provider_;
   std::unique_ptr<cc::SkiaPaintCanvas> skia_canvas_;
-  std::unique_ptr<PaintRecorder> recorder_;
+  std::unique_ptr<MemoryManagedPaintRecorder> recorder_;
 
   bool needs_flush_ = false;
 
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 114cd18..41058175f 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
@@ -524,4 +524,52 @@
             kColorParams.GetOpacityMode());
 }
 
+TEST_F(CanvasResourceProviderTest, FlushForImage) {
+  const IntSize kSize(10, 10);
+  const CanvasColorParams kColorParams(
+      CanvasColorSpace::kSRGB, CanvasColorParams::GetNativeCanvasPixelFormat(),
+      kNonOpaque);
+
+  auto src_provider = CanvasResourceProvider::CreateSharedImageProvider(
+      kSize, context_provider_wrapper_, kMedium_SkFilterQuality, kColorParams,
+      true /* is_origin_top_left */, RasterMode::kGPU, 0u);
+
+  auto dst_provider = CanvasResourceProvider::CreateSharedImageProvider(
+      kSize, context_provider_wrapper_, kMedium_SkFilterQuality, kColorParams,
+      true /* is_origin_top_left */, RasterMode::kGPU, 0u);
+
+  MemoryManagedPaintCanvas* dst_canvas =
+      static_cast<MemoryManagedPaintCanvas*>(dst_provider->Canvas());
+
+  PaintImage paint_image =
+      src_provider->Snapshot()->PaintImageForCurrentFrame();
+  PaintImage::ContentId src_content_id = paint_image.GetContentIdForFrame(0u);
+
+  EXPECT_FALSE(dst_canvas->IsCachingImage(src_content_id));
+
+  cc::PaintFlags flags;
+  dst_canvas->drawImage(paint_image, 0, 0, &flags);
+
+  EXPECT_TRUE(dst_canvas->IsCachingImage(src_content_id));
+
+  src_provider->Canvas()->clear(
+      SK_ColorWHITE);  // Modify the canvas to trigger OnFlushForImage
+  src_provider
+      ->ProduceCanvasResource();  // So that all the cached draws are executed
+
+  // The paint canvas may have moved
+  dst_canvas = static_cast<MemoryManagedPaintCanvas*>(dst_provider->Canvas());
+
+  // TODO(aaronhk): The resource on the src_provider should be the same before
+  // and after the draw. Something about the program flow within
+  // this testing framework (but not in layout tests) makes a reference to
+  // the src_resource stick around throughout the FlushForImage call so the
+  // src_resource changes in this test. Things work as expected for actual
+  // browser code like canvas_to_canvas_draw.html.
+
+  // OnFlushForImage should detect the modification of the source resource and
+  // clear the cache of the destination canvas to avoid a copy-on-write.
+  EXPECT_FALSE(dst_canvas->IsCachingImage(src_content_id));
+}
+
 }  // 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 ec7473c..90ea268 100644
--- a/third_party/blink/renderer/platform/graphics/graphics_context.cc
+++ b/third_party/blink/renderer/platform/graphics/graphics_context.cc
@@ -355,8 +355,7 @@
 
 }  // namespace
 
-int GraphicsContext::FocusRingOutsetExtent(int offset,
-                                           int width) {
+int GraphicsContext::FocusRingOutsetExtent(int offset, int width) {
   // Unlike normal outlines (whole width is outside of the offset), focus
   // rings can be drawn with the center of the path aligned with the offset, so
   // only half of the width is outside of the offset.
@@ -1268,13 +1267,7 @@
 
 void GraphicsContext::SetURLForRect(const KURL& link,
                                     const IntRect& dest_rect) {
-  DCHECK(canvas_ || tracker_);
-
-  // Intercept URL rects when painting previews.
-  if (IsPaintingPreview() && tracker_) {
-    tracker_->AnnotateLink(GURL(link), dest_rect);
-    return;
-  }
+  DCHECK(canvas_);
 
   sk_sp<SkData> url(SkData::MakeWithCString(link.GetString().Utf8().c_str()));
   canvas_->Annotate(cc::PaintCanvas::AnnotationType::URL, dest_rect,
@@ -1283,13 +1276,7 @@
 
 void GraphicsContext::SetURLFragmentForRect(const String& dest_name,
                                             const IntRect& rect) {
-  DCHECK(canvas_ || tracker_);
-
-  // Intercept URL rects when painting previews.
-  if (IsPaintingPreview() && tracker_) {
-    tracker_->AnnotateLink(GURL(dest_name.Utf8()), rect);
-    return;
-  }
+  DCHECK(canvas_);
 
   sk_sp<SkData> sk_dest_name(SkData::MakeWithCString(dest_name.Utf8().c_str()));
   canvas_->Annotate(cc::PaintCanvas::AnnotationType::LINK_TO_DESTINATION, rect,
@@ -1300,6 +1287,10 @@
                                                 const IntPoint& location) {
   DCHECK(canvas_);
 
+  // Paint previews don't make use of linked destinations.
+  if (tracker_)
+    return;
+
   SkRect rect = SkRect::MakeXYWH(location.X(), location.Y(), 0, 0);
   sk_sp<SkData> sk_name(SkData::MakeWithCString(name.Utf8().c_str()));
   canvas_->Annotate(cc::PaintCanvas::AnnotationType::NAMED_DESTINATION, rect,
diff --git a/third_party/blink/renderer/platform/graphics/memory_managed_paint_canvas.cc b/third_party/blink/renderer/platform/graphics/memory_managed_paint_canvas.cc
index 527bbf2..7c11403f 100644
--- a/third_party/blink/renderer/platform/graphics/memory_managed_paint_canvas.cc
+++ b/third_party/blink/renderer/platform/graphics/memory_managed_paint_canvas.cc
@@ -46,4 +46,9 @@
     set_needs_flush_callback_.Run();
 }
 
+bool MemoryManagedPaintCanvas::IsCachingImage(
+    const cc::PaintImage::ContentId content_id) const {
+  return cached_image_ids_.Contains(content_id);
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/graphics/memory_managed_paint_canvas.h b/third_party/blink/renderer/platform/graphics/memory_managed_paint_canvas.h
index 1a7c000..2d2d17a 100644
--- a/third_party/blink/renderer/platform/graphics/memory_managed_paint_canvas.h
+++ b/third_party/blink/renderer/platform/graphics/memory_managed_paint_canvas.h
@@ -7,8 +7,10 @@
 
 #include <memory>
 
+#include "cc/paint/paint_canvas.h"
 #include "cc/paint/record_paint_canvas.h"
 #include "third_party/blink/public/platform/platform.h"
+#include "third_party/blink/renderer/platform/wtf/casting.h"
 #include "third_party/blink/renderer/platform/wtf/hash_set.h"
 
 namespace blink {
@@ -36,10 +38,14 @@
                      const cc::PaintFlags* flags,
                      SkCanvas::SrcRectConstraint constraint) override;
 
+  bool IsCachingImage(const cc::PaintImage::ContentId content_id) const;
+
  private:
   void UpdateMemoryUsage(const cc::PaintImage& image);
 
-  HashSet<int, DefaultHash<int>::Hash, WTF::UnsignedWithZeroKeyHashTraits<int>>
+  HashSet<cc::PaintImage::ContentId,
+          DefaultHash<cc::PaintImage::ContentId>::Hash,
+          WTF::UnsignedWithZeroKeyHashTraits<cc::PaintImage::ContentId>>
       cached_image_ids_;
   uint64_t total_stored_image_memory_ = 0;
 
diff --git a/third_party/blink/renderer/platform/scheduler/BUILD.gn b/third_party/blink/renderer/platform/scheduler/BUILD.gn
index 22550984..3a2eb4a 100644
--- a/third_party/blink/renderer/platform/scheduler/BUILD.gn
+++ b/third_party/blink/renderer/platform/scheduler/BUILD.gn
@@ -262,7 +262,7 @@
     "//third_party/blink/renderer/platform/scheduler:test_support",
   ]
 
-  if (is_linux) {
+  if (is_linux || is_chromeos) {
     sources += [
       "test/fuzzer/sequence_manager_fuzzer_processor.cc",
       "test/fuzzer/sequence_manager_fuzzer_processor.h",
diff --git a/third_party/blink/renderer/platform/scheduler/common/throttling/cpu_time_budget_pool.cc b/third_party/blink/renderer/platform/scheduler/common/throttling/cpu_time_budget_pool.cc
index c1beca5..f3af71aa2 100644
--- a/third_party/blink/renderer/platform/scheduler/common/throttling/cpu_time_budget_pool.cc
+++ b/third_party/blink/renderer/platform/scheduler/common/throttling/cpu_time_budget_pool.cc
@@ -23,7 +23,6 @@
     : BudgetPool(name, budget_pool_controller),
       current_budget_level_(base::TimeDelta(),
                             "RendererScheduler.BackgroundBudgetMs",
-                            budget_pool_controller,
                             tracing_controller,
                             TimeDeltaToMilliseconds),
       last_checkpoint_(now),
diff --git a/third_party/blink/renderer/platform/scheduler/common/tracing_helper.h b/third_party/blink/renderer/platform/scheduler/common/tracing_helper.h
index d82b092..34639cb 100644
--- a/third_party/blink/renderer/platform/scheduler/common/tracing_helper.h
+++ b/third_party/blink/renderer/platform/scheduler/common/tracing_helper.h
@@ -93,14 +93,13 @@
   DISALLOW_NEW();
 
  public:
-  StateTracer(const char* name, const void* object)
-      : name_(name), object_(object), slice_is_open_(false) {
+  explicit StateTracer(const char* name) : name_(name), slice_is_open_(false) {
     internal::ValidateTracingCategory(category);
   }
 
   ~StateTracer() {
     if (slice_is_open_)
-      TRACE_EVENT_NESTABLE_ASYNC_END0(category, name_, TRACE_ID_LOCAL(object_));
+      TRACE_EVENT_NESTABLE_ASYNC_END0(category, name_, TRACE_ID_LOCAL(this));
   }
 
   // String will be copied before leaving this function.
@@ -122,25 +121,23 @@
  private:
   void TraceImpl(const char* state, bool need_copy) {
     if (slice_is_open_) {
-      TRACE_EVENT_NESTABLE_ASYNC_END0(category, name_, TRACE_ID_LOCAL(object_));
+      TRACE_EVENT_NESTABLE_ASYNC_END0(category, name_, TRACE_ID_LOCAL(this));
       slice_is_open_ = false;
     }
     if (!state || !is_enabled())
       return;
 
     if (need_copy) {
-      TRACE_EVENT_NESTABLE_ASYNC_BEGIN1(category, name_,
-                                        TRACE_ID_LOCAL(object_), "state",
-                                        TRACE_STR_COPY(state));
+      TRACE_EVENT_NESTABLE_ASYNC_BEGIN1(category, name_, TRACE_ID_LOCAL(this),
+                                        "state", TRACE_STR_COPY(state));
     } else {
-      TRACE_EVENT_NESTABLE_ASYNC_BEGIN1(
-          category, name_, TRACE_ID_LOCAL(object_), "state", state);
+      TRACE_EVENT_NESTABLE_ASYNC_BEGIN1(category, name_, TRACE_ID_LOCAL(this),
+                                        "state", state);
     }
     slice_is_open_ = true;
   }
 
   const char* const name_;    // Not owned.
-  const void* const object_;  // Not owned.
 
   // We have to track whether slice is open to avoid confusion since assignment,
   // "absent" state and OnTraceLogEnabled can happen anytime.
@@ -160,11 +157,10 @@
 
   TraceableState(T initial_state,
                  const char* name,
-                 const void* object,
                  TraceableVariableController* controller,
                  ConverterFuncPtr converter)
       : TraceableVariable(controller),
-        StateTracer<category>(name, object),
+        StateTracer<category>(name),
         converter_(converter),
         state_(initial_state) {
     Trace();
@@ -227,12 +223,10 @@
 
   TraceableCounter(T initial_value,
                    const char* name,
-                   const void* object,
                    TraceableVariableController* controller,
                    ConverterFuncPtr converter)
       : TraceableVariable(controller),
         name_(name),
-        object_(object),
         converter_(converter),
         value_(initial_value) {
     internal::ValidateTracingCategory(category);
@@ -241,16 +235,10 @@
 
   TraceableCounter(T initial_value,
                    const char* name,
-                   const void* object,
                    TraceableVariableController* controller)
-      : TraceableVariable(controller),
-        name_(name),
-        object_(object),
-        converter_([](const T& value) { return static_cast<double>(value); }),
-        value_(initial_value) {
-    internal::ValidateTracingCategory(category);
-    Trace();
-  }
+      : TraceableCounter(initial_value, name, controller, [](const T& value) {
+          return static_cast<double>(value);
+        }) {}
 
   TraceableCounter& operator=(const T& value) {
     value_ = value;
@@ -281,12 +269,11 @@
   void OnTraceLogEnabled() final { Trace(); }
 
   void Trace() const {
-    TRACE_COUNTER_ID1(category, name_, object_, converter_(value_));
+    TRACE_COUNTER_ID1(category, name_, this, converter_(value_));
   }
 
  private:
   const char* const name_;    // Not owned.
-  const void* const object_;  // Not owned.
   const ConverterFuncPtr converter_;
 
   T value_;
diff --git a/third_party/blink/renderer/platform/scheduler/common/tracing_helper_unittest.cc b/third_party/blink/renderer/platform/scheduler/common/tracing_helper_unittest.cc
index bae7678f..fceebdef 100644
--- a/third_party/blink/renderer/platform/scheduler/common/tracing_helper_unittest.cc
+++ b/third_party/blink/renderer/platform/scheduler/common/tracing_helper_unittest.cc
@@ -36,7 +36,7 @@
     : public TraceableState<int, TracingCategoryName::kDefault> {
  public:
   TraceableStateForTest(TraceableVariableController* controller)
-      : TraceableState(0, "State", controller, controller, SignOfInt) {
+      : TraceableState(0, "State", controller, SignOfInt) {
     // We shouldn't expect trace in constructor here because mock isn't set yet.
     mock_trace_for_test_ = &MockTrace;
   }
@@ -73,9 +73,9 @@
 TEST(TracingHelperTest, TraceableStateOperators) {
   TraceableVariableController controller;
   TraceableState<int, TracingCategoryName::kDebug> x(-1, "X", &controller,
-                                                     &controller, SignOfInt);
+                                                     SignOfInt);
   TraceableState<int, TracingCategoryName::kDebug> y(1, "Y", &controller,
-                                                     &controller, SignOfInt);
+                                                     SignOfInt);
   EXPECT_EQ(0, x + y);
   EXPECT_FALSE(x == y);
   EXPECT_TRUE(x != y);
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.cc b/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.cc
index 159492c..537aebf 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.cc
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.cc
@@ -123,62 +123,52 @@
       throttling_state_(SchedulingLifecycleState::kNotThrottled),
       frame_visible_(true,
                      "FrameScheduler.FrameVisible",
-                     this,
                      &tracing_controller_,
                      VisibilityStateToString),
       frame_paused_(false,
                     "FrameScheduler.FramePaused",
-                    this,
                     &tracing_controller_,
                     PausedStateToString),
       frame_origin_type_(frame_type == FrameType::kMainFrame
                              ? FrameOriginType::kMainFrame
                              : FrameOriginType::kSameOriginToMainFrame,
                          "FrameScheduler.Origin",
-                         this,
                          &tracing_controller_,
                          FrameOriginTypeToString),
       subresource_loading_paused_(false,
                                   "FrameScheduler.SubResourceLoadingPaused",
-                                  this,
                                   &tracing_controller_,
                                   PausedStateToString),
-      url_tracer_("FrameScheduler.URL", this),
+      url_tracer_("FrameScheduler.URL"),
       task_queues_throttled_(false,
                              "FrameScheduler.TaskQueuesThrottled",
-                             this,
                              &tracing_controller_,
                              YesNoStateToString),
       preempted_for_cooperative_scheduling_(
           false,
           "FrameScheduler.PreemptedForCooperativeScheduling",
-          this,
           &tracing_controller_,
           YesNoStateToString),
       all_throttling_opt_out_count_(0),
       aggressive_throttling_opt_out_count_(0),
       opted_out_from_all_throttling_(false,
                                      "FrameScheduler.AllThrottlingDisabled",
-                                     this,
                                      &tracing_controller_,
                                      YesNoStateToString),
       opted_out_from_aggressive_throttling_(
           false,
           "FrameScheduler.AggressiveThrottlingDisabled",
-          this,
           &tracing_controller_,
           YesNoStateToString),
       subresource_loading_pause_count_(0u),
       opted_out_from_back_forward_cache_(
           false,
           "FrameScheduler.OptedOutFromBackForwardCache",
-          this,
           &tracing_controller_,
           YesNoStateToString),
       page_frozen_for_tracing_(
           parent_page_scheduler_ ? parent_page_scheduler_->IsFrozen() : true,
           "FrameScheduler.PageFrozen",
-          this,
           &tracing_controller_,
           FrozenStateToString),
       page_visibility_for_tracing_(
@@ -186,23 +176,19 @@
               ? PageVisibilityState::kVisible
               : PageVisibilityState::kHidden,
           "FrameScheduler.PageVisibility",
-          this,
           &tracing_controller_,
           PageVisibilityStateToString),
       page_keep_active_for_tracing_(
           parent_page_scheduler_ ? parent_page_scheduler_->KeepActive() : false,
           "FrameScheduler.KeepActive",
-          this,
           &tracing_controller_,
           KeepActiveStateToString),
       waiting_for_contentful_paint_(true,
                                     "FrameScheduler.WaitingForContentfulPaint",
-                                    this,
                                     &tracing_controller_,
                                     YesNoStateToString),
       waiting_for_meaningful_paint_(true,
                                     "FrameScheduler.WaitingForMeaningfulPaint",
-                                    this,
                                     &tracing_controller_,
                                     YesNoStateToString) {
   frame_task_queue_controller_.reset(
@@ -386,9 +372,16 @@
     case TaskType::kInternalContentCapture:
       return ThrottleableTaskQueueTraits().SetPrioritisationType(
           QueueTraits::PrioritisationType::kBestEffort);
-    case TaskType::kJavascriptTimer:
+    case TaskType::kJavascriptTimerDelayed:
       return ThrottleableTaskQueueTraits().SetPrioritisationType(
           QueueTraits::PrioritisationType::kJavaScriptTimer);
+    case TaskType::kJavascriptTimerImmediate: {
+      return DeferrableTaskQueueTraits()
+          .SetPrioritisationType(
+              QueueTraits::PrioritisationType::kJavaScriptTimer)
+          .SetCanBeThrottled(!base::FeatureList::IsEnabled(
+              features::kOptOutZeroTimeoutTimersFromThrottling));
+    }
     case TaskType::kInternalLoading:
     case TaskType::kNetworking:
     case TaskType::kNetworkingWithURLLoaderAnnotation:
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl_unittest.cc b/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl_unittest.cc
index 3f60fb9c..12b1cbb 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl_unittest.cc
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl_unittest.cc
@@ -90,7 +90,8 @@
 // FrameSchedulerImpl::CreateQueueTraitsForTaskType().
 constexpr TaskType kAllFrameTaskTypes[] = {
     TaskType::kInternalContentCapture,
-    TaskType::kJavascriptTimer,
+    TaskType::kJavascriptTimerDelayed,
+    TaskType::kJavascriptTimerImmediate,
     TaskType::kInternalLoading,
     TaskType::kNetworking,
     TaskType::kNetworkingWithURLLoaderAnnotation,
@@ -138,7 +139,7 @@
     TaskType::kInternalHighPriorityLocalFrame};
 
 static_assert(
-    static_cast<int>(TaskType::kCount) == 72,
+    static_cast<int>(TaskType::kCount) == 73,
     "When adding a TaskType, make sure that kAllFrameTaskTypes is updated.");
 
 void AppendToVectorTestTask(Vector<String>* vector, String value) {
@@ -324,6 +325,12 @@
             PrioritisationType::kJavaScriptTimer));
   }
 
+  scoped_refptr<TaskQueue> JavaScriptTimerNonThrottleableTaskQueue() {
+    return GetTaskQueue(
+        FrameSchedulerImpl::DeferrableTaskQueueTraits().SetPrioritisationType(
+            PrioritisationType::kJavaScriptTimer));
+  }
+
   scoped_refptr<TaskQueue> LoadingTaskQueue() {
     return GetTaskQueue(FrameSchedulerImpl::LoadingTaskQueueTraits());
   }
@@ -729,7 +736,7 @@
       2 / kTaskPeriod;
   // This TaskRunner is throttled.
   const scoped_refptr<base::SingleThreadTaskRunner> task_runner =
-      frame_scheduler_->GetTaskRunner(TaskType::kJavascriptTimer);
+      frame_scheduler_->GetTaskRunner(TaskType::kJavascriptTimerDelayed);
 
   // Hide the page. This enables wake up throttling.
   EXPECT_TRUE(page_scheduler_->IsPageVisible());
@@ -757,14 +764,14 @@
   constexpr int kNumTasks = 3;
   // |task_runner| is throttled.
   const scoped_refptr<base::SingleThreadTaskRunner> task_runner =
-      frame_scheduler_->GetTaskRunner(TaskType::kJavascriptTimer);
+      frame_scheduler_->GetTaskRunner(TaskType::kJavascriptTimerDelayed);
   // |other_task_runner| is throttled. It belongs to a different frame on the
   // same page.
   const auto other_frame_scheduler = CreateFrameScheduler(
       page_scheduler_.get(), frame_scheduler_delegate_.get(), nullptr,
       FrameScheduler::FrameType::kSubframe);
   const scoped_refptr<base::SingleThreadTaskRunner> other_task_runner =
-      frame_scheduler_->GetTaskRunner(TaskType::kJavascriptTimer);
+      frame_scheduler_->GetTaskRunner(TaskType::kJavascriptTimerDelayed);
 
   // Fast-forward the time to a multiple of |kDefaultThrottledWakeUpInterval|.
   // Otherwise, the time at which tasks run will vary.
@@ -2180,8 +2187,18 @@
   // Make sure the queue lookup and task type to queue traits map works as
   // expected. This test will fail if these task types are moved to different
   // default queues.
-  EXPECT_EQ(GetTaskQueue(TaskType::kJavascriptTimer),
+  EXPECT_EQ(GetTaskQueue(TaskType::kJavascriptTimerDelayed),
             JavaScriptTimerTaskQueue());
+  EXPECT_EQ(GetTaskQueue(TaskType::kJavascriptTimerImmediate),
+            JavaScriptTimerTaskQueue());
+  {
+    base::test::ScopedFeatureList feature_list;
+    feature_list.InitAndEnableFeature(
+        features::kOptOutZeroTimeoutTimersFromThrottling);
+    EXPECT_EQ(GetTaskQueue(TaskType::kJavascriptTimerImmediate),
+              JavaScriptTimerNonThrottleableTaskQueue());
+  }
+
   EXPECT_EQ(GetTaskQueue(TaskType::kWebSocket), DeferrableTaskQueue());
   EXPECT_EQ(GetTaskQueue(TaskType::kDatabaseAccess), PausableTaskQueue());
   EXPECT_EQ(GetTaskQueue(TaskType::kPostedMessage), PausableTaskQueue());
@@ -2193,9 +2210,10 @@
             ForegroundOnlyTaskQueue());
 }
 
-// Verify that kJavascriptTimer is the only non-internal TaskType that can be
-// throttled. This ensures that the Javascript timer throttling experiment only
-// affects wake ups from Javascript timers https://crbug.com/1075553
+// Verify that kJavascriptTimerDelayed is the only non-internal TaskType that
+// can be throttled. This ensures that the Javascript timer throttling
+// experiment only affects wake ups from Javascript timers
+// https://crbug.com/1075553
 TEST_F(FrameSchedulerImplTest, ThrottledTaskTypes) {
   page_scheduler_->SetPageVisible(false);
 
@@ -2205,7 +2223,8 @@
                  << TaskTypeNames::TaskTypeToString(task_type));
     switch (task_type) {
       case TaskType::kInternalContentCapture:
-      case TaskType::kJavascriptTimer:
+      case TaskType::kJavascriptTimerDelayed:
+      case TaskType::kJavascriptTimerImmediate:
       case TaskType::kInternalTranslation:
         EXPECT_TRUE(IsTaskTypeThrottled(task_type));
         break;
@@ -2275,7 +2294,7 @@
 }
 
 TEST_F(FrameSchedulerImplTest, ComputePriorityForDetachedFrame) {
-  auto task_queue = GetTaskQueue(TaskType::kJavascriptTimer);
+  auto task_queue = GetTaskQueue(TaskType::kJavascriptTimerDelayed);
   // Just check that it does not crash.
   page_scheduler_.reset();
   frame_scheduler_->ComputePriority(task_queue.get());
@@ -2415,7 +2434,7 @@
 TEST_F(FrameSchedulerImplTest, FeatureUpload) {
   ResetFrameScheduler(FrameScheduler::FrameType::kMainFrame);
 
-  frame_scheduler_->GetTaskRunner(TaskType::kJavascriptTimer)
+  frame_scheduler_->GetTaskRunner(TaskType::kJavascriptTimerDelayed)
       ->PostTask(
           FROM_HERE,
           base::BindOnce(
@@ -2454,7 +2473,7 @@
 
   FeatureHandle feature_handle;
 
-  frame_scheduler_->GetTaskRunner(TaskType::kJavascriptTimer)
+  frame_scheduler_->GetTaskRunner(TaskType::kJavascriptTimerDelayed)
       ->PostTask(
           FROM_HERE,
           base::BindOnce(
@@ -2474,7 +2493,7 @@
               },
               frame_scheduler_.get(), frame_scheduler_delegate_.get(),
               &feature_handle));
-  frame_scheduler_->GetTaskRunner(TaskType::kJavascriptTimer)
+  frame_scheduler_->GetTaskRunner(TaskType::kJavascriptTimerDelayed)
       ->PostTask(FROM_HERE,
                  base::BindOnce(
                      [](FrameSchedulerImpl* frame_scheduler,
@@ -2584,8 +2603,8 @@
               testing::ElementsAre("V1", "V2", "B1", "B2", "U1", "U2"));
 }
 
-// Verify that tasks posted with TaskType::kJavascriptTimer run at the expected
-// time when throttled.
+// Verify that tasks posted with TaskType::kJavascriptTimerDelayed run at the
+// expected time when throttled.
 TEST_F(FrameSchedulerImplTest, ThrottledJSTimerTasksRunTime) {
   // Snap the time to a multiple of 1 second. Otherwise, the exact run time
   // of throttled tasks after hiding the page will vary.
@@ -2596,7 +2615,7 @@
   page_scheduler_->SetPageVisible(false);
 
   const scoped_refptr<base::SingleThreadTaskRunner> task_runner =
-      frame_scheduler_->GetTaskRunner(TaskType::kJavascriptTimer);
+      frame_scheduler_->GetTaskRunner(TaskType::kJavascriptTimerDelayed);
   std::vector<base::TimeTicks> run_times;
 
   // Post tasks.
@@ -2694,7 +2713,7 @@
 
   // Throttled TaskRunner to which tasks are posted in this test.
   const scoped_refptr<base::SingleThreadTaskRunner> task_runner =
-      frame_scheduler_->GetTaskRunner(TaskType::kJavascriptTimer);
+      frame_scheduler_->GetTaskRunner(TaskType::kJavascriptTimerDelayed);
 
   // Snap the time to a multiple of
   // |kIntensiveThrottlingDurationBetweenWakeUps|. Otherwise, the time at which
@@ -2850,7 +2869,7 @@
 
   // Throttled TaskRunner to which tasks are posted in this test.
   const scoped_refptr<base::SingleThreadTaskRunner> task_runner =
-      frame_scheduler_->GetTaskRunner(TaskType::kJavascriptTimer);
+      frame_scheduler_->GetTaskRunner(TaskType::kJavascriptTimerDelayed);
 
   // Snap the time to a multiple of
   // |kIntensiveThrottlingDurationBetweenWakeUps|. Otherwise, the time at which
@@ -3000,7 +3019,7 @@
        ManySameFrameOriginFrames) {
   ASSERT_FALSE(frame_scheduler_->IsCrossOriginToMainFrame());
   const scoped_refptr<base::SingleThreadTaskRunner> task_runner =
-      frame_scheduler_->GetTaskRunner(TaskType::kJavascriptTimer);
+      frame_scheduler_->GetTaskRunner(TaskType::kJavascriptTimerDelayed);
 
   // Create a FrameScheduler that is same-origin with the main frame, and an
   // associated throttled TaskRunner.
@@ -3010,7 +3029,7 @@
                            FrameScheduler::FrameType::kSubframe);
   ASSERT_FALSE(other_frame_scheduler->IsCrossOriginToMainFrame());
   const scoped_refptr<base::SingleThreadTaskRunner> other_task_runner =
-      other_frame_scheduler->GetTaskRunner(TaskType::kJavascriptTimer);
+      other_frame_scheduler->GetTaskRunner(TaskType::kJavascriptTimerDelayed);
 
   // Snap the time to a multiple of
   // |kIntensiveThrottlingDurationBetweenWakeUps|. Otherwise, the time at which
@@ -3059,14 +3078,14 @@
   constexpr int kNumTasks = 3;
   // |task_runner| is throttled.
   const scoped_refptr<base::SingleThreadTaskRunner> task_runner =
-      frame_scheduler_->GetTaskRunner(TaskType::kJavascriptTimer);
+      frame_scheduler_->GetTaskRunner(TaskType::kJavascriptTimerDelayed);
   // |other_task_runner| is throttled. It belongs to a different frame on the
   // same page.
   const auto other_frame_scheduler = CreateFrameScheduler(
       page_scheduler_.get(), frame_scheduler_delegate_.get(), nullptr,
       FrameScheduler::FrameType::kSubframe);
   const scoped_refptr<base::SingleThreadTaskRunner> other_task_runner =
-      frame_scheduler_->GetTaskRunner(TaskType::kJavascriptTimer);
+      frame_scheduler_->GetTaskRunner(TaskType::kJavascriptTimerDelayed);
 
   // Fast-forward the time to a multiple of
   // |kIntensiveThrottlingDurationBetweenWakeUps|. Otherwise,
@@ -3183,14 +3202,14 @@
   constexpr int kNumTasks = 3;
   // |task_runner| is throttled.
   const scoped_refptr<base::SingleThreadTaskRunner> task_runner =
-      frame_scheduler_->GetTaskRunner(TaskType::kJavascriptTimer);
+      frame_scheduler_->GetTaskRunner(TaskType::kJavascriptTimerDelayed);
   // |other_task_runner| is throttled. It belongs to a different frame on the
   // same page.
   const auto other_frame_scheduler = CreateFrameScheduler(
       page_scheduler_.get(), frame_scheduler_delegate_.get(), nullptr,
       FrameScheduler::FrameType::kSubframe);
   const scoped_refptr<base::SingleThreadTaskRunner> other_task_runner =
-      frame_scheduler_->GetTaskRunner(TaskType::kJavascriptTimer);
+      frame_scheduler_->GetTaskRunner(TaskType::kJavascriptTimerDelayed);
 
   // Fast-forward the time to a multiple of
   // |kIntensiveThrottlingDurationBetweenWakeUps|. Otherwise,
@@ -3312,7 +3331,7 @@
        FrameChangesOriginType) {
   EXPECT_FALSE(frame_scheduler_->IsCrossOriginToMainFrame());
   const scoped_refptr<base::SingleThreadTaskRunner> task_runner =
-      frame_scheduler_->GetTaskRunner(TaskType::kJavascriptTimer);
+      frame_scheduler_->GetTaskRunner(TaskType::kJavascriptTimerDelayed);
 
   // Create a new FrameScheduler that remains cross-origin with the main frame
   // throughout the test.
@@ -3322,7 +3341,8 @@
                            FrameScheduler::FrameType::kSubframe);
   cross_origin_frame_scheduler->SetCrossOriginToMainFrame(true);
   const scoped_refptr<base::SingleThreadTaskRunner> cross_origin_task_runner =
-      cross_origin_frame_scheduler->GetTaskRunner(TaskType::kJavascriptTimer);
+      cross_origin_frame_scheduler->GetTaskRunner(
+          TaskType::kJavascriptTimerDelayed);
 
   // Snap the time to a multiple of
   // |kIntensiveThrottlingDurationBetweenWakeUps|. Otherwise, the time at which
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.cc b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.cc
index 1b9223c..c3cb873 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.cc
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.cc
@@ -348,94 +348,77 @@
                           kShortIdlePeriodDurationPercentile),
       current_use_case(UseCase::kNone,
                        "Scheduler.UseCase",
-                       main_thread_scheduler_impl,
                        &main_thread_scheduler_impl->tracing_controller_,
                        UseCaseToString),
       longest_jank_free_task_duration(
           base::TimeDelta(),
           "Scheduler.LongestJankFreeTaskDuration",
-          main_thread_scheduler_impl,
           &main_thread_scheduler_impl->tracing_controller_,
           TimeDeltaToMilliseconds),
       renderer_pause_count(0,
                            "Scheduler.PauseCount",
-                           main_thread_scheduler_impl,
                            &main_thread_scheduler_impl->tracing_controller_),
       rail_mode_for_tracing(current_policy.rail_mode(),
                             "Scheduler.RAILMode",
-                            main_thread_scheduler_impl,
                             &main_thread_scheduler_impl->tracing_controller_,
                             RAILModeToString),
       renderer_hidden(false,
                       "RendererVisibility",
-                      main_thread_scheduler_impl,
                       &main_thread_scheduler_impl->tracing_controller_,
                       HiddenStateToString),
       renderer_backgrounded(kLaunchingProcessIsBackgrounded,
                             "RendererPriority",
-                            main_thread_scheduler_impl,
                             &main_thread_scheduler_impl->tracing_controller_,
                             BackgroundStateToString),
       keep_active_fetch_or_worker(
           false,
           "Scheduler.KeepRendererActive",
-          main_thread_scheduler_impl,
           &main_thread_scheduler_impl->tracing_controller_,
           YesNoStateToString),
       blocking_input_expected_soon(
           false,
           "Scheduler.BlockingInputExpectedSoon",
-          main_thread_scheduler_impl,
           &main_thread_scheduler_impl->tracing_controller_,
           YesNoStateToString),
       have_reported_blocking_intervention_in_current_policy(
           false,
           "Scheduler.HasReportedBlockingInterventionInCurrentPolicy",
-          main_thread_scheduler_impl,
           &main_thread_scheduler_impl->tracing_controller_,
           YesNoStateToString),
       have_reported_blocking_intervention_since_navigation(
           false,
           "Scheduler.HasReportedBlockingInterventionSinceNavigation",
-          main_thread_scheduler_impl,
           &main_thread_scheduler_impl->tracing_controller_,
           YesNoStateToString),
       has_visible_render_widget_with_touch_handler(
           false,
           "Scheduler.HasVisibleRenderWidgetWithTouchHandler",
-          main_thread_scheduler_impl,
           &main_thread_scheduler_impl->tracing_controller_,
           YesNoStateToString),
       in_idle_period_for_testing(
           false,
           "Scheduler.InIdlePeriod",
-          main_thread_scheduler_impl,
           &main_thread_scheduler_impl->tracing_controller_,
           YesNoStateToString),
       use_virtual_time(false,
                        "Scheduler.UseVirtualTime",
-                       main_thread_scheduler_impl,
                        &main_thread_scheduler_impl->tracing_controller_,
                        YesNoStateToString),
       is_audio_playing(false,
                        "RendererAudioState",
-                       main_thread_scheduler_impl,
                        &main_thread_scheduler_impl->tracing_controller_,
                        AudioPlayingStateToString),
       compositor_will_send_main_frame_not_expected(
           false,
           "Scheduler.CompositorWillSendMainFrameNotExpected",
-          main_thread_scheduler_impl,
           &main_thread_scheduler_impl->tracing_controller_,
           YesNoStateToString),
       has_navigated(false,
                     "Scheduler.HasNavigated",
-                    main_thread_scheduler_impl,
                     &main_thread_scheduler_impl->tracing_controller_,
                     YesNoStateToString),
       pause_timers_for_webview(false,
                                "Scheduler.PauseTimersForWebview",
-                               main_thread_scheduler_impl,
                                &main_thread_scheduler_impl->tracing_controller_,
                                YesNoStateToString),
       background_status_changed_at(now),
@@ -446,19 +429,16 @@
           renderer_backgrounded),
       process_type(WebRendererProcessType::kRenderer,
                    "RendererProcessType",
-                   main_thread_scheduler_impl,
                    &main_thread_scheduler_impl->tracing_controller_,
                    RendererProcessTypeToString),
       task_description_for_tracing(
           base::nullopt,
           "Scheduler.MainThreadTask",
-          main_thread_scheduler_impl,
           &main_thread_scheduler_impl->tracing_controller_,
           OptionalTaskDescriptionToString),
       task_priority_for_tracing(
           base::nullopt,
           "Scheduler.TaskPriority",
-          main_thread_scheduler_impl,
           &main_thread_scheduler_impl->tracing_controller_,
           OptionalTaskPriorityToString),
       virtual_time_policy(VirtualTimePolicy::kAdvance),
@@ -469,7 +449,6 @@
       prioritize_compositing_after_input(
           false,
           "Scheduler.PrioritizeCompositingAfterInput",
-          main_thread_scheduler_impl,
           &main_thread_scheduler_impl->tracing_controller_,
           YesNoStateToString),
       compositor_priority_experiments(main_thread_scheduler_impl),
@@ -482,60 +461,50 @@
     : awaiting_touch_start_response(
           false,
           "Scheduler.AwaitingTouchstartResponse",
-          main_thread_scheduler_impl,
           &main_thread_scheduler_impl->tracing_controller_,
           YesNoStateToString),
       in_idle_period(false,
                      "Scheduler.InIdlePeriod",
-                     main_thread_scheduler_impl,
                      &main_thread_scheduler_impl->tracing_controller_,
                      YesNoStateToString),
       begin_main_frame_on_critical_path(
           false,
           "Scheduler.BeginMainFrameOnCriticalPath",
-          main_thread_scheduler_impl,
           &main_thread_scheduler_impl->tracing_controller_,
           YesNoStateToString),
       last_gesture_was_compositor_driven(
           false,
           "Scheduler.LastGestureWasCompositorDriven",
-          main_thread_scheduler_impl,
           &main_thread_scheduler_impl->tracing_controller_,
           YesNoStateToString),
       default_gesture_prevented(
           true,
           "Scheduler.DefaultGesturePrevented",
-          main_thread_scheduler_impl,
           &main_thread_scheduler_impl->tracing_controller_,
           YesNoStateToString),
       have_seen_a_blocking_gesture(
           false,
           "Scheduler.HaveSeenBlockingGesture",
-          main_thread_scheduler_impl,
           &main_thread_scheduler_impl->tracing_controller_,
           YesNoStateToString),
       waiting_for_any_main_frame_contentful_paint(
           false,
           "Scheduler.WaitingForMainFrameContentfulPaint",
-          main_thread_scheduler_impl,
           &main_thread_scheduler_impl->tracing_controller_,
           YesNoStateToString),
       waiting_for_any_main_frame_meaningful_paint(
           false,
           "Scheduler.WaitingForMeaningfulPaint",
-          main_thread_scheduler_impl,
           &main_thread_scheduler_impl->tracing_controller_,
           YesNoStateToString),
       have_seen_input_since_navigation(
           false,
           "Scheduler.HaveSeenInputSinceNavigation",
-          main_thread_scheduler_impl,
           &main_thread_scheduler_impl->tracing_controller_,
           YesNoStateToString),
       begin_main_frame_scheduled_count(
           0,
           "Scheduler.BeginMainFrameScheduledCount",
-          main_thread_scheduler_impl,
           &main_thread_scheduler_impl->tracing_controller_) {}
 
 MainThreadSchedulerImpl::SchedulingSettings::SchedulingSettings() {
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl_unittest.cc b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl_unittest.cc
index e4b7a31..6ee6e345 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl_unittest.cc
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl_unittest.cc
@@ -3869,8 +3869,8 @@
       CreateFrameScheduler(page_scheduler.get(), &frame_delegate, nullptr,
                            FrameScheduler::FrameType::kSubframe);
 
-  scoped_refptr<TaskQueue> timer_tq =
-      QueueForTaskType(frame_scheduler.get(), TaskType::kJavascriptTimer);
+  scoped_refptr<TaskQueue> timer_tq = QueueForTaskType(
+      frame_scheduler.get(), TaskType::kJavascriptTimerDelayed);
   ForceUpdatePolicyAndGetCurrentUseCase();
 
   EXPECT_FALSE(timer_tq->IsQueueEnabled());
@@ -3892,8 +3892,8 @@
       CreateFrameScheduler(page_scheduler.get(), &frame_delegate, nullptr,
                            FrameScheduler::FrameType::kSubframe);
 
-  scoped_refptr<TaskQueue> timer_tq =
-      QueueForTaskType(frame_scheduler.get(), TaskType::kJavascriptTimer);
+  scoped_refptr<TaskQueue> timer_tq = QueueForTaskType(
+      frame_scheduler.get(), TaskType::kJavascriptTimerDelayed);
 
   FakeInputEvent mouse_move_event{WebInputEvent::Type::kMouseMove,
                                   blink::WebInputEvent::kLeftButtonDown};
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/task_type_names.cc b/third_party/blink/renderer/platform/scheduler/main_thread/task_type_names.cc
index 07d10cf1..f462aab 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/task_type_names.cc
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/task_type_names.cc
@@ -35,8 +35,10 @@
       return "CanvasBlobSerialization";
     case TaskType::kMicrotask:
       return "Microtask";
-    case TaskType::kJavascriptTimer:
-      return "JavascriptTimer";
+    case TaskType::kJavascriptTimerDelayed:
+      return "JavascriptTimerDelayed";
+    case TaskType::kJavascriptTimerImmediate:
+      return "JavascriptTimerImmediate";
     case TaskType::kRemoteEvent:
       return "RemoteEvent";
     case TaskType::kWebSocket:
diff --git a/third_party/blink/renderer/platform/scheduler/worker/worker_scheduler.cc b/third_party/blink/renderer/platform/scheduler/worker/worker_scheduler.cc
index 5d2f6746..04fa08a 100644
--- a/third_party/blink/renderer/platform/scheduler/worker/worker_scheduler.cc
+++ b/third_party/blink/renderer/platform/scheduler/worker/worker_scheduler.cc
@@ -133,7 +133,8 @@
 scoped_refptr<base::SingleThreadTaskRunner> WorkerScheduler::GetTaskRunner(
     TaskType type) const {
   switch (type) {
-    case TaskType::kJavascriptTimer:
+    case TaskType::kJavascriptTimerDelayed:
+    case TaskType::kJavascriptTimerImmediate:
     case TaskType::kPostedMessage:
     case TaskType::kWorkerAnimation:
       return throttleable_task_queue_->CreateTaskRunner(type);
diff --git a/third_party/blink/renderer/platform/scheduler/worker/worker_scheduler_unittest.cc b/third_party/blink/renderer/platform/scheduler/worker/worker_scheduler_unittest.cc
index e979188..a6552d0 100644
--- a/third_party/blink/renderer/platform/scheduler/worker/worker_scheduler_unittest.cc
+++ b/third_party/blink/renderer/platform/scheduler/worker/worker_scheduler_unittest.cc
@@ -286,7 +286,7 @@
   // Tests interlacing pausable, throttable and unpausable tasks and
   // ensures that the pausable & throttable tasks don't run when paused.
   // Throttable
-  PostTestTask(&run_order, "T1", TaskType::kJavascriptTimer);
+  PostTestTask(&run_order, "T1", TaskType::kJavascriptTimerDelayed);
   // Pausable
   PostTestTask(&run_order, "T2", TaskType::kNetworking);
   // Unpausable
@@ -304,7 +304,7 @@
   auto pause_handle = worker_scheduler_->Pause();
   {
     auto pause_handle2 = worker_scheduler_->Pause();
-    PostTestTask(&run_order, "T1", TaskType::kJavascriptTimer);
+    PostTestTask(&run_order, "T1", TaskType::kJavascriptTimerDelayed);
     PostTestTask(&run_order, "T2", TaskType::kNetworking);
   }
   RunUntilIdle();
diff --git a/third_party/blink/renderer/platform/scheduler/worker/worker_thread_scheduler_unittest.cc b/third_party/blink/renderer/platform/scheduler/worker/worker_thread_scheduler_unittest.cc
index 76cce482..4ddb680 100644
--- a/third_party/blink/renderer/platform/scheduler/worker/worker_thread_scheduler_unittest.cc
+++ b/third_party/blink/renderer/platform/scheduler/worker/worker_thread_scheduler_unittest.cc
@@ -539,7 +539,7 @@
   scheduler_->SetUkmRecorderForTest(std::move(owned_ukm_recorder));
 
   base::sequence_manager::FakeTask task(
-      static_cast<int>(TaskType::kJavascriptTimer));
+      static_cast<int>(TaskType::kJavascriptTimerDelayed));
   base::sequence_manager::FakeTaskTiming task_timing(
       base::TimeTicks() + base::TimeDelta::FromMilliseconds(200),
       base::TimeTicks() + base::TimeDelta::FromMilliseconds(700),
@@ -557,7 +557,8 @@
   ukm::TestUkmRecorder::ExpectEntryMetric(entries[0], "RendererBackgrounded",
                                           true);
   ukm::TestUkmRecorder::ExpectEntryMetric(
-      entries[0], "TaskType", static_cast<int>(TaskType::kJavascriptTimer));
+      entries[0], "TaskType",
+      static_cast<int>(TaskType::kJavascriptTimerDelayed));
   ukm::TestUkmRecorder::ExpectEntryMetric(
       entries[0], "FrameStatus",
       static_cast<int>(FrameStatus::kCrossOriginBackground));
diff --git a/third_party/blink/renderer/platform/widget/compositing/queue_report_time_swap_promise.cc b/third_party/blink/renderer/platform/widget/compositing/queue_report_time_swap_promise.cc
new file mode 100644
index 0000000..8a6c899e
--- /dev/null
+++ b/third_party/blink/renderer/platform/widget/compositing/queue_report_time_swap_promise.cc
@@ -0,0 +1,69 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/widget/compositing/queue_report_time_swap_promise.h"
+
+#if defined(OS_ANDROID)
+#include "third_party/blink/public/platform/platform.h"
+#endif
+
+namespace blink {
+
+QueueReportTimeSwapPromise::QueueReportTimeSwapPromise(
+    int source_frame_number,
+    DrainCallback drain_callback,
+    base::OnceClosure swap_callback,
+    scoped_refptr<base::SingleThreadTaskRunner> compositor_task_runner)
+    : source_frame_number_(source_frame_number),
+      drain_callback_(std::move(drain_callback)),
+      swap_callback_(std::move(swap_callback)),
+#if defined(OS_ANDROID)
+      call_swap_on_activate_(
+          Platform::Current()->IsSynchronousCompositingEnabled()),
+#endif
+      compositor_task_runner_(std::move(compositor_task_runner)) {
+}
+
+QueueReportTimeSwapPromise::~QueueReportTimeSwapPromise() {
+  if (compositor_task_runner_ && (drain_callback_ || swap_callback_)) {
+    DCHECK(!compositor_task_runner_->BelongsToCurrentThread());
+    compositor_task_runner_->PostTask(
+        FROM_HERE,
+        base::BindOnce([](DrainCallback, base::OnceClosure) {},
+                       std::move(drain_callback_), std::move(swap_callback_)));
+  }
+}
+
+void QueueReportTimeSwapPromise::WillSwap(
+    viz::CompositorFrameMetadata* metadata) {
+  DCHECK_GT(metadata->frame_token, 0u);
+}
+
+void QueueReportTimeSwapPromise::DidSwap() {
+  if (swap_callback_)
+    std::move(swap_callback_).Run();
+}
+
+cc::SwapPromise::DidNotSwapAction QueueReportTimeSwapPromise::DidNotSwap(
+    DidNotSwapReason reason) {
+  if (reason == cc::SwapPromise::SWAP_FAILS ||
+      reason == cc::SwapPromise::COMMIT_NO_UPDATE) {
+    if (drain_callback_)
+      std::move(drain_callback_).Run(source_frame_number_);
+    if (swap_callback_)
+      std::move(swap_callback_).Run();
+  }
+  return DidNotSwapAction::BREAK_PROMISE;
+}
+
+void QueueReportTimeSwapPromise::DidActivate() {
+  if (drain_callback_)
+    std::move(drain_callback_).Run(source_frame_number_);
+#if defined(OS_ANDROID)
+  if (call_swap_on_activate_ && swap_callback_)
+    std::move(swap_callback_).Run();
+#endif
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/platform/widget/compositing/queue_report_time_swap_promise.h b/third_party/blink/renderer/platform/widget/compositing/queue_report_time_swap_promise.h
new file mode 100644
index 0000000..a1de1aaf
--- /dev/null
+++ b/third_party/blink/renderer/platform/widget/compositing/queue_report_time_swap_promise.h
@@ -0,0 +1,52 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_WIDGET_COMPOSITING_QUEUE_REPORT_TIME_SWAP_PROMISE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WIDGET_COMPOSITING_QUEUE_REPORT_TIME_SWAP_PROMISE_H_
+
+#include "base/memory/scoped_refptr.h"
+#include "base/single_thread_task_runner.h"
+#include "build/build_config.h"
+#include "cc/trees/swap_promise.h"
+
+namespace blink {
+
+// This class invokes DrainCallback to drain queued callbacks for frame numbers
+// lower or equal to |source_frame_number| when the commit results in a
+// successful activation of the pending layer tree in swap promise.
+//
+// This class doesn't have the reporting callback of the swap time.
+class QueueReportTimeSwapPromise : public cc::SwapPromise {
+ public:
+  using DrainCallback = base::OnceCallback<void(int)>;
+  QueueReportTimeSwapPromise(
+      int source_frame_number,
+      DrainCallback drain_callback,
+      base::OnceClosure swap_callback,
+      scoped_refptr<base::SingleThreadTaskRunner> compositor_task_runner);
+  ~QueueReportTimeSwapPromise() override;
+  QueueReportTimeSwapPromise(const QueueReportTimeSwapPromise&) = delete;
+  QueueReportTimeSwapPromise& operator=(const QueueReportTimeSwapPromise&) =
+      delete;
+
+  void WillSwap(viz::CompositorFrameMetadata* metadata) override;
+  void DidSwap() override;
+  cc::SwapPromise::DidNotSwapAction DidNotSwap(
+      DidNotSwapReason reason) override;
+  void DidActivate() override;
+  int64_t TraceId() const override { return 0; }
+
+ private:
+  int source_frame_number_;
+  DrainCallback drain_callback_;
+  base::OnceClosure swap_callback_;
+#if defined(OS_ANDROID)
+  const bool call_swap_on_activate_;
+#endif
+  scoped_refptr<base::SingleThreadTaskRunner> compositor_task_runner_;
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WIDGET_COMPOSITING_QUEUE_REPORT_TIME_SWAP_PROMISE_H_
diff --git a/third_party/blink/renderer/platform/widget/compositing/widget_compositor.cc b/third_party/blink/renderer/platform/widget/compositing/widget_compositor.cc
new file mode 100644
index 0000000..88d33c7
--- /dev/null
+++ b/third_party/blink/renderer/platform/widget/compositing/widget_compositor.cc
@@ -0,0 +1,126 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/widget/compositing/widget_compositor.h"
+
+#include "cc/trees/layer_tree_host.h"
+#include "third_party/blink/renderer/platform/widget/compositing/queue_report_time_swap_promise.h"
+#include "third_party/blink/renderer/platform/widget/widget_base.h"
+
+namespace blink {
+
+WidgetCompositor::WidgetCompositor(
+    base::WeakPtr<WidgetBase> widget_base,
+    scoped_refptr<base::SingleThreadTaskRunner> main_task_runner,
+    scoped_refptr<base::SingleThreadTaskRunner> compositor_task_runner,
+    mojo::PendingReceiver<mojom::blink::WidgetCompositor> receiver)
+    : widget_base_(widget_base),
+      main_task_runner_(std::move(main_task_runner)),
+      compositor_task_runner_(std::move(compositor_task_runner)),
+      swap_queue_(std::make_unique<WidgetSwapQueue>()) {
+  if (!compositor_task_runner_) {
+    BindOnThread(std::move(receiver));
+  } else {
+    compositor_task_runner_->PostTask(
+        FROM_HERE, base::BindOnce(&WidgetCompositor::BindOnThread,
+                                  base::Unretained(this), std::move(receiver)));
+  }
+}
+
+void WidgetCompositor::Shutdown() {
+  if (!compositor_task_runner_) {
+    ResetOnThread();
+  } else {
+    compositor_task_runner_->PostTask(
+        FROM_HERE, base::BindOnce(&WidgetCompositor::ResetOnThread,
+                                  scoped_refptr<WidgetCompositor>(this)));
+  }
+}
+
+void WidgetCompositor::BindOnThread(
+    mojo::PendingReceiver<mojom::blink::WidgetCompositor> receiver) {
+  DCHECK(CalledOnValidCompositorThread());
+  receiver_.Bind(std::move(receiver), compositor_task_runner_);
+}
+
+void WidgetCompositor::ResetOnThread() {
+  DCHECK(CalledOnValidCompositorThread());
+  receiver_.reset();
+}
+
+void WidgetCompositor::VisualStateRequest(VisualStateRequestCallback callback) {
+  DCHECK(CalledOnValidCompositorThread());
+
+  auto drain_callback =
+      WTF::Bind(&WidgetCompositor::DrainQueue, base::RetainedRef(this));
+  auto swap_callback = WTF::Bind(&WidgetCompositor::VisualStateResponse,
+                                 base::RetainedRef(this));
+  if (!compositor_task_runner_) {
+    CreateQueueSwapPromise(std::move(drain_callback), std::move(swap_callback),
+                           std::move(callback));
+  } else {
+    main_task_runner_->PostTask(
+        FROM_HERE,
+        base::BindOnce(&WidgetCompositor::CreateQueueSwapPromise,
+                       base::RetainedRef(this), std::move(drain_callback),
+                       std::move(swap_callback), std::move(callback)));
+  }
+}
+
+cc::LayerTreeHost* WidgetCompositor::LayerTreeHost() const {
+  return widget_base_->LayerTreeHost();
+}
+
+void WidgetCompositor::CreateQueueSwapPromise(
+    base::OnceCallback<void(int)> drain_callback,
+    base::OnceClosure swap_callback,
+    VisualStateRequestCallback callback) {
+  DCHECK(main_task_runner_->BelongsToCurrentThread());
+
+  if (!widget_base_)
+    return;
+
+  bool first_message_for_frame = false;
+  int source_frame_number = LayerTreeHost()->SourceFrameNumber();
+  swap_queue_->Queue(source_frame_number, std::move(callback),
+                     &first_message_for_frame);
+  if (first_message_for_frame) {
+    LayerTreeHost()->QueueSwapPromise(
+        std::make_unique<QueueReportTimeSwapPromise>(
+            source_frame_number, std::move(drain_callback),
+            std::move(swap_callback), compositor_task_runner_));
+    // Request a main frame if one is not already in progress. This might either
+    // A) request a commit ahead of time or B) request a commit which is not
+    // needed because there are not pending updates. If B) then the frame will
+    // be aborted early and the swap promises will be broken (see
+    // EarlyOut_NoUpdates).
+    LayerTreeHost()->SetNeedsAnimateIfNotInsideMainFrame();
+  } else if (compositor_task_runner_) {
+    // Delete callbacks on the compositor thread.
+    compositor_task_runner_->PostTask(
+        FROM_HERE,
+        base::BindOnce([](base::OnceCallback<void(int)>, base::OnceClosure) {},
+                       std::move(drain_callback), std::move(swap_callback)));
+  }
+}
+
+void WidgetCompositor::VisualStateResponse() {
+  DCHECK(CalledOnValidCompositorThread());
+  Vector<VisualStateRequestCallback> callbacks;
+  swap_queue_->GetCallbacks(&callbacks);
+  for (auto& callback : callbacks)
+    std::move(callback).Run();
+}
+
+void WidgetCompositor::DrainQueue(int source_frame_number) {
+  DCHECK(CalledOnValidCompositorThread());
+  swap_queue_->Drain(source_frame_number);
+}
+
+bool WidgetCompositor::CalledOnValidCompositorThread() {
+  return !compositor_task_runner_ ||
+         compositor_task_runner_->BelongsToCurrentThread();
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/platform/widget/compositing/widget_compositor.h b/third_party/blink/renderer/platform/widget/compositing/widget_compositor.h
new file mode 100644
index 0000000..dd8832f
--- /dev/null
+++ b/third_party/blink/renderer/platform/widget/compositing/widget_compositor.h
@@ -0,0 +1,63 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_WIDGET_COMPOSITING_WIDGET_COMPOSITOR_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WIDGET_COMPOSITING_WIDGET_COMPOSITOR_H_
+
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/receiver.h"
+#include "third_party/blink/public/mojom/page/widget.mojom-blink.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/widget/compositing/widget_swap_queue.h"
+
+namespace cc {
+class LayerTreeHost;
+}
+
+namespace blink {
+
+class WidgetBase;
+
+class PLATFORM_EXPORT WidgetCompositor
+    : public base::RefCountedThreadSafe<WidgetCompositor>,
+      public mojom::blink::WidgetCompositor {
+ public:
+  WidgetCompositor(
+      base::WeakPtr<WidgetBase> widget_base,
+      scoped_refptr<base::SingleThreadTaskRunner> main_task_runner,
+      scoped_refptr<base::SingleThreadTaskRunner> compositor_task_runner,
+      mojo::PendingReceiver<mojom::blink::WidgetCompositor> receiver);
+  ~WidgetCompositor() override = default;
+  WidgetCompositor(const WidgetCompositor&) = delete;
+  WidgetCompositor& operator=(const WidgetCompositor&) = delete;
+
+  void Shutdown();
+
+  // mojom::WidgetCompositor:
+  void VisualStateRequest(VisualStateRequestCallback callback) override;
+
+  virtual cc::LayerTreeHost* LayerTreeHost() const;
+
+ private:
+  void BindOnThread(
+      mojo::PendingReceiver<mojom::blink::WidgetCompositor> receiver);
+  void ResetOnThread();
+  void CreateQueueSwapPromise(base::OnceCallback<void(int)> drain_callback,
+                              base::OnceClosure swap_callback,
+                              VisualStateRequestCallback callback);
+  void VisualStateResponse();
+  void DrainQueue(int source_frame_number);
+  bool CalledOnValidCompositorThread();
+
+  // Note that |widget_base_| is safe to be accessed on the main thread.
+  base::WeakPtr<WidgetBase> widget_base_;
+  scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_;
+  scoped_refptr<base::SingleThreadTaskRunner> compositor_task_runner_;
+  mojo::Receiver<mojom::blink::WidgetCompositor> receiver_{this};
+  std::unique_ptr<WidgetSwapQueue> swap_queue_;
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WIDGET_COMPOSITING_WIDGET_COMPOSITOR_H_
diff --git a/third_party/blink/renderer/platform/widget/compositing/widget_compositor_unittest.cc b/third_party/blink/renderer/platform/widget/compositing/widget_compositor_unittest.cc
new file mode 100644
index 0000000..0658962
--- /dev/null
+++ b/third_party/blink/renderer/platform/widget/compositing/widget_compositor_unittest.cc
@@ -0,0 +1,103 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/widget/compositing/widget_compositor.h"
+
+#include "base/test/task_environment.h"
+#include "cc/test/layer_tree_test.h"
+#include "cc/trees/layer_tree_host.h"
+#include "mojo/public/cpp/bindings/remote.h"
+#include "third_party/blink/renderer/platform/widget/widget_base.h"
+#include "third_party/blink/renderer/platform/widget/widget_base_client.h"
+
+namespace blink {
+
+class StubWidgetBaseClient : public WidgetBaseClient {
+ public:
+  void BeginMainFrame(base::TimeTicks) override {}
+  void RecordTimeToFirstActivePaint(base::TimeDelta) override {}
+  void UpdateLifecycle(WebLifecycleUpdate, DocumentUpdateReason) override {}
+  void RequestNewLayerTreeFrameSink(LayerTreeFrameSinkCallback) override {}
+  WebInputEventResult DispatchBufferedTouchEvents() override {
+    return WebInputEventResult::kNotHandled;
+  }
+  WebInputEventResult HandleInputEvent(const WebCoalescedInputEvent&) override {
+    return WebInputEventResult::kNotHandled;
+  }
+  bool SupportsBufferedTouchEvents() override { return false; }
+  bool WillHandleGestureEvent(const WebGestureEvent&) override { return false; }
+  bool WillHandleMouseEvent(const WebMouseEvent&) override { return false; }
+  void ObserveGestureEventAndResult(const WebGestureEvent&,
+                                    const gfx::Vector2dF&,
+                                    const cc::OverscrollBehavior&,
+                                    bool) override {}
+  void FocusChanged(bool) override {}
+  void UpdateVisualProperties(
+      const VisualProperties& visual_properties) override {}
+  void UpdateScreenRects(const gfx::Rect& widget_screen_rect,
+                         const gfx::Rect& window_screen_rect) override {}
+};
+
+class FakeWidgetCompositor : public WidgetCompositor {
+ public:
+  FakeWidgetCompositor(
+      cc::LayerTreeHost* layer_tree_host,
+      base::WeakPtr<WidgetBase> widget_base,
+      scoped_refptr<base::SingleThreadTaskRunner> main_task_runner,
+      scoped_refptr<base::SingleThreadTaskRunner> compositor_task_runner,
+      mojo::PendingReceiver<mojom::blink::WidgetCompositor> receiver)
+      : WidgetCompositor(widget_base,
+                         std::move(main_task_runner),
+                         std::move(compositor_task_runner),
+                         std::move(receiver)),
+        layer_tree_host_(layer_tree_host) {}
+
+  cc::LayerTreeHost* LayerTreeHost() const override { return layer_tree_host_; }
+
+  cc::LayerTreeHost* layer_tree_host_;
+};
+
+class WidgetCompositorTest : public cc::LayerTreeTest {
+ public:
+  using CompositorMode = cc::CompositorMode;
+
+  void BeginTest() override {
+    widget_base_ = std::make_unique<WidgetBase>(
+        &client_,
+        blink::CrossVariantMojoAssociatedRemote<
+            blink::mojom::WidgetHostInterfaceBase>(),
+        blink::CrossVariantMojoAssociatedReceiver<
+            blink::mojom::WidgetInterfaceBase>());
+
+    widget_compositor_ = base::MakeRefCounted<FakeWidgetCompositor>(
+        layer_tree_host(), widget_base_->GetWeakPtr(),
+        layer_tree_host()->GetTaskRunnerProvider()->MainThreadTaskRunner(),
+        layer_tree_host()->GetTaskRunnerProvider()->ImplThreadTaskRunner(),
+        remote_.BindNewPipeAndPassReceiver());
+
+    remote_->VisualStateRequest(base::BindOnce(
+        &WidgetCompositorTest::VisualStateResponse, base::Unretained(this)));
+    PostSetNeedsCommitToMainThread();
+  }
+
+  void VisualStateResponse() {
+    is_callback_run_ = true;
+    widget_compositor_->Shutdown();
+    widget_compositor_ = nullptr;
+    EndTest();
+  }
+
+  void AfterTest() override { EXPECT_TRUE(is_callback_run_); }
+
+  mojo::Remote<mojom::blink::WidgetCompositor> remote_;
+  StubWidgetBaseClient client_;
+  std::unique_ptr<WidgetBase> widget_base_;
+  scoped_refptr<FakeWidgetCompositor> widget_compositor_;
+  bool is_callback_run_ = false;
+  base::test::SingleThreadTaskEnvironment task_environment_;
+};
+
+SINGLE_AND_MULTI_THREAD_TEST_F(WidgetCompositorTest);
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/platform/widget/compositing/widget_swap_queue.cc b/third_party/blink/renderer/platform/widget/compositing/widget_swap_queue.cc
new file mode 100644
index 0000000..bcb37940
--- /dev/null
+++ b/third_party/blink/renderer/platform/widget/compositing/widget_swap_queue.cc
@@ -0,0 +1,37 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/widget/compositing/widget_swap_queue.h"
+
+namespace blink {
+
+void WidgetSwapQueue::Queue(int source_frame_number,
+                            VisualStateRequestCallback callback,
+                            bool* is_first) {
+  base::AutoLock lock(lock_);
+  if (is_first)
+    *is_first = (queue_.count(source_frame_number) == 0);
+
+  queue_[source_frame_number].push_back(std::move(callback));
+}
+
+void WidgetSwapQueue::Drain(int source_frame_number) {
+  base::AutoLock lock(lock_);
+  auto end = queue_.upper_bound(source_frame_number);
+  for (auto i = queue_.begin(); i != end; i++) {
+    DCHECK(i->first <= source_frame_number);
+    std::move(i->second.begin(), i->second.end(),
+              std::back_inserter(next_callbacks_));
+  }
+  queue_.erase(queue_.begin(), end);
+}
+
+void WidgetSwapQueue::GetCallbacks(
+    Vector<VisualStateRequestCallback>* callbacks) {
+  std::move(next_callbacks_.begin(), next_callbacks_.end(),
+            std::back_inserter(*callbacks));
+  next_callbacks_.clear();
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/platform/widget/compositing/widget_swap_queue.h b/third_party/blink/renderer/platform/widget/compositing/widget_swap_queue.h
new file mode 100644
index 0000000..8f73484a
--- /dev/null
+++ b/third_party/blink/renderer/platform/widget/compositing/widget_swap_queue.h
@@ -0,0 +1,61 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_WIDGET_COMPOSITING_WIDGET_SWAP_QUEUE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_WIDGET_COMPOSITING_WIDGET_SWAP_QUEUE_H_
+
+#include <map>
+#include "base/synchronization/lock.h"
+#include "third_party/blink/public/mojom/page/widget.mojom-blink.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace blink {
+
+// Queue used to keep track of which VisualStateRequestCallback should be
+// invoked after a particular compositor frame swap. The callbacks are
+// guaranteed to be processed after the frame is processed, but there is no
+// guarantee that nothing else happens between processing the frame and
+// processing the callback.
+class WidgetSwapQueue {
+ public:
+  using VisualStateRequestCallback =
+      mojom::blink::WidgetCompositor::VisualStateRequestCallback;
+  WidgetSwapQueue() = default;
+  ~WidgetSwapQueue() = default;
+  WidgetSwapQueue(const WidgetSwapQueue&) = delete;
+  WidgetSwapQueue& operator=(const WidgetSwapQueue&) = delete;
+
+  // Returns true if there are no callback in the queue.
+  bool Empty() const { return queue_.empty(); }
+
+  // Queues the callback to be returned on a matching Drain call.
+  //
+  // |source_frame_number| - frame number to queue |callback| for.
+  // |callback| - callback to queue. The method takes ownership of |callback|.
+  // |is_first| - output parameter. Set to true if this was the first message
+  //              enqueued for the given source_frame_number.
+  void Queue(int source_frame_number,
+             VisualStateRequestCallback callback,
+             bool* is_first);
+
+  // The method will append cllbacks queued for frame numbers lower or equal to
+  // |source_frame_number|
+  //
+  // |source_frame_number| - swapped frame number.
+  void Drain(int source_frame_number);
+
+  // The method will clear |next_callbacks_| after copying to |callbacks|.
+  //
+  // |callbacks| - vector to store callbacks.
+  void GetCallbacks(Vector<VisualStateRequestCallback>* callbacks);
+
+ private:
+  base::Lock lock_;
+  std::map<int, Vector<VisualStateRequestCallback>> queue_;
+  Vector<VisualStateRequestCallback> next_callbacks_;
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_WIDGET_COMPOSITING_WIDGET_SWAP_QUEUE_H_
diff --git a/third_party/blink/renderer/platform/widget/widget_base.cc b/third_party/blink/renderer/platform/widget/widget_base.cc
index e82d3049..7ac1a8f 100644
--- a/third_party/blink/renderer/platform/widget/widget_base.cc
+++ b/third_party/blink/renderer/platform/widget/widget_base.cc
@@ -24,6 +24,7 @@
 #include "third_party/blink/renderer/platform/scheduler/public/thread.h"
 #include "third_party/blink/renderer/platform/widget/compositing/layer_tree_settings.h"
 #include "third_party/blink/renderer/platform/widget/compositing/layer_tree_view.h"
+#include "third_party/blink/renderer/platform/widget/compositing/widget_compositor.h"
 #include "third_party/blink/renderer/platform/widget/frame_widget.h"
 #include "third_party/blink/renderer/platform/widget/input/ime_event_guard.h"
 #include "third_party/blink/renderer/platform/widget/input/main_thread_event_queue.h"
@@ -189,6 +190,11 @@
       FROM_HERE,
       base::BindOnce([](scoped_refptr<WidgetInputHandlerManager> manager) {},
                      std::move(widget_input_handler_manager_)));
+
+  if (widget_compositor_) {
+    widget_compositor_->Shutdown();
+    widget_compositor_ = nullptr;
+  }
 }
 
 cc::LayerTreeHost* WidgetBase::LayerTreeHost() const {
@@ -289,6 +295,15 @@
             visual_properties.screen_info.device_scale_factor));
   }
 
+  // Inform the rendering thread of the color space indicating the presence of
+  // HDR capabilities. The HDR bit happens to be globally true/false for all
+  // browser windows (on Windows OS) and thus would be the same for all
+  // RenderWidgets, so clobbering each other works out since only the HDR bit is
+  // used. See https://crbug.com/803451 and
+  // https://chromium-review.googlesource.com/c/chromium/src/+/852912/15#message-68bbd3e25c3b421a79cd028b2533629527d21fee
+  Platform::Current()->SetRenderingColorSpace(
+      visual_properties.screen_info.color_space);
+
   LayerTreeHost()->SetBrowserControlsParams(
       visual_properties.browser_controls_params);
 
@@ -654,6 +669,18 @@
   client_->FocusChanged(enable);
 }
 
+void WidgetBase::BindWidgetCompositor(
+    mojo::PendingReceiver<mojom::blink::WidgetCompositor> receiver) {
+  if (widget_compositor_)
+    widget_compositor_->Shutdown();
+
+  widget_compositor_ = base::MakeRefCounted<WidgetCompositor>(
+      weak_ptr_factory_.GetWeakPtr(),
+      LayerTreeHost()->GetTaskRunnerProvider()->MainThreadTaskRunner(),
+      LayerTreeHost()->GetTaskRunnerProvider()->ImplThreadTaskRunner(),
+      std::move(receiver));
+}
+
 void WidgetBase::UpdateCompositionInfo(bool immediate_request) {
   if (!monitor_composition_info_ && !immediate_request)
     return;  // Do not calculate composition info if not requested.
diff --git a/third_party/blink/renderer/platform/widget/widget_base.h b/third_party/blink/renderer/platform/widget/widget_base.h
index 2d9e2e8..f2820ec 100644
--- a/third_party/blink/renderer/platform/widget/widget_base.h
+++ b/third_party/blink/renderer/platform/widget/widget_base.h
@@ -39,6 +39,7 @@
 class LayerTreeView;
 class WidgetBaseClient;
 class WidgetInputHandlerManager;
+class WidgetCompositor;
 
 namespace scheduler {
 class WebRenderWidgetSchedulingState;
@@ -215,6 +216,13 @@
                CrossVariantMojoRemote<
                    mojom::blink::PointerLockContextInterfaceBase>)> callback);
 
+  void BindWidgetCompositor(
+      mojo::PendingReceiver<mojom::blink::WidgetCompositor> receiver);
+
+  base::WeakPtr<WidgetBase> GetWeakPtr() {
+    return weak_ptr_factory_.GetWeakPtr();
+  }
+
  private:
   bool CanComposeInline();
   void UpdateTextInputStateInternal(bool show_virtual_keyboard,
@@ -242,6 +250,7 @@
   base::TimeTicks was_shown_time_ = base::TimeTicks::Now();
   bool has_focus_ = false;
   WidgetBaseInputHandler input_handler_{this};
+  scoped_refptr<WidgetCompositor> widget_compositor_;
 
   // Stores the current selection bounds.
   gfx::Rect selection_focus_rect_;
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index 483b66c..8f48075 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -740,6 +740,96 @@
 crbug.com/1003506 external/wpt/css/css-flexbox/percentage-heights-007.html [ Failure ]
 crbug.com/1081802 external/wpt/css/css-flexbox/overflow-area-001.html [ Failure ]
 crbug.com/1081802 external/wpt/css/css-flexbox/overflow-area-002.html [ Failure ]
+crbug.com/807497 external/wpt/css/css-flexbox/anonymous-flex-item-005.html [ Failure ]
+crbug.com/1111708 external/wpt/css/css-flexbox/flexbox_justifycontent-center-overflow.html [ Failure ]
+crbug.com/1111710 external/wpt/css/css-flexbox/getcomputedstyle/flexbox_computedstyle_flex-basis-0percent.html [ Failure ]
+crbug.com/1111710 external/wpt/css/css-flexbox/getcomputedstyle/flexbox_computedstyle_flex-shorthand-number.html [ Failure ]
+
+# These testcases are incorrect, mark them as failing until they're fixed in the testsuite.
+# https://lists.w3.org/Archives/Public/www-style/2016Jan/0275.html
+# https://lists.w3.org/Archives/Public/www-style/2016Jan/0276.html
+crbug.com/249112 external/wpt/css/css-flexbox/flex-minimum-height-flex-items-005.xht [ Failure ]
+crbug.com/249112 external/wpt/css/css-flexbox/flex-minimum-height-flex-items-007.xht [ Failure ]
+crbug.com/249112 external/wpt/css/css-flexbox/flex-minimum-width-flex-items-005.xht [ Failure ]
+crbug.com/249112 external/wpt/css/css-flexbox/flex-minimum-width-flex-items-007.xht [ Failure ]
+
+# Not implemented yet
+crbug.com/336604 external/wpt/css/css-flexbox/flexbox_visibility-collapse-line-wrapping.html [ Failure ]
+crbug.com/336604 external/wpt/css/css-flexbox/flexbox_visibility-collapse.html [ Failure ]
+
+# These require justify-content:right and align-content:end
+crbug.com/1011718 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-content-horiz-001a.xhtml [ Failure ]
+crbug.com/1011718 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-content-horiz-001b.xhtml [ Failure ]
+crbug.com/1011718 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-content-vert-001a.xhtml [ Failure ]
+crbug.com/1011718 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-content-vert-001b.xhtml [ Failure ]
+crbug.com/1011718 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-content-vert-002.xhtml [ Failure ]
+crbug.com/1011718 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-content-horiz-002.xhtml [ Failure ]
+crbug.com/1011718 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-justify-content-horiz-001a.xhtml [ Failure ]
+crbug.com/1011718 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-justify-content-horiz-001b.xhtml [ Failure ]
+crbug.com/1011718 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-justify-content-horiz-003.xhtml [ Failure ]
+crbug.com/1011718 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-justify-content-horiz-005.xhtml [ Failure ]
+crbug.com/1011718 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-justify-content-horiz-006.xhtml [ Failure ]
+
+# We don't support requesting flex line breaks and it is not clear that we should.
+# See https://lists.w3.org/Archives/Public/www-style/2015May/0065.html
+crbug.com/473481 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-break-request-horiz-001a.html [ Failure ]
+crbug.com/473481 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-break-request-horiz-001b.html [ Failure ]
+crbug.com/473481 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-break-request-vert-001a.html [ Failure ]
+crbug.com/473481 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-break-request-vert-001b.html [ Failure ]
+
+# We don't currently support visibility: collapse in flexbox
+crbug.com/336604 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-collapsed-item-baseline-001.html [ Failure ]
+crbug.com/336604 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-collapsed-item-horiz-001.html [ Failure ]
+crbug.com/336604 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-collapsed-item-horiz-002.html [ Failure ]
+crbug.com/336604 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-collapsed-item-horiz-003.html [ Failure ]
+
+# We don't correctly implement aspect ratios for images in Flexbox
+crbug.com/531199 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-intrinsic-ratio-001.html [ Failure ]
+crbug.com/531199 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-intrinsic-ratio-002.html [ Failure ]
+crbug.com/531199 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-intrinsic-ratio-005.html [ Failure ]
+crbug.com/531199 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-intrinsic-ratio-006.html [ Failure ]
+crbug.com/1111128 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-min-height-auto-002b.html [ Failure ]
+
+# These tests are incorrect, as Firefox has a bug in this area. https://bugzilla.mozilla.org/show_bug.cgi?id=1136312
+crbug.com/553838 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-min-height-auto-002a.html [ Failure ]
+crbug.com/553838 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-min-height-auto-002c.html [ Failure ]
+crbug.com/553838 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-min-width-auto-002a.html [ Failure ]
+crbug.com/553838 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-min-width-auto-002c.html [ Failure ]
+
+# We paint in an incorrect order when layers are present. Blocked on composite-after-paint.
+crbug.com/370604 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-paint-ordering-002.xhtml [ Failure ]
+
+# Untriaged flex failures
+crbug.com/626703 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-content-wmvert-001.xhtml [ Failure ]
+crbug.com/626703 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-baseline-horiz-001a.xhtml [ Failure ]
+crbug.com/626703 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-baseline-horiz-001b.xhtml [ Failure ]
+crbug.com/626703 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-baseline-horiz-006.xhtml [ Failure ]
+crbug.com/626703 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-baseline-horiz-007.xhtml [ Failure ]
+crbug.com/626703 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-baseline-horiz-008.xhtml [ Failure ]
+crbug.com/626703 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-vert-002.xhtml [ Failure ]
+crbug.com/626703 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-vert-rtl-005.xhtml [ Failure ]
+crbug.com/626703 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-baseline-align-self-baseline-vert-001.html [ Failure Pass ]
+crbug.com/626703 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-baseline-multi-item-vert-001a.html [ Failure Pass ]
+crbug.com/626703 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-flex-basis-content-001a.html [ Failure ]
+crbug.com/626703 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-flex-basis-content-001b.html [ Failure ]
+crbug.com/626703 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-flex-basis-content-002a.html [ Failure ]
+crbug.com/626703 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-flex-basis-content-002b.html [ Failure ]
+crbug.com/626703 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-flex-basis-content-003a.html [ Failure ]
+crbug.com/626703 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-flex-basis-content-004a.html [ Failure ]
+crbug.com/626703 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-intrinsic-ratio-001v.html [ Failure ]
+crbug.com/626703 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-intrinsic-ratio-002v.html [ Failure ]
+crbug.com/626703 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-intrinsic-ratio-005v.html [ Failure ]
+crbug.com/626703 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-intrinsic-ratio-006v.html [ Failure ]
+crbug.com/626703 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-intrinsic-ratio-007.html [ Failure ]
+crbug.com/626703 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-intrinsic-ratio-007v.html [ Failure ]
+crbug.com/626703 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-justify-content-vert-006.xhtml [ Failure ]
+crbug.com/626703 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-justify-content-wmvert-001.xhtml [ Failure ]
+crbug.com/626703 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-safe-overflow-position-001.html [ Failure ]
+crbug.com/715718 [ Win ] external/wpt/css/css-flexbox/align-items-004.htm [ Failure Pass ]
+crbug.com/715718 [ Win ] external/wpt/css/css-flexbox/flex-minimum-width-flex-items-001.xht [ Failure Pass ]
+crbug.com/715718 [ Win ] external/wpt/css/css-flexbox/flex-minimum-width-flex-items-003.xht [ Failure Pass ]
+crbug.com/715718 [ Win ] external/wpt/css/css-flexbox/flexbox_flex-natural-mixed-basis-auto.html [ Failure Pass ]
+crbug.com/898186 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-intrinsic-ratio-003v.html [ Pass Failure ]
 
 # [css-lists]
 crbug.com/1066577 external/wpt/css/css-lists/li-value-reversed-005.html [ Failure ]
@@ -1969,71 +2059,11 @@
 # Chrome touch-action computation ignores div transforms.
 crbug.com/715148 external/wpt/pointerevents/pointerevent_touch-action-rotated-divs_touch-manual.html [ Skip ]
 
-# These testcases are incorrect, mark them as failing until they're fixed in the testsuite.
-# https://lists.w3.org/Archives/Public/www-style/2016Jan/0275.html
-# https://lists.w3.org/Archives/Public/www-style/2016Jan/0276.html
-crbug.com/249112 external/wpt/css/css-flexbox/flex-minimum-height-flex-items-005.xht [ Failure ]
-crbug.com/249112 external/wpt/css/css-flexbox/flex-minimum-height-flex-items-007.xht [ Failure ]
-crbug.com/249112 external/wpt/css/css-flexbox/flex-minimum-width-flex-items-005.xht [ Failure ]
-crbug.com/249112 external/wpt/css/css-flexbox/flex-minimum-width-flex-items-007.xht [ Failure ]
-
-# Not implemented yet
-crbug.com/336604 external/wpt/css/css-flexbox/flexbox_visibility-collapse-line-wrapping.html [ Failure ]
-crbug.com/336604 external/wpt/css/css-flexbox/flexbox_visibility-collapse.html [ Failure ]
-
-crbug.com/807497 external/wpt/css/css-flexbox/anonymous-flex-item-005.html [ Failure ]
-crbug.com/467127 external/wpt/css/css-flexbox/flexbox_justifycontent-center-overflow.html [ Failure ]
-crbug.com/467127 external/wpt/css/css-flexbox/getcomputedstyle/flexbox_computedstyle_flex-basis-0percent.html [ Failure ]
-crbug.com/467127 external/wpt/css/css-flexbox/getcomputedstyle/flexbox_computedstyle_flex-shorthand-number.html [ Failure ]
-
-crbug.com/467127 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-content-horiz-001a.xhtml [ Failure ]
-crbug.com/467127 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-content-horiz-001b.xhtml [ Failure ]
-crbug.com/467127 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-content-vert-001a.xhtml [ Failure ]
-crbug.com/467127 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-content-vert-001b.xhtml [ Failure ]
-crbug.com/467127 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-justify-content-horiz-001a.xhtml [ Failure ]
-crbug.com/467127 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-justify-content-horiz-001b.xhtml [ Failure ]
-crbug.com/467127 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-justify-content-horiz-003.xhtml [ Failure ]
-crbug.com/467127 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-justify-content-horiz-005.xhtml [ Failure ]
-crbug.com/467127 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-writing-mode-010.html [ Failure Pass ]
-crbug.com/467127 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-writing-mode-012.html [ Failure Pass ]
-crbug.com/467127 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-writing-mode-013.html [ Failure Pass ]
-crbug.com/467127 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-writing-mode-014.html [ Failure Pass ]
-crbug.com/467127 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-writing-mode-015.html [ Failure Pass ]
-
 crbug.com/654999 [ Win ] fast/forms/color/color-suggestion-picker-appearance-zoom200.html [ Pass Failure ]
 crbug.com/654999 [ Linux ] fast/forms/color/color-suggestion-picker-appearance-zoom200.html [ Pass Failure ]
 
 crbug.com/658304 [ Win ] fast/forms/select/input-select-after-resize.html [ Crash Timeout Pass Failure ]
 
-# We don't support requesting flex line breaks and it is not clear that we should.
-# See https://lists.w3.org/Archives/Public/www-style/2015May/0065.html
-crbug.com/473481 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-break-request-horiz-001a.html [ Failure ]
-crbug.com/473481 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-break-request-horiz-001b.html [ Failure ]
-crbug.com/473481 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-break-request-vert-001a.html [ Failure ]
-crbug.com/473481 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-break-request-vert-001b.html [ Failure ]
-
-# We don't currently support visibility: collapse in flexbox
-crbug.com/336604 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-collapsed-item-baseline-001.html [ Failure ]
-crbug.com/336604 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-collapsed-item-horiz-001.html [ Failure ]
-crbug.com/336604 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-collapsed-item-horiz-002.html [ Failure ]
-crbug.com/336604 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-collapsed-item-horiz-003.html [ Failure ]
-
-# We don't correctly implement aspect ratios for images in Flexbox
-crbug.com/531199 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-intrinsic-ratio-001.html [ Failure ]
-crbug.com/531199 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-intrinsic-ratio-002.html [ Failure ]
-crbug.com/531199 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-intrinsic-ratio-005.html [ Failure ]
-crbug.com/531199 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-intrinsic-ratio-006.html [ Failure ]
-crbug.com/531199 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-min-height-auto-002b.html [ Failure ]
-
-# These tests are incorrect, as Firefox has a bug in this area. https://bugzilla.mozilla.org/show_bug.cgi?id=1136312
-crbug.com/553838 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-min-height-auto-002a.html [ Failure ]
-crbug.com/553838 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-min-height-auto-002c.html [ Failure ]
-crbug.com/553838 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-min-width-auto-002a.html [ Failure ]
-crbug.com/553838 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-min-width-auto-002c.html [ Failure ]
-
-# We paint in an incorrect order when layers are present
-crbug.com/370604 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-paint-ordering-002.xhtml [ Failure ]
-
 crbug.com/736319 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/writing-modes-3/text-combine-upright-break-inside-001a.html [ Failure ]
 crbug.com/736319 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/writing-modes-3/text-combine-upright-compression-001.html [ Failure ]
 crbug.com/736319 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/writing-modes-3/text-combine-upright-compression-002.html [ Failure ]
@@ -2190,11 +2220,6 @@
 
 crbug.com/713587 external/wpt/css/css-ui/caret-color-006.html [ Skip ]
 
-# Automatically-added expectations for CSS tests which are temporarily
-# commented-out while the test directory is disabled (see top of file).
-#crbug.com/626703 [ Win ] external/wpt/css/css-flexbox/flex-minimum-width-flex-items-001.xht [ Failure ]
-#crbug.com/626703 [ Win ] external/wpt/css/css-flexbox/flex-minimum-width-flex-items-003.xht [ Failure ]
-#crbug.com/626703 [ Win ] external/wpt/css/css-flexbox/flexbox_flex-natural-mixed-basis-auto.html [ Failure ]
 crbug.com/626703 external/wpt/css/css-writing-modes/sizing-orthog-htb-in-vrl-013.xht [ Failure ]
 crbug.com/626703 external/wpt/css/css-writing-modes/sizing-orthog-vlr-in-htb-008.xht [ Failure ]
 crbug.com/626703 external/wpt/css/css-writing-modes/sizing-orthog-vlr-in-htb-020.xht [ Failure ]
@@ -2657,6 +2682,9 @@
 crbug.com/27659 external/wpt/css/css-ruby/ruby-whitespace-001.html [ Failure ]
 crbug.com/27659 external/wpt/css/css-ruby/ruby-whitespace-002.html [ Failure ]
 
+# WebRTC: Perfect Negotiation is flaky in Unified Plan.
+# This will be fixed when crbug.com/1060083 is fixed.
+crbug.com/1060083 external/wpt/webrtc/RTCPeerConnection-perfect-negotiation.https.html [ Pass Failure Timeout ]
 # WebRTC: Perfect Negotiation times out in Plan B. This is expected.
 crbug.com/980872 virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-perfect-negotiation.https.html [ Timeout ]
 
@@ -2954,9 +2982,6 @@
 crbug.com/626703 external/wpt/webaudio/the-audio-api/processing-model/feedback-delay-time.html [ Failure ]
 crbug.com/626703 external/wpt/webaudio/the-audio-api/processing-model/delay-time-clamping.html [ Failure ]
 crbug.com/626703 external/wpt/webaudio/the-audio-api/processing-model/cycle-without-delay.html [ Failure ]
-crbug.com/626703 [ Linux ] external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-baseline-horiz-008.xhtml [ Failure ]
-crbug.com/626703 [ Mac ] external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-baseline-horiz-008.xhtml [ Failure ]
-crbug.com/626703 [ Win ] external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-baseline-horiz-008.xhtml [ Failure ]
 crbug.com/626703 [ Win7 ] external/wpt/pointerevents/pointerevent_touch-action-inherit_child-pan-x-child-pan-y_touch.html [ Timeout ]
 crbug.com/626703 [ Win7 ] external/wpt/pointerevents/pointerevent_touch-action-inherit_child-none_touch.html [ Timeout ]
 crbug.com/626703 [ Win ] external/wpt/css/css-text/word-break/word-break-keep-all-008.html [ Failure ]
@@ -3127,11 +3152,9 @@
 crbug.com/626703 external/wpt/html/semantics/forms/the-fieldset-element/accessibility/legend-display-none-manual.html [ Skip ]
 crbug.com/626703 external/wpt/webrtc/RTCPeerConnection-setRemoteDescription-tracks.https.html [ Timeout ]
 crbug.com/626703 external/wpt/webrtc/RTCPeerConnection-remote-track-mute.https.html [ Timeout ]
-crbug.com/626703 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-intrinsic-ratio-007v.html [ Failure ]
 crbug.com/626703 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/counter-styles-3/system-cyclic-invalid.html [ Pass Failure Timeout ]
 crbug.com/626703 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/counter-styles-3/system-alphabetic-invalid.html [ Pass Failure Timeout ]
 crbug.com/626703 [ Mac10.11 ] external/wpt/mimesniff/mime-types/parsing.any.html [ Timeout ]
-crbug.com/626703 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-intrinsic-ratio-007.html [ Failure ]
 crbug.com/626703 external/wpt/html/infrastructure/urls/resolving-urls/query-encoding/navigation.sub.html?encoding=windows-1252 [ Timeout ]
 crbug.com/626703 external/wpt/css/css-values/ch-unit-011.html [ Failure ]
 crbug.com/626703 [ Mac ] external/wpt/html/browsers/the-window-object/apis-for-creating-and-navigating-browsing-contexts-by-name/open-features-tokenization-width-height.html [ Crash Timeout ]
@@ -3262,15 +3285,7 @@
 crbug.com/626703 external/wpt/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/tasks.window.html [ Timeout ]
 crbug.com/875411 external/wpt/svg/text/reftests/text-complex-002.svg [ Failure ]
 crbug.com/875411 external/wpt/svg/text/reftests/text-shape-inside-001.svg [ Failure ]
-crbug.com/626703 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-vert-002.xhtml [ Failure ]
-crbug.com/626703 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-content-wmvert-001.xhtml [ Failure ]
-crbug.com/626703 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-justify-content-vert-006.xhtml [ Failure ]
-crbug.com/626703 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-vert-rtl-005.xhtml [ Failure ]
-crbug.com/626703 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-justify-content-wmvert-001.xhtml [ Failure ]
 crbug.com/875411 external/wpt/svg/text/reftests/text-complex-001.svg [ Failure ]
-crbug.com/626703 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-content-vert-002.xhtml [ Failure ]
-crbug.com/626703 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-justify-content-horiz-006.xhtml [ Failure ]
-crbug.com/626703 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-content-horiz-002.xhtml [ Failure ]
 crbug.com/875411 external/wpt/svg/text/reftests/text-shape-inside-002.svg [ Failure ]
 crbug.com/626703 external/wpt/speech-api/SpeechSynthesis-speak-without-activation-fails.tentative.html [ Failure ]
 crbug.com/366553 external/wpt/svg/text/reftests/text-inline-size-007.svg [ Failure ]
@@ -3294,7 +3309,6 @@
 crbug.com/626703 external/wpt/css/css-lists/content-property/marker-text-matches-square.html [ Failure ]
 crbug.com/626703 external/wpt/svg/linking/reftests/use-descendant-combinator-003.html [ Failure ]
 crbug.com/626703 external/wpt/css/css-lists/content-property/marker-text-matches-circle.html [ Failure ]
-crbug.com/626703 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-safe-overflow-position-001.html [ Failure ]
 crbug.com/626703 external/wpt/content-security-policy/securitypolicyviolation/inside-service-worker.https.html [ Timeout ]
 crbug.com/626703 external/wpt/content-security-policy/securitypolicyviolation/inside-shared-worker.html [ Timeout ]
 crbug.com/626703 external/wpt/content-security-policy/securitypolicyviolation/inside-dedicated-worker.html [ Timeout ]
@@ -3390,17 +3404,11 @@
 crbug.com/626703 [ IOS ] external/wpt/websockets/Create-Secure-extensions-empty.any.html [ Timeout ]
 crbug.com/626703 [ IOS ] external/wpt/websockets/Create-Secure-extensions-empty.any.worker.html [ Timeout ]
 crbug.com/626703 external/wpt/html/browsers/history/joint-session-history/joint-session-history-remove-iframe.html [ Timeout ]
-crbug.com/626703 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-flex-basis-content-003a.html [ Failure ]
-crbug.com/626703 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-flex-basis-content-004a.html [ Failure ]
 crbug.com/626703 external/wpt/css/css-shapes/shape-outside/shape-image/gradients/shape-outside-linear-gradient-008.html [ Failure ]
 crbug.com/626703 external/wpt/css/css-shapes/shape-outside/shape-image/gradients/shape-outside-linear-gradient-006.html [ Failure ]
 crbug.com/626703 external/wpt/css/css-shapes/shape-outside/shape-image/gradients/shape-outside-linear-gradient-005.html [ Failure ]
 crbug.com/626703 external/wpt/css/css-fonts/font-variant-descriptor-01.html [ Failure ]
 crbug.com/626703 external/wpt/css/css-shapes/shape-outside/shape-image/gradients/shape-outside-linear-gradient-007.html [ Failure ]
-crbug.com/626703 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-flex-basis-content-001a.html [ Failure ]
-crbug.com/626703 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-flex-basis-content-002a.html [ Failure ]
-crbug.com/626703 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-flex-basis-content-002b.html [ Failure ]
-crbug.com/626703 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-flex-basis-content-001b.html [ Failure ]
 crbug.com/626703 [ Win7 ] external/wpt/css/css-fonts/variations/font-opentype-collections.html [ Timeout ]
 crbug.com/626703 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/counter-styles-3/system-numeric.html [ Failure ]
 crbug.com/626703 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/counter-styles-3/descriptor-suffix-invalid.html [ Failure ]
@@ -3429,14 +3437,10 @@
 crbug.com/626703 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/counter-styles-3/descriptor-symbols.html [ Failure ]
 crbug.com/626703 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/counter-styles-3/system-extends-invalid.html [ Failure ]
 crbug.com/626703 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/counter-styles-3/redefine-builtin.html [ Failure ]
-crbug.com/626703 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-intrinsic-ratio-006v.html [ Failure ]
 crbug.com/626703 external/wpt/css/css-fonts/font-feature-resolution-001.html [ Failure ]
 crbug.com/626703 external/wpt/css/css-fonts/font-feature-resolution-002.html [ Failure ]
 crbug.com/626703 external/wpt/css/css-fonts/font-variant-05.xht [ Failure ]
 crbug.com/626703 external/wpt/css/css-fonts/font-variant-06.xht [ Failure ]
-crbug.com/626703 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-intrinsic-ratio-001v.html [ Failure ]
-crbug.com/626703 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-intrinsic-ratio-002v.html [ Failure ]
-crbug.com/626703 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-intrinsic-ratio-005v.html [ Failure ]
 crbug.com/626703 external/wpt/css/mediaqueries/viewport-script-dynamic.html [ Failure ]
 crbug.com/626703 external/wpt/css/css-writing-modes/available-size-014.html [ Failure ]
 crbug.com/626703 external/wpt/css/css-writing-modes/available-size-013.html [ Failure ]
@@ -3519,12 +3523,6 @@
 crbug.com/626703 external/wpt/css/css-fonts/font-variant-position.html [ Failure ]
 crbug.com/626703 external/wpt/css/css-tables/floats/floats-wrap-bfc-006b.xht [ Failure ]
 crbug.com/626703 external/wpt/css/css-tables/floats/floats-wrap-bfc-006c.xht [ Failure ]
-crbug.com/626703 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-baseline-horiz-001a.xhtml [ Failure ]
-crbug.com/626703 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-baseline-horiz-001b.xhtml [ Failure ]
-crbug.com/626703 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-baseline-horiz-006.xhtml [ Failure ]
-crbug.com/626703 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-baseline-horiz-007.xhtml [ Failure ]
-crbug.com/626703 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-baseline-align-self-baseline-vert-001.html [ Failure Pass ]
-crbug.com/626703 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-baseline-multi-item-vert-001a.html [ Failure Pass ]
 crbug.com/626703 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/writing-modes-3/logical-physical-mapping-001.html [ Failure ]
 crbug.com/626703 external/wpt/encoding/eof-utf-8-three.html [ Failure ]
 crbug.com/626703 external/wpt/encoding/eof-utf-8-two.html [ Failure ]
@@ -4871,10 +4869,6 @@
 crbug.com/875003 [ Win ] editing/caret/caret-is-hidden-when-no-focus.html [ Pass Failure ]
 
 crbug.com/715718 external/wpt/media-source/mediasource-remove.html [ Failure Pass ]
-crbug.com/715718 [ Win ] external/wpt/css/css-flexbox/align-items-004.htm [ Failure Pass ]
-crbug.com/715718 [ Win ] external/wpt/css/css-flexbox/flex-minimum-width-flex-items-001.xht [ Failure Pass ]
-crbug.com/715718 [ Win ] external/wpt/css/css-flexbox/flex-minimum-width-flex-items-003.xht [ Failure Pass ]
-crbug.com/715718 [ Win ] external/wpt/css/css-flexbox/flexbox_flex-natural-mixed-basis-auto.html [ Failure Pass ]
 
 # New failure when importing css-variables:
 crbug.com/791529 external/wpt/css/css-variables/variable-transitions-from-no-value.html [ Skip ]
@@ -5466,8 +5460,6 @@
 crbug.com/874162 [ Mac ] fast/events/middleClickAutoscroll-nested-divs.html [ Skip ]
 crbug.com/874162 [ Mac ] fast/events/selection-autoscroll-borderbelt.html [ Skip ]
 
-crbug.com/898186 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-intrinsic-ratio-003v.html [ Pass Failure ]
-
 # Sheriff 2018-09-10
 crbug.com/881207 fast/js/regress/splice-to-remove.html [ Timeout Pass ]
 
diff --git a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
index 6e34349b..f726331f 100644
--- a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
+++ b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
@@ -79300,6 +79300,32 @@
        {}
       ]
      ],
+     "position-relative-006.html": [
+      "545edc990b9ff89ddcb5bbf09dc0b8c8d8238888",
+      [
+       null,
+       [
+        [
+         "/css/reference/ref-filled-green-100px-square.xht",
+         "=="
+        ]
+       ],
+       {}
+      ]
+     ],
+     "position-relative-007.html": [
+      "2425514f308afebd3ce32ae6e04c57077428c05e",
+      [
+       null,
+       [
+        [
+         "/css/reference/ref-filled-green-100px-square.xht",
+         "=="
+        ]
+       ],
+       {}
+      ]
+     ],
      "position-relative-table-tbody-left-absolute-child.html": [
       "98e759a8c0a83817b3d691503e807ed5ed549936",
       [
@@ -354300,6 +354326,13 @@
       {}
      ]
     ],
+    "intersection-ratio-ib-split.html": [
+     "905ea436fd6a85fff228f5676742b89ab001a3b7",
+     [
+      null,
+      {}
+     ]
+    ],
     "isIntersecting-change-events.html": [
      "99bc65bd60afee82f2ddd6b5380437d94811e30d",
      [
diff --git a/third_party/blink/web_tests/external/wpt/IndexedDB/idbobjectstore_putall.tentative.any.js b/third_party/blink/web_tests/external/wpt/IndexedDB/idbobjectstore_putall.tentative.any.js
index 8bdc765..a312d71 100644
--- a/third_party/blink/web_tests/external/wpt/IndexedDB/idbobjectstore_putall.tentative.any.js
+++ b/third_party/blink/web_tests/external/wpt/IndexedDB/idbobjectstore_putall.tentative.any.js
@@ -3,23 +3,24 @@
 promise_test(async testCase => {
   const db = await createDatabase(testCase, db => {
     const store = createBooksStore(testCase, db);
-    let values = [
-      {isbn: 'one', title: 'title1'},
-      {isbn: 'two', title: 'title2'},
-      {isbn: 'three', title: 'title3'}
-    ];
-    const putAllRequests = store.putAll(values);
-    putAllRequests.forEach(async request => {
-      await promiseForRequest(testCase, request);
-    });
   });
-
-  const txn = db.transaction(['books'], 'readonly');
+  const txn = db.transaction(['books'], 'readwrite');
   const objectStore = txn.objectStore('books');
-  const getRequest1 = objectStore.get('one');
-  const getRequest2 = objectStore.get('two');
-  const getRequest3 = objectStore.get('three');
+  let values = [
+    {isbn: 'one', title: 'title1'},
+    {isbn: 'two', title: 'title2'},
+    {isbn: 'three', title: 'title3'}
+  ];
+  let putAllRequest = objectStore.putAll(values);
+  await promiseForRequest(testCase, putAllRequest);
   await promiseForTransaction(testCase, txn);
+
+  const txn2 = db.transaction(['books'], 'readonly');
+  const objectStore2 = txn2.objectStore('books');
+  const getRequest1 = objectStore2.get('one');
+  const getRequest2 = objectStore2.get('two');
+  const getRequest3 = objectStore2.get('three');
+  await promiseForTransaction(testCase, txn2);
   assert_array_equals(
       [getRequest1.result.title,
           getRequest2.result.title,
@@ -27,4 +28,4 @@
       ['title1', 'title2', 'title3'],
       'All three retrieved titles should match those that were put.');
   db.close();
-}, 'Data can be successfully inputted into an object store using putAll.');
\ No newline at end of file
+}, 'Data can be successfully inputted into an object store using putAll.');
diff --git a/third_party/blink/web_tests/external/wpt/intersection-observer/intersection-ratio-ib-split.html b/third_party/blink/web_tests/external/wpt/intersection-observer/intersection-ratio-ib-split.html
new file mode 100644
index 0000000..905ea436
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/intersection-observer/intersection-ratio-ib-split.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<meta name="viewport" content="width=device-width,initial-scale=1">
+<link rel="author" href="mailto:emilio@crisal.io" title="Emilio Cobos Álvarez">
+<link rel="author" href="https://mozilla.org" title="Mozilla">
+<link rel="help" href="https://w3c.github.io/IntersectionObserver/#dom-intersectionobserverentry-intersectionratio">
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1581876">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+  block {
+    display: block;
+    width: 50vw;
+    height: 50vh;
+    background: green;
+  }
+</style>
+<inline>
+  <block></block>
+</inline>
+<script>
+promise_test(async function() {
+  for (let element of document.querySelectorAll("inline, block")) {
+    let entries = await new Promise(resolve => {
+      new IntersectionObserver(resolve).observe(element);
+    });
+    assert_equals(entries.length, 1, element.nodeName + ": Should get an entry");
+    assert_true(entries[0].isIntersecting, element.nodeName + ": Should be intersecting");
+    assert_equals(entries[0].intersectionRatio, 1, element.nodeName + ": Should be fully intersecting");
+  }
+}, "IntersectionObserver on an IB split gets the right intersection ratio");
+</script>
diff --git a/third_party/blink/web_tests/fast/forms/calendar-picker/calendar-picker-isDayOutsideOfRange.html b/third_party/blink/web_tests/fast/forms/calendar-picker/calendar-picker-isDayOutsideOfRange.html
new file mode 100644
index 0000000..c1f9445
--- /dev/null
+++ b/third_party/blink/web_tests/fast/forms/calendar-picker/calendar-picker-isDayOutsideOfRange.html
@@ -0,0 +1,62 @@
+<!DOCTYPE html>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../forms/resources/picker-common.js"></script>
+
+<input type=date id=dateElement>
+
+<script>
+promise_test(async () => {
+  await openPickerWithPromise(dateElement);
+  const {Month, Day, isDayOutsideOfRange} = popupWindow;
+
+  assert_true(isDayOutsideOfRange(
+    /*day=*/new Day(1, 1, 1),
+    /*minDay=*/new Day(2, 1, 1),
+    /*maxDay=*/new Day(3, 1, 1)),
+    'day.year < min < max: day is outside of range');
+  assert_false(isDayOutsideOfRange(
+    /*day=*/new Day(2, 9, 9),
+    /*minDay=*/new Day(1, 1, 1),
+    /*maxDay=*/new Day(3, 1, 1)),
+    'min < day.year < max: day is within range');
+  assert_true(isDayOutsideOfRange(
+    /*day=*/new Day(3, 1, 1),
+    /*minDay=*/new Day(1, 1, 1),
+    /*maxDay=*/new Day(2, 1, 1)),
+    'min < max < day.year: day is outside of range');
+
+  assert_true(isDayOutsideOfRange(
+    /*day=*/new Day(1, 1, 1),
+    /*minDay=*/new Day(1, 2, 1),
+    /*maxDay=*/new Day(1, 3, 1)),
+    'day.month < min < max: day is outside of range');
+  assert_false(isDayOutsideOfRange(
+    /*day=*/new Day(1, 2, 9),
+    /*minDay=*/new Day(1, 1, 1),
+    /*maxDay=*/new Day(1, 3, 1)),
+    'min < day.month < max: day is within range');
+  assert_true(isDayOutsideOfRange(
+    /*day=*/new Day(1, 3, 1),
+    /*minDay=*/new Day(1, 1, 1),
+    /*maxDay=*/new Day(1, 2, 1)),
+    'min < max < day.month: day is outside of range');
+
+  assert_true(isDayOutsideOfRange(
+    /*day=*/new Day(1, 1, 1),
+    /*minDay=*/new Day(1, 1, 2),
+    /*maxDay=*/new Day(1, 1, 3)),
+    'day.date < min < max: day is outside of range');
+  assert_false(isDayOutsideOfRange(
+    /*day=*/new Day(1, 1, 2),
+    /*minDay=*/new Day(1, 1, 1),
+    /*maxDay=*/new Day(1, 1, 3)),
+    'min < day.date < max: day is within range');
+  assert_true(isDayOutsideOfRange(
+    /*day=*/new Day(1, 1, 3),
+    /*minDay=*/new Day(1, 1, 1),
+    /*maxDay=*/new Day(1, 1, 2)),
+    'min < max < day.date: day is outside of range');
+
+}, `Tests behavior of the date picker's internal function isTodayOutsideOfRange.`);
+</script>
diff --git a/third_party/blink/web_tests/fast/forms/calendar-picker/calendar-picker-isWeekOutsideOfRange.html b/third_party/blink/web_tests/fast/forms/calendar-picker/calendar-picker-isWeekOutsideOfRange.html
new file mode 100644
index 0000000..b3b25b7e
--- /dev/null
+++ b/third_party/blink/web_tests/fast/forms/calendar-picker/calendar-picker-isWeekOutsideOfRange.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../forms/resources/picker-common.js"></script>
+
+<input type=date id=dateElement>
+
+<script>
+promise_test(async () => {
+  await openPickerWithPromise(dateElement);
+  const {Month, Day, Week, isWeekOutsideOfRange} = popupWindow;
+
+  assert_true(isWeekOutsideOfRange(
+    /*week=*/new Week(1, 1),
+    /*minWeek=*/new Week(2, 1),
+    /*maxWeek=*/new Week(3, 1)),
+    'week.year < min < max: week is outside of range');
+  assert_false(isWeekOutsideOfRange(
+    /*week=*/new Week(2, 9),
+    /*minWeek=*/new Week(1, 1),
+    /*maxWeek=*/new Week(3, 1)),
+    'min < week.year < max: week is within range');
+  assert_true(isWeekOutsideOfRange(
+    /*week=*/new Week(3, 1),
+    /*minWeek=*/new Week(1, 1),
+    /*maxWeek=*/new Week(2, 1)),
+    'min < max < week.year: week is outside of range');
+
+  assert_true(isWeekOutsideOfRange(
+    /*week=*/new Week(1, 1),
+    /*minWeek=*/new Week(1, 2),
+    /*maxWeek=*/new Week(1, 3)),
+    'week.week < min < max: week is outside of range');
+  assert_false(isWeekOutsideOfRange(
+    /*week=*/new Week(1, 2),
+    /*minWeek=*/new Week(1, 1),
+    /*maxWeek=*/new Week(1, 3)),
+    'min < week.week < max: week is within range');
+  assert_true(isWeekOutsideOfRange(
+    /*week=*/new Week(1, 3),
+    /*minWeek=*/new Week(1, 1),
+    /*maxWeek=*/new Week(1, 2)),
+    'min < max < week.week: week is outside of range');
+
+}, `Tests behavior of the date picker's internal function isTodayOutsideOfRange.`);
+</script>
diff --git a/third_party/blink/web_tests/http/tests/devtools/elements/highlight/highlight-css-grid-expected.txt b/third_party/blink/web_tests/http/tests/devtools/elements/highlight/highlight-css-grid-expected.txt
index 4b4fddf..3e4f72ac 100644
--- a/third_party/blink/web_tests/http/tests/devtools/elements/highlight/highlight-css-grid-expected.txt
+++ b/third_party/blink/web_tests/http/tests/devtools/elements/highlight/highlight-css-grid-expected.txt
@@ -219,30 +219,30 @@
       ],
       "positiveRowLineNumberPositions": [
         {
-          "x": 118,
+          "x": 173,
           "y": 408
         },
         {
-          "x": 150.5,
+          "x": 205.5,
           "y": 408
         },
         {
-          "x": 183,
+          "x": 238,
           "y": 408
         }
       ],
       "positiveColumnLineNumberPositions": [
         {
           "x": 173,
-          "y": 1198
+          "y": 408
         },
         {
           "x": 173,
-          "y": 1135.5
+          "y": 345.5
         },
         {
           "x": 173,
-          "y": 923
+          "y": 133
         }
       ],
       "negativeRowLineNumberOffsets": [
@@ -266,11 +266,11 @@
         },
         {
           "x": 238,
-          "y": 1135.5
+          "y": 345.5
         },
         {
           "x": 238,
-          "y": 923
+          "y": 133
         }
       ],
       "areaNames": {},
diff --git a/third_party/blink/web_tests/http/tests/devtools/elements/highlight/highlight-multiple-css-grid-expected.txt b/third_party/blink/web_tests/http/tests/devtools/elements/highlight/highlight-multiple-css-grid-expected.txt
index 5add83b..50fc05a 100644
--- a/third_party/blink/web_tests/http/tests/devtools/elements/highlight/highlight-multiple-css-grid-expected.txt
+++ b/third_party/blink/web_tests/http/tests/devtools/elements/highlight/highlight-multiple-css-grid-expected.txt
@@ -124,30 +124,30 @@
       ],
       "positiveRowLineNumberPositions": [
         {
-          "x": 118,
+          "x": 173,
           "y": 408
         },
         {
-          "x": 150.5,
+          "x": 205.5,
           "y": 408
         },
         {
-          "x": 183,
+          "x": 238,
           "y": 408
         }
       ],
       "positiveColumnLineNumberPositions": [
         {
           "x": 173,
-          "y": 1198
+          "y": 408
         },
         {
           "x": 173,
-          "y": 1135.5
+          "y": 345.5
         },
         {
           "x": 173,
-          "y": 923
+          "y": 133
         }
       ],
       "negativeRowLineNumberOffsets": [
@@ -171,11 +171,11 @@
         },
         {
           "x": 238,
-          "y": 1135.5
+          "y": 345.5
         },
         {
           "x": 238,
-          "y": 923
+          "y": 133
         }
       ],
       "areaNames": {},
diff --git a/third_party/blink/web_tests/http/tests/push_messaging/push-subscription-stringification.html b/third_party/blink/web_tests/http/tests/push_messaging/push-subscription-stringification.html
index 32828ffc..a8ba724 100644
--- a/third_party/blink/web_tests/http/tests/push_messaging/push-subscription-stringification.html
+++ b/third_party/blink/web_tests/http/tests/push_messaging/push-subscription-stringification.html
@@ -3,6 +3,7 @@
 <head>
 <title>Stringifying a PushSubscription object includes all object members</title>
 <link rel="manifest" href="resources/push_manifest.json">
+<script src="resources/push-constants.js"></script>
 <script src="../resources/testharness.js"></script>
 <script src="../resources/testharnessreport.js"></script>
 <script src="../serviceworker/resources/test-helpers.js"></script>
@@ -36,9 +37,12 @@
             assert_own_property(reflectedObject, 'endpoint');
             assert_equals(reflectedObject.endpoint, pushSubscription.endpoint);
 
+            // Expiration time should be in the expected window
             assert_own_property(reflectedObject, 'expirationTime');
-            assert_equals(reflectedObject.expirationTime, null);
-
+            assert_true(typeof pushSubscription.expirationTime === 'number');
+            assert_approx_equals(pushSubscription.expirationTime, Date.now() +
+                                 EXPIRATION_WINDOW, EXPIRATION_TIME_TOLERANCE,
+                                 `Expiration time should be in expected window: ${EXPIRATION_TIME_TOLERANCE} ms`);
             assert_own_property(reflectedObject, 'keys');
             assert_own_property(reflectedObject.keys, 'p256dh');
             assert_own_property(reflectedObject.keys, 'auth');
diff --git a/third_party/blink/web_tests/http/tests/push_messaging/resources/push-constants.js b/third_party/blink/web_tests/http/tests/push_messaging/resources/push-constants.js
index 7db1d186..33ddb4e 100644
--- a/third_party/blink/web_tests/http/tests/push_messaging/resources/push-constants.js
+++ b/third_party/blink/web_tests/http/tests/push_messaging/resources/push-constants.js
@@ -17,4 +17,8 @@
 
 const INVALID_BASE64URL_ENCODED_KEY = "BJEOX8aRBRLJ1SitNXbDH9Lc5CCIsTE33YwGakcXvoQ5K7KZq13-jxB2vhMTcX9c4-tC0h6CIFifzJhH6cNS!!!"
 
-const INVALID_BASE64_ENCODED_KEY = "BJEOX8aRBRLJ1SitNXbDH9Lc5CCIsTE33YwGakcXvoQ5K7KZq13+jxB2vhMTcX9c4+tC0h6CIFifzJhH6cNS/wQ="
\ No newline at end of file
+const INVALID_BASE64_ENCODED_KEY = "BJEOX8aRBRLJ1SitNXbDH9Lc5CCIsTE33YwGakcXvoQ5K7KZq13+jxB2vhMTcX9c4+tC0h6CIFifzJhH6cNS/wQ="
+
+const EXPIRATION_WINDOW = 90 * 24 * 60 * 60 * 1000;
+
+const EXPIRATION_TIME_TOLERANCE = 24 * 60 * 60 * 1000;
diff --git a/third_party/blink/web_tests/http/tests/push_messaging/subscribe-success-in-document.html b/third_party/blink/web_tests/http/tests/push_messaging/subscribe-success-in-document.html
index 0924a195..5fccd681 100644
--- a/third_party/blink/web_tests/http/tests/push_messaging/subscribe-success-in-document.html
+++ b/third_party/blink/web_tests/http/tests/push_messaging/subscribe-success-in-document.html
@@ -3,6 +3,7 @@
 <head>
 <title>subscribe() succeeds when permission is granted and resolves with a valid subscription</title>
 <link rel="manifest" href="resources/push_manifest.json">
+<script src="resources/push-constants.js"></script>
 <script src="../resources/testharness.js"></script>
 <script src="../resources/testharnessreport.js"></script>
 <script src="../serviceworker/resources/test-helpers.js"></script>
@@ -31,11 +32,12 @@
             assert_idl_attribute(pushSubscription, 'endpoint');
             assert_equals(typeof pushSubscription.endpoint, 'string');
 
+            // Expiration time should be in the expected window
             assert_idl_attribute(pushSubscription, 'expirationTime');
-            // TODO(viviy): check for not null after passing expiration time in
-            // PushMessagingManager
-            assert_equals(pushSubscription.expirationTime, null);
-
+            assert_true(typeof pushSubscription.expirationTime === 'number');
+            assert_approx_equals(pushSubscription.expirationTime, Date.now() +
+                                 EXPIRATION_WINDOW, EXPIRATION_TIME_TOLERANCE,
+                                 `Expiration time should be in expected window: ${EXPIRATION_TIME_TOLERANCE} ms`);
             try {
                 var endpointUrl = new URL(pushSubscription.endpoint);
             } catch(e) {
diff --git a/third_party/boringssl/BUILD.gn b/third_party/boringssl/BUILD.gn
index b435499..fab8905 100644
--- a/third_party/boringssl/BUILD.gn
+++ b/third_party/boringssl/BUILD.gn
@@ -72,7 +72,7 @@
     } else if (current_cpu == "x64") {
       if (is_mac) {
         sources += crypto_sources_mac_x86_64
-      } else if (is_linux || is_android) {
+      } else if (is_linux || is_chromeos || is_android) {
         sources += crypto_sources_linux_x86_64
       } else {
         public_configs = [ ":no_asm_config" ]
@@ -80,13 +80,13 @@
     } else if (current_cpu == "x86") {
       if (is_mac) {
         sources += crypto_sources_mac_x86
-      } else if (is_linux || is_android) {
+      } else if (is_linux || is_chromeos || is_android) {
         sources += crypto_sources_linux_x86
       } else {
         public_configs = [ ":no_asm_config" ]
       }
     } else if (current_cpu == "arm") {
-      if (is_linux || is_android) {
+      if (is_linux || is_chromeos || is_android) {
         sources += crypto_sources_linux_arm
       } else if (is_ios) {
         sources += crypto_sources_ios_arm
@@ -94,7 +94,7 @@
         public_configs = [ ":no_asm_config" ]
       }
     } else if (current_cpu == "arm64") {
-      if (is_linux || is_android) {
+      if (is_linux || is_chromeos || is_android) {
         sources += crypto_sources_linux_aarch64
       } else if (is_ios) {
         sources += crypto_sources_ios_aarch64
diff --git a/third_party/breakpad/BUILD.gn b/third_party/breakpad/BUILD.gn
index 9867688..cd2a60b 100644
--- a/third_party/breakpad/BUILD.gn
+++ b/third_party/breakpad/BUILD.gn
@@ -471,7 +471,7 @@
   }
 }
 
-if (is_linux || is_android) {
+if (is_linux || is_chromeos || is_android) {
   if (current_toolchain == host_toolchain) {
     executable("symupload") {
       sources = [
diff --git a/third_party/crashpad/crashpad/build/crashpad_buildconfig.gni b/third_party/crashpad/crashpad/build/crashpad_buildconfig.gni
index d919a8b..a8bf6cf8 100644
--- a/third_party/crashpad/crashpad/build/crashpad_buildconfig.gni
+++ b/third_party/crashpad/crashpad/build/crashpad_buildconfig.gni
@@ -38,7 +38,7 @@
   crashpad_is_mac = is_mac
   crashpad_is_ios = is_ios
   crashpad_is_win = is_win
-  crashpad_is_linux = is_linux
+  crashpad_is_linux = is_linux || is_chromeos
   crashpad_is_android = is_android
   crashpad_is_fuchsia = is_fuchsia
 
diff --git a/third_party/dav1d/BUILD.gn b/third_party/dav1d/BUILD.gn
index 33b855f9d..327cdcc 100644
--- a/third_party/dav1d/BUILD.gn
+++ b/third_party/dav1d/BUILD.gn
@@ -113,7 +113,7 @@
   if (is_mac) {
     dav1d_copts += [ "-D_DARWIN_C_SOURCE" ]
   }
-  if (is_linux || is_android || current_os == "aix") {
+  if (is_linux || is_chromeos || is_android || current_os == "aix") {
     if (!is_clang) {
       dav1d_copts += [ "-D_GNU_SOURCE" ]
     }
diff --git a/third_party/expat/BUILD.gn b/third_party/expat/BUILD.gn
index 1bddfcb0..2dc7964 100644
--- a/third_party/expat/BUILD.gn
+++ b/third_party/expat/BUILD.gn
@@ -9,7 +9,7 @@
 # let's not pull it in twice.
 # Chromecast doesn't ship expat as a system library.
 # Libfuzzer and AFL need to build library from sources.
-if (is_linux && !is_chromecast && !use_fuzzing_engine) {
+if ((is_linux || is_chromeos) && !is_chromecast && !use_fuzzing_engine) {
   config("expat_config") {
     libs = [ "expat" ]
   }
diff --git a/third_party/fontconfig/BUILD.gn b/third_party/fontconfig/BUILD.gn
index 2f93387..1bee7acb 100644
--- a/third_party/fontconfig/BUILD.gn
+++ b/third_party/fontconfig/BUILD.gn
@@ -5,7 +5,7 @@
 import("//build/config/sanitizers/sanitizers.gni")
 import("//third_party/fontconfig/fontconfig.gni")
 
-assert(is_linux)
+assert(is_linux || is_chromeos)
 
 if (use_bundled_fontconfig) {
   config("fontconfig_config") {
diff --git a/third_party/fontconfig/fontconfig.gni b/third_party/fontconfig/fontconfig.gni
index 2baef3b5..50d9f1fa 100644
--- a/third_party/fontconfig/fontconfig.gni
+++ b/third_party/fontconfig/fontconfig.gni
@@ -5,8 +5,8 @@
 import("//build/config/chromeos/args.gni")
 import("//build/config/features.gni")
 
-assert(is_linux)
+assert(is_linux || is_chromeos)
 
 declare_args() {
-  use_bundled_fontconfig = is_linux && !is_chromeos_device
+  use_bundled_fontconfig = (is_linux || is_chromeos) && !is_chromeos_device
 }
diff --git a/third_party/freetype/BUILD.gn b/third_party/freetype/BUILD.gn
index d8599ae..b84a743 100644
--- a/third_party/freetype/BUILD.gn
+++ b/third_party/freetype/BUILD.gn
@@ -131,13 +131,13 @@
     ]
   }
 
-  if (is_linux || is_chromecast) {
+  if (is_linux || is_chromeos || is_chromecast) {
     # Needed for content_shell on Linux and Chromecast, since fontconfig
     # requires FT_Get_BDF_Property.
     sources += [ "src/src/base/ftbdf.c" ]
   }
 
-  if (is_linux || is_chromecast) {
+  if (is_linux || is_chromeos || is_chromecast) {
     # Needed on Fedora whose libfreetype builds ftsynth.c containing
     # FT_GlyphSlot_Embolden, which we need to replace in content_shell if
     # we are linking against our own FreeType.
diff --git a/third_party/ijar/BUILD.gn b/third_party/ijar/BUILD.gn
index 127fb96..9ad722f 100644
--- a/third_party/ijar/BUILD.gn
+++ b/third_party/ijar/BUILD.gn
@@ -4,7 +4,7 @@
 
 # A tool that removes all non-interface-specific parts from a .jar file.
 
-if (is_linux) {
+if (is_linux || is_chromeos) {
   executable("ijar") {
     sources = [
       "classfile.cc",
diff --git a/third_party/libdrm/BUILD.gn b/third_party/libdrm/BUILD.gn
index b870f33..1da5e188 100644
--- a/third_party/libdrm/BUILD.gn
+++ b/third_party/libdrm/BUILD.gn
@@ -1,7 +1,7 @@
 # Copyright 2016 The Chromium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
-assert(is_linux)
+assert(is_linux || is_chromeos)
 
 config("libdrm_config") {
   # TODO(thomasanderson): Remove this hack once
diff --git a/third_party/libusb/BUILD.gn b/third_party/libusb/BUILD.gn
index 2159671f..fe8c6649 100644
--- a/third_party/libusb/BUILD.gn
+++ b/third_party/libusb/BUILD.gn
@@ -28,7 +28,7 @@
   if (is_clang) {
     # guid_eq in windows_usb.c is unused.
     cflags = [ "-Wno-unused-function" ]
-    if (is_linux && !use_udev) {
+    if ((is_linux || is_chromeos) && !use_udev) {
       cflags += [ "-Wno-pointer-sign" ]
     }
   }
@@ -115,7 +115,7 @@
     ]
   }
 
-  if (is_linux) {
+  if (is_linux || is_chromeos) {
     sources += [
       "src/libusb/os/linux_usbfs.c",
       "src/libusb/os/linux_usbfs.h",
@@ -139,7 +139,7 @@
     deps += [ "//build/linux/libudev" ]
   }
 
-  if (is_linux && !use_udev) {
+  if ((is_linux || is_chromeos) && !use_udev) {
     sources += [ "src/libusb/os/linux_netlink.c" ]
     defines += [ "HAVE_LINUX_NETLINK_H" ]
   }
diff --git a/third_party/libxml/BUILD.gn b/third_party/libxml/BUILD.gn
index 653a076ef..a155534 100644
--- a/third_party/libxml/BUILD.gn
+++ b/third_party/libxml/BUILD.gn
@@ -4,7 +4,7 @@
 
 # Define an "os_include" variable that points at the OS-specific generated
 # headers.  These were generated by running the configure script offline.
-if (is_linux || is_android || is_nacl || is_fuchsia) {
+if (is_linux || is_chromeos || is_android || is_nacl || is_fuchsia) {
   os_include = "linux"
 } else if (is_apple) {
   os_include = "mac"
@@ -66,7 +66,7 @@
       # TODO(hans): See if we can fix upstream (http://crbug.com/763944).
       "-Wno-enum-compare",
     ]
-  } else if (is_linux) {
+  } else if (is_linux || is_chromeos) {
     cflags = [
       # gcc spits out a bunch of warnings about passing too many arguments to
       # __xmlSimpleError.
diff --git a/third_party/libxslt/BUILD.gn b/third_party/libxslt/BUILD.gn
index 4aa6502..2cc220dc 100644
--- a/third_party/libxslt/BUILD.gn
+++ b/third_party/libxslt/BUILD.gn
@@ -81,7 +81,7 @@
   public_configs = [ ":libxslt_config" ]
 
   cflags = []
-  if (is_linux || is_android || is_fuchsia) {
+  if (is_linux || is_chromeos || is_android || is_fuchsia) {
     include_dirs = [ "linux" ]
   } else if (is_win) {
     include_dirs = [ "win32" ]
diff --git a/third_party/minigbm/BUILD.gn b/third_party/minigbm/BUILD.gn
index 8cc5f9d..79ebcab 100644
--- a/third_party/minigbm/BUILD.gn
+++ b/third_party/minigbm/BUILD.gn
@@ -5,7 +5,7 @@
 import("//build/config/chromecast_build.gni")
 import("//build/config/linux/pkg_config.gni")
 
-assert(is_linux)
+assert(is_linux || is_chromeos)
 
 declare_args() {
   # Controls whether the build should use the version of minigbm library shipped
diff --git a/third_party/openh264/BUILD.gn b/third_party/openh264/BUILD.gn
index 581ad6e..ef4937a 100644
--- a/third_party/openh264/BUILD.gn
+++ b/third_party/openh264/BUILD.gn
@@ -45,7 +45,7 @@
 # is believed to work.
 # MSAN builds are flaky with assembler. crbug.com/685168
 
-use_assembler = (is_win || is_linux) &&
+use_assembler = (is_win || is_linux || is_chromeos) &&
                 (current_cpu == "x86" || current_cpu == "x64") && !is_msan
 
 # This IF statement will make the targets visible only on specific builds,
@@ -55,7 +55,7 @@
   if (!is_component_build) {
     if (is_apple) {
       asm_defines += [ "WELS_PRIVATE_EXTERN=private_extern" ]
-    } else if (is_linux || is_android || is_fuchsia) {
+    } else if (is_linux || is_chromeos || is_android || is_fuchsia) {
       asm_defines += [ "WELS_PRIVATE_EXTERN=hidden" ]
     }
   }
@@ -74,7 +74,7 @@
         ]
       } else if (is_win) {
         defines += [ "WIN64" ]
-      } else if (is_linux) {
+      } else if (is_linux || is_chromeos) {
         defines += [ "UNIX64" ]
       }
     }
@@ -95,7 +95,7 @@
         ]
       } else if (is_win) {
         defines += [ "WIN64" ]
-      } else if (is_linux) {
+      } else if (is_linux || is_chromeos) {
         defines += [ "UNIX64" ]
       }
     }
@@ -116,12 +116,12 @@
         ]
       } else if (is_win) {
         defines += [ "WIN64" ]
-      } else if (is_linux) {
+      } else if (is_linux || is_chromeos) {
         defines += [ "UNIX64" ]
       }
     }
   }
-}  # if (is_win || is_linux)
+}  # if (is_win || is_linux || is_chromeos)
 
 source_set("common") {
   sources = openh264_common_sources
diff --git a/third_party/opus/BUILD.gn b/third_party/opus/BUILD.gn
index a6f4000..413a0e9 100644
--- a/third_party/opus/BUILD.gn
+++ b/third_party/opus/BUILD.gn
@@ -21,7 +21,7 @@
 # If OPUS Run Time CPU Detections (RTCD) shall be used.
 # Based on the conditions in celt/arm/armcpu.c:
 # defined(_MSC_VER) || defined(__linux__).
-use_opus_arm_rtcd = current_cpu == "arm" && (is_win || is_android || is_linux)
+use_opus_arm_rtcd = current_cpu == "arm" && (is_win || is_android || is_linux || is_chromeos)
 
 config("opus_config") {
   include_dirs = [ "src/include" ]
@@ -612,7 +612,7 @@
 # Compilation fails on windows due to wstring/string mistmatch.
 # This is not worth looking at it since the benchmark is tailored for android.
 # It is ok to run it on linux though, for experimentation purpose.
-if (is_android || is_linux) {
+if (is_android || is_linux || is_chromeos) {
   test("opus_tests") {
     sources = [ "tests/opus_benchmark.cc" ]
 
diff --git a/third_party/sqlite/BUILD.gn b/third_party/sqlite/BUILD.gn
index 772114f..6994028 100644
--- a/third_party/sqlite/BUILD.gn
+++ b/third_party/sqlite/BUILD.gn
@@ -138,7 +138,7 @@
       ]
     }
   }
-  if (is_linux) {
+  if (is_linux || is_chromeos) {
     cflags += [
       # SQLite doesn't believe in compiler warnings, preferring testing.
       # http://www.sqlite.org/faq.html#q17
@@ -191,7 +191,7 @@
     }
   }
 
-  if (is_linux || is_android) {
+  if (is_linux || is_chromeos || is_android) {
     defines += [
       # Linux provides fdatasync(), a faster equivalent of fsync().
       "fdatasync=fdatasync",
@@ -270,7 +270,7 @@
     }
   }
 
-  if (is_linux || is_android) {
+  if (is_linux || is_chromeos || is_android) {
     defines += [
       # Linux provides fdatasync(), a faster equivalent of fsync().
       "fdatasync=fdatasync",
@@ -344,7 +344,7 @@
   ]
 }
 
-if (is_win || is_mac || is_linux) {
+if (is_win || is_mac || is_linux || is_chromeos) {
   executable("sqlite_shell") {
     include_dirs = [
       # SQLite's shell.c contains an '#include "sqlite3.h", which we want to be
diff --git a/third_party/usrsctp/BUILD.gn b/third_party/usrsctp/BUILD.gn
index caf61a64..00ed6acb 100644
--- a/third_party/usrsctp/BUILD.gn
+++ b/third_party/usrsctp/BUILD.gn
@@ -121,7 +121,7 @@
     "-UINET6",
   ]
 
-  if (is_linux || is_android) {
+  if (is_linux || is_chromeos || is_android) {
     defines += [
       "__Userspace_os_Linux",
       "_GNU_SOURCE",
diff --git a/third_party/widevine/cdm/BUILD.gn b/third_party/widevine/cdm/BUILD.gn
index 1919784f..6225a61 100644
--- a/third_party/widevine/cdm/BUILD.gn
+++ b/third_party/widevine/cdm/BUILD.gn
@@ -135,7 +135,7 @@
 # This target exists for tests to depend on that pulls in a runtime dependency
 # on the license server.
 group("widevine_test_license_server") {
-  if (bundle_widevine_cdm && is_linux) {
+  if (bundle_widevine_cdm && (is_linux || is_chromeos)) {
     data = [ "//third_party/widevine/test/license_server/" ]
   }
 }
diff --git a/tools/android/avd/proto/creation/generic_android23.textpb b/tools/android/avd/proto/creation/generic_android23.textpb
index cd0054b..f5e0d93 100644
--- a/tools/android/avd/proto/creation/generic_android23.textpb
+++ b/tools/android/avd/proto/creation/generic_android23.textpb
@@ -6,13 +6,13 @@
 
 emulator_package {
   package_name: "chromium/third_party/android_sdk/public/emulator"
-  version: "A4EvXZUIuQho0QRDJopMUpgyp6NA3aiDQjGKPUKbowMC"
+  version: "xhyuoquVvBTcJelgRjMKZeoBVSQRjB7pLVJPt5C9saIC"
   dest_path: ".emulator_sdk"
 }
 
 system_image_package {
   package_name: "chromium/third_party/android_sdk/public/system-images/android-23/google_apis/x86"
-  version: "j_921Gw3fAlW8WYL9A6BeEFLQ8CSC9LMhPZKAq8DCxkC"
+  version: "npuCAATVbhmywZwGhI3tMoECTrBBzzyJLpjAPXqtmYYC"
   dest_path: ".emulator_sdk"
 }
 system_image_name: "system-images;android-23;google_apis;x86"
@@ -34,4 +34,7 @@
     key: "GLDMA"
     value: "off"
   }
+  # Tests can run into low memory issue with the default ram size 1024MB
+  # Incease to 2048MB, which is the same as that on Nexus 5X
+  ram_size: 2048
 }
diff --git a/tools/android/avd/proto/creation/generic_android23_tablet.textpb b/tools/android/avd/proto/creation/generic_android23_tablet.textpb
index a0bfaa2..3ba60e6 100644
--- a/tools/android/avd/proto/creation/generic_android23_tablet.textpb
+++ b/tools/android/avd/proto/creation/generic_android23_tablet.textpb
@@ -6,13 +6,13 @@
 
 emulator_package {
   package_name: "chromium/third_party/android_sdk/public/emulator"
-  version: "A4EvXZUIuQho0QRDJopMUpgyp6NA3aiDQjGKPUKbowMC"
+  version: "xhyuoquVvBTcJelgRjMKZeoBVSQRjB7pLVJPt5C9saIC"
   dest_path: ".emulator_sdk"
 }
 
 system_image_package {
   package_name: "chromium/third_party/android_sdk/public/system-images/android-23/google_apis/x86"
-  version: "j_921Gw3fAlW8WYL9A6BeEFLQ8CSC9LMhPZKAq8DCxkC"
+  version: "npuCAATVbhmywZwGhI3tMoECTrBBzzyJLpjAPXqtmYYC"
   dest_path: ".emulator_sdk"
 }
 system_image_name: "system-images;android-23;google_apis;x86"
@@ -37,4 +37,7 @@
     key: "GLDMA"
     value: "off"
   }
+  # Tests can run into low memory issue with the default ram size 1024MB
+  # Incease to 2048MB, which is the same as that on Nexus 5X
+  ram_size: 2048
 }
diff --git a/tools/android/avd/proto/creation/generic_android28.textpb b/tools/android/avd/proto/creation/generic_android28.textpb
index 367fe1d1..d77326f2 100644
--- a/tools/android/avd/proto/creation/generic_android28.textpb
+++ b/tools/android/avd/proto/creation/generic_android28.textpb
@@ -6,13 +6,13 @@
 
 emulator_package {
   package_name: "chromium/third_party/android_sdk/public/emulator"
-  version: "A4EvXZUIuQho0QRDJopMUpgyp6NA3aiDQjGKPUKbowMC"
+  version: "xhyuoquVvBTcJelgRjMKZeoBVSQRjB7pLVJPt5C9saIC"
   dest_path: ".emulator_sdk"
 }
 
 system_image_package {
   package_name: "chromium/third_party/android_sdk/public/system-images/android-28/google_apis/x86"
-  version: "FqzSrAzROgfIjXE0eJ8hmf--GqFjwf9aIDzBVdvPIbwC"
+  version: "LDa0XkTjgGYx7Amzg5qjIRgCfc4F_pq7rKMJVdACYx8C"
   dest_path: ".emulator_sdk"
 }
 system_image_name: "system-images;android-28;google_apis;x86"
diff --git a/tools/android/avd/proto/creation/generic_playstore_android28.textpb b/tools/android/avd/proto/creation/generic_playstore_android28.textpb
index 854807ee..4196b6e 100644
--- a/tools/android/avd/proto/creation/generic_playstore_android28.textpb
+++ b/tools/android/avd/proto/creation/generic_playstore_android28.textpb
@@ -6,7 +6,7 @@
 
 emulator_package {
   package_name: "chromium/third_party/android_sdk/public/emulator"
-  version: "A4EvXZUIuQho0QRDJopMUpgyp6NA3aiDQjGKPUKbowMC"
+  version: "lnt2Oz8NS73mAJL389R1QwgbM2qDSKNIRWTUOdmywukC"
   dest_path: ".emulator_sdk"
 }
 
diff --git a/tools/binary_size/libsupersize/caspian/wasmbuild.patch b/tools/binary_size/libsupersize/caspian/wasmbuild.patch
index c1866163..65d45ded 100644
--- a/tools/binary_size/libsupersize/caspian/wasmbuild.patch
+++ b/tools/binary_size/libsupersize/caspian/wasmbuild.patch
@@ -1,24 +1,25 @@
 diff --git a/build/config/BUILDCONFIG.gn b/build/config/BUILDCONFIG.gn
-index 4cc6bfc45e64..02fb53d01d0a 100644
+index 59dace167eef..ebbe3f37d101 100644
 --- a/build/config/BUILDCONFIG.gn
 +++ b/build/config/BUILDCONFIG.gn
-@@ -291,9 +291,10 @@ is_ios = current_os == "ios"
+@@ -291,10 +291,11 @@ is_ios = current_os == "ios"
  is_linux = current_os == "chromeos" || current_os == "linux"
  is_mac = current_os == "mac"
  is_nacl = current_os == "nacl"
 +is_wasm = current_os == "wasm"
  is_win = current_os == "win" || current_os == "winuwp"
  
+ is_apple = is_ios || is_mac
 -is_posix = !is_win && !is_fuchsia
 +is_posix = !is_win && !is_fuchsia && !is_wasm
  
  # =============================================================================
  # SOURCES FILTERS
 diff --git a/build/config/compiler/BUILD.gn b/build/config/compiler/BUILD.gn
-index d483562f6b0b..6b00d5d2ab2b 100644
+index f4ba837449b5..0fe4f8247c68 100644
 --- a/build/config/compiler/BUILD.gn
 +++ b/build/config/compiler/BUILD.gn
-@@ -575,6 +575,19 @@ config("compiler") {
+@@ -578,6 +578,19 @@ config("compiler") {
      ldflags += [ "-stdlib=libc++" ]
    }
  
@@ -38,7 +39,7 @@
    # Add flags for link-time optimization. These flags enable
    # optimizations/transformations that require whole-program visibility at link
    # time, so they need to be applied to all translation units, and we may end up
-@@ -1492,7 +1505,8 @@ config("default_warnings") {
+@@ -1495,7 +1508,8 @@ config("default_warnings") {
          cflags += [ "-Wno-nonportable-include-path" ]
        }
  
@@ -48,7 +49,7 @@
          # Flags NaCl (Clang 3.7) and Xcode 9.2 (Clang clang-900.0.39.2) do not
          # recognize.
          cflags += [
-@@ -2248,6 +2262,9 @@ config("symbols") {
+@@ -2257,6 +2271,9 @@ config("symbols") {
          "-debug-info-kind=constructor",
        ]
      }
@@ -59,10 +60,10 @@
      cflags = []
      if (is_mac && enable_dsyms) {
 diff --git a/build/toolchain/toolchain.gni b/build/toolchain/toolchain.gni
-index 80c2e7b5e4a2..1976fab7b86e 100644
+index d556b0e0927e..f4597ab04037 100644
 --- a/build/toolchain/toolchain.gni
 +++ b/build/toolchain/toolchain.gni
-@@ -59,6 +59,9 @@ if (is_mac || is_ios) {
+@@ -59,6 +59,9 @@ if (is_apple) {
    shlib_extension = ".so"
  } else if (is_win) {
    shlib_extension = ".dll"
diff --git a/tools/metrics/actions/actions.xml b/tools/metrics/actions/actions.xml
index a37068b..ce36e9f 100644
--- a/tools/metrics/actions/actions.xml
+++ b/tools/metrics/actions/actions.xml
@@ -9033,6 +9033,15 @@
   <description>User opened a URL in Chrome via Apple Shortcuts.</description>
 </action>
 
+<action name="IOSLaunchedByOpenInIncognitoIntent">
+  <owner>gujen@chromium.org</owner>
+  <owner>rohitrao@chromium.org</owner>
+  <description>
+    User opened a URL/a list of URLs in Chrome in Incognito mode via Apple
+    Shortcuts.
+  </description>
+</action>
+
 <action name="IOSLaunchedBySearchInChromeIntent">
   <owner>justincohen@chromium.org</owner>
   <description>
@@ -20691,12 +20700,31 @@
 </action>
 
 <action name="SharingQRCode.DialogLaunched">
+  <owner>skare@chromium.org</owner>
   <owner>src/components/send_tab_to_self/OWNERS</owner>
   <description>
     User launched the QR Generator dialog (desktop platforms).
   </description>
 </action>
 
+<action name="SharingQRCode.DialogLaunched.ContextMenuImage">
+  <owner>skare@chromium.org</owner>
+  <owner>src/components/send_tab_to_self/OWNERS</owner>
+  <description>
+    User launched the QR Generator dialog for an image via the context menu on
+    desktop.
+  </description>
+</action>
+
+<action name="SharingQRCode.DialogLaunched.ContextMenuPage">
+  <owner>skare@chromium.org</owner>
+  <owner>src/components/send_tab_to_self/OWNERS</owner>
+  <description>
+    User launched the QR Generator dialog for a page via the context menu on
+    desktop.
+  </description>
+</action>
+
 <action name="SharingQRCode.DownloadQRCode">
   <owner>tgupta@chromium.org</owner>
   <owner>src/components/send_tab_to_self/OWNERS</owner>
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index b8e52628..0dcc0ef 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -60221,7 +60221,7 @@
   <int value="7" label="MediaElementEvent"/>
   <int value="8" label="CanvasBlobSerialization"/>
   <int value="9" label="Microtask"/>
-  <int value="10" label="JavascriptTimer"/>
+  <int value="10" label="JavascriptTimerDelayed"/>
   <int value="11" label="RemoteEvent"/>
   <int value="12" label="WebSocket"/>
   <int value="13" label="PostedMessage"/>
@@ -60283,6 +60283,7 @@
   <int value="69" label="MainThreadTaskQueueNonWaking"/>
   <int value="70" label="InternalFindInPage"/>
   <int value="71" label="InternalHighPriorityLocalFrame"/>
+  <int value="72" label="JavascriptTimerImmediate"/>
 </enum>
 
 <enum name="RendererSchedulerTaskUseCase">
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index e4624bc..14ca58e8 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -9608,6 +9608,18 @@
   </summary>
 </histogram>
 
+<histogram base="true" name="Ash.Assistant.AnimationSmoothness" units="%"
+    expires_after="2021-07-20">
+<!-- Name completed by histogram_suffixes name="AshAssistantAnimationSmoothness" -->
+
+  <owner>cowmoo@chromium.org</owner>
+  <owner>xiaohuic@chromium.org</owner>
+  <summary>
+    Relative smoothness of assistant related animations. 100% represents ideally
+    smooth 60 frames per second.
+  </summary>
+</histogram>
+
 <histogram name="Ash.BackGesture.EndScenarioType"
     enum="BackGestureEndScenarioType" expires_after="2020-12-23">
   <owner>minch@chromium.org</owner>
@@ -145175,6 +145187,9 @@
 
 <histogram name="ResourceCoordinator.LocalDB.DatabaseInit"
     enum="LocalSiteCharacteristicsDBInitStatus" expires_after="M77">
+  <obsolete>
+    Removed July 2020.
+  </obsolete>
   <owner>sebmarchand@chromium.org</owner>
   <summary>
     The result of opening the Local Site Characteristics database.
@@ -145183,6 +145198,9 @@
 
 <histogram name="ResourceCoordinator.LocalDB.DatabaseInitAfterDelete"
     enum="LocalSiteCharacteristicsDBInitStatus" expires_after="M77">
+  <obsolete>
+    Removed July 2020.
+  </obsolete>
   <owner>sebmarchand@chromium.org</owner>
   <summary>
     The result of opening the Local Site Characteristics database after deleting
@@ -145192,6 +145210,9 @@
 
 <histogram name="ResourceCoordinator.LocalDB.DatabaseInitAfterRepair"
     enum="LocalSiteCharacteristicsDBInitStatus" expires_after="M77">
+  <obsolete>
+    Removed July 2020.
+  </obsolete>
   <owner>sebmarchand@chromium.org</owner>
   <summary>
     The result of opening the Local Site Characteristics database after a repair
@@ -145201,6 +145222,9 @@
 
 <histogram name="ResourceCoordinator.LocalDB.DatabaseRepair"
     enum="BooleanSuccess" expires_after="M77">
+  <obsolete>
+    Removed July 2020.
+  </obsolete>
   <owner>sebmarchand@chromium.org</owner>
   <summary>
     The result of trying to repair the Local Site Characteristics database after
@@ -145210,6 +145234,9 @@
 
 <histogram name="ResourceCoordinator.LocalDB.ObservationTimeBeforeFirstUse"
     units="ms" expires_after="M85">
+  <obsolete>
+    Removed July 2020.
+  </obsolete>
 <!-- Name completed by histogram_suffixes name="LocalSiteCharacteristicsFeatures" -->
 
   <owner>sebmarchand@chromium.org</owner>
@@ -145221,6 +145248,9 @@
 
 <histogram name="ResourceCoordinator.LocalDB.OnDiskSize" units="KB"
     expires_after="M85">
+  <obsolete>
+    Removed July 2020.
+  </obsolete>
   <owner>sebmarchand@chromium.org</owner>
   <summary>
     The size of the Local Site Characteristics database on disk. Recorded at
@@ -145230,6 +145260,9 @@
 
 <histogram name="ResourceCoordinator.LocalDB.ReadHasCompletedBeforeQuery"
     enum="Boolean" expires_after="M77">
+  <obsolete>
+    Removed July 2020.
+  </obsolete>
   <owner>sebmarchand@chromium.org</owner>
   <summary>
     Boolean indicating if the read operation from the Local Site Characteristics
@@ -198543,6 +198576,18 @@
   <affected-histogram name="ArcAuth.MainAccountResolutionStatus"/>
 </histogram_suffixes>
 
+<histogram_suffixes name="AshAssistantAnimationSmoothness" separator=".">
+  <suffix name="CardElement"
+      label="Animation for showing and hiding card responses"/>
+  <suffix name="ResizeAssistantPageView"
+      label="Expand assistant from launcher"/>
+  <suffix name="SuggestionChip"
+      label="Animation for showing and hiding suggestion chips"/>
+  <suffix name="TextElement"
+      label="Animation for showing and hiding text response"/>
+  <affected-histogram name="Ash.Assistant.AnimationSmoothness"/>
+</histogram_suffixes>
+
 <histogram_suffixes name="AssistantProactiveSuggestionsShowAttemptByCategory"
     separator="." ordering="prefix,3">
   <suffix name="AbortedByDuplicateSuppression"
@@ -205840,6 +205885,9 @@
 </histogram_suffixes>
 
 <histogram_suffixes name="LocalSiteCharacteristicsFeatures" separator=".">
+  <obsolete>
+    Removed July 2020.
+  </obsolete>
   <suffix name="AudioUsageInBackground"
       label="A tab played some audio while it was in background."/>
   <suffix name="FaviconUpdateInBackground"
diff --git a/tools/perf/core/perfetto_binary_roller/binary_deps.json b/tools/perf/core/perfetto_binary_roller/binary_deps.json
index ae35bf456..0ae6f1f0 100644
--- a/tools/perf/core/perfetto_binary_roller/binary_deps.json
+++ b/tools/perf/core/perfetto_binary_roller/binary_deps.json
@@ -9,8 +9,8 @@
             "remote_path": "perfetto_binaries/trace_processor_shell/mac/f6c424bb59238a75fbb10d4d802b5ad31107a3a3/trace_processor_shell"
         },
         "linux": {
-            "hash": "d2519e9005a8c9ad14abc39f10566856720e9068",
-            "remote_path": "perfetto_binaries/trace_processor_shell/linux/f6c424bb59238a75fbb10d4d802b5ad31107a3a3/trace_processor_shell"
+            "hash": "6272ed239cb17d8d5de1880b69d56c41e9d57f09",
+            "remote_path": "perfetto_binaries/trace_processor_shell/linux/4766bd88cf78535fd52edab11fa99374581b2bee/trace_processor_shell"
         }
     },
     "power_profile.sql": {
diff --git a/ui/android/java/res/values-night/colors.xml b/ui/android/java/res/values-night/colors.xml
index 31d9e3e..a211303 100644
--- a/ui/android/java/res/values-night/colors.xml
+++ b/ui/android/java/res/values-night/colors.xml
@@ -40,6 +40,7 @@
     <color name="ripple_color_blue">@color/ripple_color_blue_light</color>
 
     <!-- Colors used for Widgets (checkboxes, switches, buttons, etc)-->
+    <color name="default_control_color_normal">@color/default_control_color_normal_dark</color>
     <color name="default_control_color_active">@color/default_control_color_active_dark</color>
 
     <!-- Chip colors -->
diff --git a/ui/android/java/res/values/semantic_colors_adaptive.xml b/ui/android/java/res/values/semantic_colors_adaptive.xml
index f52e4807..d143c80 100644
--- a/ui/android/java/res/values/semantic_colors_adaptive.xml
+++ b/ui/android/java/res/values/semantic_colors_adaptive.xml
@@ -74,6 +74,7 @@
     <color name="ripple_color_blue">@color/ripple_color_blue_dark</color>
 
     <!-- Colors used for Widgets (checkboxes, switches, buttons, etc)-->
+    <color name="default_control_color_normal" tools:ignore="UnusedResources">@color/default_control_color_normal_light</color>
     <color name="default_control_color_active" tools:ignore="UnusedResources">@color/default_control_color_active_light</color>
 
     <!-- Chip colors -->
diff --git a/ui/base/BUILD.gn b/ui/base/BUILD.gn
index 94c6f10..7768cec5 100644
--- a/ui/base/BUILD.gn
+++ b/ui/base/BUILD.gn
@@ -428,6 +428,7 @@
     ":ui_data_pack",
     "//base",
     "//skia",
+    "//third_party/abseil-cpp:absl",
     "//ui/gfx",
     "//ui/gfx/geometry",
   ]
diff --git a/ui/base/models/image_model.cc b/ui/base/models/image_model.cc
index ce409c3..acecd35 100644
--- a/ui/base/models/image_model.cc
+++ b/ui/base/models/image_model.cc
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include <tuple>
+
 #include "ui/base/models/image_model.h"
 
 namespace ui {
@@ -11,7 +13,7 @@
 VectorIconModel::VectorIconModel(const gfx::VectorIcon& vector_icon,
                                  int color_id,
                                  int icon_size)
-    : vector_icon_(&vector_icon), icon_size_(icon_size), color_id_(color_id) {}
+    : vector_icon_(&vector_icon), icon_size_(icon_size), color_(color_id) {}
 
 VectorIconModel::VectorIconModel(const gfx::VectorIcon& vector_icon,
                                  SkColor color,
@@ -29,8 +31,8 @@
 VectorIconModel& VectorIconModel::operator=(VectorIconModel&&) = default;
 
 bool VectorIconModel::operator==(const VectorIconModel& other) const {
-  return vector_icon_ == other.vector_icon_ && icon_size_ == other.icon_size_ &&
-         color_ == other.color_ && color_id_ == other.color_id_;
+  return std::tie(vector_icon_, icon_size_, color_) ==
+         std::tie(other.vector_icon_, other.icon_size_, other.color_);
 }
 
 bool VectorIconModel::operator!=(const VectorIconModel& other) const {
@@ -40,9 +42,9 @@
 ImageModel::ImageModel() = default;
 
 ImageModel::ImageModel(const VectorIconModel& vector_icon_model)
-    : vector_icon_model_(vector_icon_model) {}
+    : icon_(vector_icon_model) {}
 
-ImageModel::ImageModel(const gfx::Image& image) : image_(image) {}
+ImageModel::ImageModel(const gfx::Image& image) : icon_(image) {}
 
 ImageModel::ImageModel(const gfx::ImageSkia& image_skia)
     : ImageModel(gfx::Image(image_skia)) {}
@@ -86,11 +88,13 @@
 }
 
 bool ImageModel::IsVectorIcon() const {
-  return vector_icon_model_ && !vector_icon_model_.value().is_empty();
+  return absl::holds_alternative<VectorIconModel>(icon_) &&
+         !absl::get<VectorIconModel>(icon_).is_empty();
 }
 
 bool ImageModel::IsImage() const {
-  return image_ && !image_.value().IsEmpty();
+  return absl::holds_alternative<gfx::Image>(icon_) &&
+         !absl::get<gfx::Image>(icon_).IsEmpty();
 }
 
 gfx::Size ImageModel::Size() const {
@@ -101,33 +105,18 @@
   return IsImage() ? GetImage().Size() : gfx::Size();
 }
 
-const VectorIconModel ImageModel::GetVectorIcon() const {
+VectorIconModel ImageModel::GetVectorIcon() const {
   DCHECK(IsVectorIcon());
-  return vector_icon_model_.value();
+  return absl::get<VectorIconModel>(icon_);
 }
 
-const gfx::Image ImageModel::GetImage() const {
+gfx::Image ImageModel::GetImage() const {
   DCHECK(IsImage());
-  return image_.value();
+  return absl::get<gfx::Image>(icon_);
 }
 
 bool ImageModel::operator==(const ImageModel& other) const {
-  if (IsEmpty() != other.IsEmpty())
-    return false;
-
-  if (IsEmpty())
-    return true;
-
-  if (IsVectorIcon() != other.IsVectorIcon())
-    return false;
-
-  if (IsImage()) {
-    return GetImage().AsImageSkia().BackedBySameObjectAs(
-        other.GetImage().AsImageSkia());
-  }
-
-  DCHECK(IsVectorIcon());
-  return GetVectorIcon() == other.GetVectorIcon();
+  return icon_ == other.icon_;
 }
 
 bool ImageModel::operator!=(const ImageModel& other) const {
diff --git a/ui/base/models/image_model.h b/ui/base/models/image_model.h
index 408a7c2..7c9fb9b 100644
--- a/ui/base/models/image_model.h
+++ b/ui/base/models/image_model.h
@@ -7,8 +7,9 @@
 
 #include "base/callback.h"
 #include "base/component_export.h"
-#include "base/optional.h"
+#include "third_party/abseil-cpp/absl/types/variant.h"
 #include "third_party/skia/include/core/SkColor.h"
+#include "ui/gfx/color_palette.h"
 #include "ui/gfx/image/image.h"
 #include "ui/gfx/image/image_skia.h"
 
@@ -57,17 +58,13 @@
 
   const gfx::VectorIcon* vector_icon() const { return vector_icon_; }
   int icon_size() const { return icon_size_; }
-  int color_id() const { return color_id_.value(); }
-  SkColor color() const { return color_.value(); }
-  bool has_color() const { return color_.has_value(); }
+  int color_id() const { return absl::get<int>(color_); }
+  SkColor color() const { return absl::get<SkColor>(color_); }
+  bool has_color() const { return absl::holds_alternative<SkColor>(color_); }
 
   const gfx::VectorIcon* vector_icon_ = nullptr;
   int icon_size_ = 0;
-  // Only one of the following will ever be assigned.
-  // TODO: Update to use std::variant or base:Variant once one of them is
-  // available to use.
-  base::Optional<int> color_id_;
-  base::Optional<SkColor> color_;
+  absl::variant<int, SkColor> color_ = gfx::kPlaceholderColor;
 };
 
 // ImageModel encapsulates either a gfx::Image or a VectorIconModel. Only one
@@ -97,8 +94,8 @@
   bool IsImage() const;
   gfx::Size Size() const;
   // Only valid if IsVectorIcon() or IsImage() return true, respectively.
-  const VectorIconModel GetVectorIcon() const;
-  const gfx::Image GetImage() const;
+  VectorIconModel GetVectorIcon() const;
+  gfx::Image GetImage() const;
 
   // Checks if both model yield equal images.
   bool operator==(const ImageModel& other) const;
@@ -109,11 +106,7 @@
   ImageModel(const gfx::ImageSkia& image_skia);
   ImageModel(const VectorIconModel& vector_icon_model);
 
-  // Only one of the following will ever be assigned.
-  // TODO: Update to use std::variant or base:Variant once one of them is
-  // available to use.
-  base::Optional<VectorIconModel> vector_icon_model_;
-  base::Optional<gfx::Image> image_;
+  absl::variant<VectorIconModel, gfx::Image> icon_;
 };
 
 }  // namespace ui
diff --git a/ui/compositor/animation_metrics_reporter.h b/ui/compositor/animation_metrics_reporter.h
index 1b12fb5..d06fe232 100644
--- a/ui/compositor/animation_metrics_reporter.h
+++ b/ui/compositor/animation_metrics_reporter.h
@@ -5,7 +5,6 @@
 #ifndef UI_COMPOSITOR_ANIMATION_METRICS_REPORTER_H_
 #define UI_COMPOSITOR_ANIMATION_METRICS_REPORTER_H_
 
-#include "base/metrics/histogram_macros.h"
 #include "ui/compositor/compositor_export.h"
 
 namespace ui {
@@ -23,25 +22,6 @@
   virtual void Report(int value) = 0;
 };
 
-// A subclass of AnimationMetricsReporter that writes into a percentage
-// histogram when Report() is called.
-template <const char* histogram_name>
-class COMPOSITOR_EXPORT HistogramPercentageMetricsReporter
-    : public AnimationMetricsReporter {
- public:
-  HistogramPercentageMetricsReporter() = default;
-  HistogramPercentageMetricsReporter(
-      const HistogramPercentageMetricsReporter&) = delete;
-  HistogramPercentageMetricsReporter& operator=(
-      const HistogramPercentageMetricsReporter&) = delete;
-  ~HistogramPercentageMetricsReporter() override = default;
-
-  // AnimationMetricsReporter:
-  void Report(int value) override {
-    UMA_HISTOGRAM_PERCENTAGE(histogram_name, value);
-  }
-};
-
 }  // namespace ui
 
 #endif  // UI_COMPOSITOR_ANIMATION_METRICS_REPORTER_H_
diff --git a/ui/gfx/image/image.cc b/ui/gfx/image/image.cc
index 1a3c9ca2..bfc36b6 100644
--- a/ui/gfx/image/image.cc
+++ b/ui/gfx/image/image.cc
@@ -371,6 +371,10 @@
 
 Image::~Image() {}
 
+bool Image::operator==(const Image& other) const {
+  return storage_ == other.storage_;
+}
+
 // static
 Image Image::CreateFrom1xBitmap(const SkBitmap& bitmap) {
   return Image(ImageSkia::CreateFrom1xBitmap(bitmap));
diff --git a/ui/gfx/image/image.h b/ui/gfx/image/image.h
index 57596d2a..466b7bf 100644
--- a/ui/gfx/image/image.h
+++ b/ui/gfx/image/image.h
@@ -93,6 +93,9 @@
   // representations.
   ~Image();
 
+  // True iff both images are backed by the same storage.
+  bool operator==(const Image& other) const;
+
   // Creates an image from the passed in 1x bitmap.
   // WARNING: The resulting image will be pixelated when painted on a high
   // density display.
diff --git a/ui/message_center/BUILD.gn b/ui/message_center/BUILD.gn
index 187c455..4df6e85 100644
--- a/ui/message_center/BUILD.gn
+++ b/ui/message_center/BUILD.gn
@@ -138,6 +138,8 @@
     sources = [
       "fake_message_center.cc",
       "fake_message_center.h",
+      "lock_screen/fake_lock_screen_controller.cc",
+      "lock_screen/fake_lock_screen_controller.h",
     ]
 
     deps = [
@@ -160,8 +162,6 @@
     }
 
     sources = [
-      "lock_screen/fake_lock_screen_controller.cc",
-      "lock_screen/fake_lock_screen_controller.h",
       "message_center_impl_unittest.cc",
       "notification_list_unittest.cc",
       "public/cpp/notification_delegate_unittest.cc",
diff --git a/url/android/java/src/org/chromium/url/GURL.java b/url/android/java/src/org/chromium/url/GURL.java
index b9d0788..5361fdd3 100644
--- a/url/android/java/src/org/chromium/url/GURL.java
+++ b/url/android/java/src/org/chromium/url/GURL.java
@@ -84,9 +84,6 @@
 
     /**
      * Enables debug stack trace gathering for GURL.
-     *
-     * TODO(https://crbug.com/783819): Remove this when the the fraction of users hitting this
-     * drops.
      */
     public static void setReportDebugThrowableCallback(ReportDebugThrowableCallback callback) {
         sReportCallback = callback;
@@ -107,7 +104,8 @@
             RecordHistogram.recordTimesHistogram("Startup.Android.GURLEnsureMainDexInitialized",
                     SystemClock.elapsedRealtime() - time);
             if (sReportCallback != null && new Random().nextInt(100) < DEBUG_REPORT_PERCENTAGE) {
-                final Throwable throwable = new Throwable("This is not a crash, please ignore.");
+                final Throwable throwable =
+                        new Throwable("This is not a crash, please ignore. See crbug.com/1065377.");
                 // This isn't an assert, because by design this is possible, but we would prefer
                 // this path does not get hit more than necessary and getting stack traces from the
                 // wild will help find issues.
diff --git a/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/DownloadCallbackTest.java b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/DownloadCallbackTest.java
index d768ef3..bb59addd 100644
--- a/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/DownloadCallbackTest.java
+++ b/weblayer/browser/android/javatests/src/org/chromium/weblayer/test/DownloadCallbackTest.java
@@ -27,6 +27,7 @@
 import org.chromium.weblayer.DownloadError;
 import org.chromium.weblayer.DownloadState;
 import org.chromium.weblayer.Profile;
+import org.chromium.weblayer.WebLayer;
 import org.chromium.weblayer.shell.InstrumentationActivity;
 
 import java.io.File;
@@ -42,6 +43,8 @@
     public InstrumentationActivityTestRule mActivityTestRule =
             new InstrumentationActivityTestRule();
 
+    private static boolean sIsFileNameSupported;
+
     private InstrumentationActivity mActivity;
     private Callback mCallback;
 
@@ -51,6 +54,7 @@
         public String mContentDisposition;
         public String mMimetype;
         public String mLocation;
+        public String mFileName;
         public @DownloadState int mState;
         public @DownloadError int mError;
         public long mContentLength;
@@ -86,6 +90,9 @@
         public void onDownloadCompleted(Download download) {
             mSeenCompleted = true;
             mLocation = download.getLocation().toString();
+            if (sIsFileNameSupported) {
+                mFileName = download.getFileNameToReportToUser().toString();
+            }
             mState = download.getState();
             mError = download.getError();
             mMimetype = download.getMimeType();
@@ -131,6 +138,9 @@
             Profile profile = mActivity.getBrowser().getProfile();
             profile.setDownloadCallback(mCallback);
             profile.setDownloadDirectory(new File(tempDownloadDirectory));
+
+            sIsFileNameSupported =
+                    WebLayer.getSupportedMajorVersion(mActivity.getApplicationContext()) >= 86;
         });
     }
 
@@ -196,6 +206,9 @@
 
         Assert.assertTrue(mCallback.mLocation.contains(
                 "org.chromium.weblayer.shell/cache/weblayer/Downloads/"));
+        if (sIsFileNameSupported) {
+            Assert.assertTrue(mCallback.mFileName.contains("test"));
+        }
         Assert.assertEquals(DownloadState.COMPLETE, mCallback.mState);
         Assert.assertEquals(DownloadError.NO_ERROR, mCallback.mError);
         Assert.assertEquals("text/html", mCallback.mMimetype);
diff --git a/weblayer/browser/download_impl.cc b/weblayer/browser/download_impl.cc
index 0e8440e..1daa690 100644
--- a/weblayer/browser/download_impl.cc
+++ b/weblayer/browser/download_impl.cc
@@ -54,6 +54,13 @@
       base::android::ConvertUTF8ToJavaString(env, GetLocation().value()));
 }
 
+base::android::ScopedJavaLocalRef<jstring>
+DownloadImpl::GetFileNameToReportToUser(JNIEnv* env) {
+  return base::android::ScopedJavaLocalRef<jstring>(
+      base::android::ConvertUTF8ToJavaString(
+          env, GetFileNameToReportToUser().value()));
+}
+
 base::android::ScopedJavaLocalRef<jstring> DownloadImpl::GetMimeTypeImpl(
     JNIEnv* env) {
   return base::android::ScopedJavaLocalRef<jstring>(
@@ -117,6 +124,10 @@
   return item_->GetTargetFilePath();
 }
 
+base::FilePath DownloadImpl::GetFileNameToReportToUser() {
+  return item_->GetFileNameToReportUser();
+}
+
 std::string DownloadImpl::GetMimeType() {
   return item_->GetMimeType();
 }
diff --git a/weblayer/browser/download_impl.h b/weblayer/browser/download_impl.h
index 3319120..d00bc2a 100644
--- a/weblayer/browser/download_impl.h
+++ b/weblayer/browser/download_impl.h
@@ -41,6 +41,8 @@
   void Resume(JNIEnv* env) { Resume(); }
   void Cancel(JNIEnv* env) { Cancel(); }
   base::android::ScopedJavaLocalRef<jstring> GetLocation(JNIEnv* env);
+  base::android::ScopedJavaLocalRef<jstring> GetFileNameToReportToUser(
+      JNIEnv* env);
   // Add Impl suffix to avoid compiler clash with the C++ interface method.
   base::android::ScopedJavaLocalRef<jstring> GetMimeTypeImpl(JNIEnv* env);
   int GetError(JNIEnv* env) { return static_cast<int>(GetError()); }
@@ -58,6 +60,7 @@
   void Resume() override;
   void Cancel() override;
   base::FilePath GetLocation() override;
+  base::FilePath GetFileNameToReportToUser() override;
   std::string GetMimeType() override;
   DownloadError GetError() override;
 
diff --git a/weblayer/browser/java/org/chromium/weblayer_private/DownloadImpl.java b/weblayer/browser/java/org/chromium/weblayer_private/DownloadImpl.java
index 6dd5292d..cdb14cc 100644
--- a/weblayer/browser/java/org/chromium/weblayer_private/DownloadImpl.java
+++ b/weblayer/browser/java/org/chromium/weblayer_private/DownloadImpl.java
@@ -8,6 +8,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.res.Resources;
+import android.net.Uri;
 import android.os.RemoteException;
 import android.text.TextUtils;
 
@@ -91,10 +92,9 @@
 
             Intent openIntent = new Intent(Intent.ACTION_VIEW);
             if (TextUtils.isEmpty(mimeType)) {
-                openIntent.setData(ContentUriUtils.getContentUriFromFile(new File(location)));
+                openIntent.setData(getDownloadUri(location));
             } else {
-                openIntent.setDataAndType(
-                        ContentUriUtils.getContentUriFromFile(new File(location)), mimeType);
+                openIntent.setDataAndType(getDownloadUri(location), mimeType);
             }
             openIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
             openIntent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
@@ -256,6 +256,13 @@
     }
 
     @Override
+    public String getFileNameToReportToUser() {
+        StrictModeWorkaround.apply();
+        throwIfNativeDestroyed();
+        return DownloadImplJni.get().getFileNameToReportToUser(mNativeDownloadImpl);
+    }
+
+    @Override
     public String getMimeType() {
         StrictModeWorkaround.apply();
         throwIfNativeDestroyed();
@@ -347,9 +354,9 @@
                 .setPriorityBeforeO(NotificationCompat.PRIORITY_DEFAULT);
 
         // The filename might not have been available initially.
-        String location = getLocation();
-        if (!TextUtils.isEmpty((location))) {
-            builder.setContentTitle((new File(location)).getName());
+        String name = getFileNameToReportToUser();
+        if (!TextUtils.isEmpty(name)) {
+            builder.setContentTitle(name);
         }
 
         if (state == DownloadState.CANCELLED) {
@@ -453,6 +460,11 @@
         return new NotificationManagerProxyImpl(ContextUtils.getApplicationContext());
     }
 
+    private static Uri getDownloadUri(String location) {
+        if (ContentUriUtils.isContentUri(location)) return Uri.parse(location);
+        return ContentUriUtils.getContentUriFromFile(new File(location));
+    }
+
     @CalledByNative
     private void onNativeDestroyed() {
         mNativeDownloadImpl = 0;
@@ -470,6 +482,7 @@
         void resume(long nativeDownloadImpl);
         void cancel(long nativeDownloadImpl);
         String getLocation(long nativeDownloadImpl);
+        String getFileNameToReportToUser(long nativeDownloadImpl);
         String getMimeTypeImpl(long nativeDownloadImpl);
         int getError(long nativeDownloadImpl);
     }
diff --git a/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IDownload.aidl b/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IDownload.aidl
index 4cd8eb6..8f686ec 100644
--- a/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IDownload.aidl
+++ b/weblayer/browser/java/org/chromium/weblayer_private/interfaces/IDownload.aidl
@@ -18,4 +18,5 @@
   int getError() = 7;
   String getMimeType() = 8;
   void disableNotification() = 9;
+  String getFileNameToReportToUser() = 10;
 }
diff --git a/weblayer/public/download.h b/weblayer/public/download.h
index 46fc8c3..5f1122f 100644
--- a/weblayer/public/download.h
+++ b/weblayer/public/download.h
@@ -79,6 +79,10 @@
   // available until the download completes successfully.
   virtual base::FilePath GetLocation() = 0;
 
+  // Returns the file name for the download that should be displayed to the
+  // user.
+  virtual base::FilePath GetFileNameToReportToUser() = 0;
+
   // Returns the effective MIME type of downloaded content.
   virtual std::string GetMimeType() = 0;
 
diff --git a/weblayer/public/java/org/chromium/weblayer/Download.java b/weblayer/public/java/org/chromium/weblayer/Download.java
index 48e64f1..4c80639 100644
--- a/weblayer/public/java/org/chromium/weblayer/Download.java
+++ b/weblayer/public/java/org/chromium/weblayer/Download.java
@@ -129,6 +129,24 @@
     }
 
     /**
+     * Returns the file name for the download that should be displayed to the user.
+     *
+     * @since 86
+     */
+    @NonNull
+    public File getFileNameToReportToUser() {
+        ThreadCheck.ensureOnUiThread();
+        if (WebLayer.getSupportedMajorVersionInternal() < 86) {
+            throw new UnsupportedOperationException();
+        }
+        try {
+            return new File(mDownloadImpl.getFileNameToReportToUser());
+        } catch (RemoteException e) {
+            throw new APICallException(e);
+        }
+    }
+
+    /**
      * Returns the effective MIME type of downloaded content.
      */
     @NonNull