diff --git a/DEPS b/DEPS
index 92005a0..34b1fbf 100644
--- a/DEPS
+++ b/DEPS
@@ -129,11 +129,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': '7340124746df2f1ce25e0aaa134ee370d0ebffee',
+  'skia_revision': 'aba7a5c93b91ee49c8a281daf19e8715978b7d81',
   # 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': '72592828213b06ab2f4b69f2ed5685c1e43123b1',
+  'v8_revision': 'a3c1051c26e72b7b86bd93d580e4a39ebdcbecd2',
   # 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.
@@ -141,15 +141,15 @@
   # 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': '5f388c24529334c694e245be69226581a01d1622',
+  'angle_revision': '90b1865e2a8b644bc3e200726de9e3a321ee5c18',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
-  'swiftshader_revision': '60da1580366f44667980c4402b53cd00097f9443',
+  'swiftshader_revision': '4ba1911b2d7f526695ffed525faa9c903cd6737b',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
-  'pdfium_revision': '7e4877f634e4e4db77ec1a26974cd314a2f004db',
+  'pdfium_revision': '3419af426bd572e23f3e8b0ab994589e3e4020af',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling openmax_dl
   # and whatever else without interference from each other.
@@ -180,7 +180,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling NaCl
   # and whatever else without interference from each other.
-  'nacl_revision': '2c300847c7700af6948ad78e79ccd21bd7ba0c14',
+  'nacl_revision': '067afa275dcb4b3c6eca979ea5e3fe75073c4080',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling freetype
   # and whatever else without interference from each other.
@@ -196,7 +196,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling catapult
   # and whatever else without interference from each other.
-  'catapult_revision': 'bf0179e27c9fafbf52a6ec6e771d89a9d81a5f47',
+  'catapult_revision': '979fc35bbcca4c9015176a71d8d71abe023d94e8',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -805,7 +805,7 @@
 
   # Build tools for Chrome OS. Note: This depends on third_party/pyelftools.
   'src/third_party/chromite': {
-      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + 'bd047d05d120039e5820795e2c7b6b5b1f3873e3',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + 'd5f1da86cc3a55856e1d259b941c2c7f967e12cf',
       'condition': 'checkout_linux',
   },
 
@@ -830,7 +830,7 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '9198ef8ede661f96912f5f226d566ae1f1998f7b',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '0b62ed79ed5049332f58c06dbdb8e8bc4105010e',
 
   'src/third_party/devtools-node-modules':
     Var('chromium_git') + '/external/github.com/ChromeDevTools/devtools-node-modules' + '@' + Var('devtools_node_modules_revision'),
@@ -1172,7 +1172,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' +  'cc57bf714a300c5e0b6bc04f76ea058e50c38399',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' +  '766ef6cc7749a612c271720bf04f545d0e8a57da',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + 'ac0d98b5cee6c024b0cffeb4f8f45b6fc5ccdb78',
@@ -1384,7 +1384,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@98c9c09ca5ff59cdc9120a58e5adae9a201a9c1f',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@cdd88f554acbe96eea5126535fe546c88e5f3ab1',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/ash/display/display_prefs_unittest.cc b/ash/display/display_prefs_unittest.cc
index 523c551..80b2bbf 100644
--- a/ash/display/display_prefs_unittest.cc
+++ b/ash/display/display_prefs_unittest.cc
@@ -1402,6 +1402,13 @@
   // Load the preferences and simulate a native display reconfiguration. Expect
   // that we are mirroring now.
   LoadDisplayPreferences();
+
+  // Simulate a change in display configuration between loading the prefs, and
+  // reconfiguring after the prefs have been loaded. Make sure that the external
+  // display mirror configs are not overwritten, and the loaded prefs will be
+  // applied.
+  display_manager()->SetMirrorMode(display::MirrorMode::kOff, base::nullopt);
+
   display_manager()->OnNativeDisplaysChanged(display_info_list);
   EXPECT_TRUE(display_manager()->IsInMirrorMode());
 }
diff --git a/ash/shelf/shelf_tooltip_preview_bubble.cc b/ash/shelf/shelf_tooltip_preview_bubble.cc
index 5825b39b2..d8f7c75 100644
--- a/ash/shelf/shelf_tooltip_preview_bubble.cc
+++ b/ash/shelf/shelf_tooltip_preview_bubble.cc
@@ -9,11 +9,15 @@
 #include "ash/wm/pip/pip_positioner.h"
 #include "ash/wm/window_preview_view.h"
 #include "base/strings/utf_string_conversions.h"
+#include "ui/display/screen.h"
 #include "ui/views/bubble/bubble_frame_view.h"
 #include "ui/views/layout/box_layout.h"
 
 namespace ash {
 
+// The delay after which a preview bubble gets dismissed (after the mouse
+// has gone away for instance).s
+constexpr int kPreviewBubbleDismissDelay = 350;
 
 // The padding inside the tooltip.
 constexpr int kTooltipPaddingTop = 8;
@@ -41,6 +45,9 @@
   set_border_radius(kPreviewBubbleBorderRadius);
   // The parent class sets non-zero margins. Reset them to zero.
   set_margins(gfx::Insets());
+  // We hide this tooltip on mouse exit, so we want to get enter/exit events
+  // at this level, even for subviews.
+  set_notify_enter_exit_on_child(true);
 
   SetLayoutManager(std::make_unique<views::BoxLayout>(
       views::BoxLayout::kHorizontal,
@@ -66,10 +73,11 @@
 void ShelfTooltipPreviewBubble::RemovePreview(WindowPreview* to_remove) {
   base::Erase(previews_, to_remove);
   RemoveChildView(to_remove);
-  // If we don't have any previews left, close the tooltip.
-  if (previews_.empty()) {
+  // If we don't have any previews left, close the tooltip. Bypass
+  // considerations of where the mouse pointer is when this happens: we don't
+  // want to show an empty tooltip even if the mouse is on it.
+  if (previews_.empty())
     manager_->Close();
-  }
 }
 
 gfx::Rect ShelfTooltipPreviewBubble::GetBubbleBounds() {
@@ -86,6 +94,10 @@
   return bounds;
 }
 
+void ShelfTooltipPreviewBubble::OnMouseExited(const ui::MouseEvent& event) {
+  DismissAfterDelay();
+}
+
 bool ShelfTooltipPreviewBubble::ShouldCloseOnPressDown() {
   return false;
 }
@@ -105,12 +117,35 @@
   return std::min(max_ratio, kShelfTooltipPreviewMaxRatio);
 }
 
+void ShelfTooltipPreviewBubble::DismissAfterDelay() {
+  dismiss_timer_.Start(
+      FROM_HERE, base::TimeDelta::FromMilliseconds(kPreviewBubbleDismissDelay),
+      base::BindOnce(&ShelfTooltipPreviewBubble::Dismiss,
+                     base::Unretained(this)));
+}
+
+void ShelfTooltipPreviewBubble::Dismiss() {
+  dismiss_timer_.Stop();
+
+  const auto cursor_position =
+      display::Screen::GetScreen()->GetCursorScreenPoint();
+  // Cancel dismissal if the mouse is within our bounds again, or if it's
+  // within the anchor's bounds. That way the preview tooltip will remain
+  // shown if the mouse goes between the bubble and its anchor.
+  if (GetBoundsInScreen().Contains(cursor_position) ||
+      GetAnchorRect().Contains(cursor_position)) {
+    return;
+  }
+  manager_->Close();
+}
+
 void ShelfTooltipPreviewBubble::OnPreviewDismissed(WindowPreview* preview) {
   RemovePreview(preview);
 }
 
 void ShelfTooltipPreviewBubble::OnPreviewActivated(WindowPreview* preview) {
-  // Always close the tooltip when a window has been focused.
+  // Always close the tooltip when a window has been focused. Bypass
+  // considerations of where the mouse pointer is when this happens.
   manager_->Close();
 }
 
diff --git a/ash/shelf/shelf_tooltip_preview_bubble.h b/ash/shelf/shelf_tooltip_preview_bubble.h
index 354aac7..3df5896 100644
--- a/ash/shelf/shelf_tooltip_preview_bubble.h
+++ b/ash/shelf/shelf_tooltip_preview_bubble.h
@@ -13,6 +13,7 @@
 #include "ash/shelf/shelf_tooltip_manager.h"
 #include "ash/shelf/window_preview.h"
 #include "ash/wm/window_mirror_view.h"
+#include "base/timer/timer.h"
 #include "ui/aura/window.h"
 #include "ui/views/controls/label.h"
 
@@ -35,6 +36,7 @@
 
   // BubbleDialogDelegateView overrides:
   gfx::Rect GetBubbleBounds() override;
+  void OnMouseExited(const ui::MouseEvent& event) override;
 
   // ShelfBubble:
   bool ShouldCloseOnPressDown() override;
@@ -45,10 +47,13 @@
   void OnPreviewDismissed(WindowPreview* preview) override;
   void OnPreviewActivated(WindowPreview* preview) override;
 
+  void DismissAfterDelay();
+  void Dismiss();
+
   std::vector<WindowPreview*> previews_;
 
-  // Preferred size for the tooltip.
   ShelfTooltipManager* manager_;
+  base::OneShotTimer dismiss_timer_;
 
   const ShelfAlignment shelf_alignment_;
 
diff --git a/base/allocator/partition_allocator/partition_alloc_perftest.cc b/base/allocator/partition_allocator/partition_alloc_perftest.cc
index f143659..85b782a5 100644
--- a/base/allocator/partition_allocator/partition_alloc_perftest.cc
+++ b/base/allocator/partition_allocator/partition_alloc_perftest.cc
@@ -7,6 +7,8 @@
 #include "base/time/time.h"
 #include "base/timer/lap_timer.h"
 
+#include "build/build_config.h"
+
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/perf/perf_test.h"
 
@@ -100,7 +102,13 @@
                          timer_.LapsPerSecond(), "runs/s", true);
 }
 
-TEST_F(MemoryAllocationPerfTest, MultiBucket) {
+// Failing on Nexus5x: crbug.com/949838
+#if defined(OS_ANDROID)
+#define MAYBE_MultiBucket DISABLED_MultiBucket
+#else
+#define MAYBE_MultiBucket MultiBucket
+#endif
+TEST_F(MemoryAllocationPerfTest, MAYBE_MultiBucket) {
   timer_.Reset();
   MemoryAllocationPerfNode* first = reinterpret_cast<MemoryAllocationPerfNode*>(
       alloc_.root()->Alloc(40, "<testing>"));
diff --git a/base/android/android_image_reader_compat.cc b/base/android/android_image_reader_compat.cc
index 0101516..62c153ba 100644
--- a/base/android/android_image_reader_compat.cc
+++ b/base/android/android_image_reader_compat.cc
@@ -56,9 +56,10 @@
   // devices, this is unlikely to happen in the foreseeable future, so we use
   // dynamic loading.
 
-  // Functions are not present for android version older than OREO
+  // Functions are not present for android version older than OREO.
+  // Currently we want to enable AImageReader only for android P+ devices.
   if (base::android::BuildInfo::GetInstance()->sdk_int() <
-      base::android::SDK_VERSION_OREO) {
+      base::android::SDK_VERSION_P) {
     return false;
   }
 
diff --git a/base/containers/span.h b/base/containers/span.h
index c22d51f..f56172d1a065 100644
--- a/base/containers/span.h
+++ b/base/containers/span.h
@@ -492,7 +492,8 @@
 }
 
 template <typename Container,
-          typename T = typename Container::value_type,
+          typename T = std::remove_pointer_t<
+              decltype(base::data(std::declval<Container&>()))>,
           typename = internal::EnableIfSpanCompatibleContainer<Container&, T>>
 constexpr span<T> make_span(Container& container) noexcept {
   return container;
@@ -500,7 +501,8 @@
 
 template <
     typename Container,
-    typename T = const typename Container::value_type,
+    typename T = std::remove_pointer_t<
+        decltype(base::data(std::declval<const Container&>()))>,
     typename = internal::EnableIfSpanCompatibleContainer<const Container&, T>>
 constexpr span<T> make_span(const Container& container) noexcept {
   return container;
@@ -508,7 +510,8 @@
 
 template <size_t N,
           typename Container,
-          typename T = typename Container::value_type,
+          typename T = std::remove_pointer_t<
+              decltype(base::data(std::declval<Container&>()))>,
           typename = internal::EnableIfSpanCompatibleContainer<Container&, T>>
 constexpr span<T, N> make_span(Container& container) noexcept {
   return span<T, N>(container);
@@ -517,7 +520,8 @@
 template <
     size_t N,
     typename Container,
-    typename T = const typename Container::value_type,
+    typename T = std::remove_pointer_t<
+        decltype(base::data(std::declval<const Container&>()))>,
     typename = internal::EnableIfSpanCompatibleContainer<const Container&, T>>
 constexpr span<T, N> make_span(const Container& container) noexcept {
   return span<T, N>(container);
diff --git a/base/containers/span_unittest.cc b/base/containers/span_unittest.cc
index 0705a97..7f8f8df 100644
--- a/base/containers/span_unittest.cc
+++ b/base/containers/span_unittest.cc
@@ -1001,11 +1001,14 @@
   EXPECT_EQ(nullptr, empty_span.data());
 
   std::vector<int> vector = {1, 1, 2, 3, 5, 8};
-  span<int> span(vector.data(), vector.size());
+  span<int> expected_span(vector.data(), vector.size());
   auto made_span = make_span(vector.data(), vector.size());
-  EXPECT_EQ(span.data(), made_span.data());
-  EXPECT_EQ(span.size(), made_span.size());
+  EXPECT_EQ(expected_span.data(), made_span.data());
+  EXPECT_EQ(expected_span.size(), made_span.size());
   static_assert(decltype(made_span)::extent == dynamic_extent, "");
+  static_assert(
+      std::is_same<decltype(expected_span), decltype(made_span)>::value,
+      "the type of made_span differs from expected_span!");
 }
 
 TEST(SpanTest, MakeSpanFromPointerPair) {
@@ -1015,93 +1018,130 @@
   EXPECT_EQ(nullptr, empty_span.data());
 
   std::vector<int> vector = {1, 1, 2, 3, 5, 8};
-  span<int> span(vector.data(), vector.size());
+  span<int> expected_span(vector.data(), vector.size());
   auto made_span = make_span(vector.data(), vector.data() + vector.size());
-  EXPECT_EQ(span.data(), made_span.data());
-  EXPECT_EQ(span.size(), made_span.size());
+  EXPECT_EQ(expected_span.data(), made_span.data());
+  EXPECT_EQ(expected_span.size(), made_span.size());
   static_assert(decltype(made_span)::extent == dynamic_extent, "");
+  static_assert(
+      std::is_same<decltype(expected_span), decltype(made_span)>::value,
+      "the type of made_span differs from expected_span!");
 }
 
 TEST(SpanTest, MakeSpanFromConstexprArray) {
   static constexpr int kArray[] = {1, 2, 3, 4, 5};
-  constexpr span<const int> span(kArray);
-  EXPECT_EQ(span.data(), make_span(kArray).data());
-  EXPECT_EQ(span.size(), make_span(kArray).size());
-  static_assert(decltype(make_span(kArray))::extent == 5, "");
+  constexpr span<const int, 5> expected_span(kArray);
+  constexpr auto made_span = make_span(kArray);
+  EXPECT_EQ(expected_span.data(), made_span.data());
+  EXPECT_EQ(expected_span.size(), made_span.size());
+  static_assert(decltype(made_span)::extent == 5, "");
+  static_assert(
+      std::is_same<decltype(expected_span), decltype(made_span)>::value,
+      "the type of made_span differs from expected_span!");
 }
 
 TEST(SpanTest, MakeSpanFromStdArray) {
   const std::array<int, 5> kArray = {{1, 2, 3, 4, 5}};
-  span<const int> span(kArray);
-  EXPECT_EQ(span.data(), make_span(kArray).data());
-  EXPECT_EQ(span.size(), make_span(kArray).size());
-  static_assert(decltype(make_span(kArray))::extent == 5, "");
+  span<const int, 5> expected_span(kArray);
+  auto made_span = make_span(kArray);
+  EXPECT_EQ(expected_span.data(), made_span.data());
+  EXPECT_EQ(expected_span.size(), made_span.size());
+  static_assert(decltype(made_span)::extent == 5, "");
+  static_assert(
+      std::is_same<decltype(expected_span), decltype(made_span)>::value,
+      "the type of made_span differs from expected_span!");
 }
 
 TEST(SpanTest, MakeSpanFromConstContainer) {
   const std::vector<int> vector = {-1, -2, -3, -4, -5};
-  span<const int> span(vector);
-  EXPECT_EQ(span.data(), make_span(vector).data());
-  EXPECT_EQ(span.size(), make_span(vector).size());
-  static_assert(decltype(make_span(vector))::extent == dynamic_extent, "");
+  span<const int> expected_span(vector);
+  auto made_span = make_span(vector);
+  EXPECT_EQ(expected_span.data(), made_span.data());
+  EXPECT_EQ(expected_span.size(), made_span.size());
+  static_assert(decltype(made_span)::extent == dynamic_extent, "");
+  static_assert(
+      std::is_same<decltype(expected_span), decltype(made_span)>::value,
+      "the type of made_span differs from expected_span!");
 }
 
 TEST(SpanTest, MakeStaticSpanFromConstContainer) {
   const std::vector<int> vector = {-1, -2, -3, -4, -5};
-  span<const int, 5> span(vector);
-  EXPECT_EQ(span.data(), make_span<5>(vector).data());
-  EXPECT_EQ(span.size(), make_span<5>(vector).size());
-  static_assert(decltype(make_span<5>(vector))::extent == 5, "");
+  span<const int, 5> expected_span(vector);
+  auto made_span = make_span<5>(vector);
+  EXPECT_EQ(expected_span.data(), made_span.data());
+  EXPECT_EQ(expected_span.size(), made_span.size());
+  static_assert(decltype(made_span)::extent == 5, "");
+  static_assert(
+      std::is_same<decltype(expected_span), decltype(made_span)>::value,
+      "the type of made_span differs from expected_span!");
 }
 
 TEST(SpanTest, MakeSpanFromContainer) {
   std::vector<int> vector = {-1, -2, -3, -4, -5};
-  span<int> span(vector);
-  EXPECT_EQ(span.data(), make_span(vector).data());
-  EXPECT_EQ(span.size(), make_span(vector).size());
-  static_assert(decltype(make_span(vector))::extent == dynamic_extent, "");
+  span<int> expected_span(vector);
+  auto made_span = make_span(vector);
+  EXPECT_EQ(expected_span.data(), made_span.data());
+  EXPECT_EQ(expected_span.size(), made_span.size());
+  static_assert(decltype(made_span)::extent == dynamic_extent, "");
+  static_assert(
+      std::is_same<decltype(expected_span), decltype(made_span)>::value,
+      "the type of made_span differs from expected_span!");
 }
 
 TEST(SpanTest, MakeStaticSpanFromContainer) {
   std::vector<int> vector = {-1, -2, -3, -4, -5};
-  span<int, 5> span(vector);
-  EXPECT_EQ(span.data(), make_span<5>(vector).data());
-  EXPECT_EQ(span.size(), make_span<5>(vector).size());
+  span<int, 5> expected_span(vector);
+  auto made_span = make_span<5>(vector);
+  EXPECT_EQ(expected_span.data(), make_span<5>(vector).data());
+  EXPECT_EQ(expected_span.size(), make_span<5>(vector).size());
   static_assert(decltype(make_span<5>(vector))::extent == 5, "");
+  static_assert(
+      std::is_same<decltype(expected_span), decltype(made_span)>::value,
+      "the type of made_span differs from expected_span!");
 }
 
 TEST(SpanTest, MakeSpanFromDynamicSpan) {
   static constexpr int kArray[] = {1, 2, 3, 4, 5};
-  constexpr span<const int> span(kArray);
-  static_assert(std::is_same<decltype(span)::element_type,
-                             decltype(make_span(span))::element_type>::value,
+  constexpr span<const int> expected_span(kArray);
+  constexpr auto made_span = make_span(expected_span);
+  static_assert(std::is_same<decltype(expected_span)::element_type,
+                             decltype(made_span)::element_type>::value,
                 "make_span(span) should have the same element_type as span");
 
-  static_assert(span.data() == make_span(span).data(),
+  static_assert(expected_span.data() == made_span.data(),
                 "make_span(span) should have the same data() as span");
 
-  static_assert(span.size() == make_span(span).size(),
+  static_assert(expected_span.size() == made_span.size(),
                 "make_span(span) should have the same size() as span");
 
-  static_assert(decltype(make_span(span))::extent == decltype(span)::extent,
+  static_assert(decltype(made_span)::extent == decltype(expected_span)::extent,
                 "make_span(span) should have the same extent as span");
+
+  static_assert(
+      std::is_same<decltype(expected_span), decltype(made_span)>::value,
+      "the type of made_span differs from expected_span!");
 }
 
 TEST(SpanTest, MakeSpanFromStaticSpan) {
   static constexpr int kArray[] = {1, 2, 3, 4, 5};
-  constexpr span<const int, 5> span(kArray);
-  static_assert(std::is_same<decltype(span)::element_type,
-                             decltype(make_span(span))::element_type>::value,
+  constexpr span<const int, 5> expected_span(kArray);
+  constexpr auto made_span = make_span(expected_span);
+  static_assert(std::is_same<decltype(expected_span)::element_type,
+                             decltype(made_span)::element_type>::value,
                 "make_span(span) should have the same element_type as span");
 
-  static_assert(span.data() == make_span(span).data(),
+  static_assert(expected_span.data() == made_span.data(),
                 "make_span(span) should have the same data() as span");
 
-  static_assert(span.size() == make_span(span).size(),
+  static_assert(expected_span.size() == made_span.size(),
                 "make_span(span) should have the same size() as span");
 
-  static_assert(decltype(make_span(span))::extent == decltype(span)::extent,
+  static_assert(decltype(made_span)::extent == decltype(expected_span)::extent,
                 "make_span(span) should have the same extent as span");
+
+  static_assert(
+      std::is_same<decltype(expected_span), decltype(made_span)>::value,
+      "the type of made_span differs from expected_span!");
 }
 
 TEST(SpanTest, StdTupleSize) {
diff --git a/base/containers/span_unittest.nc b/base/containers/span_unittest.nc
index 9859495..5ef2ec999 100644
--- a/base/containers/span_unittest.nc
+++ b/base/containers/span_unittest.nc
@@ -249,6 +249,13 @@
   return std::get<0>(s);
 }
 
+#elif defined(NCTEST_CONST_VECTOR_DEDUCES_AS_CONST_SPAN)  // [r"fatal error: no viable conversion from 'span<const int>' to 'span<int>'"]
+
+int WontCompile() {
+  const std::vector<int> v;
+  span<int> s = make_span(v);
+}
+
 #endif
 
 }  // namespace base
diff --git a/base/sampling_heap_profiler/sampling_heap_profiler.cc b/base/sampling_heap_profiler/sampling_heap_profiler.cc
index e7183b29..f8736ef 100644
--- a/base/sampling_heap_profiler/sampling_heap_profiler.cc
+++ b/base/sampling_heap_profiler/sampling_heap_profiler.cc
@@ -105,12 +105,18 @@
     return 0;
   }
 #endif
-  PoissonAllocationSampler::Get()->AddSamplesObserver(this);
+
+  AutoLock lock(start_stop_mutex_);
+  if (!running_sessions_++)
+    PoissonAllocationSampler::Get()->AddSamplesObserver(this);
   return last_sample_ordinal_;
 }
 
 void SamplingHeapProfiler::Stop() {
-  PoissonAllocationSampler::Get()->RemoveSamplesObserver(this);
+  AutoLock lock(start_stop_mutex_);
+  DCHECK_GT(running_sessions_, 0);
+  if (!--running_sessions_)
+    PoissonAllocationSampler::Get()->RemoveSamplesObserver(this);
 }
 
 void SamplingHeapProfiler::SetSamplingInterval(size_t sampling_interval) {
diff --git a/base/sampling_heap_profiler/sampling_heap_profiler.h b/base/sampling_heap_profiler/sampling_heap_profiler.h
index e12898b..398bf0e9 100644
--- a/base/sampling_heap_profiler/sampling_heap_profiler.h
+++ b/base/sampling_heap_profiler/sampling_heap_profiler.h
@@ -5,6 +5,7 @@
 #ifndef BASE_SAMPLING_HEAP_PROFILER_SAMPLING_HEAP_PROFILER_H_
 #define BASE_SAMPLING_HEAP_PROFILER_SAMPLING_HEAP_PROFILER_H_
 
+#include <atomic>
 #include <unordered_map>
 #include <unordered_set>
 #include <vector>
@@ -124,13 +125,21 @@
   // deleted.
   std::unordered_set<const char*> strings_;
 
+  // Mutex to make |running_sessions_| and Add/Remove samples observer access
+  // atomic.
+  Lock start_stop_mutex_;
+
+  // Number of the running sessions.
+  int running_sessions_ = 0;
+
   // Last sample ordinal used to mark samples recorded during single session.
-  uint32_t last_sample_ordinal_ = 1;
+  std::atomic<uint32_t> last_sample_ordinal_{1};
 
   // Whether it should record thread names.
-  bool record_thread_names_ = false;
+  std::atomic<bool> record_thread_names_{false};
 
   friend class NoDestructor<SamplingHeapProfiler>;
+  friend class SamplingHeapProfilerTest;
 
   DISALLOW_COPY_AND_ASSIGN(SamplingHeapProfiler);
 };
diff --git a/base/sampling_heap_profiler/sampling_heap_profiler_unittest.cc b/base/sampling_heap_profiler/sampling_heap_profiler_unittest.cc
index 8e08e01..d1623e71 100644
--- a/base/sampling_heap_profiler/sampling_heap_profiler_unittest.cc
+++ b/base/sampling_heap_profiler/sampling_heap_profiler_unittest.cc
@@ -10,6 +10,7 @@
 #include "base/allocator/allocator_shim.h"
 #include "base/debug/alias.h"
 #include "base/rand_util.h"
+#include "base/synchronization/waitable_event.h"
 #include "base/threading/simple_thread.h"
 #include "build/build_config.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -28,6 +29,18 @@
   size_t GetNextSample(size_t mean_interval) {
     return PoissonAllocationSampler::GetNextSampleInterval(mean_interval);
   }
+
+  static int GetRunningSessionsCount() {
+    return SamplingHeapProfiler::Get()->running_sessions_;
+  }
+
+  static void RunStartStopLoop(SamplingHeapProfiler* profiler) {
+    for (int i = 0; i < 100000; ++i) {
+      profiler->Start();
+      EXPECT_LE(1, GetRunningSessionsCount());
+      profiler->Stop();
+    }
+  }
 };
 
 class SamplesCollector : public PoissonAllocationSampler::SamplesObserver {
@@ -180,14 +193,14 @@
 
 TEST_F(SamplingHeapProfilerTest, DISABLED_ParallelLargeSmallStats) {
   CheckAllocationPattern([]() {
-    SimpleThread* t1 = new MyThread1();
-    SimpleThread* t2 = new MyThread2();
-    t1->Start();
-    t2->Start();
+    MyThread1 t1;
+    MyThread1 t2;
+    t1.Start();
+    t2.Start();
     for (int i = 0; i < kNumberOfAllocations; ++i)
       Allocate3();
-    t1->Join();
-    t2->Join();
+    t1.Join();
+    t2.Join();
   });
 }
 
@@ -237,4 +250,39 @@
   sampler->RemoveSamplesObserver(&collector);
 }
 
+class StartStopThread : public SimpleThread {
+ public:
+  StartStopThread(WaitableEvent* event)
+      : SimpleThread("MyThread2"), event_(event) {}
+  void Run() override {
+    auto* profiler = SamplingHeapProfiler::Get();
+    event_->Signal();
+    SamplingHeapProfilerTest::RunStartStopLoop(profiler);
+  }
+
+ private:
+  WaitableEvent* event_;
+};
+
+TEST_F(SamplingHeapProfilerTest, StartStop) {
+  auto* profiler = SamplingHeapProfiler::Get();
+  EXPECT_EQ(0, GetRunningSessionsCount());
+  profiler->Start();
+  EXPECT_EQ(1, GetRunningSessionsCount());
+  profiler->Start();
+  EXPECT_EQ(2, GetRunningSessionsCount());
+  profiler->Stop();
+  EXPECT_EQ(1, GetRunningSessionsCount());
+  profiler->Stop();
+  EXPECT_EQ(0, GetRunningSessionsCount());
+
+  WaitableEvent event;
+  StartStopThread thread(&event);
+  thread.Start();
+  event.Wait();
+  RunStartStopLoop(profiler);
+  thread.Join();
+  EXPECT_EQ(0, GetRunningSessionsCount());
+}
+
 }  // namespace base
diff --git a/base/type_id.h b/base/type_id.h
index ac44ee1..0c284bf 100644
--- a/base/type_id.h
+++ b/base/type_id.h
@@ -80,7 +80,7 @@
 #if defined(__clang__)
       "base::internal::UniqueIdFromType() [Type = (anonymous namespace)::";
 #elif defined(COMPILER_GCC)
-      "base::internal::UniqueIdFromType() [with T = {anonymous}::";
+      "base::internal::UniqueIdFromType() [with Type = {anonymous}::";
 #elif defined(COMPILER_MSVC)
       "base::internal::UniqueIdFromType<`anonymous namespace'::";
 #else
diff --git a/base/type_id_unittest.cc b/base/type_id_unittest.cc
index 8c32334..2bf28ae 100644
--- a/base/type_id_unittest.cc
+++ b/base/type_id_unittest.cc
@@ -76,8 +76,8 @@
             TypeIdTestSupportB::GetTypeIdForUniquePtrInt());
 }
 
-// TODO(crbug.com/928806): Failing consistently on Android.
-#if defined(OS_ANDROID)
+// TODO(crbug.com/928806): Failing consistently on Android and GCC
+#if defined(OS_ANDROID) || (defined(COMPILER_GCC) && !defined(__clang__))
 TEST(TypeId, DISABLED_IdenticalTypesFromComponentAndStaticLibrary) {
 #else
 TEST(TypeId, IdenticalTypesFromComponentAndStaticLibrary) {
diff --git a/build/fuchsia/linux.sdk.sha1 b/build/fuchsia/linux.sdk.sha1
index dfac1051..aaa72eb 100644
--- a/build/fuchsia/linux.sdk.sha1
+++ b/build/fuchsia/linux.sdk.sha1
@@ -1 +1 @@
-8916983715801543744
\ No newline at end of file
+8916875429194723312
\ No newline at end of file
diff --git a/build/fuchsia/mac.sdk.sha1 b/build/fuchsia/mac.sdk.sha1
index 8fa3ec7..5baed26 100644
--- a/build/fuchsia/mac.sdk.sha1
+++ b/build/fuchsia/mac.sdk.sha1
@@ -1 +1 @@
-8916982364174025824
\ No newline at end of file
+8916873457256596624
\ No newline at end of file
diff --git a/cc/trees/layer_tree_host.cc b/cc/trees/layer_tree_host.cc
index 687ab1b..59c4845 100644
--- a/cc/trees/layer_tree_host.cc
+++ b/cc/trees/layer_tree_host.cc
@@ -883,9 +883,11 @@
   if (inner_viewport_scroll_delta.IsZero() && info.page_scale_delta == 1.f &&
       info.elastic_overscroll_delta.IsZero() && !info.top_controls_delta &&
       !info.browser_controls_constraint_changed &&
-      !info.scroll_gesture_did_end) {
+      !info.scroll_gesture_did_end &&
+      info.is_pinch_gesture_active == is_pinch_gesture_active_from_impl_) {
     return;
   }
+  is_pinch_gesture_active_from_impl_ = info.is_pinch_gesture_active;
 
   // Preemptively apply the scroll offset and scale delta here before sending
   // it to the client.  If the client comes back and sets it to the same
@@ -1326,11 +1328,16 @@
       this, [](Layer* layer) { layer->SetNeedsDisplay(); });
 }
 
-void LayerTreeHost::SetExternalPageScaleFactor(float page_scale_factor) {
-  if (external_page_scale_factor_ == page_scale_factor)
+void LayerTreeHost::SetExternalPageScaleFactor(
+    float page_scale_factor,
+    bool is_external_pinch_gesture_active) {
+  if (external_page_scale_factor_ == page_scale_factor &&
+      is_external_pinch_gesture_active_ == is_external_pinch_gesture_active) {
     return;
+  }
 
   external_page_scale_factor_ = page_scale_factor;
+  is_external_pinch_gesture_active_ = is_external_pinch_gesture_active;
   SetNeedsCommit();
 }
 
@@ -1651,6 +1658,7 @@
   host_impl->SetHasGpuRasterizationTrigger(has_gpu_rasterization_trigger_);
   host_impl->SetContentHasSlowPaths(content_has_slow_paths_);
   host_impl->SetContentHasNonAAPaint(content_has_non_aa_paint_);
+  host_impl->set_pinch_gesture_active(is_external_pinch_gesture_active_);
   RecordGpuRasterizationHistogram(host_impl);
 
   host_impl->SetDebugState(debug_state_);
diff --git a/cc/trees/layer_tree_host.h b/cc/trees/layer_tree_host.h
index 85c520d..cd2c6e69 100644
--- a/cc/trees/layer_tree_host.h
+++ b/cc/trees/layer_tree_host.h
@@ -471,7 +471,11 @@
   // 'external_page_scale_factor', a value that affects raster scale in the
   // same way that page_scale_factor does, but doesn't affect any geometry
   // calculations.
-  void SetExternalPageScaleFactor(float page_scale_factor);
+  void SetExternalPageScaleFactor(float page_scale_factor,
+                                  bool is_external_pinch_gesture_active);
+  bool is_external_pinch_gesture_active_for_testing() {
+    return is_external_pinch_gesture_active_;
+  }
 
   // Requests that we force send RenderFrameMetadata with the next frame.
   void RequestForceSendMetadata() { force_send_metadata_request_ = true; }
@@ -805,6 +809,9 @@
   float min_page_scale_factor_ = 1.f;
   float max_page_scale_factor_ = 1.f;
   float external_page_scale_factor_ = 1.f;
+  bool is_external_pinch_gesture_active_ = false;
+  // Used to track the out-bound state for ApplyViewportChanges.
+  bool is_pinch_gesture_active_from_impl_ = false;
 
   int raster_color_space_id_ = -1;
   gfx::ColorSpace raster_color_space_;
diff --git a/cc/trees/layer_tree_host_impl.h b/cc/trees/layer_tree_host_impl.h
index 8e76053..c2d3791e 100644
--- a/cc/trees/layer_tree_host_impl.h
+++ b/cc/trees/layer_tree_host_impl.h
@@ -591,6 +591,15 @@
   }
 
   bool pinch_gesture_active() const { return pinch_gesture_active_; }
+  // Used to set the pinch gesture active state when the pinch gesture is
+  // handled on another layer tree. In a page with OOPIFs, only the main
+  // frame's layer tree directly handles pinch events. But layer trees for
+  // sub-frames need to know when pinch gestures are active so they can
+  // throttle the re-rastering. This function allows setting this flag on
+  // OOPIF layer trees using information sent (initially) from the main-frame.
+  void set_pinch_gesture_active(bool external_pinch_gesture_active) {
+    pinch_gesture_active_ = external_pinch_gesture_active;
+  }
 
   void SetTreePriority(TreePriority priority);
   TreePriority GetTreePriority() const;
@@ -1006,6 +1015,15 @@
   bool did_scroll_x_for_scroll_gesture_;
   bool did_scroll_y_for_scroll_gesture_;
 
+  // This value is used to allow the compositor to throttle re-rastering during
+  // pinch gestures, when the page scale factor may be changing frequently. It
+  // is set in one of two ways:
+  // i) In a layer tree serving the root of the frame/compositor tree, it is
+  // directly set during processing of GesturePinch events on the impl thread
+  // (only the root layer tree has access to these).
+  // ii) In a layer tree serving a sub-frame in the frame/compositor tree, it
+  // is set from the main thread during the commit process, using information
+  // sent from the root layer tree via IPC messaging.
   bool pinch_gesture_active_ = false;
   bool pinch_gesture_end_should_clear_scrolling_node_ = false;
 
diff --git a/chrome/VERSION b/chrome/VERSION
index c092988..593c8a2 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=75
 MINOR=0
-BUILD=3758
+BUILD=3760
 PATCH=0
diff --git a/chrome/android/chrome_test_java_sources.gni b/chrome/android/chrome_test_java_sources.gni
index a71fdbb..b005fbe 100644
--- a/chrome/android/chrome_test_java_sources.gni
+++ b/chrome/android/chrome_test_java_sources.gni
@@ -356,6 +356,13 @@
   "javatests/src/org/chromium/chrome/browser/payments/PaymentRequestServiceWorkerPaymentAppTest.java",
   "javatests/src/org/chromium/chrome/browser/payments/PaymentRequestShippingAddressAndOptionTest.java",
   "javatests/src/org/chromium/chrome/browser/payments/PaymentRequestShippingAddressChangeTest.java",
+  "javatests/src/org/chromium/chrome/browser/payments/PaymentRequestShowPromiseDigitalGoodsTest.java",
+  "javatests/src/org/chromium/chrome/browser/payments/PaymentRequestShowPromiseInvalidDetailsTest.java",
+  "javatests/src/org/chromium/chrome/browser/payments/PaymentRequestShowPromiseRejectTest.java",
+  "javatests/src/org/chromium/chrome/browser/payments/PaymentRequestShowPromiseSingleOptionShippingTest.java",
+  "javatests/src/org/chromium/chrome/browser/payments/PaymentRequestShowPromiseSingleOptionShippingWithUpdateTest.java",
+  "javatests/src/org/chromium/chrome/browser/payments/PaymentRequestShowPromiseUnsupportedTest.java",
+  "javatests/src/org/chromium/chrome/browser/payments/PaymentRequestShowPromiseUSOnlyShippingTest.java",
   "javatests/src/org/chromium/chrome/browser/payments/PaymentRequestShowTwiceTest.java",
   "javatests/src/org/chromium/chrome/browser/payments/PaymentRequestTabTest.java",
   "javatests/src/org/chromium/chrome/browser/payments/PaymentRequestTestRule.java",
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/MultiThumbnailCardProvider.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/MultiThumbnailCardProvider.java
index 5112350b..63ce54f 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/MultiThumbnailCardProvider.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/MultiThumbnailCardProvider.java
@@ -16,9 +16,6 @@
 
 import org.chromium.base.ApiCompatibilityUtils;
 import org.chromium.base.Callback;
-import org.chromium.base.PathUtils;
-import org.chromium.base.ThreadUtils;
-import org.chromium.base.task.AsyncTask;
 import org.chromium.base.task.PostTask;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.compositor.layouts.content.TabContentManager;
@@ -26,7 +23,6 @@
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
 import org.chromium.content_public.browser.UiThreadTaskTraits;
 
-import java.io.File;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.atomic.AtomicInteger;
@@ -47,9 +43,10 @@
     private final int mSize;
     private final List<RectF> mRects = new ArrayList<>(4);
 
-    private class MultiThumbnailFetchingTask extends AsyncTask<Void> {
+    private class MultiThumbnailFetcher {
         private final Tab mInitialTab;
         private final Callback<Bitmap> mFinalCallback;
+        private final boolean mForceUpdate;
         private final List<Tab> mTabs = new ArrayList<>(4);
         private final AtomicInteger mThumbnailsToFetch = new AtomicInteger();
 
@@ -57,9 +54,10 @@
         private Bitmap mMultiThumbnailBitmap;
         private String mText;
 
-        MultiThumbnailFetchingTask(Tab initialTab, Callback<Bitmap> finalCallback) {
+        MultiThumbnailFetcher(Tab initialTab, Callback<Bitmap> finalCallback, boolean forceUpdate) {
             mFinalCallback = finalCallback;
             mInitialTab = initialTab;
+            mForceUpdate = forceUpdate;
         }
 
         private void initializeAndStartFetching(Tab tab) {
@@ -99,22 +97,14 @@
             // Fetch and draw all.
             for (int i = 0; i < 4; i++) {
                 if (mTabs.get(i) != null) {
-                    if (hasThumbnailFileForTab(mTabs.get(i).getId())) {
-                        final int index = i;
-                        mTabContentManager.getTabThumbnailWithCallback(mTabs.get(i), result -> {
-                            drawBitmapOnCanvasWithFrame(result, index, mEmptyThumbnailPaint);
-                            if (mThumbnailsToFetch.decrementAndGet() == 0) {
-                                PostTask.postTask(UiThreadTaskTraits.USER_VISIBLE,
-                                        () -> mFinalCallback.onResult(mMultiThumbnailBitmap));
-                            }
-                        }, false);
-                    } else {
-                        drawBitmapOnCanvasWithFrame(null, i, mEmptyThumbnailPaint);
+                    final int index = i;
+                    mTabContentManager.getTabThumbnailWithCallback(mTabs.get(i), result -> {
+                        drawBitmapOnCanvasWithFrame(result, index, mEmptyThumbnailPaint);
                         if (mThumbnailsToFetch.decrementAndGet() == 0) {
                             PostTask.postTask(UiThreadTaskTraits.USER_VISIBLE,
                                     () -> mFinalCallback.onResult(mMultiThumbnailBitmap));
                         }
-                    }
+                    }, mForceUpdate && i == 0);
                 } else {
                     drawBitmapOnCanvasWithFrame(null, i, mEmptyThumbnailPaint);
                     if (mText != null && i == 3) {
@@ -148,14 +138,9 @@
             mCanvas.drawRoundRect(mRects.get(index), mRadius, mRadius, mThumbnailFramePaint);
         }
 
-        @Override
-        protected Void doInBackground() {
+        private void fetch() {
             initializeAndStartFetching(mInitialTab);
-            return null;
         }
-
-        @Override
-        protected void onPostExecute(Void aVoid) {}
     }
 
     MultiThumbnailCardProvider(Context context, TabContentManager tabContentManager,
@@ -212,15 +197,6 @@
             return;
         }
 
-        new MultiThumbnailFetchingTask(tab, finalCallback)
-                .executeOnExecutor(AsyncTask.SERIAL_EXECUTOR);
-    }
-
-    private static boolean hasThumbnailFileForTab(int tabId) {
-        ThreadUtils.assertOnBackgroundThread();
-
-        // The thumbnail file path for a tab with id, ID is "cache_directory_path/ID".
-        File file = new File(PathUtils.getThumbnailCacheDirectory() + "/" + tabId);
-        return file.exists();
+        new MultiThumbnailFetcher(tab, finalCallback, forceUpdate).fetch();
     }
 }
diff --git a/chrome/android/java/res/values/ids.xml b/chrome/android/java/res/values/ids.xml
index e7ee576..b658a52 100644
--- a/chrome/android/java/res/values/ids.xml
+++ b/chrome/android/java/res/values/ids.xml
@@ -67,6 +67,8 @@
     <item type="id" name="payments_add_option_button" />
     <item type="id" name="payments_edit_cancel_button" />
     <item type="id" name="payments_edit_checkbox" />
+    <item type="id" name="payments_description_label" />
+    <item type="id" name="payments_warning_label" />
 
     <!-- Password update prompt -->
     <item type="id" name="password_infobar_accounts_spinner" />
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentRequestFactory.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentRequestFactory.java
index a9e64c68..40f7c76 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentRequestFactory.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentRequestFactory.java
@@ -38,7 +38,7 @@
         }
 
         @Override
-        public void show(boolean isUserGesture) {
+        public void show(boolean isUserGesture, boolean waitForUpdatedDetails) {
             if (mClient != null) {
                 mClient.onError(PaymentErrorReason.USER_CANCEL);
                 mClient.close();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentRequestImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentRequestImpl.java
index d4b6a4b..5547717 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentRequestImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentRequestImpl.java
@@ -109,6 +109,13 @@
      */
     public interface PaymentRequestServiceObserverForTest {
         /**
+         * Called after an instance of PaymentRequestImpl has been created.
+         *
+         * @param paymentRequest The newly created instance of PaymentRequestImpl.
+         */
+        void onPaymentRequestCreated(PaymentRequestImpl paymentRequest);
+
+        /**
          * Called when an abort request was denied.
          */
         void onPaymentRequestServiceUnableToAbort();
@@ -302,6 +309,7 @@
     private boolean mHideServerAutofillInstruments;
     private ContactEditor mContactEditor;
     private boolean mHasRecordedAbortReason;
+    private boolean mWaitForUpdatedDetails;
     private Map<String, CurrencyFormatter> mCurrencyFormatterMap;
     private TabModelSelector mObservedTabModelSelector;
     private TabModel mObservedTabModel;
@@ -375,6 +383,8 @@
 
         mJourneyLogger = new JourneyLogger(mIsIncognito, mWebContents);
         mCurrencyFormatterMap = new HashMap<>();
+
+        if (sObserverForTest != null) sObserverForTest.onPaymentRequestCreated(this);
     }
 
     /**
@@ -472,7 +482,19 @@
         }
         mJourneyLogger.setRequestedPaymentMethodTypes(mMerchantSupportsAutofillPaymentInstruments,
                 requestedMethodGoogle, requestedMethodOther);
+        calculateWhetherShouldSkipShowingPaymentRequestUi(
+                false /* skipUiForNonUrlPaymentMethodIdentifiersForTest */);
+    }
 
+    /**
+     * Calculate whether the browser payment sheet should be skipped directly into the payment app.
+     *
+     * @param skipUiForNonUrlPaymentMethodIdentifiersForTest Whether non-URL payment method
+     *                                                       identifiers should skip UI. Used only
+     *                                                       in tests.
+     */
+    private void calculateWhetherShouldSkipShowingPaymentRequestUi(
+            boolean skipUiForNonUrlPaymentMethodIdentifiersForTest) {
         // If there is a single payment method and the merchant has not requested any other
         // information, we can safely go directly to the payment app instead of showing
         // Payment Request UI.
@@ -485,7 +507,8 @@
                 // This excludes AutofillPaymentApp as its UI is rendered inline in
                 // the payment request UI, thus can't be skipped.
                 && mMethodData.keySet().iterator().next() != null
-                && mMethodData.keySet().iterator().next().startsWith(UrlConstants.HTTPS_URL_PREFIX);
+                && (mMethodData.keySet().iterator().next().startsWith(UrlConstants.HTTPS_URL_PREFIX)
+                        || skipUiForNonUrlPaymentMethodIdentifiersForTest);
     }
 
     /** @return Whether the UI was built. */
@@ -526,7 +549,7 @@
                     false /* includeNameInLabel */);
         }
 
-        if (mRequestShipping) {
+        if (mRequestShipping && !mWaitForUpdatedDetails) {
             createShippingSection(activity, Collections.unmodifiableList(profiles));
         }
 
@@ -622,7 +645,7 @@
      * Called by the merchant website to show the payment request to the user.
      */
     @Override
-    public void show(boolean isUserGesture) {
+    public void show(boolean isUserGesture, boolean waitForUpdatedDetails) {
         if (mClient == null) return;
 
         if (mUI != null) {
@@ -657,6 +680,8 @@
         }
 
         mIsUserGestureShow = isUserGesture;
+        mWaitForUpdatedDetails = waitForUpdatedDetails;
+
         if (!mShouldSkipShowingPaymentRequestUi) {
             if (!buildUI(chromeActivity)) return;
             mUI.show();
@@ -669,7 +694,7 @@
         // If we are skipping showing the Payment Request UI, we should call into the
         // PaymentApp immediately after we determine the instruments are ready and UI is shown.
         if (mShouldSkipShowingPaymentRequestUi && isFinishedQueryingPaymentApps()
-                && mIsCurrentPaymentRequestShowing) {
+                && mIsCurrentPaymentRequestShowing && !mWaitForUpdatedDetails) {
             assert !mPaymentMethodsSection.isEmpty();
 
             PaymentInstrument selectedInstrument =
@@ -838,6 +863,11 @@
     public void updateWith(PaymentDetails details) {
         if (mClient == null) return;
 
+        if (mWaitForUpdatedDetails) {
+            initializeWithUpdatedDetails(details);
+            return;
+        }
+
         if (mUI == null) {
             mJourneyLogger.setAborted(AbortReason.INVALID_DATA_FROM_RENDERER);
             disconnectFromClientWithDebugMessage(
@@ -845,6 +875,14 @@
             return;
         }
 
+        if (!mRequestShipping) {
+            mJourneyLogger.setAborted(AbortReason.INVALID_DATA_FROM_RENDERER);
+            disconnectFromClientWithDebugMessage(
+                    "PaymentRequestUpdateEvent.updateWith() called without passing a promise into "
+                    + "PaymentRequest.show() and without requestShipping:true");
+            return;
+        }
+
         if (!parseAndValidateDetailsOrDisconnectFromClient(details)) return;
 
         if (mInvokedPaymentInstrument != null) {
@@ -872,6 +910,49 @@
         enableUserInterfaceAfterPaymentRequestUpdateEvent();
     }
 
+    private void initializeWithUpdatedDetails(PaymentDetails details) {
+        assert mWaitForUpdatedDetails;
+
+        ChromeActivity chromeActivity = ChromeActivity.fromWebContents(mWebContents);
+        if (chromeActivity == null) {
+            mJourneyLogger.setNotShown(NotShownReason.OTHER);
+            disconnectFromClientWithDebugMessage("Unable to find Chrome activity");
+            return;
+        }
+
+        if (!parseAndValidateDetailsOrDisconnectFromClient(details)) return;
+
+        if (details.total == null) {
+            mJourneyLogger.setNotShown(NotShownReason.OTHER);
+            disconnectFromClientWithDebugMessage(
+                    "PaymentRequestUpdateEvent.updateWith() called without a total amount when "
+                    + "resolving the promise passed into PaymentRequest.show()");
+            return;
+        }
+
+        if (!TextUtils.isEmpty(details.error)) {
+            mJourneyLogger.setNotShown(NotShownReason.OTHER);
+            disconnectFromClientWithDebugMessage(
+                    "PaymentRequestUpdateEvent.updateWith() called with an error when resolving "
+                    + "the promise passed into PaymentRequest.show()");
+            return;
+        }
+
+        if (mRequestShipping) {
+            createShippingSection(chromeActivity,
+                    Collections.unmodifiableList(
+                            PersonalDataManager.getInstance().getProfilesToSuggest(
+                                    false /* includeNameInLabel */)));
+        }
+
+        if (!mShouldSkipShowingPaymentRequestUi) {
+            enableUserInterfaceAfterPaymentRequestUpdateEvent();
+        }
+
+        mWaitForUpdatedDetails = false;
+        triggerPaymentAppUiSkipIfApplicable(chromeActivity);
+    }
+
     /**
      * Called when the merchant received a new shipping address, shipping option, or payment method
      * info, but did not update the payment details in response.
@@ -896,11 +977,13 @@
     }
 
     private void enableUserInterfaceAfterPaymentRequestUpdateEvent() {
-        if (mPaymentInformationCallback != null) {
+        if (mPaymentInformationCallback != null && mPaymentMethodsSection != null) {
             providePaymentInformation();
         } else {
             mUI.updateOrderSummarySection(mUiShoppingCart);
-            mUI.updateSection(PaymentRequestUI.DataType.SHIPPING_OPTIONS, mUiShippingOptions);
+            if (mRequestShipping) {
+                mUI.updateSection(PaymentRequestUI.DataType.SHIPPING_OPTIONS, mUiShippingOptions);
+            }
         }
     }
 
@@ -1124,7 +1207,7 @@
     public void getDefaultPaymentInformation(Callback<PaymentInformation> callback) {
         mPaymentInformationCallback = callback;
 
-        if (mPaymentMethodsSection == null) return;
+        if (mPaymentMethodsSection == null || mWaitForUpdatedDetails) return;
 
         mHandler.post(() -> {
             if (mUI != null) providePaymentInformation();
@@ -1884,7 +1967,9 @@
         updateInstrumentModifiedTotals();
 
         // UI has requested the full list of payment instruments. Provide it now.
-        if (mPaymentInformationCallback != null) providePaymentInformation();
+        if (mPaymentInformationCallback != null && !mWaitForUpdatedDetails) {
+            providePaymentInformation();
+        }
 
         SettingsAutofillAndPaymentsObserver.getInstance().registerObserver(this);
 
@@ -2123,6 +2208,12 @@
         sIsLocalCanMakePaymentQueryQuotaEnforcedForTest = true;
     }
 
+    @VisibleForTesting
+    /* package */ void setSkipUIForNonURLPaymentMethodIdentifiersForTest() {
+        calculateWhetherShouldSkipShowingPaymentRequestUi(
+                true /* skipUiForNonUrlPaymentMethodIdentifiersForTest */);
+    }
+
     /**
      * Compares two payment instruments by frecency.
      * Return negative value if a has strictly lower frecency score than b.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/PaymentRequestSection.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/PaymentRequestSection.java
index 2a39ce30..77b0ab18 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/PaymentRequestSection.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/PaymentRequestSection.java
@@ -991,11 +991,13 @@
 
                     ApiCompatibilityUtils.setTextAppearance(
                             labelView, R.style.TextAppearance_BlackBody);
+                    labelView.setId(R.id.payments_description_label);
                 } else if (mRowType == OPTION_ROW_TYPE_WARNING) {
                     // Warnings use three columns.
                     columnSpan = 3;
                     ApiCompatibilityUtils.setTextAppearance(
                             labelView, R.style.TextAppearance_PaymentsUiSectionWarningText);
+                    labelView.setId(R.id.payments_warning_label);
                 }
 
                 // The label spans two columns if no option or edit icon, or spans three columns if
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestShowPromiseDigitalGoodsTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestShowPromiseDigitalGoodsTest.java
new file mode 100644
index 0000000..7029a80
--- /dev/null
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestShowPromiseDigitalGoodsTest.java
@@ -0,0 +1,111 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.payments;
+
+import android.support.test.filters.MediumTest;
+
+import org.junit.Assert;
+import org.junit.ClassRule;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import org.chromium.base.test.util.CommandLineFlags;
+import org.chromium.base.test.util.Feature;
+import org.chromium.chrome.R;
+import org.chromium.chrome.browser.ChromeSwitches;
+import org.chromium.chrome.browser.payments.PaymentRequestTestRule.MainActivityStartCallback;
+import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
+import org.chromium.chrome.test.ui.DisableAnimationsTestRule;
+
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeoutException;
+
+/**
+ * A payment integration test for the show promise with digital goods.
+ */
+@RunWith(ChromeJUnit4ClassRunner.class)
+@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
+public class PaymentRequestShowPromiseDigitalGoodsTest implements MainActivityStartCallback {
+    // Disable animations to reduce flakiness.
+    @ClassRule
+    public static DisableAnimationsTestRule sNoAnimationsRule = new DisableAnimationsTestRule();
+
+    @Rule
+    public PaymentRequestTestRule mRule =
+            new PaymentRequestTestRule("show_promise/digital_goods.html", this);
+
+    @Override
+    public void onMainActivityStarted()
+            throws InterruptedException, ExecutionException, TimeoutException {}
+
+    @Test
+    @MediumTest
+    @Feature({"Payments"})
+    public void testDigitalGoodsFastApp()
+            throws InterruptedException, ExecutionException, TimeoutException {
+        mRule.installPaymentApp("basic-card", PaymentRequestTestRule.HAVE_INSTRUMENTS,
+                PaymentRequestTestRule.IMMEDIATE_RESPONSE);
+        mRule.openPage();
+        mRule.executeJavaScriptAndWaitForResult("create();");
+        mRule.triggerUIAndWait(mRule.getReadyToPay());
+
+        Assert.assertEquals("USD $1.00", mRule.getOrderSummaryTotal());
+
+        mRule.clickAndWait(R.id.button_primary, mRule.getDismissed());
+
+        mRule.expectResultContains(new String[] {"\"total\":\"1.00\""});
+    }
+
+    @Test
+    @MediumTest
+    @Feature({"Payments"})
+    public void testDigitalGoodsSlowApp()
+            throws InterruptedException, ExecutionException, TimeoutException {
+        mRule.installPaymentApp("basic-card", PaymentRequestTestRule.HAVE_INSTRUMENTS,
+                PaymentRequestTestRule.DELAYED_RESPONSE, PaymentRequestTestRule.DELAYED_CREATION);
+        mRule.openPage();
+        mRule.executeJavaScriptAndWaitForResult("create();");
+        mRule.triggerUIAndWait(mRule.getReadyToPay());
+
+        Assert.assertEquals("USD $1.00", mRule.getOrderSummaryTotal());
+
+        mRule.clickAndWait(R.id.button_primary, mRule.getDismissed());
+
+        mRule.expectResultContains(new String[] {"\"total\":\"1.00\""});
+    }
+
+    @Test
+    @MediumTest
+    @Feature({"Payments"})
+    public void testSkipUIFastApp()
+            throws InterruptedException, ExecutionException, TimeoutException {
+        mRule.installPaymentApp("basic-card", PaymentRequestTestRule.HAVE_INSTRUMENTS,
+                PaymentRequestTestRule.IMMEDIATE_RESPONSE);
+        mRule.openPage();
+        mRule.executeJavaScriptAndWaitForResult("create();");
+        mRule.enableSkipUIForBasicCard();
+
+        mRule.openPageAndClickNodeAndWait("buy", mRule.getDismissed());
+
+        mRule.expectResultContains(new String[] {"\"total\":\"1.00\""});
+    }
+
+    @Test
+    @MediumTest
+    @Feature({"Payments"})
+    public void testSkipUISlowApp()
+            throws InterruptedException, ExecutionException, TimeoutException {
+        mRule.installPaymentApp("basic-card", PaymentRequestTestRule.HAVE_INSTRUMENTS,
+                PaymentRequestTestRule.DELAYED_RESPONSE, PaymentRequestTestRule.DELAYED_CREATION);
+        mRule.openPage();
+        mRule.executeJavaScriptAndWaitForResult("create();");
+        mRule.enableSkipUIForBasicCard();
+
+        mRule.openPageAndClickNodeAndWait("buy", mRule.getDismissed());
+
+        mRule.expectResultContains(new String[] {"\"total\":\"1.00\""});
+    }
+}
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestShowPromiseInvalidDetailsTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestShowPromiseInvalidDetailsTest.java
new file mode 100644
index 0000000..8276c2cd
--- /dev/null
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestShowPromiseInvalidDetailsTest.java
@@ -0,0 +1,49 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.payments;
+
+import android.support.test.filters.MediumTest;
+
+import org.junit.ClassRule;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import org.chromium.base.test.util.CommandLineFlags;
+import org.chromium.base.test.util.Feature;
+import org.chromium.chrome.browser.ChromeSwitches;
+import org.chromium.chrome.browser.payments.PaymentRequestTestRule.MainActivityStartCallback;
+import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
+import org.chromium.chrome.test.ui.DisableAnimationsTestRule;
+
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeoutException;
+
+/**
+ * A payment integration test for the show promise that resolves with invalid details.
+ */
+@RunWith(ChromeJUnit4ClassRunner.class)
+@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
+public class PaymentRequestShowPromiseInvalidDetailsTest implements MainActivityStartCallback {
+    // Disable animations to reduce flakiness.
+    @ClassRule
+    public static DisableAnimationsTestRule sNoAnimationsRule = new DisableAnimationsTestRule();
+
+    @Rule
+    public PaymentRequestTestRule mRule =
+            new PaymentRequestTestRule("show_promise/invalid_details.html", this);
+
+    @Override
+    public void onMainActivityStarted()
+            throws InterruptedException, ExecutionException, TimeoutException {}
+
+    @Test
+    @MediumTest
+    @Feature({"Payments"})
+    public void testReject() throws InterruptedException, ExecutionException, TimeoutException {
+        mRule.openPageAndClickNodeAndWait("buy", mRule.getDismissed());
+        mRule.expectResultContains(new String[] {"Total amount value should be non-negative"});
+    }
+}
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestShowPromiseRejectTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestShowPromiseRejectTest.java
new file mode 100644
index 0000000..14c3932
--- /dev/null
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestShowPromiseRejectTest.java
@@ -0,0 +1,51 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.payments;
+
+import android.support.test.filters.MediumTest;
+
+import org.junit.ClassRule;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import org.chromium.base.test.util.CommandLineFlags;
+import org.chromium.base.test.util.Feature;
+import org.chromium.chrome.browser.ChromeSwitches;
+import org.chromium.chrome.browser.payments.PaymentRequestTestRule.MainActivityStartCallback;
+import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
+import org.chromium.chrome.test.ui.DisableAnimationsTestRule;
+
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeoutException;
+
+/**
+ * A payment integration test for the show promise that is rejected.
+ */
+@RunWith(ChromeJUnit4ClassRunner.class)
+@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
+public class PaymentRequestShowPromiseRejectTest implements MainActivityStartCallback {
+    // Disable animations to reduce flakiness.
+    @ClassRule
+    public static DisableAnimationsTestRule sNoAnimationsRule = new DisableAnimationsTestRule();
+
+    @Rule
+    public PaymentRequestTestRule mRule =
+            new PaymentRequestTestRule("show_promise/reject.html", this);
+
+    @Override
+    public void onMainActivityStarted()
+            throws InterruptedException, ExecutionException, TimeoutException {}
+
+    @Test
+    @MediumTest
+    @Feature({"Payments"})
+    public void testReject() throws InterruptedException, ExecutionException, TimeoutException {
+        mRule.installPaymentApp("basic-card", PaymentRequestTestRule.HAVE_INSTRUMENTS,
+                PaymentRequestTestRule.IMMEDIATE_RESPONSE);
+        mRule.openPageAndClickNodeAndWait("buy", mRule.getDismissed());
+        mRule.expectResultContains(new String[] {"AbortError"});
+    }
+}
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestShowPromiseSingleOptionShippingTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestShowPromiseSingleOptionShippingTest.java
new file mode 100644
index 0000000..0cb7fea
--- /dev/null
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestShowPromiseSingleOptionShippingTest.java
@@ -0,0 +1,85 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.payments;
+
+import android.support.test.filters.MediumTest;
+
+import org.junit.Assert;
+import org.junit.ClassRule;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import org.chromium.base.test.util.CommandLineFlags;
+import org.chromium.base.test.util.Feature;
+import org.chromium.chrome.R;
+import org.chromium.chrome.browser.ChromeSwitches;
+import org.chromium.chrome.browser.autofill.AutofillTestHelper;
+import org.chromium.chrome.browser.autofill.PersonalDataManager.AutofillProfile;
+import org.chromium.chrome.browser.payments.PaymentRequestTestRule.MainActivityStartCallback;
+import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
+import org.chromium.chrome.test.ui.DisableAnimationsTestRule;
+
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeoutException;
+
+/**
+ * A payment integration test for the show promise with a single pre-selected shipping option and no
+ * shipping address change handler.
+ */
+@RunWith(ChromeJUnit4ClassRunner.class)
+@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
+public class PaymentRequestShowPromiseSingleOptionShippingTest
+        implements MainActivityStartCallback {
+    // Disable animations to reduce flakiness.
+    @ClassRule
+    public static DisableAnimationsTestRule sNoAnimationsRule = new DisableAnimationsTestRule();
+
+    @Rule
+    public PaymentRequestTestRule mRule =
+            new PaymentRequestTestRule("show_promise/single_option_shipping.html", this);
+
+    @Override
+    public void onMainActivityStarted()
+            throws InterruptedException, ExecutionException, TimeoutException {
+        AutofillTestHelper autofillTestHelper = new AutofillTestHelper();
+        autofillTestHelper.setProfile(new AutofillProfile("", "https://example.com", true,
+                "Jon Doe", "Google", "340 Main St", "California", "Los Angeles", "", "90291", "",
+                "US", "555-222-2222", "", "en-US"));
+        autofillTestHelper.setProfile(new AutofillProfile("", "https://example.com", true,
+                "Jane Smith", "Google", "340 Main St", "California", "Los Angeles", "", "90291", "",
+                "US", "555-111-1111", "", "en-US"));
+    }
+
+    @Test
+    @MediumTest
+    @Feature({"Payments"})
+    public void testFastApp() throws InterruptedException, ExecutionException, TimeoutException {
+        mRule.installPaymentApp("basic-card", PaymentRequestTestRule.HAVE_INSTRUMENTS,
+                PaymentRequestTestRule.IMMEDIATE_RESPONSE);
+        mRule.triggerUIAndWait(mRule.getReadyToPay());
+        Assert.assertEquals("USD $1.00", mRule.getOrderSummaryTotal());
+        Assert.assertEquals("$0.00", mRule.getShippingOptionCostSummaryOnBottomSheet());
+        mRule.clickInShippingAddressAndWait(R.id.payments_section, mRule.getReadyForInput());
+        mRule.clickOnShippingAddressSuggestionOptionAndWait(1, mRule.getReadyForInput());
+        mRule.clickAndWait(R.id.button_primary, mRule.getDismissed());
+        mRule.expectResultContains(new String[] {"\"total\":\"1.00\""});
+    }
+
+    @Test
+    @MediumTest
+    @Feature({"Payments"})
+    public void testSlowApp() throws InterruptedException, ExecutionException, TimeoutException {
+        mRule.installPaymentApp("basic-card", PaymentRequestTestRule.HAVE_INSTRUMENTS,
+                PaymentRequestTestRule.DELAYED_RESPONSE, PaymentRequestTestRule.DELAYED_CREATION);
+        mRule.triggerUIAndWait(mRule.getReadyToPay());
+        Assert.assertEquals("USD $1.00", mRule.getOrderSummaryTotal());
+        Assert.assertEquals("$0.00", mRule.getShippingOptionCostSummaryOnBottomSheet());
+        mRule.clickInShippingAddressAndWait(R.id.payments_section, mRule.getReadyForInput());
+        mRule.clickOnShippingAddressSuggestionOptionAndWait(1, mRule.getReadyForInput());
+        mRule.clickAndWait(R.id.button_primary, mRule.getDismissed());
+        mRule.expectResultContains(new String[] {"\"total\":\"1.00\""});
+    }
+}
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestShowPromiseSingleOptionShippingWithUpdateTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestShowPromiseSingleOptionShippingWithUpdateTest.java
new file mode 100644
index 0000000..3b8cedf
--- /dev/null
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestShowPromiseSingleOptionShippingWithUpdateTest.java
@@ -0,0 +1,85 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.payments;
+
+import android.support.test.filters.MediumTest;
+
+import org.junit.Assert;
+import org.junit.ClassRule;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import org.chromium.base.test.util.CommandLineFlags;
+import org.chromium.base.test.util.Feature;
+import org.chromium.chrome.R;
+import org.chromium.chrome.browser.ChromeSwitches;
+import org.chromium.chrome.browser.autofill.AutofillTestHelper;
+import org.chromium.chrome.browser.autofill.PersonalDataManager.AutofillProfile;
+import org.chromium.chrome.browser.payments.PaymentRequestTestRule.MainActivityStartCallback;
+import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
+import org.chromium.chrome.test.ui.DisableAnimationsTestRule;
+
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeoutException;
+
+/**
+ * A payment integration test for the show promise with a single pre-selected shipping option and a
+ * shipping address change handler.
+ */
+@RunWith(ChromeJUnit4ClassRunner.class)
+@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
+public class PaymentRequestShowPromiseSingleOptionShippingWithUpdateTest
+        implements MainActivityStartCallback {
+    // Disable animations to reduce flakiness.
+    @ClassRule
+    public static DisableAnimationsTestRule sNoAnimationsRule = new DisableAnimationsTestRule();
+
+    @Rule
+    public PaymentRequestTestRule mRule = new PaymentRequestTestRule(
+            "show_promise/single_option_shipping_with_update.html", this);
+
+    @Override
+    public void onMainActivityStarted()
+            throws InterruptedException, ExecutionException, TimeoutException {
+        AutofillTestHelper autofillTestHelper = new AutofillTestHelper();
+        autofillTestHelper.setProfile(new AutofillProfile("", "https://example.com", true,
+                "Jon Doe", "Google", "340 Main St", "California", "Los Angeles", "", "90291", "",
+                "US", "555-222-2222", "", "en-US"));
+        autofillTestHelper.setProfile(new AutofillProfile("", "https://example.com", true,
+                "Jane Smith", "Google", "340 Main St", "California", "Los Angeles", "", "90291", "",
+                "US", "555-111-1111", "", "en-US"));
+    }
+
+    @Test
+    @MediumTest
+    @Feature({"Payments"})
+    public void testFastApp() throws InterruptedException, ExecutionException, TimeoutException {
+        mRule.installPaymentApp("basic-card", PaymentRequestTestRule.HAVE_INSTRUMENTS,
+                PaymentRequestTestRule.IMMEDIATE_RESPONSE);
+        mRule.triggerUIAndWait(mRule.getReadyToPay());
+        Assert.assertEquals("USD $1.00", mRule.getOrderSummaryTotal());
+        Assert.assertEquals("$0.00", mRule.getShippingOptionCostSummaryOnBottomSheet());
+        mRule.clickInShippingAddressAndWait(R.id.payments_section, mRule.getReadyForInput());
+        mRule.clickOnShippingAddressSuggestionOptionAndWait(1, mRule.getReadyForInput());
+        mRule.clickAndWait(R.id.button_primary, mRule.getDismissed());
+        mRule.expectResultContains(new String[] {"\"total\":\"1.00\""});
+    }
+
+    @Test
+    @MediumTest
+    @Feature({"Payments"})
+    public void testSlowApp() throws InterruptedException, ExecutionException, TimeoutException {
+        mRule.installPaymentApp("basic-card", PaymentRequestTestRule.HAVE_INSTRUMENTS,
+                PaymentRequestTestRule.DELAYED_RESPONSE, PaymentRequestTestRule.DELAYED_CREATION);
+        mRule.triggerUIAndWait(mRule.getReadyToPay());
+        Assert.assertEquals("USD $1.00", mRule.getOrderSummaryTotal());
+        Assert.assertEquals("$0.00", mRule.getShippingOptionCostSummaryOnBottomSheet());
+        mRule.clickInShippingAddressAndWait(R.id.payments_section, mRule.getReadyForInput());
+        mRule.clickOnShippingAddressSuggestionOptionAndWait(1, mRule.getReadyForInput());
+        mRule.clickAndWait(R.id.button_primary, mRule.getDismissed());
+        mRule.expectResultContains(new String[] {"\"total\":\"1.00\""});
+    }
+}
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestShowPromiseUSOnlyShippingTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestShowPromiseUSOnlyShippingTest.java
new file mode 100644
index 0000000..a2cb36d
--- /dev/null
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestShowPromiseUSOnlyShippingTest.java
@@ -0,0 +1,122 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.payments;
+
+import android.support.test.filters.MediumTest;
+
+import org.junit.Assert;
+import org.junit.ClassRule;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import org.chromium.base.test.util.CommandLineFlags;
+import org.chromium.base.test.util.Feature;
+import org.chromium.chrome.R;
+import org.chromium.chrome.browser.ChromeSwitches;
+import org.chromium.chrome.browser.autofill.AutofillTestHelper;
+import org.chromium.chrome.browser.autofill.PersonalDataManager.AutofillProfile;
+import org.chromium.chrome.browser.payments.PaymentRequestTestRule.MainActivityStartCallback;
+import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
+import org.chromium.chrome.test.ui.DisableAnimationsTestRule;
+
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeoutException;
+
+/**
+ * A payment integration test for the show promise with restricted shipping rules.
+ */
+@RunWith(ChromeJUnit4ClassRunner.class)
+@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
+public class PaymentRequestShowPromiseUSOnlyShippingTest implements MainActivityStartCallback {
+    // Disable animations to reduce flakiness.
+    @ClassRule
+    public static DisableAnimationsTestRule sNoAnimationsRule = new DisableAnimationsTestRule();
+
+    @Rule
+    public PaymentRequestTestRule mRule =
+            new PaymentRequestTestRule("show_promise/us_only_shipping.html", this);
+
+    @Override
+    public void onMainActivityStarted()
+            throws InterruptedException, ExecutionException, TimeoutException {}
+
+    @Test
+    @MediumTest
+    @Feature({"Payments"})
+    public void testCannotShipWithFastApp()
+            throws InterruptedException, ExecutionException, TimeoutException {
+        mRule.installPaymentApp("basic-card", PaymentRequestTestRule.HAVE_INSTRUMENTS,
+                PaymentRequestTestRule.IMMEDIATE_RESPONSE);
+        runCannotShipTest();
+    }
+
+    @Test
+    @MediumTest
+    @Feature({"Payments"})
+    public void testCannotShipWithSlowApp()
+            throws InterruptedException, ExecutionException, TimeoutException {
+        mRule.installPaymentApp("basic-card", PaymentRequestTestRule.HAVE_INSTRUMENTS,
+                PaymentRequestTestRule.DELAYED_RESPONSE, PaymentRequestTestRule.DELAYED_CREATION);
+        runCannotShipTest();
+    }
+
+    private void runCannotShipTest()
+            throws InterruptedException, ExecutionException, TimeoutException {
+        AutofillTestHelper autofillTestHelper = new AutofillTestHelper();
+        autofillTestHelper.setProfile(new AutofillProfile("", "https://example.com", true,
+                "Jon Doe", "Google", "51 Breithaupt St", "ON", "Kitchener", "", "N2H 5G5", "", "CA",
+                "555-222-2222", "", "en-CA"));
+        mRule.triggerUIAndWait(mRule.getReadyForInput());
+        Assert.assertEquals("USD $1.00", mRule.getOrderSummaryTotal());
+        mRule.clickInShippingAddressAndWait(R.id.payments_section, mRule.getReadyForInput());
+        Assert.assertEquals("To see shipping methods and requirements, select an address",
+                mRule.getShippingAddressDescriptionLabel());
+        Assert.assertEquals(null, mRule.getShippingAddressWarningLabel());
+        mRule.clickOnShippingAddressSuggestionOptionAndWait(0, mRule.getReadyForInput());
+        Assert.assertEquals(null, mRule.getShippingAddressDescriptionLabel());
+        Assert.assertEquals("Cannot ship outside of US.", mRule.getShippingAddressWarningLabel());
+        mRule.clickAndWait(R.id.button_secondary, mRule.getDismissed());
+    }
+
+    @Test
+    @MediumTest
+    @Feature({"Payments"})
+    public void testCanShipWithFastApp()
+            throws InterruptedException, ExecutionException, TimeoutException {
+        mRule.installPaymentApp("basic-card", PaymentRequestTestRule.HAVE_INSTRUMENTS,
+                PaymentRequestTestRule.IMMEDIATE_RESPONSE);
+        runCanShipTest();
+    }
+
+    @Test
+    @MediumTest
+    @Feature({"Payments"})
+    public void testCanShipWithSlowApp()
+            throws InterruptedException, ExecutionException, TimeoutException {
+        mRule.installPaymentApp("basic-card", PaymentRequestTestRule.HAVE_INSTRUMENTS,
+                PaymentRequestTestRule.DELAYED_RESPONSE, PaymentRequestTestRule.DELAYED_CREATION);
+        runCanShipTest();
+    }
+
+    private void runCanShipTest()
+            throws InterruptedException, ExecutionException, TimeoutException {
+        AutofillTestHelper autofillTestHelper = new AutofillTestHelper();
+        autofillTestHelper.setProfile(new AutofillProfile("", "https://example.com", true,
+                "Jane Smith", "Google", "340 Main St", "California", "Los Angeles", "", "90291", "",
+                "US", "555-111-1111", "", "en-US"));
+        mRule.triggerUIAndWait(mRule.getReadyForInput());
+        Assert.assertEquals("USD $1.00", mRule.getOrderSummaryTotal());
+        mRule.clickInShippingAddressAndWait(R.id.payments_section, mRule.getReadyForInput());
+        Assert.assertEquals("To see shipping methods and requirements, select an address",
+                mRule.getShippingAddressDescriptionLabel());
+        Assert.assertEquals(null, mRule.getShippingAddressWarningLabel());
+        mRule.clickOnShippingAddressSuggestionOptionAndWait(0, mRule.getReadyToPay());
+        Assert.assertEquals(null, mRule.getShippingAddressDescriptionLabel());
+        Assert.assertEquals(null, mRule.getShippingAddressWarningLabel());
+        mRule.clickAndWait(R.id.button_primary, mRule.getDismissed());
+        mRule.expectResultContains(new String[] {"\"total\":\"1.00\""});
+    }
+}
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestShowPromiseUnsupportedTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestShowPromiseUnsupportedTest.java
new file mode 100644
index 0000000..4e34e1c
--- /dev/null
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestShowPromiseUnsupportedTest.java
@@ -0,0 +1,50 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.payments;
+
+import android.support.test.filters.MediumTest;
+
+import org.junit.ClassRule;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import org.chromium.base.test.util.CommandLineFlags;
+import org.chromium.base.test.util.Feature;
+import org.chromium.chrome.browser.ChromeSwitches;
+import org.chromium.chrome.browser.payments.PaymentRequestTestRule.MainActivityStartCallback;
+import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
+import org.chromium.chrome.test.ui.DisableAnimationsTestRule;
+
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeoutException;
+
+/**
+ * A payment integration test for the show promise with an unsupported payment method.
+ */
+@RunWith(ChromeJUnit4ClassRunner.class)
+@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
+public class PaymentRequestShowPromiseUnsupportedTest implements MainActivityStartCallback {
+    // Disable animations to reduce flakiness.
+    @ClassRule
+    public static DisableAnimationsTestRule sNoAnimationsRule = new DisableAnimationsTestRule();
+
+    @Rule
+    public PaymentRequestTestRule mRule =
+            new PaymentRequestTestRule("show_promise/unsupported.html", this);
+
+    @Override
+    public void onMainActivityStarted()
+            throws InterruptedException, ExecutionException, TimeoutException {}
+
+    @Test
+    @MediumTest
+    @Feature({"Payments"})
+    public void testReject() throws InterruptedException, ExecutionException, TimeoutException {
+        mRule.openPageAndClickNodeAndWait("buy", mRule.getDismissed());
+        mRule.expectResultContains(
+                new String[] {"NotSupportedError: The payment method \"foo\" is not supported"});
+    }
+}
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestTestRule.java b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestTestRule.java
index 42ca009..c6329db 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestTestRule.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestTestRule.java
@@ -12,6 +12,7 @@
 import android.widget.CheckBox;
 import android.widget.EditText;
 import android.widget.Spinner;
+import android.widget.TextView;
 
 import org.junit.Assert;
 import org.junit.runner.Description;
@@ -117,6 +118,7 @@
     final CallbackHelper mHasEnrolledInstrumentQueryResponded;
     final CallbackHelper mExpirationMonthChange;
     final CallbackHelper mPaymentResponseReady;
+    PaymentRequestImpl mPaymentRequest;
     PaymentRequestUI mUI;
 
     private final AtomicReference<WebContents> mWebContentsRef;
@@ -163,7 +165,7 @@
         startMainActivityWithURL(mTestFilePath);
     }
 
-    private void openPage() throws InterruptedException, ExecutionException, TimeoutException {
+    protected void openPage() throws InterruptedException, ExecutionException, TimeoutException {
         onMainActivityStarted();
         ThreadUtils.runOnUiThreadBlocking(() -> {
             mWebContentsRef.set(getActivity().getCurrentWebContents());
@@ -277,6 +279,11 @@
         helper.waitForCallback(callCount);
     }
 
+    protected String executeJavaScriptAndWaitForResult(String script)
+            throws InterruptedException, TimeoutException {
+        return JavaScriptUtils.executeJavaScriptAndWaitForResult(mWebContentsRef.get(), script);
+    }
+
     /** Clicks on an HTML node. */
     protected void clickNodeAndWait(String nodeId, CallbackHelper helper)
             throws InterruptedException, TimeoutException {
@@ -494,6 +501,24 @@
                 .toString());
     }
 
+    protected String getShippingAddressWarningLabel() throws ExecutionException {
+        return ThreadUtils.runOnUiThreadBlocking(() -> {
+            View view = mUI.getShippingAddressSectionForTest().findViewById(
+                    R.id.payments_warning_label);
+            return view != null && view instanceof TextView ? ((TextView) view).getText().toString()
+                                                            : null;
+        });
+    }
+
+    protected String getShippingAddressDescriptionLabel() throws ExecutionException {
+        return ThreadUtils.runOnUiThreadBlocking(() -> {
+            View view = mUI.getShippingAddressSectionForTest().findViewById(
+                    R.id.payments_description_label);
+            return view != null && view instanceof TextView ? ((TextView) view).getText().toString()
+                                                            : null;
+        });
+    }
+
     /** Returns the focused view in the card editor view. */
     protected View getCardEditorFocusedView() {
         return mUI.getCardEditorDialog().getCurrentFocus();
@@ -767,8 +792,8 @@
                     }
                     for (int i = 0; i < contents.length; i++) {
                         if (!result.contains(contents[i])) {
-                            updateFailureReason(
-                                    String.format("Result should contain '%s'", contents[i]));
+                            updateFailureReason(String.format(
+                                    "Result '" + result + "' should contain '%s'", contents[i]));
                             return false;
                         }
                     }
@@ -864,6 +889,12 @@
                                    .findViewById(R.id.autofill_card_unmask_prompt));
     }
 
+    /** Allows to skip UI into paymenthandler for"basic-card". */
+    protected void enableSkipUIForBasicCard() {
+        ThreadUtils.runOnUiThreadBlocking(
+                () -> mPaymentRequest.setSkipUIForNonURLPaymentMethodIdentifiersForTest());
+    }
+
     @Override
     public void onPaymentRequestReadyForInput(PaymentRequestUI ui) {
         ThreadUtils.assertOnUiThread();
@@ -913,6 +944,12 @@
     }
 
     @Override
+    public void onPaymentRequestCreated(PaymentRequestImpl paymentRequest) {
+        ThreadUtils.assertOnUiThread();
+        mPaymentRequest = paymentRequest;
+    }
+
+    @Override
     public void onPaymentRequestServiceUnableToAbort() {
         ThreadUtils.assertOnUiThread();
         mUnableToAbort.notifyCalled();
@@ -1137,7 +1174,8 @@
                 Map<String, PaymentMethodData> methodData, PaymentItem total,
                 List<PaymentItem> displayItems, Map<String, PaymentDetailsModifier> modifiers,
                 InstrumentDetailsCallback detailsCallback) {
-            detailsCallback.onInstrumentDetailsReady(mDefaultMethodName, "{\"transaction\": 1337}");
+            detailsCallback.onInstrumentDetailsReady(mDefaultMethodName,
+                    "{\"transaction\": 1337, \"total\": \"" + total.amount.value + "\"}");
         }
 
         @Override
diff --git a/chrome/app/vr_strings.grdp b/chrome/app/vr_strings.grdp
index 68aa53a..1c4c5704 100644
--- a/chrome/app/vr_strings.grdp
+++ b/chrome/app/vr_strings.grdp
@@ -26,6 +26,12 @@
   <message name="IDS_VR_SHELL_SITE_IS_SHARING_SCREEN" desc="Text displayed in a transient bubble to inform the user that the page is sharing the screen.">
     Site is sharing your screen
   </message>
+  <message name="IDS_VR_SHELL_SITE_IS_USING_USB" desc="Text displayed in a transient bubble to inform the user that the page is using a usb device.">
+    Site is using usb
+  </message>
+  <message name="IDS_VR_SHELL_SITE_IS_USING_MIDI" desc="Text displayed in a transient bubble to inform the user that the page is using a midi device.">
+    Site is using midi
+  </message>
   <message name="IDS_VR_SHELL_BG_IS_USING_MICROPHONE" desc="Text displayed in a transient bubble to inform the user that a background tab is using the microphone.">
     Background tab is using your microphone
   </message>
@@ -53,6 +59,9 @@
   <message name="IDS_VR_SHELL_SITE_CAN_SHARE_SCREEN" desc="Text displayed in a transient bubble to inform the user that the page may share your screen, but isn't doing so yet.">
     Site can share your screen
   </message>
+  <message name="IDS_VR_SHELL_SITE_CAN_USE_MIDI" desc="Text displayed in a transient bubble to inform the user that the page may use midi, but isn't doing so yet.">
+    Site can use midi
+  </message>
 
   <!-- Desktop-specific permission prompts -->
   <if expr="not is_android">
diff --git a/chrome/app/vr_strings_grdp/IDS_VR_SHELL_SITE_CAN_USE_MIDI.png.sha1 b/chrome/app/vr_strings_grdp/IDS_VR_SHELL_SITE_CAN_USE_MIDI.png.sha1
new file mode 100644
index 0000000..ef811f7
--- /dev/null
+++ b/chrome/app/vr_strings_grdp/IDS_VR_SHELL_SITE_CAN_USE_MIDI.png.sha1
@@ -0,0 +1 @@
+08cc406f5af3268f88635fac97a8211cc2acad7b
\ No newline at end of file
diff --git a/chrome/app/vr_strings_grdp/IDS_VR_SHELL_SITE_IS_USING_MIDI.png.sha1 b/chrome/app/vr_strings_grdp/IDS_VR_SHELL_SITE_IS_USING_MIDI.png.sha1
new file mode 100644
index 0000000..b856cb34
--- /dev/null
+++ b/chrome/app/vr_strings_grdp/IDS_VR_SHELL_SITE_IS_USING_MIDI.png.sha1
@@ -0,0 +1 @@
+2f29233562df49efae82df0b7850f8fccfdf0db5
\ No newline at end of file
diff --git a/chrome/app/vr_strings_grdp/IDS_VR_SHELL_SITE_IS_USING_USB.png.sha1 b/chrome/app/vr_strings_grdp/IDS_VR_SHELL_SITE_IS_USING_USB.png.sha1
new file mode 100644
index 0000000..0530f2b0
--- /dev/null
+++ b/chrome/app/vr_strings_grdp/IDS_VR_SHELL_SITE_IS_USING_USB.png.sha1
@@ -0,0 +1 @@
+0670fbaa8bd7f1d36f9dce06ea233f40c25d4486
\ No newline at end of file
diff --git a/chrome/browser/apps/app_service/app_icon_factory.cc b/chrome/browser/apps/app_service/app_icon_factory.cc
index 4bc5834..d86de71 100644
--- a/chrome/browser/apps/app_service/app_icon_factory.cc
+++ b/chrome/browser/apps/app_service/app_icon_factory.cc
@@ -17,8 +17,11 @@
 #include "chrome/browser/extensions/chrome_app_icon_loader.h"
 #include "chrome/browser/extensions/extension_service.h"
 #include "content/public/common/service_manager_connection.h"
+#include "extensions/browser/component_extension_resource_manager.h"
 #include "extensions/browser/extension_system.h"
+#include "extensions/browser/extensions_browser_client.h"
 #include "extensions/browser/image_loader.h"
+#include "extensions/common/manifest.h"
 #include "extensions/common/manifest_handlers/icons_handler.h"
 #include "services/data_decoder/public/cpp/decode_image.h"
 #include "ui/base/resource/resource_bundle.h"
@@ -219,13 +222,42 @@
       }
 
       case apps::mojom::IconCompression::kCompressed: {
-        base::PostTaskWithTraitsAndReplyWithResult(
-            FROM_HERE, {base::MayBlock(), base::TaskPriority::USER_VISIBLE},
-            base::BindOnce(&CompressedDataFromResource, ext_resource),
-            base::BindOnce(&RunCallbackWithCompressedData, size_hint_in_dip,
-                           is_placeholder_icon, icon_effects,
-                           std::move(callback)));
-        return;
+        // Load component extensions' icons from statically compiled resources
+        // (built into the Chrome binary), and other extensions' icons from
+        // files on disk.
+        //
+        // For the kUncompressed case above, RunCallbackWithUncompressedImage
+        // calls extensions::ImageLoader::LoadImageAsync, which already handles
+        // that distinction. We can't use LoadImageAsync here, because the
+        // caller has asked for compressed icons (i.e. PNG-formatted data), not
+        // uncompressed (i.e. a gfx::ImageSkia).
+        if (extension->location() == extensions::Manifest::COMPONENT) {
+          extensions::ComponentExtensionResourceInfo resource_info;
+          const extensions::ComponentExtensionResourceManager* manager =
+              extensions::ExtensionsBrowserClient::Get()
+                  ->GetComponentExtensionResourceManager();
+          if (manager && manager->IsComponentExtensionResource(
+                             extension->path(), ext_resource.relative_path(),
+                             &resource_info)) {
+            base::StringPiece data =
+                ui::ResourceBundle::GetSharedInstance().GetRawDataResource(
+                    resource_info.resource_id);
+            RunCallbackWithCompressedData(
+                size_hint_in_dip, is_placeholder_icon, icon_effects,
+                std::move(callback),
+                std::vector<uint8_t>(data.begin(), data.end()));
+            return;
+          }
+        } else if (!ext_resource.GetFilePath().empty()) {
+          base::PostTaskWithTraitsAndReplyWithResult(
+              FROM_HERE, {base::MayBlock(), base::TaskPriority::USER_VISIBLE},
+              base::BindOnce(&CompressedDataFromResource,
+                             std::move(ext_resource)),
+              base::BindOnce(&RunCallbackWithCompressedData, size_hint_in_dip,
+                             is_placeholder_icon, icon_effects,
+                             std::move(callback)));
+          return;
+        }
       }
     }
   }
diff --git a/chrome/browser/apps/app_service/extension_apps.cc b/chrome/browser/apps/app_service/extension_apps.cc
index 07f77d8..2d84d7c 100644
--- a/chrome/browser/apps/app_service/extension_apps.cc
+++ b/chrome/browser/apps/app_service/extension_apps.cc
@@ -120,6 +120,8 @@
                   apps::mojom::Readiness::kTerminated, &apps);
     // blacklisted_extensions and blocked_extensions, corresponding to
     // kDisabledByBlacklist and kDisabledByPolicy, are deliberately ignored.
+    //
+    // If making changes to which sets are consulted, also change ShouldShow.
   }
   subscriber->OnApps(std::move(apps));
   subscribers_.AddPtr(std::move(subscriber));
@@ -329,6 +331,7 @@
   app->app_id = extension->id();
   app->readiness = apps::mojom::Readiness::kUninstalledByUser;
 
+  SetShowInFields(app, extension, profile_);
   Publish(std::move(app));
 }
 
@@ -363,6 +366,42 @@
 }
 
 // static
+void ExtensionApps::SetShowInFields(apps::mojom::AppPtr& app,
+                                    const extensions::Extension* extension,
+                                    Profile* profile) {
+  if (ShouldShow(extension, profile)) {
+    auto show = app_list::ShouldShowInLauncher(extension, profile)
+                    ? apps::mojom::OptionalBool::kTrue
+                    : apps::mojom::OptionalBool::kFalse;
+    app->show_in_launcher = show;
+    app->show_in_search = show;
+    app->show_in_management = ShouldShowInAppManagement(extension);
+  } else {
+    app->show_in_launcher = apps::mojom::OptionalBool::kFalse;
+    app->show_in_search = apps::mojom::OptionalBool::kFalse;
+    app->show_in_management = apps::mojom::OptionalBool::kFalse;
+  }
+}
+
+// static
+bool ExtensionApps::ShouldShow(const extensions::Extension* extension,
+                               Profile* profile) {
+  if (!profile) {
+    return false;
+  }
+
+  extensions::ExtensionRegistry* registry =
+      extensions::ExtensionRegistry::Get(profile);
+  const std::string& app_id = extension->id();
+  // These three extension sets are the same three consulted by Connect.
+  // Importantly, it will exclude previously installed but currently
+  // uninstalled extensions.
+  return registry->enabled_extensions().Contains(app_id) ||
+         registry->disabled_extensions().Contains(app_id) ||
+         registry->terminated_extensions().Contains(app_id);
+}
+
+// static
 apps::mojom::OptionalBool ExtensionApps::ShouldShowInAppManagement(
     const extensions::Extension* extension) {
   // Component extensions should not show up in App Management as they
@@ -473,13 +512,7 @@
                              ? apps::mojom::OptionalBool::kTrue
                              : apps::mojom::OptionalBool::kFalse;
 
-  auto show = app_list::ShouldShowInLauncher(extension, profile_)
-                  ? apps::mojom::OptionalBool::kTrue
-                  : apps::mojom::OptionalBool::kFalse;
-  app->show_in_launcher = show;
-  app->show_in_search = show;
-  app->show_in_management = ShouldShowInAppManagement(extension);
-
+  SetShowInFields(app, extension, profile_);
   return app;
 }
 
diff --git a/chrome/browser/apps/app_service/extension_apps.h b/chrome/browser/apps/app_service/extension_apps.h
index 76ef9819..4f0e98ea 100644
--- a/chrome/browser/apps/app_service/extension_apps.h
+++ b/chrome/browser/apps/app_service/extension_apps.h
@@ -88,8 +88,15 @@
   bool RunExtensionEnableFlow(const std::string& app_id);
 
   static bool IsBlacklisted(const std::string& app_id);
+
+  static void SetShowInFields(apps::mojom::AppPtr& app,
+                              const extensions::Extension* extension,
+                              Profile* profile);
+  static bool ShouldShow(const extensions::Extension* extension,
+                         Profile* profile);
   static apps::mojom::OptionalBool ShouldShowInAppManagement(
       const extensions::Extension* extension);
+
   void PopulatePermissions(const extensions::Extension* extension,
                            std::vector<mojom::PermissionPtr>* target);
   apps::mojom::AppPtr Convert(const extensions::Extension* extension,
diff --git a/chrome/browser/chromeos/printing/printer_configurer.cc b/chrome/browser/chromeos/printing/printer_configurer.cc
index afc94072..f105392d 100644
--- a/chrome/browser/chromeos/printing/printer_configurer.cc
+++ b/chrome/browser/chromeos/printing/printer_configurer.cc
@@ -23,7 +23,6 @@
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/chromeos/printing/ppd_provider_factory.h"
 #include "chrome/browser/component_updater/cros_component_installer_chromeos.h"
-#include "chrome/browser/local_discovery/endpoint_resolver.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/common/chrome_features.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
@@ -32,8 +31,6 @@
 #include "chromeos/printing/printer_configuration.h"
 #include "components/device_event_log/device_event_log.h"
 #include "content/public/browser/browser_thread.h"
-#include "net/base/host_port_pair.h"
-#include "net/base/ip_endpoint.h"
 #include "third_party/cros_system_api/dbus/debugd/dbus-constants.h"
 
 const std::map<const std::string, const std::string>&
@@ -103,9 +100,7 @@
 class PrinterConfigurerImpl : public PrinterConfigurer {
  public:
   explicit PrinterConfigurerImpl(Profile* profile)
-      : endpoint_resolver_(new local_discovery::EndpointResolver()),
-        ppd_provider_(CreatePpdProvider(profile)),
-        weak_factory_(this) {}
+      : ppd_provider_(CreatePpdProvider(profile)), weak_factory_(this) {}
 
   PrinterConfigurerImpl(const PrinterConfigurerImpl&) = delete;
   PrinterConfigurerImpl& operator=(const PrinterConfigurerImpl&) = delete;
@@ -118,36 +113,6 @@
     DCHECK(!printer.id().empty());
     DCHECK(!printer.uri().empty());
     PRINTER_LOG(USER) << printer.make_and_model() << " Printer setup requested";
-
-    // Ensure that |address| is non-empty before attempting to resolve it.
-    // If the uri in |printer| does not contain both a hostname and a port
-    // number then GetHostAndPort() will return an empty string.
-    auto address = printer.GetHostAndPort();
-    if (address.IsEmpty()) {
-      // Return an error and abort printer setup. If we attempt to call
-      // EndpointResolver::Start() with an empty address then it will fail
-      // silently without returning into the callback.
-      PRINTER_LOG(ERROR) << "Address is invalid";
-      std::move(callback).Run(PrinterSetupResult::kPrinterUnreachable);
-      return;
-    }
-
-    PRINTER_LOG(DEBUG) << printer.make_and_model()
-                       << " Resolving IP: " << address.ToString();
-
-    // Resolve the uri to an ip with a mutable copy of the printer.
-    endpoint_resolver_->Start(
-        address, base::BindOnce(&PrinterConfigurerImpl::OnIpResolved,
-                                weak_factory_.GetWeakPtr(),
-                                std::make_unique<Printer>(printer),
-                                std::move(callback)));
-  }
-
- private:
-  // Run installation for a printer with a resolved uri.  |callback| is called
-  // with the result of the setup when it is complete.
-  void StartConfiguration(const Printer& printer,
-                          PrinterSetupCallback callback) {
     if (!printer.IsIppEverywhere()) {
       PRINTER_LOG(DEBUG) << printer.make_and_model() << " Lookup PPD";
       ppd_provider_->ResolvePpd(
@@ -168,29 +133,7 @@
                        std::move(callback)));
   }
 
-  // Callback for when the IP for a zeroconf printer has been resolved.  If the
-  // request was successful, sets the |effective_uri| on |printer| with
-  // |endpoint| then continues setup. |cb| is called with a result reporting the
-  // success or failure of the setup operation, eventually.
-  void OnIpResolved(std::unique_ptr<Printer> printer,
-                    PrinterSetupCallback cb,
-                    const net::IPEndPoint& endpoint) {
-    bool address_resolved = endpoint.address().IsValid();
-    UMA_HISTOGRAM_BOOLEAN("Printing.CUPS.AddressResolutionResult",
-                          address_resolved);
-    if (!address_resolved) {
-      PRINTER_LOG(ERROR) << printer->make_and_model()
-                         << " IP Resolution failed";
-      // |endpoint| does not have a valid address. Address was not resolved.
-      std::move(cb).Run(kPrinterUnreachable);
-      return;
-    }
-
-    PRINTER_LOG(EVENT) << printer->make_and_model()
-                       << " IP Resolution succeeded";
-    StartConfiguration(*printer, std::move(cb));
-  }
-
+ private:
   // Receive the callback from the debug daemon client once we attempt to
   // add the printer.
   void OnAddedPrinter(const Printer& printer,
@@ -296,7 +239,6 @@
     }
   }
 
-  std::unique_ptr<local_discovery::EndpointResolver> endpoint_resolver_;
   scoped_refptr<PpdProvider> ppd_provider_;
   base::WeakPtrFactory<PrinterConfigurerImpl> weak_factory_;
 };
diff --git a/chrome/browser/extensions/api/cast_streaming/performance_test.cc b/chrome/browser/extensions/api/cast_streaming/performance_test.cc
index 8d3d46c..03a5a0e5 100644
--- a/chrome/browser/extensions/api/cast_streaming/performance_test.cc
+++ b/chrome/browser/extensions/api/cast_streaming/performance_test.cc
@@ -67,13 +67,13 @@
 // long enough to collect sufficient tracing data; and, unfortunately, there's
 // nothing we can do about that.
 #define EXPECT_FOR_PERFORMANCE_RUN(expr)             \
-  do {                                               \
+  if (!(expr)) {                                     \
     if (is_full_performance_run()) {                 \
-      EXPECT_TRUE(expr);                             \
-    } else if (!(expr)) {                            \
+      LOG(ERROR) << "Failure: " << #expr;            \
+    } else {                                         \
       LOG(WARNING) << "Allowing failure: " << #expr; \
     }                                                \
-  } while (false)
+  }
 
 enum TestFlags {
   kSmallWindow = 1 << 2,      // Window size: 1 = 800x600, 0 = 2000x1000
@@ -168,7 +168,7 @@
              const std::string& modifier,
              const std::string& trace,
              const std::string& unit) {
-    if (num_values_ >= 20) {
+    if (num_values_ > 0) {
       perf_test::PrintResultMeanAndError(measurement,
                                          modifier,
                                          trace,
@@ -176,8 +176,7 @@
                                          unit,
                                          true);
     } else {
-      LOG(ERROR) << "Not enough events (" << num_values_ << ") for "
-                 << measurement << modifier << " " << trace;
+      LOG(ERROR) << "No events for " << measurement << modifier << " " << trace;
     }
   }
 
diff --git a/chrome/browser/extensions/api/tab_capture/tab_capture_performance_test_base.cc b/chrome/browser/extensions/api/tab_capture/tab_capture_performance_test_base.cc
index 7b455d2..fe7bf28a2 100644
--- a/chrome/browser/extensions/api/tab_capture/tab_capture_performance_test_base.cc
+++ b/chrome/browser/extensions/api/tab_capture/tab_capture_performance_test_base.cc
@@ -22,6 +22,7 @@
 #include "chrome/common/chrome_paths.h"
 #include "chrome/test/base/tracing.h"
 #include "chrome/test/base/ui_test_utils.h"
+#include "content/public/common/content_features.h"
 #include "content/public/test/browser_test_utils.h"
 #include "extensions/browser/extension_registry.h"
 #include "extensions/browser/extension_system.h"
@@ -32,6 +33,7 @@
 #include "net/test/embedded_test_server/embedded_test_server.h"
 #include "net/test/embedded_test_server/http_request.h"
 #include "net/test/embedded_test_server/http_response.h"
+#include "services/service_manager/sandbox/features.h"
 #include "third_party/zlib/google/compression_utils.h"
 #include "ui/gl/gl_switches.h"
 
@@ -47,6 +49,15 @@
   // Because screen capture is involved, require pixel output.
   EnablePixelOutput();
 
+  feature_list_.InitWithFeatures(
+      {
+          service_manager::features::kAudioServiceSandbox,
+          features::kAudioServiceAudioStreams,
+          features::kAudioServiceLaunchOnStartup,
+          features::kAudioServiceOutOfProcess,
+      },
+      {});
+
   InProcessBrowserTest::SetUp();
 }
 
diff --git a/chrome/browser/extensions/api/tab_capture/tab_capture_performance_test_base.h b/chrome/browser/extensions/api/tab_capture/tab_capture_performance_test_base.h
index 51d0fd4..8a1c7146 100644
--- a/chrome/browser/extensions/api/tab_capture/tab_capture_performance_test_base.h
+++ b/chrome/browser/extensions/api/tab_capture/tab_capture_performance_test_base.h
@@ -11,6 +11,7 @@
 
 #include "base/macros.h"
 #include "base/strings/string_piece.h"
+#include "base/test/scoped_feature_list.h"
 #include "base/test/trace_event_analyzer.h"
 #include "chrome/test/base/in_process_browser_test.h"
 
@@ -135,6 +136,9 @@
 
   const extensions::Extension* extension_ = nullptr;
 
+  // Manages the Audio Service feature set, enabled for these performance tests.
+  base::test::ScopedFeatureList feature_list_;
+
   DISALLOW_COPY_AND_ASSIGN(TabCapturePerformanceTestBase);
 };
 
diff --git a/chrome/browser/extensions/extension_service.cc b/chrome/browser/extensions/extension_service.cc
index 3b26e20..caeefb2 100644
--- a/chrome/browser/extensions/extension_service.cc
+++ b/chrome/browser/extensions/extension_service.cc
@@ -1208,6 +1208,8 @@
 }
 
 void ExtensionService::AddComponentExtension(const Extension* extension) {
+  extension_prefs_->ClearInapplicableDisableReasonsForComponentExtension(
+      extension->id());
   const std::string old_version_string(
       extension_prefs_->GetVersionString(extension->id()));
   const base::Version old_version(old_version_string);
diff --git a/chrome/browser/notifications/proto/BUILD.gn b/chrome/browser/notifications/proto/BUILD.gn
index 647f392..8e80255 100644
--- a/chrome/browser/notifications/proto/BUILD.gn
+++ b/chrome/browser/notifications/proto/BUILD.gn
@@ -6,7 +6,9 @@
 
 proto_library("proto") {
   sources = [
+    "client_state.proto",
     "icon.proto",
+    "impression.proto",
     "notification_data.proto",
   ]
 }
diff --git a/chrome/browser/notifications/proto/client_state.proto b/chrome/browser/notifications/proto/client_state.proto
new file mode 100644
index 0000000..666a267
--- /dev/null
+++ b/chrome/browser/notifications/proto/client_state.proto
@@ -0,0 +1,52 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+syntax = "proto2";
+
+option optimize_for = LITE_RUNTIME;
+
+package notifications.proto;
+
+import "impression.proto";
+
+// Contains details about supression and recovery after suppression expired.
+// Should match SuppressionInfo in impression_types.h.
+// Next tag: 4
+message SuppressionInfo {
+  // The suppression trigger time stamp in milliseconds since epoch.
+  optional int64 last_trigger_time = 1;
+
+  // The duration for the suppression in milliseconds.
+  optional int64 duration_ms = 2;
+
+  // The maximum number of notification to show after the suppression expired.
+  optional int32 recover_goal = 3;
+}
+
+// Stores the global states about how often the notification can be shown
+// to the user and the history of user interactions to a particular notification
+// client. Should match ClientState in impression_types.h.
+// Next tag: 5
+message ClientState {
+  // The type of clients using the notification scheduler system.
+  enum SchedulerClientType {
+    TEST_1 = -1;
+    TEST_2 = -2;
+    TEST_3 = -3;
+    UNKNOWN = 0;
+  }
+
+  // The type of notification using the scheduler.
+  optional SchedulerClientType type = 1;
+
+  // The maximum number of notifications shown to the user for this type. May
+  // change if the user interacts with the notification.
+  optional int32 current_max_daily_show = 2;
+
+  // A list of user impression history. Sorted by creation time.
+  repeated Impression impressions = 3;
+
+  // Suppression details, no value if there is currently no suppression.
+  optional SuppressionInfo suppression_info = 4;
+}
diff --git a/chrome/browser/notifications/proto/impression.proto b/chrome/browser/notifications/proto/impression.proto
new file mode 100644
index 0000000..ad61641
--- /dev/null
+++ b/chrome/browser/notifications/proto/impression.proto
@@ -0,0 +1,59 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+syntax = "proto2";
+
+option optimize_for = LITE_RUNTIME;
+
+package notifications.proto;
+
+// Contains data to determine when a notification should be shown to the user
+// and the user impression towards this notification. Should match Impression in
+// impression_types.h.
+// Next tag: 6
+message Impression {
+  // The type of user feedback from a displayed notification. Should match
+  // UserFeedback in notification_scheduler_types.h.
+  enum UserFeedback {
+    NO_FEEDBACK = 0;
+    HELPFUL = 1;
+    NOT_HELPFUL = 2;
+    CLICK = 3;
+    DISMISS = 4;
+    IGNORE = 5;
+  }
+
+  // The user impression of a particular notification. Should match
+  // ImpressionResult in notification_scheduler_types.h.
+  enum ImpressionResult {
+    INVALID = 0;
+    POSITIVE = 1;
+    NEGATIVE = 2;
+    NEUTRAL = 3;
+  }
+
+  // Enum to describe the time to process scheduled notification data. Should
+  // match SchedulerTaskTime in internal_types.h.
+  enum SchedulerTaskTime {
+    UNKNOWN_TASK_TIME = 0;
+    MORNING = 1;
+    EVENING = 2;
+  }
+
+  // Creation time stamp in milliseconds since epoch.
+  optional int64 create_time = 1;
+
+  // The user feedback on the notification.
+  optional UserFeedback feedback = 2;
+
+  // The impression type.
+  optional ImpressionResult impression = 3;
+
+  // If the user feedback is used in computing the current notification deliver
+  // rate.
+  optional bool integrated = 4;
+
+  // The task start time when the notification is shown.
+  optional SchedulerTaskTime task_start_time = 5;
+}
diff --git a/chrome/browser/notifications/scheduler/icon_store.cc b/chrome/browser/notifications/scheduler/icon_store.cc
index 60245c0..a228d38 100644
--- a/chrome/browser/notifications/scheduler/icon_store.cc
+++ b/chrome/browser/notifications/scheduler/icon_store.cc
@@ -18,7 +18,7 @@
 
 void ProtoToData(notifications::proto::Icon* proto,
                  notifications::IconEntry* icon_entry) {
-  IconProtoToEntry(proto, icon_entry);
+  IconEntryFromProto(proto, icon_entry);
 }
 
 }  // namespace leveldb_proto
diff --git a/chrome/browser/notifications/scheduler/icon_store.h b/chrome/browser/notifications/scheduler/icon_store.h
index a05645b..2fece4e 100644
--- a/chrome/browser/notifications/scheduler/icon_store.h
+++ b/chrome/browser/notifications/scheduler/icon_store.h
@@ -11,6 +11,7 @@
 #include "base/bind.h"
 #include "base/callback.h"
 #include "base/macros.h"
+#include "base/memory/weak_ptr.h"
 #include "chrome/browser/notifications/proto/icon.pb.h"
 #include "chrome/browser/notifications/scheduler/icon_entry.h"
 #include "components/leveldb_proto/public/proto_database.h"
diff --git a/chrome/browser/notifications/scheduler/impression_history_tracker.cc b/chrome/browser/notifications/scheduler/impression_history_tracker.cc
index c482f21..286aa47 100644
--- a/chrome/browser/notifications/scheduler/impression_history_tracker.cc
+++ b/chrome/browser/notifications/scheduler/impression_history_tracker.cc
@@ -70,7 +70,7 @@
         break;
       case UserFeedback::kIgnore:
         break;
-      case UserFeedback::kUnknown:
+      case UserFeedback::kNoFeedback:
         FALLTHROUGH;
       default:
         // The user didn't interact with the notification yet.
diff --git a/chrome/browser/notifications/scheduler/impression_history_tracker_unittest.cc b/chrome/browser/notifications/scheduler/impression_history_tracker_unittest.cc
index a199a4a0..fbf7af99 100644
--- a/chrome/browser/notifications/scheduler/impression_history_tracker_unittest.cc
+++ b/chrome/browser/notifications/scheduler/impression_history_tracker_unittest.cc
@@ -91,10 +91,10 @@
                              config().impression_expiration;
   auto not_expired_time = base::Time::Now() + base::TimeDelta::FromDays(1) -
                           config().impression_expiration;
-  Impression expired{expired_create_time, UserFeedback::kUnknown,
-                     ImpressionResult::kUnknown, false /* integrated */};
-  Impression not_expired{not_expired_time, UserFeedback::kUnknown,
-                         ImpressionResult::kUnknown, false /* integrated */};
+  Impression expired{expired_create_time, UserFeedback::kNoFeedback,
+                     ImpressionResult::kInvalid, false /* integrated */};
+  Impression not_expired{not_expired_time, UserFeedback::kNoFeedback,
+                         ImpressionResult::kInvalid, false /* integrated */};
 
   test_case.input = {{SchedulerClientType::kTest1,
                       2 /* current_max_daily_show */,
@@ -120,7 +120,7 @@
   test_case.input = {{SchedulerClientType::kTest1,
                       2 /* current_max_daily_show */,
                       {{create_time, UserFeedback::kHelpful,
-                        ImpressionResult::kUnknown, false /* integrated */}},
+                        ImpressionResult::kInvalid, false /* integrated */}},
                       base::nullopt /* suppression_info */}};
 
   // Positive impression should bump |the current_max_daily_show| and update
diff --git a/chrome/browser/notifications/scheduler/impression_types.cc b/chrome/browser/notifications/scheduler/impression_types.cc
index efdf022..5f9b019c 100644
--- a/chrome/browser/notifications/scheduler/impression_types.cc
+++ b/chrome/browser/notifications/scheduler/impression_types.cc
@@ -27,8 +27,10 @@
          duration == other.duration && recover_goal == other.recover_goal;
 }
 
-ClientState::ClientState(SchedulerClientType type)
-    : type(type), current_max_daily_show(0) {}
+ClientState::ClientState()
+    : type(SchedulerClientType::kUnknown), current_max_daily_show(0) {}
+
+ClientState::ClientState(const ClientState& other) = default;
 
 ClientState::~ClientState() = default;
 
@@ -52,7 +54,9 @@
            << "feedback: " << static_cast<int>(impression.feedback) << " \n"
            << "impression result: " << static_cast<int>(impression.impression)
            << " \n"
-           << "integrated: " << impression.integrated << " \n";
+           << "integrated: " << impression.integrated << " \n"
+           << "task start time: "
+           << static_cast<int>(impression.task_start_time) << "\n";
     log += stream.str();
   }
 
diff --git a/chrome/browser/notifications/scheduler/impression_types.h b/chrome/browser/notifications/scheduler/impression_types.h
index 53f2c31d..cd24af9 100644
--- a/chrome/browser/notifications/scheduler/impression_types.h
+++ b/chrome/browser/notifications/scheduler/impression_types.h
@@ -32,12 +32,12 @@
 
   // The user feedback on the notification, each notification will have at most
   // one feedback. Sets after the user interacts with the notification.
-  UserFeedback feedback = UserFeedback::kUnknown;
+  UserFeedback feedback = UserFeedback::kNoFeedback;
 
   // The impression type. The client of a notification type takes one or several
   // user feedbacks as input and generate a user impression, which will
   // eventually affect the rate to deliver notifications to the user.
-  ImpressionResult impression = ImpressionResult::kUnknown;
+  ImpressionResult impression = ImpressionResult::kInvalid;
 
   // If the user feedback is used in computing the current notification deliver
   // rate.
@@ -71,7 +71,7 @@
 // client.
 struct ClientState {
   using Impressions = std::deque<Impression>;
-  explicit ClientState(SchedulerClientType type);
+  ClientState();
   explicit ClientState(const ClientState& other);
   ~ClientState();
 
@@ -81,7 +81,7 @@
   std::string DebugPrint() const;
 
   // The type of notification using the scheduler.
-  const SchedulerClientType type;
+  SchedulerClientType type;
 
   // The maximum number of notifications shown to the user for this type. May
   // change if the user interacts with the notification.
diff --git a/chrome/browser/notifications/scheduler/notification_scheduler_types.h b/chrome/browser/notifications/scheduler/notification_scheduler_types.h
index ecd65840..12b7546 100644
--- a/chrome/browser/notifications/scheduler/notification_scheduler_types.h
+++ b/chrome/browser/notifications/scheduler/notification_scheduler_types.h
@@ -21,8 +21,8 @@
 
 // The type of user feedback from a displayed notification.
 enum class UserFeedback {
-  // Unknown feedback from the user.
-  kUnknown = 0,
+  // No user feedback yet.
+  kNoFeedback = 0,
   // The user taps the helpful button, potentially a strong indicator of user's
   // positive preference on the notification.
   kHelpful = 1,
@@ -40,15 +40,15 @@
 
 // The user impression of a particular notification.
 enum class ImpressionResult {
-  // Unknown user impression.
-  kUnknown = 0,
+  // Invalid user impression.
+  kInvalid = 0,
   // Positive user impression that the user may like the notification.
   kPositive = 1,
   // Positive user impression that the user may dislike the notification.
   kNegative = 2,
-  // The feedback is netural to the user.
-  kNetural = 3,
-  kMaxValue = kNetural
+  // The feedback is neutral to the user.
+  kNeutral = 3,
+  kMaxValue = kNeutral
 };
 
 }  // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/proto_conversion.cc b/chrome/browser/notifications/scheduler/proto_conversion.cc
index c42457cd..4e7c1141 100644
--- a/chrome/browser/notifications/scheduler/proto_conversion.cc
+++ b/chrome/browser/notifications/scheduler/proto_conversion.cc
@@ -11,16 +11,233 @@
 
 namespace notifications {
 
+namespace {
+
+// Helper method to convert base::TimeDelta to integer for serialization. Loses
+// precision beyond miliseconds.
+int64_t TimeDeltaToMilliseconds(const base::TimeDelta& delta) {
+  return delta.InMilliseconds();
+}
+
+// Helper method to convert serialized time delta as integer to base::TimeDelta
+// for deserialization. Loses precision beyond miliseconds.
+base::TimeDelta MillisecondsToTimeDelta(int64_t serialized_delat_ms) {
+  return base::TimeDelta::FromMilliseconds(serialized_delat_ms);
+}
+
+// Helper method to convert base::Time to integer for serialization. Loses
+// precision beyond miliseconds.
+int64_t TimeToMilliseconds(const base::Time& time) {
+  return time.ToDeltaSinceWindowsEpoch().InMilliseconds();
+}
+
+// Helper method to convert serialized time as integer to base::Time for
+// deserialization. Loses precision beyond miliseconds.
+base::Time MillisecondsToTime(int64_t serialized_time_ms) {
+  return base::Time::FromDeltaSinceWindowsEpoch(
+      base::TimeDelta::FromMilliseconds(serialized_time_ms));
+}
+
+// Converts SchedulerClientType to its associated enum in proto buffer.
+proto::ClientState_SchedulerClientType ToSchedulerClientType(
+    SchedulerClientType type) {
+  switch (type) {
+    case SchedulerClientType::kTest1:
+      return proto::ClientState_SchedulerClientType_TEST_1;
+    case SchedulerClientType::kTest2:
+      return proto::ClientState_SchedulerClientType_TEST_2;
+    case SchedulerClientType::kTest3:
+      return proto::ClientState_SchedulerClientType_TEST_3;
+    case SchedulerClientType::kUnknown:
+      return proto::ClientState_SchedulerClientType_UNKNOWN;
+  }
+  NOTREACHED();
+}
+
+// Converts SchedulerClientType from its associated enum in proto buffer.
+SchedulerClientType FromSchedulerClientType(
+    proto::ClientState_SchedulerClientType proto_type) {
+  switch (proto_type) {
+    case proto::ClientState_SchedulerClientType_TEST_1:
+      return SchedulerClientType::kTest1;
+    case proto::ClientState_SchedulerClientType_TEST_2:
+      return SchedulerClientType::kTest2;
+    case proto::ClientState_SchedulerClientType_TEST_3:
+      return SchedulerClientType::kTest3;
+    case proto::ClientState_SchedulerClientType_UNKNOWN:
+      return SchedulerClientType::kUnknown;
+  }
+  NOTREACHED();
+}
+
+// Converts UserFeedback to its associated enum in proto buffer.
+proto::Impression_UserFeedback ToUserFeedback(UserFeedback feedback) {
+  switch (feedback) {
+    case UserFeedback::kNoFeedback:
+      return proto::Impression_UserFeedback_NO_FEEDBACK;
+    case UserFeedback::kHelpful:
+      return proto::Impression_UserFeedback_HELPFUL;
+    case UserFeedback::kNotHelpful:
+      return proto::Impression_UserFeedback_NOT_HELPFUL;
+    case UserFeedback::kClick:
+      return proto::Impression_UserFeedback_CLICK;
+    case UserFeedback::kDismiss:
+      return proto::Impression_UserFeedback_DISMISS;
+    case UserFeedback::kIgnore:
+      return proto::Impression_UserFeedback_IGNORE;
+  }
+  NOTREACHED();
+}
+
+// Converts UserFeedback from its associated enum in proto buffer.
+UserFeedback FromUserFeedback(proto::Impression_UserFeedback feedback) {
+  switch (feedback) {
+    case proto::Impression_UserFeedback_NO_FEEDBACK:
+      return UserFeedback::kNoFeedback;
+    case proto::Impression_UserFeedback_HELPFUL:
+      return UserFeedback::kHelpful;
+    case proto::Impression_UserFeedback_NOT_HELPFUL:
+      return UserFeedback::kNotHelpful;
+    case proto::Impression_UserFeedback_CLICK:
+      return UserFeedback::kClick;
+    case proto::Impression_UserFeedback_DISMISS:
+      return UserFeedback::kDismiss;
+    case proto::Impression_UserFeedback_IGNORE:
+      return UserFeedback::kIgnore;
+  }
+  NOTREACHED();
+}
+
+// Converts ImpressionResult to its associated enum in proto buffer.
+proto::Impression_ImpressionResult ToImpressionResult(ImpressionResult result) {
+  switch (result) {
+    case ImpressionResult::kInvalid:
+      return proto::Impression_ImpressionResult_INVALID;
+    case ImpressionResult::kPositive:
+      return proto::Impression_ImpressionResult_POSITIVE;
+    case ImpressionResult::kNegative:
+      return proto::Impression_ImpressionResult_NEGATIVE;
+    case ImpressionResult::kNeutral:
+      return proto::Impression_ImpressionResult_NEUTRAL;
+  }
+  NOTREACHED();
+}
+
+// Converts ImpressionResult from its associated enum in proto buffer.
+ImpressionResult FromImpressionResult(
+    proto::Impression_ImpressionResult result) {
+  switch (result) {
+    case proto::Impression_ImpressionResult_INVALID:
+      return ImpressionResult::kInvalid;
+    case proto::Impression_ImpressionResult_POSITIVE:
+      return ImpressionResult::kPositive;
+    case proto::Impression_ImpressionResult_NEGATIVE:
+      return ImpressionResult::kNegative;
+    case proto::Impression_ImpressionResult_NEUTRAL:
+      return ImpressionResult::kNeutral;
+  }
+  NOTREACHED();
+}
+
+// Converts ImpressionResult to its associated enum in proto buffer.
+proto::Impression_SchedulerTaskTime ToSchedulerTaskTime(
+    SchedulerTaskTime time) {
+  switch (time) {
+    case SchedulerTaskTime::kUnknown:
+      return proto::Impression_SchedulerTaskTime_UNKNOWN_TASK_TIME;
+    case SchedulerTaskTime::kMorning:
+      return proto::Impression_SchedulerTaskTime_MORNING;
+    case SchedulerTaskTime::kEvening:
+      return proto::Impression_SchedulerTaskTime_EVENING;
+  }
+  NOTREACHED();
+}
+
+// Converts ImpressionResult from its associated enum in proto buffer.
+SchedulerTaskTime FromSchedulerTaskTime(
+    proto::Impression_SchedulerTaskTime time) {
+  switch (time) {
+    case proto::Impression_SchedulerTaskTime_UNKNOWN_TASK_TIME:
+      return SchedulerTaskTime::kUnknown;
+    case proto::Impression_SchedulerTaskTime_MORNING:
+      return SchedulerTaskTime::kMorning;
+    case proto::Impression_SchedulerTaskTime_EVENING:
+      return SchedulerTaskTime::kEvening;
+  }
+  NOTREACHED();
+}
+
+}  // namespace
+
 void IconEntryToProto(IconEntry* entry, notifications::proto::Icon* proto) {
   proto->mutable_uuid()->swap(entry->uuid);
   proto->mutable_icon()->swap(entry->data);
 }
 
-void IconProtoToEntry(proto::Icon* proto, notifications::IconEntry* entry) {
+void IconEntryFromProto(proto::Icon* proto, notifications::IconEntry* entry) {
   DCHECK(proto->has_uuid());
   DCHECK(proto->has_icon());
   entry->data.swap(*proto->mutable_icon());
   entry->uuid.swap(*proto->mutable_uuid());
 }
 
+void ClientStateToProto(ClientState* client_state,
+                        notifications::proto::ClientState* proto) {
+  proto->set_type(ToSchedulerClientType(client_state->type));
+  proto->set_current_max_daily_show(client_state->current_max_daily_show);
+
+  for (const auto& impression : client_state->impressions) {
+    auto* impression_ptr = proto->add_impressions();
+    impression_ptr->set_create_time(TimeToMilliseconds(impression.create_time));
+    impression_ptr->set_feedback(ToUserFeedback(impression.feedback));
+    impression_ptr->set_impression(ToImpressionResult(impression.impression));
+    impression_ptr->set_integrated(impression.integrated);
+    impression_ptr->set_task_start_time(
+        ToSchedulerTaskTime(impression.task_start_time));
+  }
+
+  if (client_state->suppression_info.has_value()) {
+    const auto& suppression = *client_state->suppression_info;
+    auto* suppression_proto = proto->mutable_suppression_info();
+    suppression_proto->set_last_trigger_time(
+        TimeToMilliseconds(suppression.last_trigger_time));
+    suppression_proto->set_duration_ms(
+        TimeDeltaToMilliseconds(suppression.duration));
+    suppression_proto->set_recover_goal(suppression.recover_goal);
+  }
+}
+
+void ClientStateFromProto(proto::ClientState* proto,
+                          notifications::ClientState* client_state) {
+  DCHECK(proto->has_type());
+  DCHECK(proto->has_current_max_daily_show());
+  client_state->type = FromSchedulerClientType(proto->type());
+  client_state->current_max_daily_show = proto->current_max_daily_show();
+
+  for (const auto& proto_impression : proto->impressions()) {
+    Impression impression;
+    DCHECK(proto_impression.has_create_time());
+    impression.create_time = MillisecondsToTime(proto_impression.create_time());
+    impression.feedback = FromUserFeedback(proto_impression.feedback());
+    impression.impression = FromImpressionResult(proto_impression.impression());
+    impression.integrated = proto_impression.integrated();
+    impression.task_start_time =
+        FromSchedulerTaskTime(proto_impression.task_start_time());
+    client_state->impressions.emplace_back(std::move(impression));
+  }
+
+  if (proto->has_suppression_info()) {
+    const auto& proto_suppression = proto->suppression_info();
+    DCHECK(proto_suppression.has_last_trigger_time());
+    DCHECK(proto_suppression.has_duration_ms());
+    DCHECK(proto_suppression.has_recover_goal());
+
+    SuppressionInfo suppression_info(
+        MillisecondsToTime(proto_suppression.last_trigger_time()),
+        MillisecondsToTimeDelta(proto_suppression.duration_ms()));
+    suppression_info.recover_goal = proto_suppression.recover_goal();
+    client_state->suppression_info = std::move(suppression_info);
+  }
+}
+
 }  // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/proto_conversion.h b/chrome/browser/notifications/scheduler/proto_conversion.h
index eee7422..5795db2 100644
--- a/chrome/browser/notifications/scheduler/proto_conversion.h
+++ b/chrome/browser/notifications/scheduler/proto_conversion.h
@@ -8,8 +8,10 @@
 #include <memory>
 
 #include "base/macros.h"
+#include "chrome/browser/notifications/proto/client_state.pb.h"
 #include "chrome/browser/notifications/proto/icon.pb.h"
 #include "chrome/browser/notifications/scheduler/icon_entry.h"
+#include "chrome/browser/notifications/scheduler/impression_types.h"
 
 namespace notifications {
 
@@ -17,7 +19,15 @@
 void IconEntryToProto(IconEntry* entry, notifications::proto::Icon* proto);
 
 // Converts an icon proto to icon entry.
-void IconProtoToEntry(proto::Icon* proto, notifications::IconEntry* entry);
+void IconEntryFromProto(proto::Icon* proto, notifications::IconEntry* entry);
+
+// Converts client state to proto.
+void ClientStateToProto(ClientState* client_state,
+                        notifications::proto::ClientState* proto);
+
+// Converts proto to client state.
+void ClientStateFromProto(proto::ClientState* proto,
+                          notifications::ClientState* client_state);
 
 }  // namespace notifications
 
diff --git a/chrome/browser/notifications/scheduler/proto_conversion_unittest.cc b/chrome/browser/notifications/scheduler/proto_conversion_unittest.cc
index ba37e9ad..0430ca0 100644
--- a/chrome/browser/notifications/scheduler/proto_conversion_unittest.cc
+++ b/chrome/browser/notifications/scheduler/proto_conversion_unittest.cc
@@ -5,6 +5,7 @@
 #include "chrome/browser/notifications/scheduler/proto_conversion.h"
 
 #include "base/logging.h"
+#include "chrome/browser/notifications/scheduler/test/test_utils.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 using IconProto = notifications::proto::Icon;
@@ -15,13 +16,24 @@
 const char kUuid[] = "123";
 const char kData[] = "bitmapdata";
 
-TEST(ProtoConversionTest, IconProtoToEntry) {
+void TestClientStateConversion(ClientState* client_state) {
+  DCHECK(client_state);
+  notifications::proto::ClientState proto;
+  ClientState expected;
+  ClientStateToProto(client_state, &proto);
+  ClientStateFromProto(&proto, &expected);
+  EXPECT_EQ(*client_state, expected)
+      << " \n Output: \n " << client_state->DebugPrint() << " \n Expected: \n"
+      << expected.DebugPrint();
+}
+
+TEST(ProtoConversionTest, IconEntryFromProto) {
   IconProto proto;
   proto.set_uuid(kUuid);
   proto.set_icon(kData);
   IconEntry entry;
 
-  IconProtoToEntry(&proto, &entry);
+  IconEntryFromProto(&proto, &entry);
 
   // Verify entry data.
   EXPECT_EQ(entry.uuid, kUuid);
@@ -41,5 +53,87 @@
   EXPECT_EQ(proto.uuid(), kUuid);
 }
 
+// Verifies client state proto conversion.
+TEST(ProtoConversionTest, ClientStateProtoConversion) {
+  // Verify basic fields.
+  ClientState client_state;
+  test::ImpressionTestData test_data{
+      SchedulerClientType::kTest1, 3, {}, base::nullopt};
+  test::AddImpressionTestData(test_data, &client_state);
+  TestClientStateConversion(&client_state);
+
+  // Verify suppression info.
+  base::Time last_trigger_time;
+  bool success =
+      base::Time::FromString("04/25/20 01:00:00 AM", &last_trigger_time);
+  DCHECK(success);
+  auto duration = base::TimeDelta::FromDays(7);
+  auto suppression = SuppressionInfo(last_trigger_time, duration);
+  suppression.recover_goal = 5;
+  client_state.suppression_info = std::move(suppression);
+  TestClientStateConversion(&client_state);
+}
+
+// Verifies impression proto conversion.
+TEST(ProtoConversionTest, ImpressionProtoConversion) {
+  ClientState client_state;
+  base::Time create_time;
+  bool success = base::Time::FromString("03/25/19 00:00:00 AM", &create_time);
+  DCHECK(success);
+  Impression impression{create_time, UserFeedback::kHelpful,
+                        ImpressionResult::kPositive, true,
+                        SchedulerTaskTime::kMorning};
+  client_state.impressions.emplace_back(impression);
+  TestClientStateConversion(&client_state);
+
+  auto& first_impression = *client_state.impressions.begin();
+
+  // Verify all feedback types.
+  std::vector<UserFeedback> feedback_types{
+      UserFeedback::kNoFeedback, UserFeedback::kHelpful,
+      UserFeedback::kNotHelpful, UserFeedback::kClick,
+      UserFeedback::kDismiss,    UserFeedback::kIgnore};
+  for (const auto feedback_type : feedback_types) {
+    first_impression.feedback = feedback_type;
+    TestClientStateConversion(&client_state);
+  }
+
+  // Verify all impression result types.
+  std::vector<ImpressionResult> impression_results{
+      ImpressionResult::kInvalid, ImpressionResult::kPositive,
+      ImpressionResult::kNegative, ImpressionResult::kNeutral};
+  for (const auto impression_result : impression_results) {
+    first_impression.impression = impression_result;
+    TestClientStateConversion(&client_state);
+  }
+
+  // Verify all scheduler task time types.
+  std::vector<SchedulerTaskTime> task_times{SchedulerTaskTime::kUnknown,
+                                            SchedulerTaskTime::kMorning,
+                                            SchedulerTaskTime::kEvening};
+  for (const auto task_start_time : task_times) {
+    first_impression.task_start_time = task_start_time;
+    TestClientStateConversion(&client_state);
+  }
+}
+
+// Verifies multiple impressions are serialized correctly.
+TEST(ProtoConversionTest, MultipleImpressionConversion) {
+  ClientState client_state;
+  base::Time create_time;
+  bool success = base::Time::FromString("04/25/20 01:00:00 AM", &create_time);
+  DCHECK(success);
+
+  Impression impression{create_time, UserFeedback::kHelpful,
+                        ImpressionResult::kPositive, true,
+                        SchedulerTaskTime::kMorning};
+  Impression other_impression{create_time, UserFeedback::kNoFeedback,
+                              ImpressionResult::kNegative, false,
+                              SchedulerTaskTime::kEvening};
+  client_state.impressions.emplace_back(std::move(impression));
+  client_state.impressions.emplace_back(std::move(other_impression));
+  TestClientStateConversion(&client_state);
+}
+
 }  // namespace
 }  // namespace notifications
diff --git a/chrome/browser/notifications/scheduler/test/test_utils.cc b/chrome/browser/notifications/scheduler/test/test_utils.cc
index 38f17cd..b7cb20ae 100644
--- a/chrome/browser/notifications/scheduler/test/test_utils.cc
+++ b/chrome/browser/notifications/scheduler/test/test_utils.cc
@@ -22,17 +22,24 @@
 
 ImpressionTestData::~ImpressionTestData() = default;
 
+void AddImpressionTestData(const ImpressionTestData& data,
+                           ClientState* client_state) {
+  DCHECK(client_state);
+  client_state->type = data.type;
+  client_state->current_max_daily_show = data.current_max_daily_show;
+  for (const auto& impression : data.impressions) {
+    client_state->impressions.emplace_back(impression);
+  }
+  client_state->suppression_info = data.suppression_info;
+}
+
 void AddImpressionTestData(
     const std::vector<ImpressionTestData>& test_data,
     ImpressionHistoryTracker::ClientStates* client_states) {
   DCHECK(client_states);
   for (const auto& test_data : test_data) {
-    auto client_state = std::make_unique<ClientState>(test_data.type);
-    client_state->current_max_daily_show = test_data.current_max_daily_show;
-    for (const auto& impression : test_data.impressions) {
-      client_state->impressions.emplace_back(impression);
-    }
-    client_state->suppression_info = test_data.suppression_info;
+    auto client_state = std::make_unique<ClientState>();
+    AddImpressionTestData(test_data, client_state.get());
     client_states->emplace(test_data.type, std::move(client_state));
   }
 }
diff --git a/chrome/browser/notifications/scheduler/test/test_utils.h b/chrome/browser/notifications/scheduler/test/test_utils.h
index 32f218c..bea9a70 100644
--- a/chrome/browser/notifications/scheduler/test/test_utils.h
+++ b/chrome/browser/notifications/scheduler/test/test_utils.h
@@ -28,6 +28,10 @@
   base::Optional<SuppressionInfo> suppression_info;
 };
 
+// Add one impression test data into a client state.
+void AddImpressionTestData(const ImpressionTestData& data,
+                           ClientState* client_state);
+
 // Adds impression test data into client states container.
 void AddImpressionTestData(
     const std::vector<ImpressionTestData>& test_data,
diff --git a/chrome/browser/performance_manager/chrome_content_browser_client_performance_manager_part.cc b/chrome/browser/performance_manager/chrome_content_browser_client_performance_manager_part.cc
index a452293..519d355 100644
--- a/chrome/browser/performance_manager/chrome_content_browser_client_performance_manager_part.cc
+++ b/chrome/browser/performance_manager/chrome_content_browser_client_performance_manager_part.cc
@@ -51,11 +51,9 @@
                           base::Unretained(render_process_host)),
       base::SequencedTaskRunnerHandle::Get());
 
-  // When a RenderFrameHost is "resurrected" with a new process  it will already
-  // have user data attached. This will happen on renderer crash.
-  if (!performance_manager::RenderProcessUserData::GetForRenderProcessHost(
-          render_process_host)) {
-    performance_manager::RenderProcessUserData::CreateForRenderProcessHost(
-        render_process_host);
-  }
+  // Ideally this would strictly be a "CreateForRenderProcess", but when a
+  // RenderFrameHost is "resurrected" with a new process it will already have
+  // user data attached. This will happen on renderer crash.
+  performance_manager::RenderProcessUserData::GetOrCreateForRenderProcessHost(
+      render_process_host);
 }
diff --git a/chrome/browser/performance_manager/graph/frame_node_impl.cc b/chrome/browser/performance_manager/graph/frame_node_impl.cc
index 08c6eed3..daa0c0f 100644
--- a/chrome/browser/performance_manager/graph/frame_node_impl.cc
+++ b/chrome/browser/performance_manager/graph/frame_node_impl.cc
@@ -12,13 +12,18 @@
 namespace performance_manager {
 
 FrameNodeImpl::FrameNodeImpl(Graph* graph,
+                             ProcessNodeImpl* process_node,
                              PageNodeImpl* page_node,
-                             FrameNodeImpl* parent_frame_node)
+                             FrameNodeImpl* parent_frame_node,
+                             int frame_tree_node_id)
     : CoordinationUnitInterface(graph),
       parent_frame_node_(parent_frame_node),
       page_node_(page_node),
-      process_node_(nullptr) {
+      process_node_(process_node),
+      frame_tree_node_id_(frame_tree_node_id) {
   DETACH_FROM_SEQUENCE(sequence_checker_);
+  DCHECK(process_node);
+  DCHECK(page_node);
 }
 
 FrameNodeImpl::~FrameNodeImpl() {
@@ -39,10 +44,8 @@
   lifecycle_state_ = state;
 
   // Notify parents of this change.
-  if (process_node_)
-    process_node_->OnFrameLifecycleStateChanged(this, old_state);
-  if (page_node_)
-    page_node_->OnFrameLifecycleStateChanged(this, old_state);
+  process_node_->OnFrameLifecycleStateChanged(this, old_state);
+  page_node_->OnFrameLifecycleStateChanged(this, old_state);
 }
 
 void FrameNodeImpl::SetHasNonEmptyBeforeUnload(bool has_nonempty_beforeunload) {
@@ -91,10 +94,13 @@
 }
 
 ProcessNodeImpl* FrameNodeImpl::process_node() const {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   return process_node_;
 }
 
+int FrameNodeImpl::frame_tree_node_id() const {
+  return frame_tree_node_id_;
+}
+
 const std::set<FrameNodeImpl*>& FrameNodeImpl::child_frame_nodes() const {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   return child_frame_nodes_;
@@ -121,14 +127,6 @@
   return network_almost_idle_.value();
 }
 
-void FrameNodeImpl::SetProcess(ProcessNodeImpl* process_node) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  DCHECK(NodeInGraph(process_node));
-  DCHECK(!process_node_);
-  process_node_ = process_node;
-  process_node->AddFrame(this);
-}
-
 void FrameNodeImpl::set_url(const GURL& url) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   url_ = url;
@@ -205,13 +203,11 @@
     intervention_policy_[i] =
         resource_coordinator::mojom::InterventionPolicy::kUnknown;
 
-  // Hook up the frame hierarchy.
+  // Wire this up to the other nodes in the graph.
   if (parent_frame_node_)
     parent_frame_node_->AddChildFrame(this);
-
-  // And join the page.
-  // TODO(siggi): Only do this for the main frame and retire the frame set.
   page_node_->AddFrame(this);
+  process_node_->AddFrame(this);
 
   NodeBase::JoinGraph();
 }
@@ -232,10 +228,9 @@
     parent_frame_node_->RemoveChildFrame(this);
   }
 
-  if (process_node_) {
-    DCHECK(NodeInGraph(process_node_));
-    process_node_->RemoveFrame(this);
-  }
+  // And leave the process.
+  DCHECK(NodeInGraph(process_node_));
+  process_node_->RemoveFrame(this);
 }
 
 void FrameNodeImpl::OnEventReceived(resource_coordinator::mojom::Event event) {
@@ -264,10 +259,4 @@
   return false;
 }
 
-void FrameNodeImpl::RemoveProcessNode(ProcessNodeImpl* process_node) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  DCHECK(process_node == process_node_);
-  process_node_ = nullptr;
-}
-
 }  // namespace performance_manager
diff --git a/chrome/browser/performance_manager/graph/frame_node_impl.h b/chrome/browser/performance_manager/graph/frame_node_impl.h
index a33e55b9..274ab4f 100644
--- a/chrome/browser/performance_manager/graph/frame_node_impl.h
+++ b/chrome/browser/performance_manager/graph/frame_node_impl.h
@@ -31,12 +31,14 @@
     return resource_coordinator::CoordinationUnitType::kFrame;
   }
 
-  // Construct a frame node associated with |page_node| and optionally with a
-  // |parent_frame_node|. For the main frame of |page_node| the
-  // |parent_frame_node| parameter should be nullptr.
+  // Construct a frame node associated with a |process_node|, a |page_node| and
+  // optionally with a |parent_frame_node|. For the main frame of |page_node|
+  // the |parent_frame_node| parameter should be nullptr.
   FrameNodeImpl(Graph* graph,
+                ProcessNodeImpl* process_node,
                 PageNodeImpl* page_node,
-                FrameNodeImpl* parent_frame_node);
+                FrameNodeImpl* parent_frame_node,
+                int frame_tree_node_id);
   ~FrameNodeImpl() override;
 
   // FrameNode implementation.
@@ -52,18 +54,17 @@
   // Getters for const properties. These can be called from any thread.
   FrameNodeImpl* parent_frame_node() const;
   PageNodeImpl* page_node() const;
+  ProcessNodeImpl* process_node() const;
+  int frame_tree_node_id() const;
 
   // Getters for non-const properties. These are not thread safe.
-  ProcessNodeImpl* process_node() const;
   const std::set<FrameNodeImpl*>& child_frame_nodes() const;
   resource_coordinator::mojom::LifecycleState lifecycle_state() const;
   bool has_nonempty_beforeunload() const;
   const GURL& url() const;
   bool network_almost_idle() const;
 
-  // Setters can only be called from the performance manager sequence.
-  // TODO(siggi): The process node can be provided at construction.
-  void SetProcess(ProcessNodeImpl* process_node);
+  // Setters are not thread safe.
   void set_url(const GURL& url);
 
   // A frame is a main frame if it has no |parent_frame_node|. This can be
@@ -95,12 +96,13 @@
   bool HasFrameNodeInAncestors(FrameNodeImpl* frame_node) const;
   bool HasFrameNodeInDescendants(FrameNodeImpl* frame_node) const;
 
-  void RemoveProcessNode(ProcessNodeImpl* process_node);
-
   FrameNodeImpl* const parent_frame_node_;
   PageNodeImpl* const page_node_;
+  ProcessNodeImpl* const process_node_;
+  // Can be used to tie together "sibling" frames, where a navigation is ongoing
+  // in a new frame that will soon replace the existing one.
+  const int frame_tree_node_id_;
 
-  ProcessNodeImpl* process_node_;
   std::set<FrameNodeImpl*> child_frame_nodes_;
   resource_coordinator::mojom::LifecycleState lifecycle_state_ =
       resource_coordinator::mojom::LifecycleState::kRunning;
diff --git a/chrome/browser/performance_manager/graph/frame_node_impl_unittest.cc b/chrome/browser/performance_manager/graph/frame_node_impl_unittest.cc
index bda0fc1d..700c4eb2 100644
--- a/chrome/browser/performance_manager/graph/frame_node_impl_unittest.cc
+++ b/chrome/browser/performance_manager/graph/frame_node_impl_unittest.cc
@@ -37,10 +37,14 @@
 }  // namespace
 
 TEST_F(FrameNodeImplTest, AddFrameHierarchyBasic) {
+  auto process = CreateNode<ProcessNodeImpl>();
   auto page = CreateNode<PageNodeImpl>();
-  auto parent_node = CreateNode<FrameNodeImpl>(page.get(), nullptr);
-  auto child2_node = CreateNode<FrameNodeImpl>(page.get(), parent_node.get());
-  auto child3_node = CreateNode<FrameNodeImpl>(page.get(), parent_node.get());
+  auto parent_node =
+      CreateNode<FrameNodeImpl>(process.get(), page.get(), nullptr, 0);
+  auto child2_node = CreateNode<FrameNodeImpl>(process.get(), page.get(),
+                                               parent_node.get(), 1);
+  auto child3_node = CreateNode<FrameNodeImpl>(process.get(), page.get(),
+                                               parent_node.get(), 2);
 
   EXPECT_EQ(nullptr, parent_node->parent_frame_node());
   EXPECT_EQ(2u, parent_node->child_frame_nodes().size());
@@ -49,8 +53,10 @@
 }
 
 TEST_F(FrameNodeImplTest, Url) {
+  auto process = CreateNode<ProcessNodeImpl>();
   auto page = CreateNode<PageNodeImpl>();
-  auto frame_node = CreateNode<FrameNodeImpl>(page.get(), nullptr);
+  auto frame_node =
+      CreateNode<FrameNodeImpl>(process.get(), page.get(), nullptr, 0);
   EXPECT_TRUE(frame_node->url().is_empty());
   const GURL url("http://www.foo.com/");
   frame_node->set_url(url);
@@ -58,10 +64,12 @@
 }
 
 TEST_F(FrameNodeImplTest, RemoveChildFrame) {
+  auto process = CreateNode<ProcessNodeImpl>();
   auto page = CreateNode<PageNodeImpl>();
-  auto parent_frame_node = CreateNode<FrameNodeImpl>(page.get(), nullptr);
-  auto child_frame_node =
-      CreateNode<FrameNodeImpl>(page.get(), parent_frame_node.get());
+  auto parent_frame_node =
+      CreateNode<FrameNodeImpl>(process.get(), page.get(), nullptr, 0);
+  auto child_frame_node = CreateNode<FrameNodeImpl>(process.get(), page.get(),
+                                                    parent_frame_node.get(), 1);
 
   // Ensure correct Parent-child relationships have been established.
   EXPECT_EQ(1u, parent_frame_node->child_frame_nodes().size());
diff --git a/chrome/browser/performance_manager/graph/mock_graphs.cc b/chrome/browser/performance_manager/graph/mock_graphs.cc
index 72260e41..ff36ddf 100644
--- a/chrome/browser/performance_manager/graph/mock_graphs.cc
+++ b/chrome/browser/performance_manager/graph/mock_graphs.cc
@@ -21,11 +21,13 @@
     : system(TestNodeWrapper<SystemNodeImpl>::Create(graph)),
       process(TestNodeWrapper<ProcessNodeImpl>::Create(graph)),
       page(TestNodeWrapper<PageNodeImpl>::Create(graph)),
-      frame(
-          TestNodeWrapper<FrameNodeImpl>::Create(graph, page.get(), nullptr)) {
+      frame(TestNodeWrapper<FrameNodeImpl>::Create(graph,
+                                                   process.get(),
+                                                   page.get(),
+                                                   nullptr,
+                                                   0)) {
   frame->SetAllInterventionPoliciesForTesting(
       resource_coordinator::mojom::InterventionPolicy::kDefault);
-  frame->SetProcess(process.get());
   process->SetPID(1);
 }
 
@@ -40,11 +42,12 @@
     : MockSinglePageInSingleProcessGraph(graph),
       other_page(TestNodeWrapper<PageNodeImpl>::Create(graph)),
       other_frame(TestNodeWrapper<FrameNodeImpl>::Create(graph,
+                                                         process.get(),
                                                          other_page.get(),
-                                                         nullptr)) {
+                                                         nullptr,
+                                                         1)) {
   other_frame->SetAllInterventionPoliciesForTesting(
       resource_coordinator::mojom::InterventionPolicy::kDefault);
-  other_frame->SetProcess(process.get());
 }
 
 MockMultiplePagesInSingleProcessGraph::
@@ -56,14 +59,15 @@
 MockSinglePageWithMultipleProcessesGraph::
     MockSinglePageWithMultipleProcessesGraph(Graph* graph)
     : MockSinglePageInSingleProcessGraph(graph),
+      other_process(TestNodeWrapper<ProcessNodeImpl>::Create(graph)),
       child_frame(TestNodeWrapper<FrameNodeImpl>::Create(graph,
+                                                         other_process.get(),
                                                          page.get(),
-                                                         frame.get())),
-      other_process(TestNodeWrapper<ProcessNodeImpl>::Create(graph)) {
+                                                         frame.get(),
+                                                         2)) {
+  other_process->SetPID(2);
   child_frame->SetAllInterventionPoliciesForTesting(
       resource_coordinator::mojom::InterventionPolicy::kDefault);
-  child_frame->SetProcess(other_process.get());
-  other_process->SetPID(2);
 }
 
 MockSinglePageWithMultipleProcessesGraph::
@@ -72,14 +76,15 @@
 MockMultiplePagesWithMultipleProcessesGraph::
     MockMultiplePagesWithMultipleProcessesGraph(Graph* graph)
     : MockMultiplePagesInSingleProcessGraph(graph),
+      other_process(TestNodeWrapper<ProcessNodeImpl>::Create(graph)),
       child_frame(TestNodeWrapper<FrameNodeImpl>::Create(graph,
+                                                         other_process.get(),
                                                          other_page.get(),
-                                                         other_frame.get())),
-      other_process(TestNodeWrapper<ProcessNodeImpl>::Create(graph)) {
+                                                         other_frame.get(),
+                                                         3)) {
+  other_process->SetPID(2);
   child_frame->SetAllInterventionPoliciesForTesting(
       resource_coordinator::mojom::InterventionPolicy::kDefault);
-  child_frame->SetProcess(other_process.get());
-  other_process->SetPID(2);
 }
 
 MockMultiplePagesWithMultipleProcessesGraph::
diff --git a/chrome/browser/performance_manager/graph/mock_graphs.h b/chrome/browser/performance_manager/graph/mock_graphs.h
index 9937128..d0aeb99 100644
--- a/chrome/browser/performance_manager/graph/mock_graphs.h
+++ b/chrome/browser/performance_manager/graph/mock_graphs.h
@@ -23,7 +23,7 @@
 //   F
 //
 // Where:
-// F: frame
+// F: frame(frame_tree_id:0)
 // Pr: process(pid:1)
 // Pg: page
 struct MockSinglePageInSingleProcessGraph {
@@ -43,8 +43,8 @@
 //   F  OF
 //
 // Where:
-// F: frame
-// OF: other_frame
+// F: frame(frame_tree_id:0)
+// OF: other_frame(frame_tree_id:1)
 // Pg: page
 // OPg: other_page
 // Pr: process(pid:1)
@@ -67,8 +67,8 @@
 // |__CF
 //
 // Where:
-// F: frame
-// CF: child_frame
+// F: frame(frame_tree_id:0)
+// CF: child_frame(frame_tree_id:2)
 // Pg: page
 // Pr: process(pid:1)
 // OPr: other_process(pid:2)
@@ -76,8 +76,8 @@
     : public MockSinglePageInSingleProcessGraph {
   explicit MockSinglePageWithMultipleProcessesGraph(Graph* graph);
   ~MockSinglePageWithMultipleProcessesGraph();
-  TestNodeWrapper<FrameNodeImpl> child_frame;
   TestNodeWrapper<ProcessNodeImpl> other_process;
+  TestNodeWrapper<FrameNodeImpl> child_frame;
 };
 
 // The following coordination unit graph topology is created to emulate a
@@ -91,9 +91,9 @@
 //         CF___|
 //
 // Where:
-// F: frame
-// OF: other_frame
-// CF: another_frame
+// F: frame(frame_tree_id:0)
+// OF: other_frame(frame_tree_id:1)
+// CF: child_frame(frame_tree_id:3)
 // Pg: page
 // OPg: other_page
 // Pr: process(pid:1)
@@ -102,8 +102,8 @@
     : public MockMultiplePagesInSingleProcessGraph {
   explicit MockMultiplePagesWithMultipleProcessesGraph(Graph* graph);
   ~MockMultiplePagesWithMultipleProcessesGraph();
-  TestNodeWrapper<FrameNodeImpl> child_frame;
   TestNodeWrapper<ProcessNodeImpl> other_process;
+  TestNodeWrapper<FrameNodeImpl> child_frame;
 };
 
 }  // namespace performance_manager
diff --git a/chrome/browser/performance_manager/graph/page_node_impl_unittest.cc b/chrome/browser/performance_manager/graph/page_node_impl_unittest.cc
index 57c3e3d..2c6250c 100644
--- a/chrome/browser/performance_manager/graph/page_node_impl_unittest.cc
+++ b/chrome/browser/performance_manager/graph/page_node_impl_unittest.cc
@@ -39,20 +39,24 @@
 }  // namespace
 
 TEST_F(PageNodeImplTest, AddFrameBasic) {
+  auto process_node = CreateNode<ProcessNodeImpl>();
   auto page_node = CreateNode<PageNodeImpl>();
-  auto parent_frame = CreateNode<FrameNodeImpl>(page_node.get(), nullptr);
-  auto child1_frame =
-      CreateNode<FrameNodeImpl>(page_node.get(), parent_frame.get());
-  auto child2_frame =
-      CreateNode<FrameNodeImpl>(page_node.get(), parent_frame.get());
+  auto parent_frame = CreateNode<FrameNodeImpl>(process_node.get(),
+                                                page_node.get(), nullptr, 0);
+  auto child1_frame = CreateNode<FrameNodeImpl>(
+      process_node.get(), page_node.get(), parent_frame.get(), 1);
+  auto child2_frame = CreateNode<FrameNodeImpl>(
+      process_node.get(), page_node.get(), parent_frame.get(), 2);
 
   // Validate that all frames are tallied to the page.
   EXPECT_EQ(3u, page_node->GetFrameNodes().size());
 }
 
 TEST_F(PageNodeImplTest, RemoveFrame) {
+  auto process_node = CreateNode<ProcessNodeImpl>();
   auto page_node = CreateNode<PageNodeImpl>();
-  auto frame_node = CreateNode<FrameNodeImpl>(page_node.get(), nullptr);
+  auto frame_node = CreateNode<FrameNodeImpl>(process_node.get(),
+                                              page_node.get(), nullptr, 0);
 
   // Ensure correct page-frame relationship has been established.
   EXPECT_EQ(1u, page_node->GetFrameNodes().size());
@@ -220,7 +224,8 @@
     resource_coordinator::mojom::InterventionPolicy f1_policy,
     resource_coordinator::mojom::InterventionPolicy f0_policy_aggregated,
     resource_coordinator::mojom::InterventionPolicy f0f1_policy_aggregated) {
-
+  TestNodeWrapper<ProcessNodeImpl> process =
+      TestNodeWrapper<ProcessNodeImpl>::Create(mock_graph);
   TestNodeWrapper<PageNodeImpl> page =
       TestNodeWrapper<PageNodeImpl>::Create(mock_graph);
 
@@ -232,8 +237,8 @@
       resource_coordinator::mojom::InterventionPolicy::kUnknown, page.get());
 
   // Create an initial frame.
-  TestNodeWrapper<FrameNodeImpl> f0 =
-      TestNodeWrapper<FrameNodeImpl>::Create(mock_graph, page.get(), nullptr);
+  TestNodeWrapper<FrameNodeImpl> f0 = TestNodeWrapper<FrameNodeImpl>::Create(
+      mock_graph, process.get(), page.get(), nullptr, 0);
   // Add a frame and expect the values to be invalidated. Reaggregate and
   // ensure the appropriate value results.
   f0->SetAllInterventionPoliciesForTesting(f0_policy);
@@ -242,8 +247,8 @@
       resource_coordinator::mojom::InterventionPolicy::kUnknown, page.get());
   ExpectInterventionPolicy(f0_policy_aggregated, page.get());
 
-  TestNodeWrapper<FrameNodeImpl> f1 =
-      TestNodeWrapper<FrameNodeImpl>::Create(mock_graph, page.get(), f0.get());
+  TestNodeWrapper<FrameNodeImpl> f1 = TestNodeWrapper<FrameNodeImpl>::Create(
+      mock_graph, process.get(), page.get(), f0.get(), 1);
   // Do it again. This time the raw values should be the same as the
   // aggregated values above.
   f1->SetAllInterventionPoliciesForTesting(f1_policy);
@@ -360,14 +365,16 @@
 TEST_F(PageNodeImplTest, IncrementalInterventionPolicy) {
   auto* mock_graph = graph();
 
+  TestNodeWrapper<ProcessNodeImpl> process =
+      TestNodeWrapper<ProcessNodeImpl>::Create(mock_graph);
   TestNodeWrapper<PageNodeImpl> page =
       TestNodeWrapper<PageNodeImpl>::Create(mock_graph);
 
   // Create two frames and immediately attach them to the page.
-  TestNodeWrapper<FrameNodeImpl> f0 =
-      TestNodeWrapper<FrameNodeImpl>::Create(mock_graph, page.get(), nullptr);
-  TestNodeWrapper<FrameNodeImpl> f1 =
-      TestNodeWrapper<FrameNodeImpl>::Create(mock_graph, page.get(), f0.get());
+  TestNodeWrapper<FrameNodeImpl> f0 = TestNodeWrapper<FrameNodeImpl>::Create(
+      mock_graph, process.get(), page.get(), nullptr, 0);
+  TestNodeWrapper<FrameNodeImpl> f1 = TestNodeWrapper<FrameNodeImpl>::Create(
+      mock_graph, process.get(), page.get(), f0.get(), 1);
   EXPECT_EQ(0u, page->GetInterventionPolicyFramesReportedForTesting());
   EXPECT_EQ(0u, page->GetInterventionPolicyFramesReportedForTesting());
   EXPECT_EQ(0u, page->GetInterventionPolicyFramesReportedForTesting());
diff --git a/chrome/browser/performance_manager/graph/process_node_impl.cc b/chrome/browser/performance_manager/graph/process_node_impl.cc
index 0aeb6ea..b5264e5 100644
--- a/chrome/browser/performance_manager/graph/process_node_impl.cc
+++ b/chrome/browser/performance_manager/graph/process_node_impl.cc
@@ -122,8 +122,8 @@
   if (process_id_ != base::kNullProcessId)
     graph()->BeforeProcessPidChange(this, base::kNullProcessId);
 
-  for (auto* child_frame : frame_nodes_)
-    child_frame->RemoveProcessNode(this);
+  // All child frames should have been removed before the process is removed.
+  DCHECK(frame_nodes_.empty());
 }
 
 void ProcessNodeImpl::OnEventReceived(
diff --git a/chrome/browser/performance_manager/observers/graph_observer_unittest.cc b/chrome/browser/performance_manager/observers/graph_observer_unittest.cc
index d0d8c77..12187ca 100644
--- a/chrome/browser/performance_manager/observers/graph_observer_unittest.cc
+++ b/chrome/browser/performance_manager/observers/graph_observer_unittest.cc
@@ -52,11 +52,12 @@
 
 
   {
-    auto page_node = CreateNode<PageNodeImpl>();
     auto process_node = CreateNode<ProcessNodeImpl>();
-    auto root_frame_node = CreateNode<FrameNodeImpl>(page_node.get(), nullptr);
-    auto frame_node =
-        CreateNode<FrameNodeImpl>(page_node.get(), root_frame_node.get());
+    auto page_node = CreateNode<PageNodeImpl>();
+    auto root_frame_node = CreateNode<FrameNodeImpl>(
+        process_node.get(), page_node.get(), nullptr, 0);
+    auto frame_node = CreateNode<FrameNodeImpl>(
+        process_node.get(), page_node.get(), root_frame_node.get(), 1);
 
     EXPECT_EQ(2u, observer->node_created_count());
   }
diff --git a/chrome/browser/performance_manager/observers/metrics_collector_unittest.cc b/chrome/browser/performance_manager/observers/metrics_collector_unittest.cc
index 05072c16..403496e 100644
--- a/chrome/browser/performance_manager/observers/metrics_collector_unittest.cc
+++ b/chrome/browser/performance_manager/observers/metrics_collector_unittest.cc
@@ -113,8 +113,10 @@
 
 TEST_F(MAYBE_MetricsCollectorTest,
        FromBackgroundedToFirstNonPersistentNotificationCreatedUMA) {
+  auto process_node = CreateNode<ProcessNodeImpl>();
   auto page_node = CreateNode<PageNodeImpl>();
-  auto frame_node = CreateNode<FrameNodeImpl>(page_node.get(), nullptr);
+  auto frame_node = CreateNode<FrameNodeImpl>(process_node.get(),
+                                              page_node.get(), nullptr, 0);
 
   page_node->OnMainFrameNavigationCommitted(PerformanceManagerClock::NowTicks(),
                                             kDummyID, kDummyUrl);
@@ -148,8 +150,10 @@
 TEST_F(
     MAYBE_MetricsCollectorTest,
     FromBackgroundedToFirstNonPersistentNotificationCreatedUMA5MinutesTimeout) {
+  auto process_node = CreateNode<ProcessNodeImpl>();
   auto page_node = CreateNode<PageNodeImpl>();
-  auto frame_node = CreateNode<FrameNodeImpl>(page_node.get(), nullptr);
+  auto frame_node = CreateNode<FrameNodeImpl>(process_node.get(),
+                                              page_node.get(), nullptr, 0);
 
   page_node->OnMainFrameNavigationCommitted(PerformanceManagerClock::NowTicks(),
                                             kDummyID, kDummyUrl);
@@ -217,11 +221,10 @@
 
 // Flaky test: https://crbug.com/833028
 TEST_F(MAYBE_MetricsCollectorTest, ResponsivenessMetric) {
-  auto page_node = CreateNode<PageNodeImpl>();
   auto process_node = CreateNode<ProcessNodeImpl>();
-
-  auto frame_node = CreateNode<FrameNodeImpl>(page_node.get(), nullptr);
-  frame_node->SetProcess(process_node.get());
+  auto page_node = CreateNode<PageNodeImpl>();
+  auto frame_node = CreateNode<FrameNodeImpl>(process_node.get(),
+                                              page_node.get(), nullptr, 0);
 
   ukm::TestUkmRecorder ukm_recorder;
   graph()->set_ukm_recorder(&ukm_recorder);
diff --git a/chrome/browser/performance_manager/performance_manager.cc b/chrome/browser/performance_manager/performance_manager.cc
index dae724cc..57a6713 100644
--- a/chrome/browser/performance_manager/performance_manager.cc
+++ b/chrome/browser/performance_manager/performance_manager.cc
@@ -7,6 +7,7 @@
 #include <memory>
 #include <utility>
 
+#include "base/containers/flat_set.h"
 #include "base/feature_list.h"
 #include "base/logging.h"
 #include "base/macros.h"
@@ -81,23 +82,34 @@
 }
 
 std::unique_ptr<FrameNodeImpl> PerformanceManager::CreateFrameNode(
+    ProcessNodeImpl* process_node,
     PageNodeImpl* page_node,
-    FrameNodeImpl* parent_frame_node) {
-  std::unique_ptr<FrameNodeImpl> new_node =
-      std::make_unique<FrameNodeImpl>(&graph_, page_node, parent_frame_node);
-  task_runner_->PostTask(
-      FROM_HERE, base::BindOnce(&Graph::AddNewNode, base::Unretained(&graph_),
-                                base::Unretained(new_node.get())));
+    FrameNodeImpl* parent_frame_node,
+    int frame_tree_node_id) {
+  return CreateNodeImpl<FrameNodeImpl>(FrameNodeCreationCallback(),
+                                       process_node, page_node,
+                                       parent_frame_node, frame_tree_node_id);
+}
 
-  return new_node;
+std::unique_ptr<FrameNodeImpl> PerformanceManager::CreateFrameNode(
+    ProcessNodeImpl* process_node,
+    PageNodeImpl* page_node,
+    FrameNodeImpl* parent_frame_node,
+    int frame_tree_node_id,
+    FrameNodeCreationCallback creation_callback) {
+  return CreateNodeImpl<FrameNodeImpl>(std::move(creation_callback),
+                                       process_node, page_node,
+                                       parent_frame_node, frame_tree_node_id);
 }
 
 std::unique_ptr<PageNodeImpl> PerformanceManager::CreatePageNode() {
-  return CreateNodeImpl<PageNodeImpl>();
+  return CreateNodeImpl<PageNodeImpl>(
+      base::OnceCallback<void(PageNodeImpl*)>());
 }
 
 std::unique_ptr<ProcessNodeImpl> PerformanceManager::CreateProcessNode() {
-  return CreateNodeImpl<ProcessNodeImpl>();
+  return CreateNodeImpl<ProcessNodeImpl>(
+      base::OnceCallback<void(ProcessNodeImpl*)>());
 }
 
 void PerformanceManager::BatchDeleteNodes(
@@ -122,13 +134,33 @@
                                         std::move(message_pipe)));
 }
 
-template <typename NodeType>
-std::unique_ptr<NodeType> PerformanceManager::CreateNodeImpl() {
-  std::unique_ptr<NodeType> new_node = std::make_unique<NodeType>(&graph_);
-  task_runner_->PostTask(
-      FROM_HERE, base::BindOnce(&Graph::AddNewNode, base::Unretained(&graph_),
-                                base::Unretained(new_node.get())));
+namespace {
 
+// Helper function for adding a node to a graph, and invoking a post-creation
+// callback immediately afterwards.
+template <typename NodeType>
+void AddNodeAndInvokeCreationCallback(
+    base::OnceCallback<void(NodeType*)> callback,
+    NodeType* node,
+    Graph* graph) {
+  graph->AddNewNode(node);
+  if (!callback.is_null())
+    std::move(callback).Run(node);
+}
+
+}  // namespace
+
+template <typename NodeType, typename... Args>
+std::unique_ptr<NodeType> PerformanceManager::CreateNodeImpl(
+    base::OnceCallback<void(NodeType*)> creation_callback,
+    Args&&... constructor_args) {
+  std::unique_ptr<NodeType> new_node = std::make_unique<NodeType>(
+      &graph_, std::forward<Args>(constructor_args)...);
+  task_runner_->PostTask(
+      FROM_HERE, base::BindOnce(&AddNodeAndInvokeCreationCallback<NodeType>,
+                                std::move(creation_callback),
+                                base::Unretained(new_node.get()),
+                                base::Unretained(&graph_)));
   return new_node;
 }
 
@@ -161,18 +193,43 @@
     std::vector<std::unique_ptr<NodeBase>> nodes) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
+  base::flat_set<ProcessNodeImpl*> process_nodes;
+
   for (auto it = nodes.begin(); it != nodes.end(); ++it) {
-    if ((*it)->id().type == resource_coordinator::CoordinationUnitType::kPage) {
-      auto* page_node = PageNodeImpl::FromNodeBase(it->get());
+    switch ((*it)->id().type) {
+      case resource_coordinator::CoordinationUnitType::kPage: {
+        auto* page_node = PageNodeImpl::FromNodeBase(it->get());
 
-      // Delete the main frame nodes until no more exist.
-      while (!page_node->main_frame_nodes().empty())
-        RemoveFrameAndChildrenFromGraph(
-            *(page_node->main_frame_nodes().begin()));
+        // Delete the main frame nodes until no more exist.
+        while (!page_node->main_frame_nodes().empty())
+          RemoveFrameAndChildrenFromGraph(
+              *(page_node->main_frame_nodes().begin()));
 
-      graph_.RemoveNode(page_node);
+        graph_.RemoveNode(page_node);
+        break;
+      }
+      case resource_coordinator::CoordinationUnitType::kProcess: {
+        // Keep track of the process nodes for removing once all frames nodes
+        // are removed.
+        auto* process_node = ProcessNodeImpl::FromNodeBase(it->get());
+        process_nodes.insert(process_node);
+        break;
+      }
+      case resource_coordinator::CoordinationUnitType::kFrame:
+        break;
+      case resource_coordinator::CoordinationUnitType::kSystem:
+      case resource_coordinator::CoordinationUnitType::kInvalidType:
+      default: {
+        NOTREACHED();
+        break;
+      }
     }
   }
+
+  // Remove the process nodes from the graph.
+  for (auto* process_node : process_nodes)
+    graph_.RemoveNode(process_node);
+
   // When |nodes| goes out of scope, all nodes are deleted.
 }
 
diff --git a/chrome/browser/performance_manager/performance_manager.h b/chrome/browser/performance_manager/performance_manager.h
index 67ba160..8d8c30b 100644
--- a/chrome/browser/performance_manager/performance_manager.h
+++ b/chrome/browser/performance_manager/performance_manager.h
@@ -37,6 +37,8 @@
 // {Frame|Page|Process|System}ResourceCoordinator classes.
 class PerformanceManager {
  public:
+  using FrameNodeCreationCallback = base::OnceCallback<void(FrameNodeImpl*)>;
+
   ~PerformanceManager();
 
   // Retrieves the currently registered instance.
@@ -63,10 +65,20 @@
   void BindInterface(mojo::InterfaceRequest<Interface> request);
 
   // Creates a new node of the requested type and adds it to the graph.
-  // May be called from any sequence.
+  // May be called from any sequence. If a |creation_callback| is provided it
+  // will be run on the performance manager sequence immediately after creating
+  // the node.
   std::unique_ptr<FrameNodeImpl> CreateFrameNode(
+      ProcessNodeImpl* process_node,
       PageNodeImpl* page_node,
-      FrameNodeImpl* parent_frame_node);
+      FrameNodeImpl* parent_frame_node,
+      int frame_tree_node_id);
+  std::unique_ptr<FrameNodeImpl> CreateFrameNode(
+      ProcessNodeImpl* process_node,
+      PageNodeImpl* page_node,
+      FrameNodeImpl* parent_frame_node,
+      int frame_tree_node_id,
+      FrameNodeCreationCallback creation_callback);
   std::unique_ptr<PageNodeImpl> CreatePageNode();
   std::unique_ptr<ProcessNodeImpl> CreateProcessNode();
 
@@ -95,8 +107,10 @@
   void PostBindInterface(const std::string& interface_name,
                          mojo::ScopedMessagePipeHandle message_pipe);
 
-  template <typename NodeType>
-  std::unique_ptr<NodeType> CreateNodeImpl();
+  template <typename NodeType, typename... Args>
+  std::unique_ptr<NodeType> CreateNodeImpl(
+      base::OnceCallback<void(NodeType*)> creation_callback,
+      Args&&... constructor_args);
 
   void PostDeleteNode(std::unique_ptr<NodeBase> node);
   void DeleteNodeImpl(std::unique_ptr<NodeBase> node);
diff --git a/chrome/browser/performance_manager/performance_manager_tab_helper.cc b/chrome/browser/performance_manager/performance_manager_tab_helper.cc
index 6ddcd77..fe8cda9 100644
--- a/chrome/browser/performance_manager/performance_manager_tab_helper.cc
+++ b/chrome/browser/performance_manager/performance_manager_tab_helper.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/performance_manager/performance_manager_tab_helper.h"
 
+#include <type_traits>
 #include <utility>
 #include <vector>
 
@@ -50,9 +51,6 @@
   UpdatePageNodeVisibility(web_contents->GetVisibility());
 
   // Dispatch creation notifications for any pre-existing frames.
-  // This seems to occur only in tests, but dealing with this allows asserting
-  // a strong invariant on the |frames_| collection.
-  // TODO(siggi): Eliminate this once test injection is all or nothing.
   std::vector<content::RenderFrameHost*> existing_frames =
       web_contents->GetAllFrames();
   for (content::RenderFrameHost* frame : existing_frames) {
@@ -108,22 +106,22 @@
     parent_frame_node = frames_[parent].get();
   }
 
-  std::unique_ptr<FrameNodeImpl> frame = performance_manager_->CreateFrameNode(
-      page_node_.get(), parent_frame_node);
+  // Ideally this would strictly be a "Get", but it is possible in tests for
+  // the the RenderProcessUserData to not have attached at this point.
+  auto* process_node = RenderProcessUserData::GetOrCreateForRenderProcessHost(
+                           render_frame_host->GetProcess())
+                           ->process_node();
 
-  RenderProcessUserData* user_data =
-      RenderProcessUserData::GetForRenderProcessHost(
-          render_frame_host->GetProcess());
-  // In unittests the user data isn't populated as the relevant main parts
-  // is not in play.
-  // TODO(siggi): Figure out how to assert on this when the main parts are
-  //     registered with the content browser client.
-  if (user_data) {
-    performance_manager_->task_runner()->PostTask(
-        FROM_HERE, base::BindOnce(&FrameNodeImpl::SetProcess,
-                                  base::Unretained(frame.get()),
-                                  user_data->process_node()));
-  }
+  // Create the frame node, and provide a callback that will run in the graph to
+  // initialize it.
+  std::unique_ptr<FrameNodeImpl> frame = performance_manager_->CreateFrameNode(
+      process_node, page_node_.get(), parent_frame_node,
+      render_frame_host->GetFrameTreeNodeId(),
+      base::BindOnce(
+          [](const GURL& url, FrameNodeImpl* frame_node) {
+            frame_node->set_url(url);
+          },
+          render_frame_host->GetLastCommittedURL()));
 
   frames_[render_frame_host] = std::move(frame);
 }
@@ -138,15 +136,11 @@
 }
 
 void PerformanceManagerTabHelper::DidStartLoading() {
-  performance_manager_->task_runner()->PostTask(
-      FROM_HERE, base::BindOnce(&PageNodeImpl::SetIsLoading,
-                                base::Unretained(page_node_.get()), true));
+  PostToGraph(FROM_HERE, &PageNodeImpl::SetIsLoading, page_node_.get(), true);
 }
 
 void PerformanceManagerTabHelper::DidStopLoading() {
-  performance_manager_->task_runner()->PostTask(
-      FROM_HERE, base::BindOnce(&PageNodeImpl::SetIsLoading,
-                                base::Unretained(page_node_.get()), false));
+  PostToGraph(FROM_HERE, &PageNodeImpl::SetIsLoading, page_node_.get(), false);
 }
 
 void PerformanceManagerTabHelper::OnVisibilityChanged(
@@ -176,9 +170,7 @@
 
   // Notify the frame of the committed URL.
   GURL url = navigation_handle->GetURL();
-  performance_manager_->task_runner()->PostTask(
-      FROM_HERE, base::BindOnce(&FrameNodeImpl::set_url,
-                                base::Unretained(frame_node), url));
+  PostToGraph(FROM_HERE, &FrameNodeImpl::set_url, frame_node, url);
 
   if (navigation_handle->IsSameDocument() ||
       !navigation_handle->IsInMainFrame()) {
@@ -188,11 +180,9 @@
   // Make sure the hierarchical structure is constructed before sending signal
   // to the performance manager.
   OnMainFrameNavigation(navigation_handle->GetNavigationId());
-  performance_manager_->task_runner()->PostTask(
-      FROM_HERE, base::BindOnce(&PageNodeImpl::OnMainFrameNavigationCommitted,
-                                base::Unretained(page_node_.get()),
-                                navigation_committed_time,
-                                navigation_handle->GetNavigationId(), url));
+  PostToGraph(FROM_HERE, &PageNodeImpl::OnMainFrameNavigationCommitted,
+              page_node_.get(), navigation_committed_time,
+              navigation_handle->GetNavigationId(), url);
 }
 
 void PerformanceManagerTabHelper::TitleWasSet(content::NavigationEntry* entry) {
@@ -201,9 +191,7 @@
     first_time_title_set_ = true;
     return;
   }
-  performance_manager_->task_runner()->PostTask(
-      FROM_HERE, base::BindOnce(&PageNodeImpl::OnTitleUpdated,
-                                base::Unretained(page_node_.get())));
+  PostToGraph(FROM_HERE, &PageNodeImpl::OnTitleUpdated, page_node_.get());
 }
 
 void PerformanceManagerTabHelper::DidUpdateFaviconURL(
@@ -213,9 +201,7 @@
     first_time_favicon_set_ = true;
     return;
   }
-  performance_manager_->task_runner()->PostTask(
-      FROM_HERE, base::BindOnce(&PageNodeImpl::OnFaviconUpdated,
-                                base::Unretained(page_node_.get())));
+  PostToGraph(FROM_HERE, &PageNodeImpl::OnFaviconUpdated, page_node_.get());
 }
 
 void PerformanceManagerTabHelper::OnInterfaceRequestFromFrame(
@@ -228,21 +214,28 @@
 
   auto it = frames_.find(render_frame_host);
   DCHECK(it != frames_.end());
+  PostToGraph(FROM_HERE, &FrameNodeImpl::AddBinding, it->second.get(),
+              resource_coordinator::mojom::FrameCoordinationUnitRequest(
+                  std::move(*interface_pipe)));
+}
+
+template <typename Functor, typename NodeType, typename... Args>
+void PerformanceManagerTabHelper::PostToGraph(const base::Location& from_here,
+                                              Functor&& functor,
+                                              NodeType* node,
+                                              Args&&... args) {
+  static_assert(std::is_base_of<NodeBase, NodeType>::value,
+                "NodeType must be descended from NodeBase");
   performance_manager_->task_runner()->PostTask(
-      FROM_HERE,
-      base::BindOnce(&FrameNodeImpl::AddBinding,
-                     base::Unretained(it->second.get()),
-                     resource_coordinator::mojom::FrameCoordinationUnitRequest(
-                         std::move(*interface_pipe))));
+      from_here, base::BindOnce(functor, base::Unretained(node),
+                                std::forward<Args>(args)...));
 }
 
 void PerformanceManagerTabHelper::OnMainFrameNavigation(int64_t navigation_id) {
   ukm_source_id_ =
       ukm::ConvertToSourceId(navigation_id, ukm::SourceIdType::NAVIGATION_ID);
-  performance_manager_->task_runner()->PostTask(
-      FROM_HERE,
-      base::BindOnce(&PageNodeImpl::SetUkmSourceId,
-                     base::Unretained(page_node_.get()), ukm_source_id_));
+  PostToGraph(FROM_HERE, &PageNodeImpl::SetUkmSourceId, page_node_.get(),
+              ukm_source_id_);
 
   first_time_title_set_ = false;
   first_time_favicon_set_ = false;
@@ -252,10 +245,8 @@
     content::Visibility visibility) {
   // TODO(fdoray): An OCCLUDED tab should not be considered visible.
   const bool is_visible = visibility != content::Visibility::HIDDEN;
-  performance_manager_->task_runner()->PostTask(
-      FROM_HERE,
-      base::BindOnce(&PageNodeImpl::SetIsVisible,
-                     base::Unretained(page_node_.get()), is_visible));
+  PostToGraph(FROM_HERE, &PageNodeImpl::SetIsVisible, page_node_.get(),
+              is_visible);
 }
 
 WEB_CONTENTS_USER_DATA_KEY_IMPL(PerformanceManagerTabHelper)
diff --git a/chrome/browser/performance_manager/performance_manager_tab_helper.h b/chrome/browser/performance_manager/performance_manager_tab_helper.h
index b4d2861..4099c1e8 100644
--- a/chrome/browser/performance_manager/performance_manager_tab_helper.h
+++ b/chrome/browser/performance_manager/performance_manager_tab_helper.h
@@ -62,13 +62,21 @@
   void SetUkmSourceIdForTesting(ukm::SourceId id) { ukm_source_id_ = id; }
 
  private:
+  friend class content::WebContentsUserData<PerformanceManagerTabHelper>;
+
   explicit PerformanceManagerTabHelper(content::WebContents* web_contents);
 
+  // Post a task to run in the performance manager sequence. The |node| will be
+  // passed as unretained, and the closure will be created with BindOnce.
+  template <typename Functor, typename NodeType, typename... Args>
+  void PostToGraph(const base::Location& from_here,
+                   Functor&& functor,
+                   NodeType* node,
+                   Args&&... args);
+
   void OnMainFrameNavigation(int64_t navigation_id);
   void UpdatePageNodeVisibility(content::Visibility visibility);
 
-  friend class content::WebContentsUserData<PerformanceManagerTabHelper>;
-
   // The performance manager for this process, if any.
   PerformanceManager* const performance_manager_;
   std::unique_ptr<PageNodeImpl> page_node_;
diff --git a/chrome/browser/performance_manager/performance_manager_unittest.cc b/chrome/browser/performance_manager/performance_manager_unittest.cc
index b20745f..20e3b491 100644
--- a/chrome/browser/performance_manager/performance_manager_unittest.cc
+++ b/chrome/browser/performance_manager/performance_manager_unittest.cc
@@ -53,49 +53,54 @@
 };
 
 TEST_F(PerformanceManagerTest, InstantiateNodes) {
+  std::unique_ptr<ProcessNodeImpl> process_node =
+      performance_manager()->CreateProcessNode();
+  EXPECT_NE(nullptr, process_node.get());
   std::unique_ptr<PageNodeImpl> page_node =
       performance_manager()->CreatePageNode();
   EXPECT_NE(nullptr, page_node.get());
 
   // Create a node of each type.
   std::unique_ptr<FrameNodeImpl> frame_node =
-      performance_manager()->CreateFrameNode(page_node.get(), nullptr);
+      performance_manager()->CreateFrameNode(process_node.get(),
+                                             page_node.get(), nullptr, 0);
   EXPECT_NE(nullptr, frame_node.get());
 
-  std::unique_ptr<ProcessNodeImpl> process_node =
-      performance_manager()->CreateProcessNode();
-  EXPECT_NE(nullptr, process_node.get());
-
-  performance_manager()->DeleteNode(std::move(process_node));
   performance_manager()->DeleteNode(std::move(frame_node));
   performance_manager()->DeleteNode(std::move(page_node));
+  performance_manager()->DeleteNode(std::move(process_node));
 }
 
 TEST_F(PerformanceManagerTest, BatchDeleteNodes) {
   // Create a page node and a small hierarchy of frames.
+  std::unique_ptr<ProcessNodeImpl> process_node =
+      performance_manager()->CreateProcessNode();
   std::unique_ptr<PageNodeImpl> page_node =
       performance_manager()->CreatePageNode();
 
   std::unique_ptr<FrameNodeImpl> parent1_frame =
-      performance_manager()->CreateFrameNode(page_node.get(), nullptr);
+      performance_manager()->CreateFrameNode(process_node.get(),
+                                             page_node.get(), nullptr, 0);
   std::unique_ptr<FrameNodeImpl> parent2_frame =
-      performance_manager()->CreateFrameNode(page_node.get(), nullptr);
+      performance_manager()->CreateFrameNode(process_node.get(),
+                                             page_node.get(), nullptr, 1);
 
   std::unique_ptr<FrameNodeImpl> child1_frame =
-      performance_manager()->CreateFrameNode(page_node.get(),
-                                             parent1_frame.get());
+      performance_manager()->CreateFrameNode(
+          process_node.get(), page_node.get(), parent1_frame.get(), 2);
   std::unique_ptr<FrameNodeImpl> child2_frame =
-      performance_manager()->CreateFrameNode(page_node.get(),
-                                             parent2_frame.get());
+      performance_manager()->CreateFrameNode(
+          process_node.get(), page_node.get(), parent2_frame.get(), 3);
 
   std::vector<std::unique_ptr<NodeBase>> nodes;
   for (size_t i = 0; i < 10; ++i) {
-    nodes.push_back(performance_manager()->CreateFrameNode(page_node.get(),
-                                                           child1_frame.get()));
-    nodes.push_back(performance_manager()->CreateFrameNode(page_node.get(),
-                                                           child1_frame.get()));
+    nodes.push_back(performance_manager()->CreateFrameNode(
+        process_node.get(), page_node.get(), child1_frame.get(), 0));
+    nodes.push_back(performance_manager()->CreateFrameNode(
+        process_node.get(), page_node.get(), child1_frame.get(), 1));
   }
 
+  nodes.push_back(std::move(process_node));
   nodes.push_back(std::move(page_node));
   nodes.push_back(std::move(parent1_frame));
   nodes.push_back(std::move(parent2_frame));
diff --git a/chrome/browser/performance_manager/render_process_user_data.cc b/chrome/browser/performance_manager/render_process_user_data.cc
index cb10c81..e4780f6 100644
--- a/chrome/browser/performance_manager/render_process_user_data.cc
+++ b/chrome/browser/performance_manager/render_process_user_data.cc
@@ -32,10 +32,6 @@
     content::RenderProcessHost* render_process_host)
     : host_(render_process_host),
       process_node_(PerformanceManager::GetInstance()->CreateProcessNode()) {
-  // The process itself shouldn't have been created at this point.
-  DCHECK(!host_->GetProcess().IsValid() ||
-         base::CommandLine::ForCurrentProcess()->HasSwitch(
-             switches::kSingleProcess));
   host_->AddObserver(this);
 
   // Push this instance to the list.
@@ -69,20 +65,26 @@
     first_->host_->RemoveUserData(kRenderProcessUserDataKey);
 }
 
-void RenderProcessUserData::CreateForRenderProcessHost(
-    content::RenderProcessHost* host) {
-  std::unique_ptr<RenderProcessUserData> user_data =
-      base::WrapUnique(new RenderProcessUserData(host));
-
-  host->SetUserData(kRenderProcessUserDataKey, std::move(user_data));
-}
-
+// static
 RenderProcessUserData* RenderProcessUserData::GetForRenderProcessHost(
     content::RenderProcessHost* host) {
   return static_cast<RenderProcessUserData*>(
       host->GetUserData(kRenderProcessUserDataKey));
 }
 
+// static
+RenderProcessUserData* RenderProcessUserData::GetOrCreateForRenderProcessHost(
+    content::RenderProcessHost* host) {
+  auto* raw_user_data = GetForRenderProcessHost(host);
+  if (raw_user_data)
+    return raw_user_data;
+  std::unique_ptr<RenderProcessUserData> user_data =
+      base::WrapUnique(new RenderProcessUserData(host));
+  raw_user_data = user_data.get();
+  host->SetUserData(kRenderProcessUserDataKey, std::move(user_data));
+  return raw_user_data;
+}
+
 void RenderProcessUserData::RenderProcessReady(
     content::RenderProcessHost* host) {
   PerformanceManager* performance_manager = PerformanceManager::GetInstance();
diff --git a/chrome/browser/performance_manager/render_process_user_data.h b/chrome/browser/performance_manager/render_process_user_data.h
index 18a2554..ac74b1d 100644
--- a/chrome/browser/performance_manager/render_process_user_data.h
+++ b/chrome/browser/performance_manager/render_process_user_data.h
@@ -28,9 +28,10 @@
  public:
   ~RenderProcessUserData() override;
 
-  static void CreateForRenderProcessHost(content::RenderProcessHost* host);
   static RenderProcessUserData* GetForRenderProcessHost(
       content::RenderProcessHost* host);
+  static RenderProcessUserData* GetOrCreateForRenderProcessHost(
+      content::RenderProcessHost* host);
 
   // Detaches all instances from their RenderProcessHosts and destroys them.
   static void DetachAndDestroyAll();
diff --git a/chrome/browser/resource_coordinator/chrome_browser_main_extra_parts_resource_coordinator.cc b/chrome/browser/resource_coordinator/chrome_browser_main_extra_parts_resource_coordinator.cc
index d05f4764..08e51b7c 100644
--- a/chrome/browser/resource_coordinator/chrome_browser_main_extra_parts_resource_coordinator.cc
+++ b/chrome/browser/resource_coordinator/chrome_browser_main_extra_parts_resource_coordinator.cc
@@ -50,8 +50,6 @@
   // Release all graph nodes before destroying the performance manager.
   // First release the browser and GPU process nodes.
   browser_child_process_watcher_.reset();
-  // Then the render process nodes.
-  performance_manager::RenderProcessUserData::DetachAndDestroyAll();
 
   // There may still be WebContents with attached tab helpers at this point in
   // time, and there's no convenient later call-out to destroy the performance
@@ -59,6 +57,10 @@
   // from any existing WebContents.
   performance_manager::PerformanceManagerTabHelper::DetachAndDestroyAll();
 
+  // Then the render process nodes. These have to be destroyed after the
+  // frame nodes.
+  performance_manager::RenderProcessUserData::DetachAndDestroyAll();
+
   performance_manager::PerformanceManager::Destroy(
       std::move(performance_manager_));
 }
diff --git a/chrome/browser/resources/chromeos/echo/OWNERS b/chrome/browser/resources/chromeos/echo/OWNERS
index c197453..d43a12b 100644
--- a/chrome/browser/resources/chromeos/echo/OWNERS
+++ b/chrome/browser/resources/chromeos/echo/OWNERS
@@ -1,5 +1,4 @@
-gauravsh@chromium.org
 jorgelo@chromium.org
 stephenlin@chromium.org
-andycai@chromium.org
-oscarpan@chromium.org
+abutzier@chromium.org
+leecy@chromium.org
diff --git a/chrome/browser/resources/chromeos/echo/manifest.json b/chrome/browser/resources/chromeos/echo/manifest.json
index 8297d27..b0efc05 100644
--- a/chrome/browser/resources/chromeos/echo/manifest.json
+++ b/chrome/browser/resources/chromeos/echo/manifest.json
@@ -41,7 +41,20 @@
     "ids": ["*"],
     "matches": [
       "*://www.google.com/*chromebook/*",
-      "*://chromebook-dot-googwebreview.appspot.com/*chromebook/*"
+      "*://www.google.com.au/*chromebook/*",
+      "*://www.google.ca/*chromebook/*",
+      "*://www.google.co.jp/*chromebook/*",
+      "*://www.google.co.uk/*chromebook/*",
+      "*://www.google.de/*chromebook/*",
+      "*://www.google.dk/*chromebook/*",
+      "*://www.google.fi/*chromebook/*",
+      "*://www.google.fr/*chromebook/*",
+      "*://www.google.ie/*chromebook/*",
+      "*://www.google.nl/*chromebook/*",
+      "*://www.google.no/*chromebook/*",
+      "*://www.google.co.nz/*chromebook/*",
+      "*://www.google.se/*chromebook/*",
+      "*://chromebook*-dot-googwebreview.appspot.com/*chromebook/*"
     ]
   }
 }
diff --git a/chrome/browser/resources/chromeos/login/BUILD.gn b/chrome/browser/resources/chromeos/login/BUILD.gn
index 3425340..b3149962 100644
--- a/chrome/browser/resources/chromeos/login/BUILD.gn
+++ b/chrome/browser/resources/chromeos/login/BUILD.gn
@@ -11,6 +11,26 @@
     ":offline_ad_login",
     ":oobe_change_picture",
     ":oobe_select",
+    ":oobe_welcome",
+  ]
+}
+
+js_library("oobe_types") {
+}
+
+# This is special file to be used as a substutute of Oobe object for closure
+# compilation until we make real one closure compile.
+# TODO (https://crbug.com/950198)
+js_library("fake_oobe") {
+  deps = [
+    ":oobe_types",
+  ]
+}
+
+js_library("login_screen_behavior") {
+  deps = [
+    ":fake_oobe",
+    "//ui/login:display_manager_types",
   ]
 }
 
@@ -20,6 +40,12 @@
 js_library("oobe_select") {
 }
 
+js_library("oobe_welcome") {
+  deps = [
+    ":login_screen_behavior",
+  ]
+}
+
 js_library("offline_ad_login") {
   deps = [
     ":oobe_dialog_host_behavior",
diff --git a/chrome/browser/resources/chromeos/login/fake_oobe.js b/chrome/browser/resources/chromeos/login/fake_oobe.js
new file mode 100644
index 0000000..e1a5b94
--- /dev/null
+++ b/chrome/browser/resources/chromeos/login/fake_oobe.js
@@ -0,0 +1,54 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview Fake out of the box experience flow (OOBE) for
+ * closure_compiler coverage.
+ */
+
+/** @typedef {string} */
+var ACCELERATOR_ENABLE_DEBBUGING = '1';
+
+/** @typedef {string} */
+var ACCELERATOR_DEVICE_REQUISITION_REMORA = '2';
+
+/** @typedef {string} */
+var ACCELERATOR_DEVICE_REQUISITION = '3';
+
+var cr;
+cr.ui = {};
+
+cr.define = function(name, constructor_function) {};
+
+/** @interface */
+class Oobe {
+  constructor() {}
+
+  /** @return {Oobe} */
+  getInstance() {}
+
+  /**
+   * @param {
+   *     ACCELERATOR_ENABLE_DEBBUGING |
+   *     ACCELERATOR_DEVICE_REQUISITION_REMORA |
+   *     ACCELERATOR_DEVICE_REQUISITION
+   * } accelerator
+   */
+  handleAccelerator(accelerator) {}
+
+  /**
+   * @param {Element} el Decorated screen element.
+   * @param {DisplayManagerScreenAttributes} attributes
+   */
+  registerScreen(el, attributes) {}
+
+  /**
+   * @return {?OobeTypes.OobeConfiguration}
+   */
+  getOobeConfiguration() {}
+
+  startDemoModeFlow() {}
+}
+
+cr.ui.Oobe = Oobe;
diff --git a/chrome/browser/resources/chromeos/login/login_screen_behavior.js b/chrome/browser/resources/chromeos/login/login_screen_behavior.js
index 7deb31b..fa8b2cf 100644
--- a/chrome/browser/resources/chromeos/login/login_screen_behavior.js
+++ b/chrome/browser/resources/chromeos/login/login_screen_behavior.js
@@ -7,8 +7,6 @@
  * 'LoginScreenBehavior' is login.Screen API implementation for Polymer objects.
  */
 
-// <include src="../../../../../ui/login/display_manager_types.js">
-
 /** @polymerBehavior */
 var LoginScreenBehavior = {
   // List of methods exported to login.screenName.<method> API.
@@ -71,34 +69,34 @@
 
   /**
    * Screen will ignore accelerators when true.
-   * @type {Boolean}
+   * @type {boolean}
    */
   ignoreAccelerators: false,
 
   /**
    * If defined, invoked for the currently active screen when screen size
    * changes.
-   * @type{function()}
+   * @type {function()|undefined}
    */
   onWindowResize: undefined,
 
   /**
    * If defined, invoked when tablet mode is changed.
    * Boolean parameter is true when device is in tablet mode.
-   * @type {function(Boolean)}
+   * @type {function(boolean)|undefined}
    */
   setTabletModeState: undefined,
 
   /**
    * If defined, invoked for the currently active screen when screen localized
    * data needs to be updated.
-   * @type{function()}
+   * @type {function()|undefined}
    */
   updateLocalizedContent: undefined,
 
   /**
    * If defined, invoked when OOBE configuration is loaded.
-   * @param {!OobeTypes.OobeConfiguration} configuration
+   * @type {OobeTypes.OobeConfiguration|undefined} configuration
    */
   updateOobeConfiguration: undefined,
 
@@ -111,11 +109,12 @@
    *     login.ScreenName.foo(); // valid
    *
    * @param {string} name Name of created class.
-   * @param {Object} id Id of div representing screen.
+   * @param {Object} api Screen API.
    * @private
    */
   registerScreenApi_: function(name, api) {
-    cr.define('login', function() {
+    // Closure compiler incorrectly parses this, so we use cr.define.call(...).
+    cr.define.call(cr.define, 'login', function() {
       var result = {};
       result[name] = api;
       return result;
diff --git a/chrome/browser/resources/chromeos/login/oobe_select.js b/chrome/browser/resources/chromeos/login/oobe_select.js
index f0b648d0..a90d571d 100644
--- a/chrome/browser/resources/chromeos/login/oobe_select.js
+++ b/chrome/browser/resources/chromeos/login/oobe_select.js
@@ -6,7 +6,16 @@
  * @fileoverview Helper functions to manage select UI elements.
  */
 
-/** @typedef {Iterable<{value: string, title: string, selected: boolean}>} */
+/**
+ * @typedef {
+ *   Iterable<{
+ *       optionGroupName: (string|undefined),
+ *       selected: boolean,
+ *       title: string,
+ *       value: string,
+ *   }>
+ * }
+ */
 var SelectListType;
 
 /**
diff --git a/chrome/browser/resources/chromeos/login/oobe_types.js b/chrome/browser/resources/chromeos/login/oobe_types.js
index a621c4c..7ca2ac39 100644
--- a/chrome/browser/resources/chromeos/login/oobe_types.js
+++ b/chrome/browser/resources/chromeos/login/oobe_types.js
@@ -13,10 +13,14 @@
 /**
  * ChromeOS OOBE language descriptor.
  * @typedef {{
- *   code: (String|undefined),
- *   displayName: (String|undefined),
- *   textDirection: (String|undefined),
- *   nativeDisplayName: (String|undefined),
+ *   code: (string|undefined),
+ *   displayName: (string|undefined),
+ *   nativeDisplayName: (string|undefined),
+ *   optionGroupName: (string|undefined),
+ *   selected: boolean,
+ *   textDirection: (string|undefined),
+ *   title: string,
+ *   value: string,
  * }}
  */
 OobeTypes.LanguageDsc;
@@ -24,9 +28,10 @@
 /**
  * ChromeOS OOBE input method descriptor.
  * @typedef {{
- *   value: (String|undefined),
- *   title: (String|undefined),
- *   selected: (Boolean|undefined),
+ *   optionGroupName: (string|undefined),
+ *   selected: boolean,
+ *   title: string,
+ *   value: string,
  * }}
  */
 OobeTypes.IMEDsc;
@@ -34,9 +39,9 @@
 /**
  * ChromeOS OOBE demo country descriptor.
  * @typedef {{
- *   value: (String|undefined),
- *   title: (String|undefined),
- *   selected: (Boolean|undefined),
+ *   value: (string|undefined),
+ *   title: (string|undefined),
+ *   selected: (boolean|undefined),
  * }}
  */
 OobeTypes.DemoCountryDsc;
@@ -44,11 +49,11 @@
 /**
  * A set of flags of accessibility options for ChromeOS OOBE.
  * @typedef {{
- *   highContrastEnabled: Boolean,
- *   spokenFeedbackEnabled: Boolean,
- *   screenMagnifierEnabled: Boolean,
- *   largeCursorEnabled: Boolean,
- *   virtualKeyboardEnabled: Boolean,
+ *   highContrastEnabled: boolean,
+ *   spokenFeedbackEnabled: boolean,
+ *   screenMagnifierEnabled: boolean,
+ *   largeCursorEnabled: boolean,
+ *   virtualKeyboardEnabled: boolean,
  * }}
  */
 OobeTypes.A11yStatuses;
@@ -70,7 +75,7 @@
  * @typedef {{
  *   value: (OobeTypes.Timezone|undefined),
  *   title: (String|undefined),
- *   selected: (Boolean|undefined),
+ *   selected: (boolean|undefined),
  * }}
  */
 OobeTypes.TimezoneDsc;
@@ -79,18 +84,18 @@
  * OOBE configuration, allows automation during OOBE.
  * Keys are also listed in chrome/browser/chromeos/login/configuration_keys.h
  * @typedef {{
- *   language: string|undefined,
- *   inputMethod: string|undefined,
- *   welcomeNext: boolean|undefined,
- *   enableDemoMode: boolean|undefined,
- *   demoPreferencesNext: boolean|undefined,
- *   networkSelectGuid: string|undefined,
- *   networkOfflineDemo: boolean|undefined,
- *   eulaAutoAccept: boolean|undefined,
- *   eulaSendStatistics: boolean|undefined,
- *   networkUseConnected: boolean|undefined,
- *   updateSkipNonCritical: boolean|undefined,
- *   arcTosAutoAccept: boolean|undefined,
+ *   language: (string|undefined),
+ *   inputMethod: (string|undefined),
+ *   welcomeNext: (boolean|undefined),
+ *   enableDemoMode: (boolean|undefined),
+ *   demoPreferencesNext: (boolean|undefined),
+ *   networkSelectGuid: (string|undefined),
+ *   networkOfflineDemo: (boolean|undefined),
+ *   eulaAutoAccept: (boolean|undefined),
+ *   eulaSendStatistics: (boolean|undefined),
+ *   networkUseConnected: (boolean|undefined),
+ *   updateSkipNonCritical: (boolean|undefined),
+ *   arcTosAutoAccept: (boolean|undefined),
  * }}
  */
 OobeTypes.OobeConfiguration;
diff --git a/chrome/browser/resources/chromeos/login/oobe_welcome.js b/chrome/browser/resources/chromeos/login/oobe_welcome.js
index 7b3e273..dc785b88 100644
--- a/chrome/browser/resources/chromeos/login/oobe_welcome.js
+++ b/chrome/browser/resources/chromeos/login/oobe_welcome.js
@@ -77,7 +77,7 @@
     debuggingLinkVisible_: Boolean,
   },
 
-  /** @override */
+  /** Overridden from LoginScreenBehavior. */
   EXTERNAL_API: [
     'onInputMethodIdSetFromBackend',
   ],
@@ -131,13 +131,17 @@
 
   /**
    * This is called when UI strings are changed.
-   * @override
+   * Overridden from LoginScreenBehavior.
    */
   updateLocalizedContent: function() {
-    this.languages = loadTimeData.getValue('languageList');
-    this.keyboards = loadTimeData.getValue('inputMethodsList');
-    this.timezones = loadTimeData.getValue('timezoneList');
-    this.highlightStrength = loadTimeData.getValue('highlightStrength');
+    this.languages = /** @type {!Array<OobeTypes.LanguageDsc>} */ (
+        loadTimeData.getValue('languageList'));
+    this.keyboards = /** @type {!Array<OobeTypes.IMEDsc>} */ (
+        loadTimeData.getValue('inputMethodsList'));
+    this.timezones = /** @type {!Array<OobeTypes.TimezoneDsc>} */ (
+        loadTimeData.getValue('timezoneList'));
+    this.highlightStrength =
+        /** @type {string} */ (loadTimeData.getValue('highlightStrength'));
 
     this.$.welcomeScreen.i18nUpdateLocale();
     this.i18nUpdateLocale();
@@ -156,8 +160,8 @@
 
   /**
    * Called when OOBE configuration is loaded.
+   * Overridden from LoginScreenBehavior.
    * @param {!OobeTypes.OobeConfiguration} configuration
-   * @override
    */
   updateOobeConfiguration: function(configuration) {
     if (!this.configuration_applied_)
@@ -199,8 +203,8 @@
 
   /**
    * Updates "device in tablet mode" state when tablet mode is changed.
-   * @param {Boolean} isInTabletMode True when in tablet mode.
-   * @override
+   * Overridden from LoginScreenBehavior.
+   * @param {boolean} isInTabletMode True when in tablet mode.
    */
   setTabletModeState: function(isInTabletMode) {
     this.$.welcomeScreen.isInTabletMode = isInTabletMode;
@@ -343,7 +347,7 @@
   /**
    * Switch UI language.
    *
-   * @param {!OobeTypes.LanguageDsc} languageId
+   * @param {string} languageId
    * @private
    */
   applySelectedLanguage_: function(languageId) {
@@ -366,7 +370,7 @@
   /**
    * Switch keyboard layout.
    *
-   * @param {!OobeTypes.IMEDsc} inputMethodId
+   * @param {string} inputMethodId
    * @private
    */
   applySelectedLkeyboard_: function(inputMethodId) {
@@ -374,7 +378,8 @@
   },
 
   onLanguagesChanged_: function() {
-    this.currentLanguage = getSelectedTitle(this.languages);
+    this.currentLanguage =
+        getSelectedTitle(/** @type {!SelectListType} */ (this.languages));
   },
 
   onInputMethodIdSetFromBackend: function(keyboard_id) {
@@ -428,8 +433,9 @@
    * @param {!Event} event
    */
   onA11yOptionChanged_: function(event) {
-    chrome.send(
-        event.currentTarget.chromeMessage, [event.currentTarget.checked]);
+    var a11ytarget = /** @type {{chromeMessage: string, checked: boolean}} */ (
+        event.currentTarget);
+    chrome.send(a11ytarget.chromeMessage, [a11ytarget.checked]);
   },
 
   /** ******************** Timezone section ******************* */
diff --git a/chrome/browser/resources/print_preview/new/button_strip.html b/chrome/browser/resources/print_preview/new/button_strip.html
index 917c067..60fdc03 100644
--- a/chrome/browser/resources/print_preview/new/button_strip.html
+++ b/chrome/browser/resources/print_preview/new/button_strip.html
@@ -17,9 +17,9 @@
         display: flex;
         flex-direction: row;
         justify-content: flex-end;
-        margin-top: 16px;
         padding-bottom: 16px;
         padding-inline-end: 16px;
+        padding-top: 16px;
       }
 
       :host-context(html:not([dark])) {
diff --git a/chrome/browser/resources/print_preview/new/link_container.html b/chrome/browser/resources/print_preview/new/link_container.html
index 98f9072..c816839 100644
--- a/chrome/browser/resources/print_preview/new/link_container.html
+++ b/chrome/browser/resources/print_preview/new/link_container.html
@@ -9,6 +9,10 @@
 <dom-module id="print-preview-link-container">
   <template>
     <style include="print-preview-shared throbber cr-hidden-style">
+      :host {
+        display: block;
+      }
+
       :host paper-icon-button-light {
         --cr-paper-icon-button-margin: {
           margin-inline-end: -2px;
@@ -36,7 +40,6 @@
       }
 
       #systemDialogLink {
-        border-top: var(--print-preview-settings-border);
         padding-top: 0.5em;
       }
 
diff --git a/chrome/browser/resources/print_preview/new/print_preview_sidebar.html b/chrome/browser/resources/print_preview/new/print_preview_sidebar.html
index 4e4ad7d..ec82d93 100644
--- a/chrome/browser/resources/print_preview/new/print_preview_sidebar.html
+++ b/chrome/browser/resources/print_preview/new/print_preview_sidebar.html
@@ -60,11 +60,24 @@
         margin-top: 12px;
       }
 
+      /* Print Preview uses lighter box-shadows compared to the default
+       * styling. */
+      :host([new-print-preview-layout_]) #cr-container-shadow-top,
+      :host([new-print-preview-layout_]) #cr-container-shadow-bottom {
+        box-shadow: inset 0 5px 3px -3px rgba(0, 0, 0, .2);
+      }
+
       .settings-section {
         display: block;
         margin-bottom: 16px;
         margin-top: 16px;
       }
+
+<if expr="not chromeos">
+      :host(:not([new-print-preview-layout_])) print-preview-link-container {
+        border-top: var(--print-preview-settings-border);
+      }
+</if>
     </style>
     <template is="dom-if" if="[[newPrintPreviewLayout_]]">
       <print-preview-header-new id="header" destination="[[destination]]"
@@ -80,7 +93,7 @@
           settings="[[settings]]" managed="[[controlsManaged]]">
       </print-preview-header>
     </template>
-    <div id="container">
+    <div id="container" show-bottom-shadow$="[[newPrintPreviewLayout_]]">
       <print-preview-destination-settings id="destinationSettings"
           cloud-print-interface="[[cloudPrintInterface]]"
           destination="{{destination}}" destination-state="{{destinationState}}"
@@ -155,12 +168,22 @@
             hidden$="[[!settings.vendorItems.available]]"
             class="settings-section">
         </print-preview-advanced-options-settings>
+<if expr="not chromeos">
+        <template is="dom-if" if="[[newPrintPreviewLayout_]]">
+          <print-preview-link-container destination="[[destination]]"
+              app-kiosk-mode="[[isInAppKioskMode_]]"
+              disabled="[[controlsDisabled_]]">
+          </print-preview-link-container>
+        </template>
+</if>
       </iron-collapse>
 <if expr="not chromeos">
+    <template is="dom-if" if="[[!newPrintPreviewLayout_]]">
       <print-preview-link-container destination="[[destination]]"
           app-kiosk-mode="[[isInAppKioskMode_]]"
           disabled="[[controlsDisabled_]]">
       </print-preview-link-container>
+    </template>
 </if>
     </div>
     <template is="dom-if" if="[[newPrintPreviewLayout_]]">
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/ntp_background/nux_ntp_background.html b/chrome/browser/resources/welcome/onboarding_welcome/ntp_background/nux_ntp_background.html
index d8b7649..87a747d 100644
--- a/chrome/browser/resources/welcome/onboarding_welcome/ntp_background/nux_ntp_background.html
+++ b/chrome/browser/resources/welcome/onboarding_welcome/ntp_background/nux_ntp_background.html
@@ -9,6 +9,7 @@
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-icon/iron-icon.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html">
 <link rel="import" href="../navigation_behavior.html">
+<link rel="import" href="../shared/chooser_shared_css.html">
 <link rel="import" href="../shared/i18n_setup.html">
 <link rel="import" href="../shared/module_metrics_proxy.html">
 <link rel="import" href="../shared/step_indicator.html">
@@ -16,7 +17,7 @@
 
 <dom-module id="nux-ntp-background">
   <template>
-    <style include="paper-button-style">
+    <style include="chooser-shared-css paper-button-style">
       :host {
         text-align: center;
       }
@@ -40,7 +41,7 @@
 
       #backgroundPreview::before {
         /* Copied from browser/resources/local_ntp/custom_backgrounds.js */
-        background-image: linear-gradient(rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.3));
+        background-image: linear-gradient(rgba(0, 0, 0, 0), rgba(0, 0, 0, .3));
         /* Pseudo element needs some content (even an empty string) to be
          * displayed. */
         content: '';
@@ -90,15 +91,10 @@
         width: 592px;
       }
 
-      .ntp-background-grid-button {
+      .option {
         align-items: stretch;
-        background: var(--cr-card-background-color);
-        border: solid 1px var(--google-grey-refresh-300);
         border-radius: 4px;
-        color: var(--cr-secondary-text-color);
-        cursor: pointer;
         display: flex;
-        flex-direction: column;
         height: 100%;
         overflow: hidden;
         padding: 0;
@@ -107,27 +103,52 @@
         width: 100%;
       }
 
-      #backgroundPreview.active + .content .ntp-background-grid-button {
+      #backgroundPreview.active + .content .option {
         border-color: var(--google-grey-refresh-700);
       }
 
-      .ntp-background-grid-button:hover {
-        box-shadow: 0 3px 6px 2px rgba(0, 36, 100, .1);
-      }
-
       /* Remove outline when button is focused using the mouse. */
-      .ntp-background-grid-button:focus:not(.keyboard-focused) {
+      .option:focus:not(.keyboard-focused) {
         outline: none;
       }
 
       .ntp-background-thumbnail {
-        background-color: white;
+        background-color: var(--cr-card-background-color);
         background-position: center center;
         background-repeat: no-repeat;
         background-size: cover;
         flex: 1;
       }
 
+      .option-name {
+        border-top: var(--cr-separator-line);
+        color: var(--navi-wallpaper-text-color);
+        height: 3rem;
+        line-height: 3rem;
+        overflow: hidden;
+        padding: 0 .75rem;
+        text-overflow: ellipsis;
+      }
+
+      .option[active] .option-name {
+        background: var(--cr-checked-color);
+        color: var(--cr-card-background-color);
+      }
+
+      .button-bar {
+        margin-top: 56px;
+      }
+
+      #skipButton {
+        background-color: var(--cr-card-background-color)
+      }
+
+      #skipButton:hover {
+        background-image:
+            linear-gradient(var(--hover-bg-color), var(--hover-bg-color));
+      }
+
+      /* Wallpaper Thumbnails */
       .art {
         background-image: url(../images/ntp_thumbnails/art.jpg);
       }
@@ -147,43 +168,6 @@
       .landscape {
         background-image: url(../images/ntp_thumbnails/landscape.jpg);
       }
-
-      .ntp-background-title {
-        border-top: var(--cr-separator-line);
-        font-size: 14px;
-        height: 48px;
-        line-height: 48px;
-        overflow: hidden;
-        padding: 0 12px;
-        text-overflow: ellipsis;
-      }
-
-      .ntp-background-grid-button[active] .ntp-background-title {
-        background: var(--google-blue-600);
-        color: white;
-      }
-
-      .button-bar {
-        display: flex;
-        justify-content: space-between;
-        margin-top: 56px;
-      }
-
-      .skip-button-container {
-        background: white;
-        border-radius: 4px;
-      }
-
-      iron-icon[icon='cr:chevron-right'] {
-        height: 20px;
-        margin-inline-end: -10px;
-        margin-inline-start: 6px;
-        width: 20px;
-      }
-
-      :host-context([dir=rtl]) iron-icon {
-        transform: scaleX(-1);
-      }
     </style>
     <div
         id="backgroundPreview"
@@ -198,24 +182,22 @@
         <template is="dom-repeat" items="[[backgrounds_]]">
           <button
               active$="[[isSelectedBackground_(item, selectedBackground_)]]"
-              class="ntp-background-grid-button"
+              class="option"
               on-click="onBackgroundClick_"
               on-keyup="onBackgroundKeyUp_"
               on-pointerdown="onBackgroundPointerDown_">
             <div
                 class$="ntp-background-thumbnail [[item.thumbnailClass]]">
             </div>
-            <div class="ntp-background-title">[[item.title]]</div>
+            <div class="option-name">[[item.title]]</div>
           </button>
         </template>
       </div>
 
       <div class="button-bar">
-        <div class="skip-button-container">
-          <paper-button id="skipButton" on-click="onSkipClicked_">
-            $i18n{skip}
-          </paper-button>
-        </div>
+        <paper-button id="skipButton" on-click="onSkipClicked_">
+          $i18n{skip}
+        </paper-button>
         <step-indicator model="[[indicatorModel]]"></step-indicator>
         <paper-button class="action-button"
             on-click="onNextClicked_">
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/shared/app_chooser.html b/chrome/browser/resources/welcome/onboarding_welcome/shared/app_chooser.html
index 5ad293f..11a9b56 100644
--- a/chrome/browser/resources/welcome/onboarding_welcome/shared/app_chooser.html
+++ b/chrome/browser/resources/welcome/onboarding_welcome/shared/app_chooser.html
@@ -19,6 +19,100 @@
 <dom-module id="app-chooser">
   <template>
     <style include="chooser-shared-css paper-button-style">
+      :host {
+        display: block;
+        white-space: nowrap;
+      }
+
+      .button-bar {
+        margin-top: 4rem;
+      }
+
+      .option {
+        -webkit-appearance: none;
+        align-items: center;
+        border-radius: 8px;
+        box-sizing: border-box;
+        display: inline-flex;
+        font-family: inherit;
+        height: 7.5rem;
+        justify-content: center;
+        outline: 0;
+        position: relative;
+        transition-duration: 500ms;
+        transition-property: box-shadow;
+        vertical-align: bottom;
+        width: 6.25rem;
+      }
+
+      .option:not(:first-of-type) {
+        margin-inline-start: 1.5rem;
+      }
+
+      .option[active] {
+        border: 1px solid var(--cr-checked-color);
+        color: var(--cr-checked-color);
+        font-weight: 500;
+      }
+
+      .option.keyboard-focused:focus {
+        outline: var(--navi-keyboard-focus-color) solid 3px;
+      }
+
+      .option-name {
+        flex-grow: 0;
+        line-height: 1.25rem;
+        text-align: center;
+        white-space: normal;
+      }
+
+      .option-icon {
+        background-position: center;
+        background-repeat: no-repeat;
+        background-size: contain;
+        height: 2rem;
+        margin: auto;
+        width: 2rem;
+      }
+
+      .option-icon-shadow {
+        background-color: var(--navi-option-icon-shadow-color);
+        border-radius: 50%;
+        display: flex;
+        height: 3rem;
+        margin-bottom: .25rem;
+        width: 3rem;
+      }
+
+      .option iron-icon {
+        --iron-icon-fill-color: var(--cr-card-background-color);
+        background: var(--navi-check-icon-color);
+        border-radius: 50%;
+        display: none;
+        height: .75rem;
+        margin: 0;
+        position: absolute;
+        right: .375rem;
+        top: .375rem;
+        width: .75rem;
+      }
+
+      :host-context([dir=rtl]) .option iron-icon {
+        left: .375rem;
+        right: unset;
+      }
+
+      .option.keyboard-focused:focus iron-icon[icon='cr:check'],
+      .option:hover iron-icon[icon='cr:check'],
+      .option[active] iron-icon[icon='cr:check'] {
+        display: block;
+      }
+
+      .option[active] iron-icon[icon='cr:check'] {
+        background: var(--cr-checked-color);
+      }
+
+      /* App Icons */
       .gmail {
         content: -webkit-image-set(
             url(chrome://welcome/images/gmail_1x.png) 1x,
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/shared/chooser_shared_css.html b/chrome/browser/resources/welcome/onboarding_welcome/shared/chooser_shared_css.html
index 5a0687fa..97449a2 100644
--- a/chrome/browser/resources/welcome/onboarding_welcome/shared/chooser_shared_css.html
+++ b/chrome/browser/resources/welcome/onboarding_welcome/shared/chooser_shared_css.html
@@ -7,109 +7,25 @@
 <dom-module id="chooser-shared-css">
   <template>
     <style include="navi-colors-css">
-      :host {
-        display: block;
-        white-space: nowrap;
-      }
-
       .option {
-        -webkit-appearance: none;
-        align-items: center;
         background: var(--cr-card-background-color);
         border: 1px solid var(--navi-border-color);
-        border-radius: 8px;
-        box-sizing: border-box;
         color: var(--cr-primary-text-color);
         cursor: pointer;
-        display: inline-flex;
         flex-direction: column;
-        font-family: inherit;
-        height: 7.5rem;
-        justify-content: center;
-        outline: 0;
-        position: relative;
-        transition-duration: 500ms;
-        transition-property: box-shadow;
-        vertical-align: bottom;
-        width: 6.25rem;
       }
 
       .option:hover {
         box-shadow: var(--navi-option-box-shadow);
       }
 
-      .option[active] {
-        border: 1px solid var(--cr-checked-color);
-        color: var(--cr-checked-color);
-        font-weight: 500;
-      }
-
-      .option:not(:first-of-type) {
-        margin-inline-start: 1.5rem;
-      }
-
-      .option.keyboard-focused:focus {
-        outline: var(--navi-keyboard-focus-color) solid 3px;
-      }
-
-      .option .option-name {
-        flex-grow: 0;
-        font-size: 0.875rem;
-        line-height: 1.25rem;
-        text-align: center;
-        white-space: normal;
-      }
-
-      .option .option-icon {
-        background-position: center;
-        background-repeat: no-repeat;
-        background-size: contain;
-        height: 2rem;
-        margin: auto;
-        width: 2rem;
-      }
-
-      .option .option-icon-shadow {
-        background-color: var(--navi-option-icon-shadow-color);
-        border-radius: 50%;
-        display: flex;
-        height: 3rem;
-        margin-bottom: 0.25rem;
-        width: 3rem;
-      }
-
-      :host-context([dir=rtl]) .option iron-icon {
-        left: 0.375rem;
-        right: unset;
-      }
-
-      .option iron-icon {
-        --iron-icon-fill-color: var(--cr-card-background-color);
-        background: var(--navi-check-icon-color);
-        border-radius: 50%;
-        display: none;
-        height: 0.75rem;
-        margin: 0;
-        position: absolute;
-        right: 0.375rem;
-        top: 0.375rem;
-        width: 0.75rem;
-      }
-
-      .option.keyboard-focused:focus iron-icon[icon='cr:check'],
-      .option:hover iron-icon[icon='cr:check'],
-      .option[active] iron-icon[icon='cr:check'] {
-        display: block;
-      }
-
-      .option[active] iron-icon[icon='cr:check'] {
-        background: var(--cr-checked-color);
+      .option-name {
+        font-size: .875rem;
       }
 
       .button-bar {
         display: flex;
         justify-content: space-between;
-        margin-top: 4rem;
       }
 
       :host-context([dir=rtl]) iron-icon[icon='cr:chevron-right'] {
@@ -118,8 +34,8 @@
 
       iron-icon[icon='cr:chevron-right'] {
         height: 1.25rem;
-        margin-inline-end: -0.625rem;
-        margin-inline-start: 0.375rem;
+        margin-inline-end: -.625rem;
+        margin-inline-start: .375rem;
         width: 1.25rem;
       }
     </style>
diff --git a/chrome/browser/resources/welcome/onboarding_welcome/shared/navi_colors_css.html b/chrome/browser/resources/welcome/onboarding_welcome/shared/navi_colors_css.html
index 2970436..66c0fd8 100644
--- a/chrome/browser/resources/welcome/onboarding_welcome/shared/navi_colors_css.html
+++ b/chrome/browser/resources/welcome/onboarding_welcome/shared/navi_colors_css.html
@@ -25,6 +25,7 @@
           opacity: .5;  /* Only in light mode */
         };
         --navi-step-indicator-color: var(--google-grey-200);
+        --navi-wallpaper-text-color: var(--google-grey-refresh-700);
       }
 
       :host-context([dark]) {
@@ -45,6 +46,7 @@
           background: var(--google-blue-refresh-300);
         };
         --navi-step-indicator-color: var(--google-grey-refresh-500);
+        --navi-wallpaper-text-color: var(--google-grey-200);
       }
     </style>
   </template>
diff --git a/chrome/browser/ui/bookmarks/bookmark_browsertest.cc b/chrome/browser/ui/bookmarks/bookmark_browsertest.cc
index 21d0f69..4995b84 100644
--- a/chrome/browser/ui/bookmarks/bookmark_browsertest.cc
+++ b/chrome/browser/ui/bookmarks/bookmark_browsertest.cc
@@ -7,6 +7,7 @@
 #include "base/macros.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/bind_test_util.h"
+#include "base/test/metrics/histogram_tester.h"
 #include "base/timer/timer.h"
 #include "build/build_config.h"
 #include "chrome/app/chrome_command_ids.h"
@@ -35,6 +36,7 @@
 #include "content/public/browser/web_contents.h"
 #include "content/public/test/browser_test_utils.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
+#include "testing/gmock/include/gmock/gmock.h"
 #include "ui/base/dragdrop/os_exchange_data.h"
 #include "ui/gfx/image/image_skia.h"
 
@@ -115,7 +117,13 @@
     return bookmark_model;
   }
 
+  base::HistogramTester* histogram_tester() { return &histogram_tester_; }
+
  private:
+  // We make the histogram tester a member field to make sure it starts
+  // recording as early as possible.
+  base::HistogramTester histogram_tester_;
+
   DISALLOW_COPY_AND_ASSIGN(BookmarkBrowsertest);
 };
 
@@ -315,3 +323,66 @@
 
   run_loop->Run();
 }
+
+// ChromeOS initializes two profiles (Default and test-user) and it's impossible
+// to distinguish UMA samples separately.
+#if !defined(OS_CHROMEOS)
+
+IN_PROC_BROWSER_TEST_F(BookmarkBrowsertest, PRE_EmitUmaForDuplicates) {
+  BookmarkModel* bookmark_model = WaitForBookmarkModel(browser()->profile());
+  const BookmarkNode* parent = bookmarks::GetParentForNewNodes(bookmark_model);
+  // Add one bookmark with a unique URL, two other bookmarks with a shared URL,
+  // and three more with another shared URL.
+  bookmark_model->AddURL(parent, parent->child_count(),
+                         base::ASCIIToUTF16("title1"), GURL("http://a.com"));
+  bookmark_model->AddURL(parent, parent->child_count(),
+                         base::ASCIIToUTF16("title2"), GURL("http://b.com"));
+  bookmark_model->AddURL(parent, parent->child_count(),
+                         base::ASCIIToUTF16("title3"), GURL("http://b.com"));
+  bookmark_model->AddURL(parent, parent->child_count(),
+                         base::ASCIIToUTF16("title4"), GURL("http://c.com"));
+  bookmark_model->AddURL(parent, parent->child_count(),
+                         base::ASCIIToUTF16("title5"), GURL("http://c.com"));
+  bookmark_model->AddURL(parent, parent->child_count(),
+                         base::ASCIIToUTF16("title6"), GURL("http://c.com"));
+}
+
+IN_PROC_BROWSER_TEST_F(BookmarkBrowsertest, EmitUmaForDuplicates) {
+  WaitForBookmarkModel(browser()->profile());
+
+  ASSERT_THAT(
+      histogram_tester()->GetAllSamples("Bookmarks.Count.OnProfileLoad"),
+      testing::ElementsAre(base::Bucket(/*min=*/6, /*count=*/1)));
+  EXPECT_THAT(histogram_tester()->GetAllSamples(
+                  "Bookmarks.Count.OnProfileLoad.DuplicateUrl"),
+              testing::ElementsAre(base::Bucket(/*min=*/5, /*count=*/1)));
+}
+
+IN_PROC_BROWSER_TEST_F(BookmarkBrowsertest, PRE_EmitUmaForEmptyTitles) {
+  BookmarkModel* bookmark_model = WaitForBookmarkModel(browser()->profile());
+  const BookmarkNode* parent = bookmarks::GetParentForNewNodes(bookmark_model);
+  // Add two bookmarks with a non-empty title and three with an empty one.
+  bookmark_model->AddURL(parent, parent->child_count(),
+                         base::ASCIIToUTF16("title1"), GURL("http://a.com"));
+  bookmark_model->AddURL(parent, parent->child_count(),
+                         base::ASCIIToUTF16("title2"), GURL("http://b.com"));
+  bookmark_model->AddURL(parent, parent->child_count(), base::string16(),
+                         GURL("http://c.com"));
+  bookmark_model->AddURL(parent, parent->child_count(), base::string16(),
+                         GURL("http://d.com"));
+  bookmark_model->AddURL(parent, parent->child_count(), base::string16(),
+                         GURL("http://e.com"));
+}
+
+IN_PROC_BROWSER_TEST_F(BookmarkBrowsertest, EmitUmaForEmptyTitles) {
+  WaitForBookmarkModel(browser()->profile());
+
+  ASSERT_THAT(
+      histogram_tester()->GetAllSamples("Bookmarks.Count.OnProfileLoad"),
+      testing::ElementsAre(base::Bucket(/*min=*/5, /*count=*/1)));
+  EXPECT_THAT(histogram_tester()->GetAllSamples(
+                  "Bookmarks.Count.OnProfileLoad.EmptyTitle"),
+              testing::ElementsAre(base::Bucket(/*min=*/3, /*count=*/1)));
+}
+
+#endif  // !defined(OS_CHROMEOS)
diff --git a/chrome/browser/ui/views/bookmarks/bookmark_bar_view_browsertest.cc b/chrome/browser/ui/views/bookmarks/bookmark_bar_view_browsertest.cc
index 3c7c75b..c7964ef 100644
--- a/chrome/browser/ui/views/bookmarks/bookmark_bar_view_browsertest.cc
+++ b/chrome/browser/ui/views/bookmarks/bookmark_bar_view_browsertest.cc
@@ -153,10 +153,10 @@
   }
 
   {
-    // Sec-Fetch-User: ?T
+    // Sec-Fetch-User: ?1
     CreateBookmarkForHeader("Sec-Fetch-User");
     NavigateToBookmark();
-    EXPECT_EQ("?T", GetContent());
+    EXPECT_EQ("?1", GetContent());
   }
 }
 
@@ -187,9 +187,9 @@
   }
 
   {
-    // Sec-Fetch-User: ?T
+    // Sec-Fetch-User: ?1
     CreateBookmarkForHeader("Sec-Fetch-User");
     NavigateToBookmark();
-    EXPECT_EQ("?T", GetContent());
+    EXPECT_EQ("?1", GetContent());
   }
 }
diff --git a/chrome/browser/ui/views/payments/payment_request_dialog_view.cc b/chrome/browser/ui/views/payments/payment_request_dialog_view.cc
index e9cbb44804..d88d6b8 100644
--- a/chrome/browser/ui/views/payments/payment_request_dialog_view.cc
+++ b/chrome/browser/ui/views/payments/payment_request_dialog_view.cc
@@ -85,6 +85,11 @@
     ++number_of_initialization_tasks_;
   }
 
+  if (!request->spec()->IsInitialized()) {
+    request->spec()->AddInitializationObserver(this);
+    ++number_of_initialization_tasks_;
+  }
+
   if (number_of_initialization_tasks_ > 0) {
     ShowProcessingSpinner();
   } else if (observer_for_testing_) {
diff --git a/chrome/browser/ui/views/payments/payment_request_show_promise_browsertest.cc b/chrome/browser/ui/views/payments/payment_request_show_promise_browsertest.cc
new file mode 100644
index 0000000..5b0eebff
--- /dev/null
+++ b/chrome/browser/ui/views/payments/payment_request_show_promise_browsertest.cc
@@ -0,0 +1,334 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/strings/utf_string_conversions.h"
+#include "build/build_config.h"
+#include "chrome/browser/ui/views/payments/payment_request_browsertest_base.h"
+#include "chrome/browser/ui/views/payments/payment_request_dialog_view_ids.h"
+#include "components/autofill/core/browser/autofill_profile.h"
+#include "components/autofill/core/browser/autofill_test_utils.h"
+#include "components/web_modal/web_contents_modal_dialog_manager.h"
+#include "content/public/test/browser_test_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/views/controls/label.h"
+#include "ui/views/view.h"
+
+namespace payments {
+namespace {
+
+class PaymentRequestShowPromiseTest : public PaymentRequestBrowserTestBase {
+ protected:
+  PaymentRequestShowPromiseTest() {}
+  ~PaymentRequestShowPromiseTest() override {}
+
+  // Installs the payment handler for "basic-card" that responds to
+  // "paymentrequest" events by echoing back the "total" object.
+  void InstallEchoPaymentHandlerForBasicCard() {
+    std::string contents;
+    ASSERT_TRUE(content::ExecuteScriptAndExtractString(
+        GetActiveWebContents(), "install();", &contents));
+    ASSERT_EQ(contents, "instruments.set(): Payment handler installed.");
+  }
+
+  // Allows to skip UI into payment handler for "basic-card".
+  void EnalbeSkipUIForForBasicCard() {
+    std::vector<PaymentRequest*> requests =
+        GetPaymentRequests(GetActiveWebContents());
+    ASSERT_EQ(1U, requests.size());
+    requests.front()
+        ->set_skip_ui_for_non_url_payment_method_identifiers_for_test();
+  }
+
+  // Shows the browser payment sheet.
+  void ShowBrowserPaymentSheet() {
+    ResetEventWaiterForSequence({DialogEvent::PROCESSING_SPINNER_SHOWN,
+                                 DialogEvent::PROCESSING_SPINNER_HIDDEN,
+                                 DialogEvent::SPEC_DONE_UPDATING,
+                                 DialogEvent::PROCESSING_SPINNER_HIDDEN,
+                                 DialogEvent::DIALOG_OPENED});
+    ASSERT_TRUE(content::ExecuteScript(GetActiveWebContents(), "buy();"));
+    WaitForObservedEvent();
+    EXPECT_TRUE(web_modal::WebContentsModalDialogManager::FromWebContents(
+                    GetActiveWebContents())
+                    ->IsDialogActive());
+  }
+
+  // Verifies that the payment sheet total is |total_amount_string|.
+  void ExpectTotal(const std::string& total_amount_string) {
+    EXPECT_EQ(base::ASCIIToUTF16(total_amount_string),
+              GetLabelText(DialogViewID::ORDER_SUMMARY_TOTAL_AMOUNT_LABEL));
+  }
+
+  // Verifies that the shipping address section does not display any warning
+  // messages.
+  void ExpectNoShippingWarningMessage() {
+    views::View* view = dialog_view()->GetViewByID(
+        static_cast<int>(DialogViewID::WARNING_LABEL));
+    if (!view || !view->visible())
+      return;
+
+    EXPECT_EQ(base::string16(), static_cast<views::Label*>(view)->text());
+  }
+
+  // Verifies that the shipping address section has |expected_message| in the
+  // header.
+  void ExpectShippingWarningMessage(const std::string& expected_message) {
+    EXPECT_EQ(base::ASCIIToUTF16(expected_message),
+              GetLabelText(DialogViewID::WARNING_LABEL));
+  }
+
+  // Selects another shipping address.
+  void SelectAnotherShippingAddress() {
+    ResetEventWaiterForSequence({DialogEvent::PROCESSING_SPINNER_SHOWN,
+                                 DialogEvent::PROCESSING_SPINNER_HIDDEN,
+                                 DialogEvent::SPEC_DONE_UPDATING});
+    ClickOnChildInListViewAndWait(
+        /*child_index=*/1, /*total_num_children=*/2,
+        DialogViewID::SHIPPING_ADDRESS_SHEET_LIST_VIEW);
+  }
+
+  // Selects the only shipping address.
+  void SelectTheOnlyShippingAddress() {
+    ResetEventWaiterForSequence({DialogEvent::PROCESSING_SPINNER_SHOWN,
+                                 DialogEvent::PROCESSING_SPINNER_HIDDEN,
+                                 DialogEvent::SPEC_DONE_UPDATING});
+    ClickOnChildInListViewAndWait(
+        /*child_index=*/0, /*total_num_children=*/1,
+        DialogViewID::SHIPPING_ADDRESS_SHEET_LIST_VIEW);
+  }
+
+  // Verifies that the first shipping option cost is |amount_string|.
+  void ExpectShippingCost(const std::string& amount_string) {
+    EXPECT_EQ(base::ASCIIToUTF16(amount_string),
+              GetLabelText(DialogViewID::SHIPPING_OPTION_AMOUNT));
+  }
+
+  // Clicks the "Pay" button and waits for the dialog to close.
+  void Pay() {
+    ResetEventWaiterForSequence(
+        {DialogEvent::PROCESSING_SPINNER_SHOWN, DialogEvent::DIALOG_CLOSED});
+    ClickOnDialogViewAndWait(DialogViewID::PAY_BUTTON, dialog_view());
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(PaymentRequestShowPromiseTest);
+};
+
+IN_PROC_BROWSER_TEST_F(PaymentRequestShowPromiseTest, DigitalGoods) {
+  NavigateTo("/show_promise/digital_goods.html");
+  InstallEchoPaymentHandlerForBasicCard();
+  ASSERT_TRUE(content::ExecuteScript(GetActiveWebContents(), "create();"));
+  ShowBrowserPaymentSheet();
+
+  EXPECT_TRUE(IsPayButtonEnabled());
+
+  OpenOrderSummaryScreen();
+
+  ExpectTotal("$1.00");
+
+  ClickOnBackArrow();
+  Pay();
+
+  ExpectBodyContains({R"({"currency":"USD","value":"1.00"})"});
+}
+
+IN_PROC_BROWSER_TEST_F(PaymentRequestShowPromiseTest, SingleOptionShipping) {
+  NavigateTo("/show_promise/single_option_shipping.html");
+  InstallEchoPaymentHandlerForBasicCard();
+  AddAutofillProfile(autofill::test::GetFullProfile());
+  AddAutofillProfile(autofill::test::GetFullProfile2());
+  ShowBrowserPaymentSheet();
+
+  EXPECT_TRUE(IsPayButtonEnabled());
+
+  OpenOrderSummaryScreen();
+
+  ExpectTotal("$1.00");
+
+  ClickOnBackArrow();
+  OpenShippingAddressSectionScreen();
+
+  ExpectNoShippingWarningMessage();
+
+  SelectAnotherShippingAddress();
+
+  ExpectNoShippingWarningMessage();
+
+  ClickOnBackArrow();
+  OpenShippingOptionSectionScreen();
+
+  ExpectShippingCost("$0.00");
+
+  ClickOnBackArrow();
+
+  EXPECT_TRUE(IsPayButtonEnabled());
+
+  Pay();
+
+  ExpectBodyContains({R"({"currency":"USD","value":"1.00"})"});
+}
+
+IN_PROC_BROWSER_TEST_F(PaymentRequestShowPromiseTest,
+                       SingleOptionShippingWithUpdate) {
+  NavigateTo("/show_promise/single_option_shipping_with_update.html");
+  InstallEchoPaymentHandlerForBasicCard();
+  AddAutofillProfile(autofill::test::GetFullProfile());
+  AddAutofillProfile(autofill::test::GetFullProfile2());
+  ShowBrowserPaymentSheet();
+
+  EXPECT_TRUE(IsPayButtonEnabled());
+
+  OpenOrderSummaryScreen();
+
+  ExpectTotal("$1.00");
+
+  ClickOnBackArrow();
+  OpenShippingAddressSectionScreen();
+
+  ExpectNoShippingWarningMessage();
+
+  SelectAnotherShippingAddress();
+
+  ExpectNoShippingWarningMessage();
+
+  ClickOnBackArrow();
+  OpenShippingOptionSectionScreen();
+
+  ExpectShippingCost("$0.00");
+
+  ClickOnBackArrow();
+
+  EXPECT_TRUE(IsPayButtonEnabled());
+
+  Pay();
+
+  ExpectBodyContains({R"({"currency":"USD","value":"1.00"})"});
+}
+
+IN_PROC_BROWSER_TEST_F(PaymentRequestShowPromiseTest, CannotShipError) {
+  NavigateTo("/show_promise/us_only_shipping.html");
+  InstallEchoPaymentHandlerForBasicCard();
+  AddAutofillProfile(autofill::test::GetFullCanadianProfile());
+  ShowBrowserPaymentSheet();
+
+  EXPECT_FALSE(IsPayButtonEnabled());
+
+  OpenOrderSummaryScreen();
+
+  ExpectTotal("$1.00");
+
+  ClickOnBackArrow();
+  OpenShippingAddressSectionScreen();
+
+  ExpectShippingWarningMessage(
+      "To see shipping methods and requirements, select an address");
+
+  SelectTheOnlyShippingAddress();
+
+  ExpectShippingWarningMessage("Cannot ship outside of US.");
+
+  ClickOnBackArrow();
+
+  EXPECT_FALSE(IsPayButtonEnabled());
+
+  ClickOnCancel();
+}
+
+#if defined(OS_WIN) || defined(OS_MACOSX)
+// Times out flakily on Windows and Mac.
+#define CanShipAfterCheckingAddress_MAYBE CanShipAfterCheckingAddress_DISABLED
+#else
+#define CanShipAfterCheckingAddress_MAYBE CanShipAfterCheckingAddress
+#endif
+IN_PROC_BROWSER_TEST_F(PaymentRequestShowPromiseTest,
+                       CanShipAfterCheckingAddress_MAYBE) {
+  NavigateTo("/show_promise/us_only_shipping.html");
+  InstallEchoPaymentHandlerForBasicCard();
+  AddAutofillProfile(autofill::test::GetFullProfile());
+  ShowBrowserPaymentSheet();
+
+  EXPECT_FALSE(IsPayButtonEnabled());
+
+  OpenOrderSummaryScreen();
+
+  ExpectTotal("$1.00");
+
+  ClickOnBackArrow();
+  OpenShippingAddressSectionScreen();
+
+  ExpectShippingWarningMessage(
+      "To see shipping methods and requirements, select an address");
+
+  SelectTheOnlyShippingAddress();
+
+  EXPECT_TRUE(IsPayButtonEnabled());
+
+  Pay();
+
+  ExpectBodyContains({R"({"currency":"USD","value":"1.00"})"});
+}
+
+IN_PROC_BROWSER_TEST_F(PaymentRequestShowPromiseTest, SkipUI) {
+  NavigateTo("/show_promise/digital_goods.html");
+  InstallEchoPaymentHandlerForBasicCard();
+  ASSERT_TRUE(content::ExecuteScript(GetActiveWebContents(), "create();"));
+  EnalbeSkipUIForForBasicCard();
+  ResetEventWaiterForSequence(
+      {DialogEvent::PROCESSING_SPINNER_SHOWN,
+       DialogEvent::PROCESSING_SPINNER_HIDDEN, DialogEvent::SPEC_DONE_UPDATING,
+       DialogEvent::PROCESSING_SPINNER_HIDDEN, DialogEvent::DIALOG_OPENED,
+       DialogEvent::PROCESSING_SPINNER_SHOWN, DialogEvent::DIALOG_CLOSED});
+  ASSERT_TRUE(content::ExecuteScript(GetActiveWebContents(), "buy();"));
+  WaitForObservedEvent();
+
+  ExpectBodyContains({R"({"currency":"USD","value":"1.00"})"});
+}
+
+IN_PROC_BROWSER_TEST_F(PaymentRequestShowPromiseTest, Reject) {
+  NavigateTo("/show_promise/reject.html");
+  ResetEventWaiterForSequence(
+      {DialogEvent::PROCESSING_SPINNER_SHOWN, DialogEvent::DIALOG_CLOSED});
+  ASSERT_TRUE(content::ExecuteScript(GetActiveWebContents(), "buy();"));
+  WaitForObservedEvent();
+
+  ExpectBodyContains({R"(AbortError)"});
+}
+
+IN_PROC_BROWSER_TEST_F(PaymentRequestShowPromiseTest, Timeout) {
+  NavigateTo("/show_promise/timeout.html");
+  ResetEventWaiterForSequence(
+      {DialogEvent::PROCESSING_SPINNER_SHOWN, DialogEvent::DIALOG_CLOSED});
+  ASSERT_TRUE(content::ExecuteScript(GetActiveWebContents(), "buy();"));
+  WaitForObservedEvent();
+
+  ExpectBodyContains({R"(AbortError)"});
+}
+
+IN_PROC_BROWSER_TEST_F(PaymentRequestShowPromiseTest,
+                       UnsupportedPaymentMethod) {
+  NavigateTo("/show_promise/unsupported.html");
+  ResetEventWaiterForSequence(
+      {DialogEvent::PROCESSING_SPINNER_SHOWN,
+       DialogEvent::PROCESSING_SPINNER_HIDDEN, DialogEvent::SPEC_DONE_UPDATING,
+       DialogEvent::PROCESSING_SPINNER_HIDDEN, DialogEvent::NOT_SUPPORTED_ERROR,
+       DialogEvent::DIALOG_CLOSED});
+  ASSERT_TRUE(content::ExecuteScript(GetActiveWebContents(), "buy();"));
+  WaitForObservedEvent();
+
+  ExpectBodyContains(
+      {R"(NotSupportedError: The payment method "foo" is not supported)"});
+}
+
+IN_PROC_BROWSER_TEST_F(PaymentRequestShowPromiseTest, InvalidDetails) {
+  NavigateTo("/show_promise/invalid_details.html");
+  ResetEventWaiterForSequence(
+      {DialogEvent::PROCESSING_SPINNER_SHOWN, DialogEvent::DIALOG_CLOSED});
+  ASSERT_TRUE(content::ExecuteScript(GetActiveWebContents(), "buy();"));
+  WaitForObservedEvent();
+
+  ExpectBodyContains({R"(Total amount value should be non-negative)"});
+}
+
+}  // namespace
+}  // namespace payments
diff --git a/chrome/browser/vr/elements/indicator_spec.cc b/chrome/browser/vr/elements/indicator_spec.cc
index f44e73f..80c5251 100644
--- a/chrome/browser/vr/elements/indicator_spec.cc
+++ b/chrome/browser/vr/elements/indicator_spec.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include "chrome/browser/vr/elements/indicator_spec.h"
+#include "build/build_config.h"
 #include "chrome/browser/vr/ui_support.h"
 #include "chrome/grit/generated_resources.h"
 
@@ -80,7 +81,26 @@
        IDS_VR_SHELL_BG_IS_SHARING_SCREEN,
        IDS_VR_SHELL_SITE_CAN_SHARE_SCREEN,
        &CapturingStateModel::screen_capture_enabled,
-       false}};
+       false},
+
+#if !defined(OS_ANDROID)
+      {kUsbConnectedIndicator, kWebXrUsbConnectedIndicator,
+       GetVrIcon(kVrUsbIcon),
+       IDS_VR_SHELL_SITE_IS_USING_USB,
+       0,
+       0,
+       &CapturingStateModel::usb_connected,
+       false},
+
+       {kMidiConnectedIndicator, kWebXrMidiConnectedIndicator,
+       GetVrIcon(kVrMidiIcon),
+       IDS_VR_SHELL_SITE_IS_USING_MIDI,
+       0,
+       IDS_VR_SHELL_SITE_CAN_USE_MIDI,
+       &CapturingStateModel::midi_connected,
+       false},
+#endif
+  };
 
   return specs;
 }
diff --git a/chrome/browser/vr/elements/ui_element_name.cc b/chrome/browser/vr/elements/ui_element_name.cc
index cdc4cb6..b8d1a0c 100644
--- a/chrome/browser/vr/elements/ui_element_name.cc
+++ b/chrome/browser/vr/elements/ui_element_name.cc
@@ -148,6 +148,10 @@
     "kContentRepositionHitPlane",
     "kContentRepositionVisibilityToggle",
     "kWebXrExternalPromptNotification",
+    "kUsbConnectedIndicator",
+    "kWebXrUsbConnectedIndicator",
+    "kMidiConnectedIndicator",
+    "kWebXrMidiConnectedIndicator",
 };
 
 static_assert(
diff --git a/chrome/browser/vr/elements/ui_element_name.h b/chrome/browser/vr/elements/ui_element_name.h
index 42feb49..0ab4061b 100644
--- a/chrome/browser/vr/elements/ui_element_name.h
+++ b/chrome/browser/vr/elements/ui_element_name.h
@@ -149,6 +149,10 @@
   kContentRepositionHitPlane,
   kContentRepositionVisibilityToggle,
   kWebXrExternalPromptNotification,
+  kUsbConnectedIndicator,
+  kWebXrUsbConnectedIndicator,
+  kMidiConnectedIndicator,
+  kWebXrMidiConnectedIndicator,
 
   // This must be last.
   kNumUiElementNames,
diff --git a/chrome/browser/vr/model/capturing_state_model.h b/chrome/browser/vr/model/capturing_state_model.h
index 6e5f0d25..d1a8e927 100644
--- a/chrome/browser/vr/model/capturing_state_model.h
+++ b/chrome/browser/vr/model/capturing_state_model.h
@@ -17,23 +17,27 @@
   bool screen_capture_enabled = false;
   bool location_access_enabled = false;
   bool bluetooth_connected = false;
+  bool usb_connected = false;
+  bool midi_connected = false;
 
   bool operator==(const CapturingStateModel& rhs) const {
     return audio_capture_enabled == rhs.audio_capture_enabled &&
            video_capture_enabled == rhs.video_capture_enabled &&
            screen_capture_enabled == rhs.screen_capture_enabled &&
            location_access_enabled == rhs.location_access_enabled &&
-           bluetooth_connected == rhs.bluetooth_connected;
+           bluetooth_connected == rhs.bluetooth_connected &&
+           usb_connected == rhs.usb_connected &&
+           midi_connected == rhs.midi_connected;
   }
 
   bool operator!=(const CapturingStateModel& rhs) const {
     return !(*this == rhs);
   }
 
-  bool IsAnyCapturingEnabled() const {
+  bool IsAtleastOnePermissionGrantedOrInUse() const {
     return audio_capture_enabled || video_capture_enabled ||
            screen_capture_enabled || location_access_enabled ||
-           bluetooth_connected;
+           bluetooth_connected || usb_connected || midi_connected;
   }
 };
 
diff --git a/chrome/browser/vr/ui_scene_creator.cc b/chrome/browser/vr/ui_scene_creator.cc
index fd69a8b..755818c 100644
--- a/chrome/browser/vr/ui_scene_creator.cc
+++ b/chrome/browser/vr/ui_scene_creator.cc
@@ -968,7 +968,7 @@
   // upfront (struct potential_capture). Then, when a device gets accessed,
   // an indicator notifies the user about its usage.
   // The below logic tries to capture this logic.
-  bool initial_toasts = !active_capture.IsAnyCapturingEnabled();
+  bool initial_toasts = !active_capture.IsAtleastOnePermissionGrantedOrInUse();
   if (active_capture != last_active_capture ||
       potential_capture != last_potential_capture) {
     auto specs = GetIndicatorSpecs();
diff --git a/chrome/browser/vr/ui_support.cc b/chrome/browser/vr/ui_support.cc
index 47e15234..35f55a55 100644
--- a/chrome/browser/vr/ui_support.cc
+++ b/chrome/browser/vr/ui_support.cc
@@ -66,6 +66,10 @@
 #if !defined(OS_ANDROID)
     case kVrOpenInBrowserIcon:
       return kOpenInBrowserIcon;
+    case kVrUsbIcon:
+      return vector_icons::kUsbIcon;
+    case kVrMidiIcon:
+      return vector_icons::kMidiIcon;
 #endif
     default:
       NOTREACHED();
diff --git a/chrome/browser/vr/ui_support.h b/chrome/browser/vr/ui_support.h
index 97c6374..41803681 100644
--- a/chrome/browser/vr/ui_support.h
+++ b/chrome/browser/vr/ui_support.h
@@ -45,6 +45,8 @@
   kVrDaydreamControllerAppButtonIcon,
   kVrDaydreamControllerHomeButtonIcon,
   kVrOpenInBrowserIcon,
+  kVrUsbIcon,
+  kVrMidiIcon,
 };
 
 VR_BASE_EXPORT const gfx::VectorIcon& GetVrIcon(VrIconId icon);
diff --git a/chrome/browser/vr/ui_unittest.cc b/chrome/browser/vr/ui_unittest.cc
index 070be975..86688f1c 100644
--- a/chrome/browser/vr/ui_unittest.cc
+++ b/chrome/browser/vr/ui_unittest.cc
@@ -181,6 +181,10 @@
 
   for (auto& spec : GetIndicatorSpecs()) {
     for (int i = 0; i < 3; ++i) {
+#if defined(OS_WIN)
+      if (i == 1)  // Windows doesn't look at background capturing. Skip.
+        continue;
+#endif
       browser_ui->SetWebVrMode(true);
       ui_->GetSchedulerUiPtr()->OnWebXrFrameAvailable();
 
@@ -189,14 +193,34 @@
       CapturingStateModel potential_capturing;
       active_capturing.*spec.signal = i == 0;
       // High accuracy location cannot be used in a background tab.
+      // Also, capturing USB and Midi cannot be done in background tab.
       background_capturing.*spec.signal =
-          i == 1 && spec.name != kLocationAccessIndicator;
-      potential_capturing.*spec.signal = true;
+          i == 1 && spec.name != kLocationAccessIndicator &&
+          spec.name != kUsbConnectedIndicator &&
+          spec.name != kMidiConnectedIndicator;
+      potential_capturing.*spec.signal =
+          i == 2 && spec.name != kUsbConnectedIndicator;
+
+      int string_id = 0;
+      switch (i) {
+        case 0:
+          string_id = spec.resource_string;
+          break;
+        case 1:
+          string_id = spec.background_resource_string;
+          break;
+        case 2:
+          string_id = spec.potential_resource_string;
+          break;
+        default:
+          NOTREACHED();
+          break;
+      }
 
       browser_ui->SetCapturingState(active_capturing, background_capturing,
                                     potential_capturing);
       EXPECT_TRUE(IsVisible(kWebVrExclusiveScreenToast));
-      EXPECT_TRUE(IsVisible(spec.webvr_name));
+      EXPECT_TRUE(IsVisible(spec.webvr_name) == (string_id != 0));
       EXPECT_TRUE(RunForSeconds(kToastTimeoutSeconds + kSmallDelaySeconds));
       EXPECT_FALSE(IsVisible(kWebVrExclusiveScreenToast));
 
@@ -624,6 +648,8 @@
   model_->active_capturing.screen_capture_enabled = true;
   model_->active_capturing.location_access_enabled = true;
   model_->active_capturing.bluetooth_connected = true;
+  model_->active_capturing.usb_connected = true;
+  model_->active_capturing.midi_connected = true;
 
   VerifyOnlyElementsVisible("Elements hidden",
                             std::set<UiElementName>{kWebVrBackground});
@@ -653,6 +679,8 @@
   model_->active_capturing.screen_capture_enabled = true;
   model_->active_capturing.location_access_enabled = true;
   model_->active_capturing.bluetooth_connected = true;
+  model_->active_capturing.usb_connected = true;
+  model_->active_capturing.midi_connected = true;
 
   // Transition to WebVR mode
   ui_->GetBrowserUiWeakPtr()->SetWebVrMode(true);
@@ -680,6 +708,8 @@
   model_->active_capturing.screen_capture_enabled = true;
   model_->active_capturing.location_access_enabled = true;
   model_->active_capturing.bluetooth_connected = true;
+  model_->active_capturing.usb_connected = true;
+  model_->active_capturing.midi_connected = true;
   EXPECT_TRUE(VerifyVisibility(indicators, true));
   EXPECT_TRUE(VerifyRequiresLayout(indicators, true));
 
@@ -701,6 +731,8 @@
   model_->active_capturing.screen_capture_enabled = false;
   model_->active_capturing.location_access_enabled = false;
   model_->active_capturing.bluetooth_connected = false;
+  model_->active_capturing.usb_connected = false;
+  model_->active_capturing.midi_connected = false;
   EXPECT_TRUE(VerifyRequiresLayout(indicators, false));
 }
 
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 5b9dee98..13e679e 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -1681,6 +1681,7 @@
         "../browser/ui/views/payments/payment_request_payment_app_browsertest.cc",
         "../browser/ui/views/payments/payment_request_payment_response_browsertest.cc",
         "../browser/ui/views/payments/payment_request_shipping_address_instance_browsertest.cc",
+        "../browser/ui/views/payments/payment_request_show_promise_browsertest.cc",
         "../browser/ui/views/payments/payment_request_update_with_browsertest.cc",
         "../browser/ui/views/payments/payment_request_use_stats_browsertest.cc",
         "../browser/ui/views/payments/payment_sheet_view_controller_browsertest.cc",
@@ -2421,9 +2422,12 @@
   ]
 
   data = [
-    "//testing:run_perf_test",
     "//tools/perf/contrib/cluster_telemetry/",
   ]
+
+  data_deps = [
+    "//testing:run_perf_test",
+  ]
 }
 
 # New target that will replace telemetry_perf_tests when testing
diff --git a/chrome/test/data/webui/print_preview/system_dialog_browsertest.js b/chrome/test/data/webui/print_preview/system_dialog_browsertest.js
index 7adbad5..ff6774a 100644
--- a/chrome/test/data/webui/print_preview/system_dialog_browsertest.js
+++ b/chrome/test/data/webui/print_preview/system_dialog_browsertest.js
@@ -45,14 +45,15 @@
       const previewArea = page.$.previewArea;
       pluginProxy.setLoadCallback(previewArea.onPluginLoad_.bind(previewArea));
       sidebar = page.$$('print-preview-sidebar');
-      linkContainer = sidebar.$$('print-preview-link-container');
       return Promise
           .all([
+            test_util.waitForRender(page),
             print_preview.Model.whenReady(),
             nativeLayer.whenCalled('getInitialSettings'),
             nativeLayer.whenCalled('getPrinterCapabilities'),
           ])
           .then(function() {
+            linkContainer = sidebar.$$('print-preview-link-container');
             return nativeLayer.whenCalled('getPreview');
           })
           .then(function() {
diff --git a/chrome/test/data/webui/welcome/nux_ntp_background_test.js b/chrome/test/data/webui/welcome/nux_ntp_background_test.js
index b8835944..0dd420fe 100644
--- a/chrome/test/data/webui/welcome/nux_ntp_background_test.js
+++ b/chrome/test/data/webui/welcome/nux_ntp_background_test.js
@@ -58,25 +58,22 @@
     });
 
     test('test displaying default and custom background', function() {
-      const options = testElement.shadowRoot.querySelectorAll(
-          '.ntp-background-grid-button');
+      const options = testElement.shadowRoot.querySelectorAll('.option');
       assertEquals(3, options.length);
 
       // the first option should be the 'Default' option
       assertEquals(
-          options[0].querySelector('.ntp-background-title').innerText,
-          'Default');
+          options[0].querySelector('.option-name').innerText, 'Default');
 
       for (let i = 0; i < backgrounds.length; i++) {
         assertEquals(
-            options[i + 1].querySelector('.ntp-background-title').innerText,
+            options[i + 1].querySelector('.option-name').innerText,
             backgrounds[i].title);
       }
     });
 
     test('test previewing a background and going back to default', function() {
-      const options = testElement.shadowRoot.querySelectorAll(
-          '.ntp-background-grid-button');
+      const options = testElement.shadowRoot.querySelectorAll('.option');
 
       options[1].click();
       return testNtpBackgroundProxy.whenCalled('preloadImage').then(() => {
@@ -98,8 +95,7 @@
     });
 
     test('test activating a background', function() {
-      const options = testElement.shadowRoot.querySelectorAll(
-          '.ntp-background-grid-button');
+      const options = testElement.shadowRoot.querySelectorAll('.option');
 
       options[1].click();
       assertFalse(options[0].hasAttribute('active'));
@@ -109,8 +105,7 @@
 
     test('test setting the background when hitting next', function() {
       // select the first non-default option and hit 'Next'
-      const options = testElement.shadowRoot.querySelectorAll(
-          '.ntp-background-grid-button');
+      const options = testElement.shadowRoot.querySelectorAll('.option');
       options[1].click();
       testElement.$$('.action-button').click();
       return Promise
@@ -124,8 +119,7 @@
     });
 
     test('test metrics for selecting an option and skipping', function() {
-      const options = testElement.shadowRoot.querySelectorAll(
-          '.ntp-background-grid-button');
+      const options = testElement.shadowRoot.querySelectorAll('.option');
       options[1].click();
       testElement.$.skipButton.click();
       return testMetricsProxy.whenCalled('recordChoseAnOptionAndChoseSkip');
@@ -135,8 +129,7 @@
         'test metrics for when there is an error previewing the background',
         function() {
           testNtpBackgroundProxy.setPreloadImageSuccess(false);
-          const options = testElement.shadowRoot.querySelectorAll(
-              '.ntp-background-grid-button');
+          const options = testElement.shadowRoot.querySelectorAll('.option');
           options[1].click();
           return testNtpBackgroundProxy.whenCalled(
               'recordBackgroundImageFailedToLoad');
@@ -146,8 +139,7 @@
         `test metrics aren't sent when previewing the background is a success`,
         function() {
           testNtpBackgroundProxy.setPreloadImageSuccess(true);
-          const options = testElement.shadowRoot.querySelectorAll(
-              '.ntp-background-grid-button');
+          const options = testElement.shadowRoot.querySelectorAll('.option');
           options[1].click();
           return testNtpBackgroundProxy.whenCalled('preloadImage').then(() => {
             assertEquals(
@@ -159,8 +151,7 @@
 
     test('test metrics for load times of background images', function() {
       testNtpBackgroundProxy.setPreloadImageSuccess(true);
-      const options = testElement.shadowRoot.querySelectorAll(
-          '.ntp-background-grid-button');
+      const options = testElement.shadowRoot.querySelectorAll('.option');
       options[1].click();
       return testNtpBackgroundProxy.whenCalled('recordBackgroundImageLoadTime');
     });
@@ -177,8 +168,7 @@
 
     test('test clearing the background when default is selected', function() {
       // select the default option and hit 'Next'
-      const options = testElement.shadowRoot.querySelectorAll(
-          '.ntp-background-grid-button');
+      const options = testElement.shadowRoot.querySelectorAll('.option');
       options[0].click();
       testElement.$$('.action-button').click();
       return testNtpBackgroundProxy.whenCalled('clearBackground');
diff --git a/chrome/tools/build/mac/copy_keystone_framework.py b/chrome/tools/build/mac/copy_keystone_framework.py
index 513fdc91..7d34575a 100644
--- a/chrome/tools/build/mac/copy_keystone_framework.py
+++ b/chrome/tools/build/mac/copy_keystone_framework.py
@@ -36,10 +36,6 @@
        os.path.join(args[1], 'Versions/Current/'),
        output_path])
 
-  # Thin the library to just the required arch.
-  library_path = os.path.join(output_path, 'KeystoneRegistration')
-  subprocess.check_call(
-      ['lipo', '-thin', 'x86_64', library_path, '-o', library_path])
   return 0
 
 
diff --git a/components/bookmarks/browser/bookmark_storage.cc b/components/bookmarks/browser/bookmark_storage.cc
index 4131094..7299cef 100644
--- a/components/bookmarks/browser/bookmark_storage.cc
+++ b/components/bookmarks/browser/bookmark_storage.cc
@@ -6,6 +6,7 @@
 
 #include <stddef.h>
 #include <algorithm>
+#include <unordered_map>
 #include <utility>
 
 #include "base/bind.h"
@@ -14,6 +15,7 @@
 #include "base/json/json_file_value_serializer.h"
 #include "base/json/json_reader.h"
 #include "base/json/json_string_value_serializer.h"
+#include "base/metrics/histogram_functions.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/numerics/safe_conversions.h"
 #include "base/sequenced_task_runner.h"
@@ -56,9 +58,61 @@
   }
 }
 
+// Helper function to recursively traverse the bookmark tree and count the
+// number of bookmarks (excluding folders) per URL (more precisely, per URL
+// hash).
+void PopulateNumNodesPerUrlHash(
+    const BookmarkNode* node,
+    std::unordered_map<size_t, int>* num_nodes_per_url_hash) {
+  DCHECK(num_nodes_per_url_hash);
+  DCHECK(node);
+
+  if (!node->is_folder())
+    (*num_nodes_per_url_hash)[std::hash<std::string>()(node->url().spec())]++;
+
+  for (int i = 0; i < node->child_count(); ++i)
+    PopulateNumNodesPerUrlHash(node->GetChild(i), num_nodes_per_url_hash);
+}
+
+// Computes the number of bookmarks (excluding folders) with a URL that is used
+// by at least one other bookmark.
+int GetNumDuplicateUrls(const BookmarkNode* root) {
+  DCHECK(root);
+
+  // The key is hash of the URL, instead of the full URL, to keep memory usage
+  // low. The value indicates the node count.
+  std::unordered_map<size_t, int> num_nodes_per_url_hash;
+  PopulateNumNodesPerUrlHash(root, &num_nodes_per_url_hash);
+
+  int num_duplicate_urls = 0;
+  for (const auto& url_hash_and_count : num_nodes_per_url_hash) {
+    if (url_hash_and_count.second > 1)
+      num_duplicate_urls += url_hash_and_count.second;
+  }
+  return num_duplicate_urls;
+}
+
+// Computes the number of bookmarks with an empty title. This includes folders
+// too except for the root.
+int GetNumNodesWithEmptyTitle(const BookmarkNode* node) {
+  DCHECK(node);
+
+  int num_nodes_with_empty_title = 0;
+
+  if (!node->is_root() && node->GetTitle().empty())
+    ++num_nodes_with_empty_title;
+
+  for (int i = 0; i < node->child_count(); ++i)
+    num_nodes_with_empty_title += GetNumNodesWithEmptyTitle(node->GetChild(i));
+
+  return num_nodes_with_empty_title;
+}
+
 }  // namespace
 
-void LoadBookmarks(const base::FilePath& path, BookmarkLoadDetails* details) {
+void LoadBookmarks(const base::FilePath& path,
+                   bool emit_experimental_uma,
+                   BookmarkLoadDetails* details) {
   bool load_index = false;
   bool bookmark_file_exists = base::PathExists(path);
   if (bookmark_file_exists) {
@@ -120,6 +174,26 @@
   UMA_HISTOGRAM_COUNTS_100000(
       "Bookmarks.Count.OnProfileLoad",
       base::saturated_cast<int>(details->url_index()->UrlCount()));
+
+  if (emit_experimental_uma && details->root_node()) {
+    TimeTicks start_time = TimeTicks::Now();
+
+    int num_duplicate_urls = GetNumDuplicateUrls(details->root_node());
+    if (num_duplicate_urls > 0) {
+      base::UmaHistogramCounts10000(
+          "Bookmarks.Count.OnProfileLoad.DuplicateUrl", num_duplicate_urls);
+    }
+
+    int num_nodes_with_empty_title =
+        GetNumNodesWithEmptyTitle(details->root_node());
+    if (num_nodes_with_empty_title > 0) {
+      base::UmaHistogramCounts10000("Bookmarks.Count.OnProfileLoad.EmptyTitle",
+                                    num_nodes_with_empty_title);
+    }
+
+    UMA_HISTOGRAM_TIMES("Bookmarks.DuplicateAndEmptyTitleDetectionTime",
+                        TimeTicks::Now() - start_time);
+  }
 }
 
 // BookmarkLoadDetails ---------------------------------------------------------
diff --git a/components/bookmarks/browser/bookmark_storage.h b/components/bookmarks/browser/bookmark_storage.h
index 1abc6da..c611e5f 100644
--- a/components/bookmarks/browser/bookmark_storage.h
+++ b/components/bookmarks/browser/bookmark_storage.h
@@ -141,8 +141,11 @@
 };
 
 // Loads the bookmarks. This is intended to be called on the background thread.
-// Updates state in |details| based on the load.
+// Updates state in |details| based on the load. |emit_experimental_uma|
+// determines whether a few newly introduced and experimental UMA metrics should
+// be logged.
 void LoadBookmarks(const base::FilePath& profile_path,
+                   bool emit_experimental_uma,
                    BookmarkLoadDetails* details);
 
 // BookmarkStorage handles reading/write the bookmark bar model. The
diff --git a/components/bookmarks/browser/model_loader.cc b/components/bookmarks/browser/model_loader.cc
index f1316cd..12fe8d32 100644
--- a/components/bookmarks/browser/model_loader.cc
+++ b/components/bookmarks/browser/model_loader.cc
@@ -5,6 +5,7 @@
 #include "components/bookmarks/browser/model_loader.h"
 
 #include "base/bind.h"
+#include "base/feature_list.h"
 #include "base/files/file_path.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "components/bookmarks/browser/bookmark_storage.h"
@@ -12,6 +13,15 @@
 
 namespace bookmarks {
 
+namespace {
+
+// TODO(mastiz): Remove this kill switch asap since the UMA metrics entail
+// negligible risks for stability or performance overhead.
+const base::Feature kEmitExperimentalBookmarkLoadUma{
+    "EmitExperimentalBookmarkLoadUma", base::FEATURE_ENABLED_BY_DEFAULT};
+
+}  // namespace
+
 // static
 scoped_refptr<ModelLoader> ModelLoader::Create(
     const base::FilePath& profile_path,
@@ -21,11 +31,18 @@
   // Note: base::MakeRefCounted is not available here, as ModelLoader's
   // constructor is private.
   auto model_loader = base::WrapRefCounted(new ModelLoader());
+  // We plumb the value for kEmitExperimentalBookmarkLoadUma as retrieved on
+  // the UI thread to avoid issues with TSAN bots (in case there are tests that
+  // override feature toggles -not necessarily this one- while bookmark loading
+  // is ongoing, which is problematic due to how feature overriding for tests is
+  // implemented).
   load_sequenced_task_runner->PostTask(
       FROM_HERE,
-      base::BindOnce(&ModelLoader::DoLoadOnBackgroundThread, model_loader,
-                     profile_path, base::ThreadTaskRunnerHandle::Get(),
-                     std::move(details), std::move(callback)));
+      base::BindOnce(
+          &ModelLoader::DoLoadOnBackgroundThread, model_loader, profile_path,
+          base::FeatureList::IsEnabled(kEmitExperimentalBookmarkLoadUma),
+          base::ThreadTaskRunnerHandle::Get(), std::move(details),
+          std::move(callback)));
   return model_loader;
 }
 
@@ -41,10 +58,11 @@
 
 void ModelLoader::DoLoadOnBackgroundThread(
     const base::FilePath& profile_path,
+    bool emit_experimental_uma,
     scoped_refptr<base::SequencedTaskRunner> main_sequenced_task_runner,
     std::unique_ptr<BookmarkLoadDetails> details,
     LoadCallback callback) {
-  LoadBookmarks(profile_path, details.get());
+  LoadBookmarks(profile_path, emit_experimental_uma, details.get());
   history_bookmark_model_ = details->url_index();
   loaded_signal_.Signal();
   main_sequenced_task_runner->PostTask(
diff --git a/components/bookmarks/browser/model_loader.h b/components/bookmarks/browser/model_loader.h
index 1a1477cb..6ff48d1d 100644
--- a/components/bookmarks/browser/model_loader.h
+++ b/components/bookmarks/browser/model_loader.h
@@ -57,6 +57,7 @@
   // Performs the load on a background thread.
   void DoLoadOnBackgroundThread(
       const base::FilePath& profile_path,
+      bool emit_experimental_uma,
       scoped_refptr<base::SequencedTaskRunner> main_sequenced_task_runner,
       std::unique_ptr<BookmarkLoadDetails> details,
       LoadCallback callback);
diff --git a/components/payments/content/payment_request.cc b/components/payments/content/payment_request.cc
index 3ae1a62..b37be61a 100644
--- a/components/payments/content/payment_request.cc
+++ b/components/payments/content/payment_request.cc
@@ -164,7 +164,7 @@
           spec_->url_payment_method_identifiers().end());
 }
 
-void PaymentRequest::Show(bool is_user_gesture) {
+void PaymentRequest::Show(bool is_user_gesture, bool wait_for_updated_details) {
   if (!IsInitialized()) {
     log_.Error("Attempted show without initialization");
     OnConnectionTerminated();
@@ -207,8 +207,14 @@
 
   is_show_user_gesture_ = is_user_gesture;
 
-  display_handle_->Show(this);
+  if (wait_for_updated_details) {
+    // Put |spec_| into uninitialized state, so the UI knows to show a spinner.
+    // This method does not block.
+    spec_->StartWaitingForUpdateWith(
+        PaymentRequestSpec::UpdateReason::INITIAL_PAYMENT_DETAILS);
+  }
 
+  display_handle_->Show(this);
   state_->AreRequestedMethodsSupported(
       base::BindOnce(&PaymentRequest::AreRequestedMethodsSupportedCallback,
                      weak_ptr_factory_.GetWeakPtr()));
@@ -253,6 +259,13 @@
     return;
   }
 
+  bool is_resolving_promise_passed_into_show_method = !spec_->IsInitialized();
+  if (is_resolving_promise_passed_into_show_method && !details->total) {
+    log_.Error("Missing total");
+    OnConnectionTerminated();
+    return;
+  }
+
   std::string error;
   if (!ValidatePaymentDetails(ConvertPaymentDetails(details), &error)) {
     log_.Error(error);
@@ -269,6 +282,15 @@
   }
 
   spec_->UpdateWith(std::move(details));
+
+  if (is_resolving_promise_passed_into_show_method) {
+    if (SatisfiesSkipUIConstraints()) {
+      skipped_payment_request_ui_ = true;
+      Pay();
+    } else if (spec_->request_shipping()) {
+      state_->SelectDefaultShippingAddressAndNotifyObservers();
+    }
+  }
 }
 
 void PaymentRequest::NoUpdatedPaymentDetails() {
@@ -425,16 +447,17 @@
 }
 
 bool PaymentRequest::SatisfiesSkipUIConstraints() const {
-  return base::FeatureList::IsEnabled(features::kWebPaymentsSingleAppUiSkip) &&
+  // Only allowing URL base payment apps to skip the payment sheet.
+  return (spec()->url_payment_method_identifiers().size() == 1 ||
+          skip_ui_for_non_url_payment_method_identifiers_for_test_) &&
+         base::FeatureList::IsEnabled(features::kWebPaymentsSingleAppUiSkip) &&
          base::FeatureList::IsEnabled(::features::kServiceWorkerPaymentApps) &&
-         is_show_user_gesture_ && state()->is_get_all_instruments_finished() &&
+         is_show_user_gesture_ && state()->IsInitialized() &&
+         spec()->IsInitialized() &&
          state()->available_instruments().size() == 1 &&
          spec()->stringified_method_data().size() == 1 &&
          !spec()->request_shipping() && !spec()->request_payer_name() &&
-         !spec()->request_payer_phone() &&
-         !spec()->request_payer_email()
-         // Only allowing URL base payment apps to skip the payment sheet.
-         && spec()->url_payment_method_identifiers().size() == 1;
+         !spec()->request_payer_phone() && !spec()->request_payer_email();
 }
 
 void PaymentRequest::OnPaymentResponseAvailable(
diff --git a/components/payments/content/payment_request.h b/components/payments/content/payment_request.h
index ef4236b..4394b82 100644
--- a/components/payments/content/payment_request.h
+++ b/components/payments/content/payment_request.h
@@ -68,7 +68,7 @@
             std::vector<mojom::PaymentMethodDataPtr> method_data,
             mojom::PaymentDetailsPtr details,
             mojom::PaymentOptionsPtr options) override;
-  void Show(bool is_user_gesture) override;
+  void Show(bool is_user_gesture, bool wait_for_updated_details) override;
   void Retry(mojom::PaymentValidationErrorsPtr errors) override;
   void UpdateWith(mojom::PaymentDetailsPtr details) override;
   void NoUpdatedPaymentDetails() override;
@@ -122,6 +122,12 @@
   PaymentRequestSpec* spec() const { return spec_.get(); }
   PaymentRequestState* state() const { return state_.get(); }
 
+  // Allow to skip UI into payment handlers for such payment methods as
+  // "basic-card". Used only in tests.
+  void set_skip_ui_for_non_url_payment_method_identifiers_for_test() {
+    skip_ui_for_non_url_payment_method_identifiers_for_test_ = true;
+  }
+
  private:
   // Returns true after init() has been called and the mojo connection has been
   // established. If the mojo connection gets later disconnected, this will
@@ -205,6 +211,11 @@
   // Whether PaymentRequest.show() has been called.
   bool is_show_called_ = false;
 
+  // Whether payment instruments for such payment methods as "basic-card" can
+  // skip UI for testing of the skip-UI flow. This is always false in
+  // production.
+  bool skip_ui_for_non_url_payment_method_identifiers_for_test_ = false;
+
   base::WeakPtrFactory<PaymentRequest> weak_ptr_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(PaymentRequest);
diff --git a/components/payments/content/payment_request_spec.cc b/components/payments/content/payment_request_spec.cc
index 00e8ff7..3c083ba 100644
--- a/components/payments/content/payment_request_spec.cc
+++ b/components/payments/content/payment_request_spec.cc
@@ -100,6 +100,7 @@
 
 void PaymentRequestSpec::UpdateWith(mojom::PaymentDetailsPtr details) {
   DCHECK(details_);
+  DCHECK(details_->total || details->total);
   if (details->total)
     details_->total = std::move(details->total);
   if (!details->display_items.empty())
@@ -214,9 +215,15 @@
 
 void PaymentRequestSpec::RecomputeSpecForDetails() {
   // Reparse the |details_| and update the observers.
-  UpdateSelectedShippingOption(/*after_update=*/true);
+  bool is_initialization =
+      current_update_reason_ == UpdateReason::INITIAL_PAYMENT_DETAILS;
+  UpdateSelectedShippingOption(/*after_update=*/!is_initialization);
 
   NotifyOnSpecUpdated();
+
+  if (is_initialization)
+    NotifyInitialized();
+
   current_update_reason_ = UpdateReason::NONE;
 }
 
@@ -229,6 +236,10 @@
   observers_.RemoveObserver(observer);
 }
 
+bool PaymentRequestSpec::IsInitialized() const {
+  return current_update_reason_ != UpdateReason::INITIAL_PAYMENT_DETAILS;
+}
+
 bool PaymentRequestSpec::request_shipping() const {
   return options_->request_shipping;
 }
@@ -365,7 +376,7 @@
   selected_shipping_option_ = nullptr;
   selected_shipping_option_error_.clear();
   if (details_->shipping_options->empty() || !details_->error.empty()) {
-    // No options are provided by the merchant.
+    // The merchant provided either no shipping options or an error message.
     if (after_update) {
       // This is after an update, which means that the selected address is not
       // supported. The merchant may have customized the error string, or a
diff --git a/components/payments/content/payment_request_spec.h b/components/payments/content/payment_request_spec.h
index 1ebea6c..a81d0ce 100644
--- a/components/payments/content/payment_request_spec.h
+++ b/components/payments/content/payment_request_spec.h
@@ -15,6 +15,7 @@
 #include "base/strings/string16.h"
 #include "components/autofill/core/browser/credit_card.h"
 #include "components/autofill/core/browser/field_types.h"
+#include "components/payments/content/initialization_task.h"
 #include "components/payments/core/currency_formatter.h"
 #include "components/payments/core/payment_options_provider.h"
 #include "third_party/blink/public/mojom/payments/payment_request.mojom.h"
@@ -33,12 +34,22 @@
 // The spec contains all the options that the merchant has specified about this
 // Payment Request. It's a (mostly) read-only view, which can be updated in
 // certain occasions by the merchant (see API).
-class PaymentRequestSpec : public PaymentOptionsProvider {
+//
+// The spec starts out completely initialized when a PaymentRequest object is
+// created, but can be placed into uninitialized state if PaymentRequest.show()
+// was called with a promise. This allows for asynchronous calculation of the
+// shopping cart contents, the total, the shipping options, and the modifiers.
+//
+// The initialization state is observed by PaymentRequestDialogView for showing
+// a "Loading..." spinner.
+class PaymentRequestSpec : public PaymentOptionsProvider,
+                           public InitializationTask {
  public:
   // This enum represents which bit of information was changed to trigger an
   // update roundtrip with the website.
   enum class UpdateReason {
     NONE,
+    INITIAL_PAYMENT_DETAILS,
     SHIPPING_OPTION,
     SHIPPING_ADDRESS,
     RETRY,
@@ -104,6 +115,9 @@
   void AddObserver(Observer* observer);
   void RemoveObserver(Observer* observer);
 
+  // InitializationTask:
+  bool IsInitialized() const override;
+
   // PaymentOptionsProvider:
   bool request_shipping() const override;
   bool request_payer_name() const override;
@@ -158,7 +172,11 @@
     return selected_shipping_option_error_;
   }
 
+  // Notifies observers that spec is going to be updated due to |reason|. This
+  // shows a spinner in UI that goes away when merchant calls updateWith() or
+  // ignores the shipping*change event.
   void StartWaitingForUpdateWith(UpdateReason reason);
+
   bool IsMixedCurrency() const;
 
   UpdateReason current_update_reason() const { return current_update_reason_; }
@@ -188,8 +206,9 @@
 
   // Updates the |selected_shipping_option| based on the data passed to this
   // payment request by the website. This will set selected_shipping_option_ to
-  // the last option marked selected in the options array. If no options are
-  // provided and this method is called |after_update|, it means the merchant
+  // the last option marked selected in the options array. If merchants provides
+  // no options or an error message this method is called |after_update| (after
+  // user changed their shipping address selection), it means the merchant
   // doesn't ship to this location. In this case,
   // |selected_shipping_option_error_| will be set.
   void UpdateSelectedShippingOption(bool after_update);
diff --git a/components/payments/content/payment_request_spec_unittest.cc b/components/payments/content/payment_request_spec_unittest.cc
index 609e7d2..58a9dd1 100644
--- a/components/payments/content/payment_request_spec_unittest.cc
+++ b/components/payments/content/payment_request_spec_unittest.cc
@@ -32,6 +32,8 @@
 
   void RecreateSpecWithOptionsAndDetails(mojom::PaymentOptionsPtr options,
                                          mojom::PaymentDetailsPtr details) {
+    if (!details->total)
+      details->total = mojom::PaymentItem::New();
     spec_ = std::make_unique<PaymentRequestSpec>(
         std::move(options), std::move(details),
         std::vector<mojom::PaymentMethodDataPtr>(), this, "en-US");
diff --git a/components/payments/content/payment_request_state.cc b/components/payments/content/payment_request_state.cc
index c86818e9..93a39e0 100644
--- a/components/payments/content/payment_request_state.cc
+++ b/components/payments/content/payment_request_state.cc
@@ -147,6 +147,7 @@
   get_all_instruments_finished_ = true;
   are_requested_methods_supported_ |= !available_instruments_.empty();
   NotifyOnGetAllPaymentInstrumentsFinished();
+  NotifyInitialized();
 
   // Fulfill the pending CanMakePayment call.
   if (can_make_payment_callback_) {
@@ -457,6 +458,18 @@
   return get_all_instruments_finished_;
 }
 
+void PaymentRequestState::SelectDefaultShippingAddressAndNotifyObservers() {
+  // Only pre-select an address if the merchant provided at least one selected
+  // shipping option, and the top profile is complete. Assumes that profiles
+  // have already been sorted for completeness and frecency.
+  if (!shipping_profiles().empty() && spec_->selected_shipping_option() &&
+      profile_comparator()->IsShippingComplete(shipping_profiles_[0])) {
+    selected_shipping_profile_ = shipping_profiles()[0];
+  }
+
+  UpdateIsReadyToPayAndNotifyObservers();
+}
+
 void PaymentRequestState::PopulateProfileCache() {
   std::vector<autofill::AutofillProfile*> profiles =
       personal_data_manager_->GetProfilesToSuggest();
@@ -511,14 +524,6 @@
 }
 
 void PaymentRequestState::SetDefaultProfileSelections() {
-  // Only pre-select an address if the merchant provided at least one selected
-  // shipping option, and the top profile is complete. Assumes that profiles
-  // have already been sorted for completeness and frecency.
-  if (!shipping_profiles().empty() && spec_->selected_shipping_option() &&
-      profile_comparator()->IsShippingComplete(shipping_profiles_[0])) {
-    selected_shipping_profile_ = shipping_profiles()[0];
-  }
-
   // Contact profiles were ordered by completeness in addition to frecency;
   // the first one is the best default selection.
   if (!contact_profiles().empty() &&
@@ -539,7 +544,8 @@
   selected_instrument_ = first_complete_instrument == instruments.end()
                              ? nullptr
                              : first_complete_instrument->get();
-  UpdateIsReadyToPayAndNotifyObservers();
+
+  SelectDefaultShippingAddressAndNotifyObservers();
 
   bool has_complete_instrument =
       available_instruments().empty()
@@ -560,7 +566,6 @@
 void PaymentRequestState::NotifyOnGetAllPaymentInstrumentsFinished() {
   for (auto& observer : observers_)
     observer.OnGetAllPaymentInstrumentsFinished();
-  NotifyInitialized();
 }
 
 void PaymentRequestState::NotifyOnSelectedInformationChanged() {
diff --git a/components/payments/content/payment_request_state.h b/components/payments/content/payment_request_state.h
index cc9dadfb..60c83e81 100644
--- a/components/payments/content/payment_request_state.h
+++ b/components/payments/content/payment_request_state.h
@@ -39,6 +39,9 @@
 // user is ready to pay. Uses information from the PaymentRequestSpec, which is
 // what the merchant has specified, as input into the "is ready to pay"
 // computation.
+//
+// The initialization state is observed by PaymentRequestDialogView for showing
+// a "Loading..." spinner.
 class PaymentRequestState : public PaymentResponseHelper::Delegate,
                             public PaymentRequestSpec::Observer,
                             public InitializationTask {
@@ -224,6 +227,9 @@
   // InitializationTask:
   bool IsInitialized() const override;
 
+  // Selects the default shipping address.
+  void SelectDefaultShippingAddressAndNotifyObservers();
+
  private:
   // Fetches the Autofill Profiles for this user from the PersonalDataManager,
   // and stores copies of them, owned by this PaymentRequestState, in
diff --git a/components/payments/content/payment_request_state_unittest.cc b/components/payments/content/payment_request_state_unittest.cc
index e53508b..99c24be 100644
--- a/components/payments/content/payment_request_state_unittest.cc
+++ b/components/payments/content/payment_request_state_unittest.cc
@@ -68,6 +68,8 @@
       mojom::PaymentOptionsPtr options,
       mojom::PaymentDetailsPtr details,
       std::vector<mojom::PaymentMethodDataPtr> method_data) {
+    if (!details->total)
+      details->total = mojom::PaymentItem::New();
     // The spec will be based on the |options| and |details| passed in.
     spec_ = std::make_unique<PaymentRequestSpec>(
         std::move(options), std::move(details), std::move(method_data),
diff --git a/components/test/data/payments/show_promise/app.js b/components/test/data/payments/show_promise/app.js
new file mode 100644
index 0000000..cec6d41
--- /dev/null
+++ b/components/test/data/payments/show_promise/app.js
@@ -0,0 +1,9 @@
+/*
+ * Copyright 2019 The Chromium Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+self.addEventListener('paymentrequest', (evt) => {
+  evt.respondWith({methodName: 'basic-card', details: evt.total});
+});
diff --git a/components/test/data/payments/show_promise/app_installer.js b/components/test/data/payments/show_promise/app_installer.js
new file mode 100644
index 0000000..ae296caf
--- /dev/null
+++ b/components/test/data/payments/show_promise/app_installer.js
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2019 The Chromium Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/**
+ * Prints output.
+ * @param {String} src - Where the message is coming from.
+ * @param {String} txt - The text to print.
+ */
+function output(src, txt) {
+  // Handle DOMException:
+  if (txt.message) {
+    txt = txt.message;
+  }
+  txt = src + ': ' + txt;
+  if (window.domAutomationController) {
+    window.domAutomationController.send(txt);
+  } else {
+    txt += ' window.domAutomationController not found.';
+  }
+  console.log(txt);
+}
+
+/**
+ * Installs the payment handler.
+ */
+function install() {  // eslint-disable-line no-unused-vars
+  if (!navigator.serviceWorker) {
+    output('install()', 'ServiceWorker API not found.');
+    return;
+  }
+
+  navigator.serviceWorker.getRegistration('app.js')
+      .then((registration) => {
+        if (registration) {
+          output(
+              'serviceWorker.getRegistration()',
+              'The ServiceWorker is already installed.');
+          return;
+        }
+        navigator.serviceWorker.register('app.js')
+            .then(() => {
+              return navigator.serviceWorker.ready;
+            })
+            .then((registration) => {
+              if (!registration.paymentManager) {
+                output(
+                    'serviceWorker.register()',
+                    'PaymentManager API not found.');
+                return;
+              }
+
+              registration.paymentManager.instruments
+                  .set('123456', {name: 'Echo Pay', method: 'basic-card'})
+                  .then(() => {
+                    output(
+                        'instruments.set()',
+                        'Payment handler installed.');
+                  })
+                  .catch((error) => {
+                    output('instruments.set()', error);
+                  });
+            })
+            .catch((error) => {
+              output('serviceWorker.register()', error);
+            });
+      })
+      .catch((error) => {
+        output('serviceWorker.getRegistration()', error);
+      });
+}
diff --git a/components/test/data/payments/show_promise/digital_goods.html b/components/test/data/payments/show_promise/digital_goods.html
new file mode 100644
index 0000000..4c79a59
--- /dev/null
+++ b/components/test/data/payments/show_promise/digital_goods.html
@@ -0,0 +1,22 @@
+<!doctype html>
+<!--
+Copyright 2019 The Chromium Authors. All rights reserved.
+Use of this source code is governed by a BSD-style license that can be
+found in the LICENSE file.
+-->
+<html lang="en">
+<head>
+  <meta charset="utf-8">
+  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1"> 
+  <title>Show Promise Test For Digital Goods</title>
+  <link rel="stylesheet" type="text/css" href="../style.css">
+</head>
+<body>
+  <div><button onclick="create()" id="create">Create</button></div>
+  <div><button onclick="buy()" id="buy">Buy</button></div>
+  <pre id="result"></pre>
+  <script src="../util.js"></script>
+  <script src="app_installer.js"></script>
+  <script src="digital_goods.js"></script>
+</body>
+</html>
\ No newline at end of file
diff --git a/components/test/data/payments/show_promise/digital_goods.js b/components/test/data/payments/show_promise/digital_goods.js
new file mode 100644
index 0000000..d5e76bd5
--- /dev/null
+++ b/components/test/data/payments/show_promise/digital_goods.js
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2019 The Chromium Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+var request = null;
+
+/**
+ * Create an instance of PaymentRequest.
+ */
+function create() {  // eslint-disable-line no-unused-vars
+  try {
+    request = new PaymentRequest([{supportedMethods: 'basic-card'}], {
+      total:
+          {label: 'PENDING TOTAL', amount: {currency: 'USD', value: '99.99'}},
+    });
+  } catch (error) {
+    print(error.message);
+  }
+}
+
+/**
+ * Launch PaymentRequest with a show promise for digital goods.
+ */
+function buy() {  // eslint-disable-line no-unused-vars
+  try {
+    request
+        .show(new Promise(function(resolve) {
+          resolve({
+            total: {label: 'Total', amount: {currency: 'USD', value: '1.00'}},
+          });
+        }))
+        .then(function(result) {
+          print(JSON.stringify(result.details));
+          return result.complete('success');
+        })
+        .catch(function(error) {
+          print(error);
+        });
+  } catch (error) {
+    print(error);
+  }
+}
diff --git a/components/test/data/payments/show_promise/invalid_details.html b/components/test/data/payments/show_promise/invalid_details.html
new file mode 100644
index 0000000..beaaf898
--- /dev/null
+++ b/components/test/data/payments/show_promise/invalid_details.html
@@ -0,0 +1,20 @@
+<!doctype html>
+<!--
+Copyright 2019 The Chromium Authors. All rights reserved.
+Use of this source code is governed by a BSD-style license that can be
+found in the LICENSE file.
+-->
+<html lang="en">
+<head>
+  <meta charset="utf-8">
+  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1"> 
+  <title>Test for Show Promise with Invalid Details</title>
+  <link rel="stylesheet" type="text/css" href="../style.css">
+</head>
+<body>
+  <div><button onclick="buy()" id="buy">Buy</button></div>
+  <pre id="result"></pre>
+  <script src="../util.js"></script>
+  <script src="invalid_details.js"></script>
+</body>
+</html>
\ No newline at end of file
diff --git a/components/test/data/payments/show_promise/invalid_details.js b/components/test/data/payments/show_promise/invalid_details.js
new file mode 100644
index 0000000..f760a258
--- /dev/null
+++ b/components/test/data/payments/show_promise/invalid_details.js
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2019 The Chromium Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/**
+ * Launch PaymentRequest with a show promise that resolve with invalid details.
+ */
+function buy() {  // eslint-disable-line no-unused-vars
+  try {
+    new PaymentRequest([{supportedMethods: 'basic-card'}], {
+      total: {
+        label: 'PENDING TOTAL',
+        amount: {currency: 'USD', value: '99.99'},
+      },
+    })
+        .show(new Promise(function(resolve) {
+          resolve({
+            total: {
+              label: 'Total',
+              amount: {currency: 'USD', value: '-1.00'},
+            },
+          });
+        }))
+        .catch(function(error) {
+          print(error);
+        });
+  } catch (error) {
+    print(error.message);
+  }
+}
diff --git a/components/test/data/payments/show_promise/reject.html b/components/test/data/payments/show_promise/reject.html
new file mode 100644
index 0000000..82b1343
--- /dev/null
+++ b/components/test/data/payments/show_promise/reject.html
@@ -0,0 +1,20 @@
+<!doctype html>
+<!--
+Copyright 2019 The Chromium Authors. All rights reserved.
+Use of this source code is governed by a BSD-style license that can be
+found in the LICENSE file.
+-->
+<html lang="en">
+<head>
+  <meta charset="utf-8">
+  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1"> 
+  <title>Test for Rejecting the Show Promise</title>
+  <link rel="stylesheet" type="text/css" href="../style.css">
+</head>
+<body>
+  <div><button onclick="buy()" id="buy">Buy</button></div>
+  <pre id="result"></pre>
+  <script src="../util.js"></script>
+  <script src="reject.js"></script>
+</body>
+</html>
\ No newline at end of file
diff --git a/components/test/data/payments/show_promise/reject.js b/components/test/data/payments/show_promise/reject.js
new file mode 100644
index 0000000..8630c7fc
--- /dev/null
+++ b/components/test/data/payments/show_promise/reject.js
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2019 The Chromium Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/**
+ * Launch PaymentRequest with a show promise and reject that promise.
+ */
+function buy() {  // eslint-disable-line no-unused-vars
+  try {
+    new PaymentRequest(
+        [{supportedMethods: 'basic-card'}],
+        {total: {label: 'Total', amount: {currency: 'USD', value: '1.00'}}})
+        .show(new Promise(function(resolve, reject) {
+          reject();
+        }))
+        .catch(function(error) {
+          print(error);
+        });
+  } catch (error) {
+    print(error);
+  }
+}
diff --git a/components/test/data/payments/show_promise/single_option_shipping.html b/components/test/data/payments/show_promise/single_option_shipping.html
new file mode 100644
index 0000000..17e1736c
--- /dev/null
+++ b/components/test/data/payments/show_promise/single_option_shipping.html
@@ -0,0 +1,21 @@
+<!doctype html>
+<!--
+Copyright 2019 The Chromium Authors. All rights reserved.
+Use of this source code is governed by a BSD-style license that can be
+found in the LICENSE file.
+-->
+<html lang="en">
+<head>
+  <meta charset="utf-8">
+  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1"> 
+  <title>Show Promise Test For Single-Option Shipping</title>
+  <link rel="stylesheet" type="text/css" href="../style.css">
+</head>
+<body>
+  <div><button onclick="buy()" id="buy">Buy</button></div>
+  <pre id="result"></pre>
+  <script src="../util.js"></script>
+  <script src="app_installer.js"></script>
+  <script src="single_option_shipping.js"></script>
+</body>
+</html>
\ No newline at end of file
diff --git a/components/test/data/payments/show_promise/single_option_shipping.js b/components/test/data/payments/show_promise/single_option_shipping.js
new file mode 100644
index 0000000..b29a9238
--- /dev/null
+++ b/components/test/data/payments/show_promise/single_option_shipping.js
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2019 The Chromium Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/**
+ * Launch PaymentRequest with a show promise and a single pre-selected option
+ * for shipping worldwide.
+ */
+function buy() {  // eslint-disable-line no-unused-vars
+  try {
+    new PaymentRequest(
+        [{supportedMethods: 'basic-card'}], {
+          total: {
+            label: 'PENDING TOTAL',
+            amount: {currency: 'USD', value: '99.99'},
+          },
+          shippingOptions: [{
+            id: '1',
+            label: 'PENDING SHIPPING',
+            amount: {currency: 'USD', value: '99.99'},
+            selected: true,
+          }],
+        },
+        {requestShipping: true})
+        .show(new Promise(function(resolve) {
+          resolve({
+            total: {label: 'Total', amount: {currency: 'USD', value: '1.00'}},
+            shippingOptions: [{
+              id: '1',
+              label: 'Free shipping',
+              amount: {currency: 'USD', value: '0.00'},
+              selected: true,
+            }],
+          });
+        }))
+        .then(function(result) {
+          print(JSON.stringify(result.details));
+          return result.complete('success');
+        })
+        .catch(function(error) {
+          print(error);
+        });
+  } catch (error) {
+    print(error.message);
+  }
+}
diff --git a/components/test/data/payments/show_promise/single_option_shipping_with_update.html b/components/test/data/payments/show_promise/single_option_shipping_with_update.html
new file mode 100644
index 0000000..c1a1e64
--- /dev/null
+++ b/components/test/data/payments/show_promise/single_option_shipping_with_update.html
@@ -0,0 +1,21 @@
+<!doctype html>
+<!--
+Copyright 2019 The Chromium Authors. All rights reserved.
+Use of this source code is governed by a BSD-style license that can be
+found in the LICENSE file.
+-->
+<html lang="en">
+<head>
+  <meta charset="utf-8">
+  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1"> 
+  <title>Show Promise Test For Single-Option Shipping With Update</title>
+  <link rel="stylesheet" type="text/css" href="../style.css">
+</head>
+<body>
+  <div><button onclick="buy()" id="buy">Buy</button></div>
+  <pre id="result"></pre>
+  <script src="../util.js"></script>
+  <script src="app_installer.js"></script>
+  <script src="single_option_shipping_with_update.js"></script>
+</body>
+</html>
\ No newline at end of file
diff --git a/components/test/data/payments/show_promise/single_option_shipping_with_update.js b/components/test/data/payments/show_promise/single_option_shipping_with_update.js
new file mode 100644
index 0000000..a3c1be0
--- /dev/null
+++ b/components/test/data/payments/show_promise/single_option_shipping_with_update.js
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2019 The Chromium Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/**
+ * Launch PaymentRequest with a show promise and a single pre-selected option
+ * for shipping worldwide and a handler for shipping address change events that
+ * does not change anything.
+ */
+function buy() {  // eslint-disable-line no-unused-vars
+  var finalizedDetails = {
+    total: {label: 'Total', amount: {currency: 'USD', value: '1.00'}},
+    shippingOptions: [{
+      id: '1',
+      label: 'Free shipping',
+      amount: {currency: 'USD', value: '0.00'},
+      selected: true,
+    }],
+  };
+
+  try {
+    var request = new PaymentRequest(
+        [{supportedMethods: 'basic-card'}], {
+          total: {
+            label: 'PENDING TOTAL',
+            amount: {currency: 'USD', value: '99.99'},
+          },
+          shippingOptions: [{
+            id: '1',
+            label: 'PENDING SHIPPING',
+            amount: {currency: 'USD', value: '99.99'},
+            selected: true,
+          }],
+        },
+        {requestShipping: true});
+
+    request.addEventListener('shippingaddresschange', function(evt) {
+      evt.updateWith(finalizedDetails);
+    });
+
+    request
+        .show(new Promise(function(resolve) {
+          resolve(finalizedDetails);
+        }))
+        .then(function(result) {
+          print(JSON.stringify(result.details));
+          return result.complete('success');
+        })
+        .catch(function(error) {
+          print(error);
+        });
+  } catch (error) {
+    print(error.message);
+  }
+}
diff --git a/components/test/data/payments/show_promise/timeout.html b/components/test/data/payments/show_promise/timeout.html
new file mode 100644
index 0000000..7f29206
--- /dev/null
+++ b/components/test/data/payments/show_promise/timeout.html
@@ -0,0 +1,20 @@
+<!doctype html>
+<!--
+Copyright 2019 The Chromium Authors. All rights reserved.
+Use of this source code is governed by a BSD-style license that can be
+found in the LICENSE file.
+-->
+<html lang="en">
+<head>
+  <meta charset="utf-8">
+  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1"> 
+  <title>Test for Timing Out the Show Promise</title>
+  <link rel="stylesheet" type="text/css" href="../style.css">
+</head>
+<body>
+  <div><button onclick="buy()" id="buy">Buy</button></div>
+  <pre id="result"></pre>
+  <script src="../util.js"></script>
+  <script src="timeout.js"></script>
+</body>
+</html>
\ No newline at end of file
diff --git a/components/test/data/payments/show_promise/timeout.js b/components/test/data/payments/show_promise/timeout.js
new file mode 100644
index 0000000..3bb80fe
--- /dev/null
+++ b/components/test/data/payments/show_promise/timeout.js
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2019 The Chromium Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/**
+ * Launch PaymentRequest with a show promise and don't resolve or reject it.
+ */
+function buy() {  // eslint-disable-line no-unused-vars
+  try {
+    new PaymentRequest(
+        [{supportedMethods: 'basic-card'}],
+        {total: {label: 'Total', amount: {currency: 'USD', value: '1.00'}}})
+        .show(new Promise(function(resolve) { /* Intentionally empty. */ }))
+        .catch(function(error) {
+          print(error);
+        });
+  } catch (error) {
+    print(error);
+  }
+}
diff --git a/components/test/data/payments/show_promise/unsupported.html b/components/test/data/payments/show_promise/unsupported.html
new file mode 100644
index 0000000..e3b00fe9
--- /dev/null
+++ b/components/test/data/payments/show_promise/unsupported.html
@@ -0,0 +1,20 @@
+<!doctype html>
+<!--
+Copyright 2019 The Chromium Authors. All rights reserved.
+Use of this source code is governed by a BSD-style license that can be
+found in the LICENSE file.
+-->
+<html lang="en">
+<head>
+  <meta charset="utf-8">
+  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1"> 
+  <title>Test for Show Promise with Unsupported Payment Method Identifier</title>
+  <link rel="stylesheet" type="text/css" href="../style.css">
+</head>
+<body>
+  <div><button onclick="buy()" id="buy">Buy</button></div>
+  <pre id="result"></pre>
+  <script src="../util.js"></script>
+  <script src="unsupported.js"></script>
+</body>
+</html>
\ No newline at end of file
diff --git a/components/test/data/payments/show_promise/unsupported.js b/components/test/data/payments/show_promise/unsupported.js
new file mode 100644
index 0000000..6196ee22
--- /dev/null
+++ b/components/test/data/payments/show_promise/unsupported.js
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2019 The Chromium Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/**
+ * Launch PaymentRequest with a show promise and an unsupported payment method
+ * identifier.
+ */
+function buy() {  // eslint-disable-line no-unused-vars
+  try {
+    new PaymentRequest([{supportedMethods: 'foo'}], {
+      total:
+          {label: 'PENDING TOTAL', amount: {currency: 'USD', value: '99.99'}},
+    })
+        .show(new Promise(function(resolve) {
+          resolve({
+            total: {
+              label: 'Total',
+              amount: {currency: 'USD', value: '1.00'},
+            },
+          });
+        }))
+        .catch(function(error) {
+          print(error);
+        });
+  } catch (error) {
+    print(error);
+  }
+}
diff --git a/components/test/data/payments/show_promise/us_only_shipping.html b/components/test/data/payments/show_promise/us_only_shipping.html
new file mode 100644
index 0000000..d5af7ae
--- /dev/null
+++ b/components/test/data/payments/show_promise/us_only_shipping.html
@@ -0,0 +1,21 @@
+<!doctype html>
+<!--
+Copyright 2019 The Chromium Authors. All rights reserved.
+Use of this source code is governed by a BSD-style license that can be
+found in the LICENSE file.
+-->
+<html lang="en">
+<head>
+  <meta charset="utf-8">
+  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1"> 
+  <title>Show Promise Test For US-Only Shipping</title>
+  <link rel="stylesheet" type="text/css" href="../style.css">
+</head>
+<body>
+  <div><button onclick="buy()" id="buy">Buy</button></div>
+  <pre id="result"></pre>
+  <script src="../util.js"></script>
+  <script src="app_installer.js"></script>
+  <script src="us_only_shipping.js"></script>
+</body>
+</html>
\ No newline at end of file
diff --git a/components/test/data/payments/show_promise/us_only_shipping.js b/components/test/data/payments/show_promise/us_only_shipping.js
new file mode 100644
index 0000000..af8480f
--- /dev/null
+++ b/components/test/data/payments/show_promise/us_only_shipping.js
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2019 The Chromium Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/**
+ * Launch PaymentRequest with a show promise and US-only shipping.
+ */
+function buy() {  // eslint-disable-line no-unused-vars
+  var detailsForUSAddress = {
+    shippingOptions: [{
+      id: '1',
+      label: 'Free shipping',
+      amount: {currency: 'USD', value: '0.00'},
+      selected: true,
+    }],
+  };
+
+  var detailsForNonUSAddress = {error: 'Cannot ship outside of US.'};
+
+  try {
+    var request = new PaymentRequest(
+        [{supportedMethods: 'basic-card'}], {
+          total: {
+            label: 'PENDING TOTAL',
+            amount: {currency: 'USD', value: '99.99'},
+          },
+        },
+        {requestShipping: true});
+
+    request.addEventListener('shippingaddresschange', function(evt) {
+      if (request.shippingAddress.country === 'US') {
+        evt.updateWith(detailsForUSAddress);
+      } else {
+        evt.updateWith(detailsForNonUSAddress);
+      }
+    });
+
+    request
+        .show(new Promise(function(resolve) {
+          resolve({
+            total: {label: 'Total', amount: {currency: 'USD', value: '1.00'}},
+          });
+        }))
+        .then(function(result) {
+          print(JSON.stringify(result.details));
+          return result.complete('success');
+        })
+        .catch(function(error) {
+          print(error);
+        });
+  } catch (error) {
+    print(error.message);
+  }
+}
diff --git a/components/viz/service/display_embedder/buffer_queue.cc b/components/viz/service/display_embedder/buffer_queue.cc
index 6f00149..a813220 100644
--- a/components/viz/service/display_embedder/buffer_queue.cc
+++ b/components/viz/service/display_embedder/buffer_queue.cc
@@ -17,6 +17,7 @@
 #include "ui/display/types/display_snapshot.h"
 #include "ui/gfx/gpu_memory_buffer.h"
 #include "ui/gfx/skia_util.h"
+#include "ui/gl/gl_enums.h"
 
 namespace viz {
 
@@ -53,13 +54,20 @@
   if (!current_surface_)
     current_surface_ = GetNextSurface();
 
-  if (current_surface_) {
-    gl_->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
-                              texture_target_, current_surface_->texture, 0);
-    if (current_surface_->stencil) {
-      gl_->FramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
-                                   GL_RENDERBUFFER, current_surface_->stencil);
-    }
+  if (!current_surface_)
+    return;
+  gl_->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+                            texture_target_, current_surface_->texture, 0);
+
+#if DCHECK_IS_ON() && defined(OS_CHROMEOS)
+  const GLenum result = gl_->CheckFramebufferStatus(GL_FRAMEBUFFER);
+  if (result != GL_FRAMEBUFFER_COMPLETE)
+    DLOG(ERROR) << " Incomplete fb: " << gl::GLEnums::GetStringError(result);
+#endif
+
+  if (current_surface_->stencil) {
+    gl_->FramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
+                                 GL_RENDERBUFFER, current_surface_->stencil);
   }
 }
 
@@ -146,8 +154,9 @@
                           const gfx::ColorSpace& color_space,
                           bool use_stencil) {
   if (size == size_ && color_space == color_space_ &&
-      use_stencil == use_stencil_)
+      use_stencil == use_stencil_) {
     return;
+  }
 #if !defined(OS_MACOSX)
   // TODO(ccameron): This assert is being hit on Mac try jobs. Determine if that
   // is cause for concern or if it is benign.
diff --git a/content/browser/browsing_instance.cc b/content/browser/browsing_instance.cc
index 00b87767..d48d1441 100644
--- a/content/browser/browsing_instance.cc
+++ b/content/browser/browsing_instance.cc
@@ -78,8 +78,10 @@
   // No current SiteInstance for this site, so let's create one.
   scoped_refptr<SiteInstanceImpl> instance = new SiteInstanceImpl(this);
 
-  // Set the site of this new SiteInstance, which will register it with us.
-  instance->SetSite(url);
+  // Set the site of this new SiteInstance, which will register it with us,
+  // unless this URL should leave the SiteInstance's site unassigned.
+  if (SiteInstance::ShouldAssignSiteForURL(url))
+    instance->SetSite(url);
   return instance;
 }
 
diff --git a/content/browser/frame_host/navigation_request.cc b/content/browser/frame_host/navigation_request.cc
index 28b2e1b..758e5ee 100644
--- a/content/browser/frame_host/navigation_request.cc
+++ b/content/browser/frame_host/navigation_request.cc
@@ -209,14 +209,14 @@
   // Blink and //content.
   if (IsSecMetadataEnabled() && IsOriginSecure(url)) {
     std::string site_value = "cross-site";
-    std::string user_value = has_user_gesture ? "?T" : std::string();
+    std::string user_value = has_user_gesture ? "?1" : std::string();
 
     // Navigations that aren't triggerable from the web (e.g. typing in the
     // address bar, or clicking a bookmark) are labeled as 'none'. Webby
     // navigations compare the |initiator_origin| to the navigation target.
     if (!PageTransitionIsWebTriggerable(transition)) {
       site_value = "none";
-      user_value = "?T";
+      user_value = "?1";
     } else if (initiator_origin) {
       url::Origin target_origin = url::Origin::Create(url);
       if (initiator_origin->IsSameOriginWith(target_origin)) {
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 b608bdf..251b1008 100644
--- a/content/browser/frame_host/render_frame_host_manager_browsertest.cc
+++ b/content/browser/frame_host/render_frame_host_manager_browsertest.cc
@@ -5692,4 +5692,102 @@
   EXPECT_EQ(speculative_rph, web_contents->GetMainFrame()->GetProcess());
 }
 
+// ContentBrowserClient that skips assigning a site URL for a given URL.
+class DontAssignSiteContentBrowserClient : public TestContentBrowserClient {
+ public:
+  // Any visit to |url_to_skip| will not cause the site to be assigned to the
+  // SiteInstance.
+  explicit DontAssignSiteContentBrowserClient(const GURL& url_to_skip)
+      : url_to_skip_(url_to_skip) {}
+
+  bool ShouldAssignSiteForURL(const GURL& url) override {
+    return url != url_to_skip_;
+  }
+
+ private:
+  GURL url_to_skip_;
+
+  DISALLOW_COPY_AND_ASSIGN(DontAssignSiteContentBrowserClient);
+};
+
+// Ensure that coming back to a NavigationEntry with a previously unassigned
+// SiteInstance (which is now used for another site) properly switches processes
+// and SiteInstances.  See https://crbug.com/945399.
+IN_PROC_BROWSER_TEST_F(RenderFrameHostManagerTest,
+                       NavigateWithUnassignedSiteInstance) {
+  ASSERT_TRUE(embedded_test_server()->Start());
+  WebContentsImpl* web_contents =
+      static_cast<WebContentsImpl*>(shell()->web_contents());
+
+  // Navigate to a URL that does not assign site URLs.
+  GURL url1(embedded_test_server()->GetURL("a.com", "/title1.html"));
+  DontAssignSiteContentBrowserClient content_browser_client(url1);
+  ContentBrowserClient* old_client =
+      SetBrowserClientForTesting(&content_browser_client);
+  EXPECT_TRUE(NavigateToURL(shell(), url1));
+  EXPECT_EQ(url1, web_contents->GetLastCommittedURL());
+  scoped_refptr<SiteInstanceImpl> instance1(
+      web_contents->GetMainFrame()->GetSiteInstance());
+  RenderProcessHost* process1 = instance1->GetProcess();
+  EXPECT_EQ(GURL(), instance1->GetSiteURL());
+
+  // Navigate to foo.com, which uses the previous SiteInstance and sets its site
+  // URL.
+  GURL url2(embedded_test_server()->GetURL("foo.com", "/title1.html"));
+  EXPECT_TRUE(NavigateToURL(shell(), url2));
+  EXPECT_EQ(instance1, web_contents->GetMainFrame()->GetSiteInstance());
+  EXPECT_EQ(GURL("http://foo.com"), instance1->GetSiteURL());
+
+  // Navigate to bar.com, which destroys the previous RenderProcessHost.
+  GURL url3(embedded_test_server()->GetURL("bar.com", "/title1.html"));
+  RenderProcessHostWatcher exit_observer(
+      process1, RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
+  EXPECT_TRUE(NavigateToURL(shell(), url3));
+  exit_observer.Wait();
+  EXPECT_NE(instance1, web_contents->GetMainFrame()->GetSiteInstance());
+
+  // At this point, process1 is deleted, and the first entry is unfortunately
+  // pointing to instance1, which has been locked to url2 and has no process.
+  EXPECT_FALSE(instance1->HasProcess());
+  if (AreAllSitesIsolatedForTesting()) {
+    // In site-per-process, we cannot use foo.com's SiteInstance for a.com.
+    EXPECT_TRUE(instance1->HasWrongProcessForURL(url1));
+  } else {
+    // If neither foo.com nor a.com require dedicated processes, then we can use
+    // the same process.
+    EXPECT_FALSE(instance1->HasWrongProcessForURL(url1));
+  }
+
+  // TODO(creis): Enable the rest of the test when https://crbug.com/793127 is
+  // fixed, since this currently fails with a DCHECK in SiteProcessCountTracker.
+#if 0
+  // Go back to the chrome-native:// URL's entry, which should swap to a new
+  // SiteInstance with an unused site URL.
+  TestNavigationObserver observer(web_contents);
+  web_contents->GetController().GoToOffset(-2);
+  observer.Wait();
+  scoped_refptr<SiteInstanceImpl> new_instance =
+      web_contents->GetMainFrame()->GetSiteInstance();
+  EXPECT_EQ(url1, web_contents->GetLastCommittedURL());
+  if (AreAllSitesIsolatedForTesting()) {
+    EXPECT_NE(instance1, new_instance);
+    EXPECT_EQ(GURL(), new_instance->GetSiteURL());
+  } else {
+    EXPECT_EQ(instance1, new_instance);
+  }
+  EXPECT_TRUE(new_instance->HasProcess());
+
+  // Because url1 does not set a site URL, it should not lock the new process
+  // either, so that it can be used for subsequent navigations.
+  content::RenderProcessHost* new_process = new_instance->GetProcess();
+  auto* policy = ChildProcessSecurityPolicy::GetInstance();
+  EXPECT_TRUE(
+      policy->CanAccessDataForOrigin(new_process->GetID(), url1));
+  EXPECT_TRUE(
+      policy->CanAccessDataForOrigin(new_process->GetID(), url2));
+#endif
+
+  SetBrowserClientForTesting(old_client);
+}
+
 }  // namespace content
diff --git a/content/browser/frame_host/sec_fetch_browsertest.cc b/content/browser/frame_host/sec_fetch_browsertest.cc
index d55e69e2..5b0a48b 100644
--- a/content/browser/frame_host/sec_fetch_browsertest.cc
+++ b/content/browser/frame_host/sec_fetch_browsertest.cc
@@ -88,9 +88,9 @@
   }
 
   {
-    // Sec-Fetch-User: ?T
+    // Sec-Fetch-User: ?1
     NavigateForHeader("Sec-Fetch-User");
-    EXPECT_EQ("?T", GetContent());
+    EXPECT_EQ("?1", GetContent());
   }
 }
 
diff --git a/content/browser/indexed_db/indexed_db_dispatcher_host_unittest.cc b/content/browser/indexed_db/indexed_db_dispatcher_host_unittest.cc
index 1d52043..684cae99 100644
--- a/content/browser/indexed_db/indexed_db_dispatcher_host_unittest.cc
+++ b/content/browser/indexed_db/indexed_db_dispatcher_host_unittest.cc
@@ -1165,7 +1165,8 @@
   context_impl_->RemoveObserver(&observer);
 }
 
-TEST_F(IndexedDBDispatcherHostTest, NotifyIndexedDBContentChanged) {
+// The test is flaky. See https://crbug.com/879213
+TEST_F(IndexedDBDispatcherHostTest, DISABLED_NotifyIndexedDBContentChanged) {
   const int64_t kDBVersion1 = 1;
   const int64_t kDBVersion2 = 2;
   const int64_t kTransactionId1 = 1;
diff --git a/content/browser/renderer_host/frame_connector_delegate.cc b/content/browser/renderer_host/frame_connector_delegate.cc
index 39f910a..c0d82a9 100644
--- a/content/browser/renderer_host/frame_connector_delegate.cc
+++ b/content/browser/renderer_host/frame_connector_delegate.cc
@@ -48,7 +48,9 @@
   render_widget_host->SetAutoResize(visual_properties.auto_resize_enabled,
                                     visual_properties.min_size_for_auto_resize,
                                     visual_properties.max_size_for_auto_resize);
-  render_widget_host->SetPageScaleFactor(visual_properties.page_scale_factor);
+  render_widget_host->SetPageScaleState(
+      visual_properties.page_scale_factor,
+      visual_properties.is_pinch_gesture_active);
 
   render_widget_host->SynchronizeVisualProperties();
 }
diff --git a/content/browser/renderer_host/render_widget_host_impl.cc b/content/browser/renderer_host/render_widget_host_impl.cc
index c9a555e7..edfc500c 100644
--- a/content/browser/renderer_host/render_widget_host_impl.cc
+++ b/content/browser/renderer_host/render_widget_host_impl.cc
@@ -422,6 +422,7 @@
       visual_properties_ack_pending_(false),
       auto_resize_enabled_(false),
       page_scale_factor_(1.f),
+      is_pinch_gesture_active_(false),
       waiting_for_screen_rects_ack_(false),
       is_unresponsive_(false),
       in_flight_event_count_(0),
@@ -898,6 +899,7 @@
   visual_properties->max_size_for_auto_resize = max_size_for_auto_resize_;
 
   visual_properties->page_scale_factor = page_scale_factor_;
+  visual_properties->is_pinch_gesture_active = is_pinch_gesture_active_;
 
   if (view_) {
     visual_properties->new_size = view_->GetRequestedRendererSize();
@@ -984,7 +986,9 @@
       old_visual_properties_->capture_sequence_number !=
           visual_properties->capture_sequence_number ||
       old_visual_properties_->page_scale_factor !=
-          visual_properties->page_scale_factor;
+          visual_properties->page_scale_factor ||
+      old_visual_properties_->is_pinch_gesture_active !=
+          visual_properties->is_pinch_gesture_active;
 
   // We should throttle sending updated VisualProperties to the renderer to
   // the rate of commit. This ensures we don't overwhelm the renderer with
@@ -2165,8 +2169,10 @@
   max_size_for_auto_resize_ = max_size;
 }
 
-void RenderWidgetHostImpl::SetPageScaleFactor(float page_scale_factor) {
+void RenderWidgetHostImpl::SetPageScaleState(float page_scale_factor,
+                                             bool is_pinch_gesture_active) {
   page_scale_factor_ = page_scale_factor;
+  is_pinch_gesture_active_ = is_pinch_gesture_active;
 }
 
 void RenderWidgetHostImpl::Destroy(bool also_delete) {
diff --git a/content/browser/renderer_host/render_widget_host_impl.h b/content/browser/renderer_host/render_widget_host_impl.h
index 5264464..02e16b3f 100644
--- a/content/browser/renderer_host/render_widget_host_impl.h
+++ b/content/browser/renderer_host/render_widget_host_impl.h
@@ -562,8 +562,8 @@
                      const gfx::Size& min_size,
                      const gfx::Size& max_size);
 
-  // Allows the main frame's page scale factor to be tracked.
-  void SetPageScaleFactor(float page_scale_factor);
+  // Allows the main frame's page scale state to be tracked.
+  void SetPageScaleState(float page_scale_factor, bool is_pinch_gesture_active);
 
   // Fills in the |visual_properties| struct.
   // Returns |false| if the update is redundant, |true| otherwise.
@@ -1018,6 +1018,8 @@
 
   // The page-scale factor of the main-frame.
   float page_scale_factor_;
+  // True when the renderer is currently undergoing a pinch-zoom gesture.
+  bool is_pinch_gesture_active_;
 
   bool waiting_for_screen_rects_ack_;
   gfx::Rect last_view_screen_rect_;
diff --git a/content/browser/site_instance_impl.cc b/content/browser/site_instance_impl.cc
index 1612c78..77bf6dc 100644
--- a/content/browser/site_instance_impl.cc
+++ b/content/browser/site_instance_impl.cc
@@ -217,6 +217,8 @@
 }
 
 void SiteInstanceImpl::SetSite(const GURL& url) {
+  // TODO(creis): Consider calling ShouldAssignSiteForURL internally, rather
+  // than before multiple call sites.  See https://crbug.com/949220.
   TRACE_EVENT2("navigation", "SiteInstanceImpl::SetSite",
                "site id", id_, "url", url.possibly_invalid_spec());
   // A SiteInstance's site should not change.
@@ -287,14 +289,6 @@
 }
 
 bool SiteInstanceImpl::HasWrongProcessForURL(const GURL& url) {
-  // Having no process isn't a problem, since we'll assign it correctly.
-  // Note that HasProcess() may return true if process_ is null, in
-  // process-per-site cases where there's an existing process available.
-  // We want to use such a process in the IsSuitableHost check, so we
-  // may end up assigning process_ in the GetProcess() call below.
-  if (!HasProcess())
-    return false;
-
   // If the URL to navigate to can be associated with any site instance,
   // we want to keep it in the same process.
   if (IsRendererDebugURL(url))
@@ -321,6 +315,37 @@
   // GetRelatedSiteInstance(url).
   browsing_instance_->GetSiteAndLockForURL(
       url, /* allow_default_instance */ true, &site_url, &origin_lock);
+
+  // Note that HasProcess() may return true if process_ is null, in
+  // process-per-site cases where there's an existing process available.
+  // We want to use such a process in the IsSuitableHost check, so we
+  // may end up assigning process_ in the GetProcess() call below.
+  if (!HasProcess()) {
+    // If there is no process or site, then this is a new SiteInstance that can
+    // be used for anything.
+    if (!HasSite())
+      return false;
+
+    // If there is no process but there is a site, then the process must have
+    // been discarded after we navigated away.  If the site URLs match, then it
+    // is safe to use this SiteInstance.
+    if (GetSiteURL() == site_url)
+      return false;
+
+    // If the site URLs do not match, but neither this SiteInstance nor the
+    // destination site_url require dedicated processes, then it is safe to use
+    // this SiteInstance.
+    if (!RequiresDedicatedProcess() &&
+        !DoesSiteRequireDedicatedProcess(GetIsolationContext(), site_url)) {
+      return false;
+    }
+
+    // Otherwise, there's no process, the site URLs don't match, and at least
+    // one of them requires a dedicated process, so it is not safe to use this
+    // SiteInstance.
+    return true;
+  }
+
   return !RenderProcessHostImpl::IsSuitableHost(
       GetProcess(), browsing_instance_->GetBrowserContext(),
       GetIsolationContext(), site_url, origin_lock);
diff --git a/content/browser/site_instance_impl_unittest.cc b/content/browser/site_instance_impl_unittest.cc
index 86379ad6..628d64b 100644
--- a/content/browser/site_instance_impl_unittest.cc
+++ b/content/browser/site_instance_impl_unittest.cc
@@ -721,13 +721,13 @@
   EXPECT_FALSE(instance->HasSite());
   EXPECT_TRUE(instance->GetSiteURL().is_empty());
 
+  // Check prior to assigning a site or process to the instance, which is
+  // expected to return false to allow the SiteInstance to be used for anything.
+  EXPECT_FALSE(instance->HasWrongProcessForURL(GURL("http://google.com")));
+
   instance->SetSite(GURL("http://evernote.com/"));
   EXPECT_TRUE(instance->HasSite());
 
-  // Check prior to "assigning" a process to the instance, which is expected
-  // to return false due to not being attached to any process yet.
-  EXPECT_FALSE(instance->HasWrongProcessForURL(GURL("http://google.com")));
-
   // The call to GetProcess actually creates a new real process, which works
   // fine, but might be a cause for problems in different contexts.
   host.reset(instance->GetProcess());
@@ -779,13 +779,13 @@
   scoped_refptr<SiteInstanceImpl> instance(
       SiteInstanceImpl::Create(browser_context.get()));
 
+  // Check prior to assigning a site or process to the instance, which is
+  // expected to return false to allow the SiteInstance to be used for anything.
+  EXPECT_FALSE(instance->HasWrongProcessForURL(GURL("http://google.com")));
+
   instance->SetSite(GURL("http://evernote.com/"));
   EXPECT_TRUE(instance->HasSite());
 
-  // Check prior to "assigning" a process to the instance, which is expected
-  // to return false due to not being attached to any process yet.
-  EXPECT_FALSE(instance->HasWrongProcessForURL(GURL("http://google.com")));
-
   // The call to GetProcess actually creates a new real process, which works
   // fine, but might be a cause for problems in different contexts.
   host.reset(instance->GetProcess());
diff --git a/content/browser/site_per_process_browsertest.cc b/content/browser/site_per_process_browsertest.cc
index e870f17..bbc5c8d 100644
--- a/content/browser/site_per_process_browsertest.cc
+++ b/content/browser/site_per_process_browsertest.cc
@@ -10880,6 +10880,17 @@
   RenderFrameSubmissionObserver observer_c(child_c);
   RenderFrameSubmissionObserver observer_d(child_d);
 
+  // Monitor visual sync messages coming from the mainframe to make sure
+  // |is_pinch_gesture_active| goes true during the pinch gesture.
+  scoped_refptr<SynchronizeVisualPropertiesMessageFilter> filter_mainframe =
+      new SynchronizeVisualPropertiesMessageFilter();
+  root->current_frame_host()->GetProcess()->AddFilter(filter_mainframe.get());
+  // Monitor frame sync messages coming from child_b as it will need to
+  // relay them to child_d.
+  scoped_refptr<SynchronizeVisualPropertiesMessageFilter> filter_child_b =
+      new SynchronizeVisualPropertiesMessageFilter();
+  child_b->current_frame_host()->GetProcess()->AddFilter(filter_child_b.get());
+
   // We need to observe a root frame submission to pick up the initial page
   // scale factor.
   observer_a.WaitForAnyFrameSubmission();
@@ -10923,6 +10934,17 @@
   observer_b.WaitForExternalPageScaleFactor(target_page_scale, kScaleTolerance);
   observer_c.WaitForExternalPageScaleFactor(target_page_scale, kScaleTolerance);
   observer_d.WaitForExternalPageScaleFactor(target_page_scale, kScaleTolerance);
+
+  // The change in |is_pinch_gesture_active| that signals the end of the pinch
+  // gesture will occur sometime after the ack for GesturePinchEnd, so we need
+  // to wait for it from each renderer. If it's never seen, the test fails by
+  // timing out.
+  filter_mainframe->WaitForPinchGestureEnd();
+  EXPECT_TRUE(filter_mainframe->pinch_gesture_active_set());
+  EXPECT_TRUE(filter_mainframe->pinch_gesture_active_cleared());
+  filter_child_b->WaitForPinchGestureEnd();
+  EXPECT_TRUE(filter_child_b->pinch_gesture_active_set());
+  EXPECT_TRUE(filter_child_b->pinch_gesture_active_cleared());
 }
 
 // Verify that sandbox flags specified by a CSP header are properly inherited by
diff --git a/content/browser/web_contents/web_contents_impl_unittest.cc b/content/browser/web_contents/web_contents_impl_unittest.cc
index 526d73f..c1bfa288 100644
--- a/content/browser/web_contents/web_contents_impl_unittest.cc
+++ b/content/browser/web_contents/web_contents_impl_unittest.cc
@@ -27,6 +27,7 @@
 #include "content/common/frame_messages.h"
 #include "content/common/input/synthetic_web_input_event_builders.h"
 #include "content/common/view_messages.h"
+#include "content/public/browser/child_process_security_policy.h"
 #include "content/public/browser/content_browser_client.h"
 #include "content/public/browser/global_request_id.h"
 #include "content/public/browser/interstitial_page_delegate.h"
@@ -1109,7 +1110,7 @@
 // Tests that if we go back twice (same-site then cross-site), and the cross-
 // site RFH commits first, we ignore the now-swapped-out RFH's commit.
 TEST_F(WebContentsImplTest, CrossSiteNavigationBackOldNavigationIgnored) {
-  // Start with a web ui page, which gets a new RVH with WebUI bindings.
+  // Start with a web ui page, which gets a new RFH with WebUI bindings.
   GURL url1(std::string(kChromeUIScheme) + "://" +
             std::string(kChromeUIGpuHost));
   NavigationSimulator::NavigateAndCommitFromBrowser(contents(), url1);
@@ -1170,6 +1171,9 @@
   webui_rfh = contents()->GetPendingMainFrame();
 
   // DidNavigate from the second back.
+  // Note that the process in instance1 is gone at this point, but we will still
+  // use instance1 and entry1 because HasWrongProcessForURL will return false
+  // when there is no process and the site URL matches.
   back_navigation2->Commit();
 
   // That should have landed us on the first entry.
@@ -1179,6 +1183,12 @@
   contents()->TestDidNavigate(google_rfh, entry2->GetUniqueID(), false, url2,
                               ui::PAGE_TRANSITION_TYPED);
   EXPECT_EQ(entry1, controller().GetLastCommittedEntry());
+
+  // The newly created process for url1 should be locked to chrome://gpu.
+  RenderProcessHost* new_process = contents()->GetMainFrame()->GetProcess();
+  auto* policy = content::ChildProcessSecurityPolicy::GetInstance();
+  EXPECT_TRUE(policy->CanAccessDataForOrigin(new_process->GetID(), url1));
+  EXPECT_FALSE(policy->CanAccessDataForOrigin(new_process->GetID(), url2));
 }
 
 // Test that during a slow cross-site navigation, a sub-frame navigation in the
diff --git a/content/browser/webauth/authenticator_impl_unittest.cc b/content/browser/webauth/authenticator_impl_unittest.cc
index 0c78440..86975327 100644
--- a/content/browser/webauth/authenticator_impl_unittest.cc
+++ b/content/browser/webauth/authenticator_impl_unittest.cc
@@ -282,15 +282,19 @@
   return criteria;
 }
 
-std::vector<PublicKeyCredentialDescriptorPtr> GetTestAllowCredentials() {
+std::vector<PublicKeyCredentialDescriptorPtr> GetTestCredentials(
+    size_t num_credentials = 1) {
   std::vector<PublicKeyCredentialDescriptorPtr> descriptors;
-  auto credential = PublicKeyCredentialDescriptor::New();
-  credential->type = PublicKeyCredentialType::PUBLIC_KEY;
-  std::vector<uint8_t> id(32, 0x0A);
-  credential->id = id;
-  credential->transports.push_back(AuthenticatorTransport::USB);
-  credential->transports.push_back(AuthenticatorTransport::BLE);
-  descriptors.push_back(std::move(credential));
+  for (size_t i = 0; i < num_credentials; i++) {
+    DCHECK(i <= std::numeric_limits<uint8_t>::max());
+    std::vector<uint8_t> id(32u, static_cast<uint8_t>(i));
+    auto credential = PublicKeyCredentialDescriptor::New();
+    credential->type = PublicKeyCredentialType::PUBLIC_KEY;
+    credential->id = id;
+    credential->transports.push_back(AuthenticatorTransport::USB);
+    credential->transports.push_back(AuthenticatorTransport::BLE);
+    descriptors.push_back(std::move(credential));
+  }
   return descriptors;
 }
 
@@ -315,7 +319,7 @@
   options->adjusted_timeout = base::TimeDelta::FromMinutes(1);
   options->user_verification =
       blink::mojom::UserVerificationRequirement::PREFERRED;
-  options->allow_credentials = GetTestAllowCredentials();
+  options->allow_credentials = GetTestCredentials();
   return options;
 }
 
@@ -1119,7 +1123,7 @@
       GetTestPublicKeyCredentialCreationOptions();
 
   // Exclude the one already registered credential.
-  options->exclude_credentials = GetTestAllowCredentials();
+  options->exclude_credentials = GetTestCredentials();
   ASSERT_TRUE(scoped_virtual_device.mutable_state()->InjectRegistration(
       options->exclude_credentials[0]->id, kTestRelyingPartyId));
 
@@ -2361,7 +2365,7 @@
 
   PublicKeyCredentialCreationOptionsPtr options =
       GetTestPublicKeyCredentialCreationOptions();
-  options->exclude_credentials = GetTestAllowCredentials();
+  options->exclude_credentials = GetTestCredentials();
   ASSERT_TRUE(scoped_virtual_device.mutable_state()->InjectRegistration(
       options->exclude_credentials[0]->id, kTestRelyingPartyId));
 
@@ -2493,6 +2497,74 @@
   }
 }
 
+TEST_F(AuthenticatorImplTest, MakeCredentialWithLargeExcludeList) {
+  TestServiceManagerContext smc;
+  NavigateAndCommit(GURL(kTestOrigin1));
+
+  for (bool has_excluded_credential : {false, true}) {
+    SCOPED_TRACE(::testing::Message()
+                 << "has_excluded_credential=" << has_excluded_credential);
+
+    device::test::ScopedVirtualFidoDevice virtual_device;
+    device::VirtualCtap2Device::Config config;
+    config.reject_large_allow_and_exclude_lists = true;
+    virtual_device.SetCtap2Config(config);
+
+    PublicKeyCredentialCreationOptionsPtr options =
+        GetTestPublicKeyCredentialCreationOptions();
+    options->exclude_credentials = GetTestCredentials(/*num_credentials=*/10);
+    if (has_excluded_credential) {
+      ASSERT_TRUE(virtual_device.mutable_state()->InjectRegistration(
+          options->exclude_credentials.back()->id, kTestRelyingPartyId));
+    }
+    TestMakeCredentialCallback callback_receiver;
+
+    AuthenticatorPtr authenticator = ConnectToAuthenticator();
+    authenticator->MakeCredential(std::move(options),
+                                  callback_receiver.callback());
+    base::RunLoop().RunUntilIdle();
+    callback_receiver.WaitForCallback();
+    EXPECT_EQ(callback_receiver.status(),
+              has_excluded_credential ? AuthenticatorStatus::CREDENTIAL_EXCLUDED
+                                      : AuthenticatorStatus::SUCCESS);
+  }
+}
+
+TEST_F(AuthenticatorImplTest, GetAssertionWithLargeAllowList) {
+  TestServiceManagerContext smc;
+  NavigateAndCommit(GURL(kTestOrigin1));
+
+  for (bool has_allowed_credential : {false, true}) {
+    SCOPED_TRACE(::testing::Message()
+                 << "has_allowed_credential=" << has_allowed_credential);
+
+    device::test::ScopedVirtualFidoDevice virtual_device;
+    device::VirtualCtap2Device::Config config;
+    config.reject_large_allow_and_exclude_lists = true;
+    virtual_device.SetCtap2Config(config);
+
+    AuthenticatorPtr authenticator = ConnectToAuthenticator();
+
+    PublicKeyCredentialRequestOptionsPtr options =
+        GetTestPublicKeyCredentialRequestOptions();
+    options->allow_credentials = GetTestCredentials(/*num_credentials=*/10);
+    if (has_allowed_credential) {
+      ASSERT_TRUE(virtual_device.mutable_state()->InjectRegistration(
+          options->allow_credentials.back()->id, kTestRelyingPartyId));
+    }
+
+    TestGetAssertionCallback callback_receiver;
+    authenticator->GetAssertion(std::move(options),
+                                callback_receiver.callback());
+    base::RunLoop().RunUntilIdle();
+    callback_receiver.WaitForCallback();
+    EXPECT_EQ(callback_receiver.status(),
+              has_allowed_credential
+                  ? AuthenticatorStatus::SUCCESS
+                  : AuthenticatorStatus::CREDENTIAL_NOT_RECOGNIZED);
+  }
+}
+
 class UVAuthenticatorImplTest : public AuthenticatorImplTest {
  public:
   UVAuthenticatorImplTest() = default;
diff --git a/content/common/frame_messages.h b/content/common/frame_messages.h
index 4784e99..48b2fe1 100644
--- a/content/common/frame_messages.h
+++ b/content/common/frame_messages.h
@@ -265,6 +265,7 @@
   IPC_STRUCT_TRAITS_MEMBER(capture_sequence_number)
   IPC_STRUCT_TRAITS_MEMBER(zoom_level)
   IPC_STRUCT_TRAITS_MEMBER(page_scale_factor)
+  IPC_STRUCT_TRAITS_MEMBER(is_pinch_gesture_active)
   IPC_STRUCT_TRAITS_MEMBER(local_surface_id_allocation)
 IPC_STRUCT_TRAITS_END()
 
diff --git a/content/common/frame_visual_properties.h b/content/common/frame_visual_properties.h
index b93180d..c897b9b 100644
--- a/content/common/frame_visual_properties.h
+++ b/content/common/frame_visual_properties.h
@@ -44,7 +44,10 @@
   // (0 is the default value which results in 1.0 zoom factor.)
   double zoom_level = 0;
 
+  // Tracks the page-scale factor and whether the frame is currently in an
+  // active pinch-zoom gesture.
   float page_scale_factor = 1.f;
+  bool is_pinch_gesture_active = false;
 
   // The time at which the viz::LocalSurfaceId used to submit this was
   // allocated.
diff --git a/content/common/navigation_params.h b/content/common/navigation_params.h
index 7ce18aaf5..eff2dc20 100644
--- a/content/common/navigation_params.h
+++ b/content/common/navigation_params.h
@@ -98,7 +98,7 @@
   kOpenerCrossOrigin = 5,
 
   // The navigation was initiated from or occurred in an iframe with
-  // |kSandboxDownloads| flag set and without user activation.
+  // |WebSandboxFlags::kDownloads| flag set and without user activation.
   kSandboxNoGesture = 7,
 
   // The navigation was initiated from or occurred in an ad frame without user
diff --git a/content/common/visual_properties.h b/content/common/visual_properties.h
index ed6f914..dcccf0b 100644
--- a/content/common/visual_properties.h
+++ b/content/common/visual_properties.h
@@ -83,6 +83,10 @@
   // This represents the page's scale factor, which changes during pinch zoom.
   // It needs to be shared with subframes.
   float page_scale_factor = 1.f;
+
+  // Indicates whether a pinch gesture is currently active. Originates in the
+  // main frame's renderer, and needs to be shared with subframes.
+  bool is_pinch_gesture_active = false;
 };
 
 }  // namespace content
diff --git a/content/common/widget_messages.h b/content/common/widget_messages.h
index 84f6662a..fcda7ced 100644
--- a/content/common/widget_messages.h
+++ b/content/common/widget_messages.h
@@ -63,6 +63,7 @@
   IPC_STRUCT_TRAITS_MEMBER(capture_sequence_number)
   IPC_STRUCT_TRAITS_MEMBER(zoom_level)
   IPC_STRUCT_TRAITS_MEMBER(page_scale_factor)
+  IPC_STRUCT_TRAITS_MEMBER(is_pinch_gesture_active)
 IPC_STRUCT_TRAITS_END()
 
 // Traits for WebDeviceEmulationParams.
diff --git a/content/public/test/browser_test_utils.cc b/content/public/test/browser_test_utils.cc
index 74a2c98..b6de992 100644
--- a/content/public/test/browser_test_utils.cc
+++ b/content/public/test/browser_test_utils.cc
@@ -3155,6 +3155,20 @@
 void SynchronizeVisualPropertiesMessageFilter::OnSynchronizeVisualProperties(
     const viz::FrameSinkId& frame_sink_id,
     const FrameVisualProperties& visual_properties) {
+  // Monitor |is_pinch_gesture_active| to determine when pinch gestures begin
+  // and end.
+  if (visual_properties.is_pinch_gesture_active &&
+      !last_pinch_gesture_active_) {
+    pinch_gesture_active_set_ = true;
+  }
+  if (!visual_properties.is_pinch_gesture_active &&
+      last_pinch_gesture_active_) {
+    pinch_gesture_active_cleared_ = true;
+    if (pinch_end_run_loop_)
+      pinch_end_run_loop_->Quit();
+  }
+  last_pinch_gesture_active_ = visual_properties.is_pinch_gesture_active;
+
   gfx::Rect screen_space_rect_in_dip = visual_properties.screen_space_rect;
   if (IsUseZoomForDSFEnabled()) {
     screen_space_rect_in_dip =
@@ -3236,6 +3250,14 @@
   return false;
 }
 
+void SynchronizeVisualPropertiesMessageFilter::WaitForPinchGestureEnd() {
+  if (pinch_gesture_active_cleared_)
+    return;
+  DCHECK(!pinch_end_run_loop_);
+  pinch_end_run_loop_ = std::make_unique<base::RunLoop>();
+  pinch_end_run_loop_->Run();
+}
+
 RenderWidgetHostMouseEventMonitor::RenderWidgetHostMouseEventMonitor(
     RenderWidgetHost* host)
     : host_(host), event_received_(false) {
diff --git a/content/public/test/browser_test_utils.h b/content/public/test/browser_test_utils.h
index 7c11f9c..69c48d88 100644
--- a/content/public/test/browser_test_utils.h
+++ b/content/public/test/browser_test_utils.h
@@ -1574,6 +1574,7 @@
 // BrowserPluginHostMsg_SynchronizeVisualProperties messages. This allows the
 // message to continue to the target child so that processing can be verified by
 // tests.
+// It also monitors for GesturePinchBegin/End events.
 class SynchronizeVisualPropertiesMessageFilter
     : public content::BrowserMessageFilter {
  public:
@@ -1592,6 +1593,11 @@
   // Waits for the next viz::LocalSurfaceId be received and returns it.
   viz::LocalSurfaceId WaitForSurfaceId();
 
+  bool pinch_gesture_active_set() { return pinch_gesture_active_set_; }
+  bool pinch_gesture_active_cleared() { return pinch_gesture_active_cleared_; }
+
+  void WaitForPinchGestureEnd();
+
  protected:
   ~SynchronizeVisualPropertiesMessageFilter() override;
 
@@ -1623,6 +1629,11 @@
   viz::LocalSurfaceId last_surface_id_;
   std::unique_ptr<base::RunLoop> surface_id_run_loop_;
 
+  bool pinch_gesture_active_set_;
+  bool pinch_gesture_active_cleared_;
+  bool last_pinch_gesture_active_;
+  std::unique_ptr<base::RunLoop> pinch_end_run_loop_;
+
   DISALLOW_COPY_AND_ASSIGN(SynchronizeVisualPropertiesMessageFilter);
 };
 
diff --git a/content/renderer/compositor/layer_tree_view.cc b/content/renderer/compositor/layer_tree_view.cc
index 5f163f8..6d7e1f34 100644
--- a/content/renderer/compositor/layer_tree_view.cc
+++ b/content/renderer/compositor/layer_tree_view.cc
@@ -425,8 +425,11 @@
   layer_tree_host_->SetRasterColorSpace(color_space);
 }
 
-void LayerTreeView::SetExternalPageScaleFactor(float page_scale_factor) {
-  layer_tree_host_->SetExternalPageScaleFactor(page_scale_factor);
+void LayerTreeView::SetExternalPageScaleFactor(
+    float page_scale_factor,
+    bool is_external_pinch_gesture_active) {
+  layer_tree_host_->SetExternalPageScaleFactor(
+      page_scale_factor, is_external_pinch_gesture_active);
 }
 
 void LayerTreeView::ClearCachesOnNextCommit() {
diff --git a/content/renderer/compositor/layer_tree_view.h b/content/renderer/compositor/layer_tree_view.h
index 7b0aac53..a477847 100644
--- a/content/renderer/compositor/layer_tree_view.h
+++ b/content/renderer/compositor/layer_tree_view.h
@@ -107,7 +107,8 @@
   bool SendMessageToMicroBenchmark(int id, std::unique_ptr<base::Value> value);
   void SetFrameSinkId(const viz::FrameSinkId& frame_sink_id);
   void SetRasterColorSpace(const gfx::ColorSpace& color_space);
-  void SetExternalPageScaleFactor(float page_scale_factor);
+  void SetExternalPageScaleFactor(float page_scale_factor,
+                                  bool is_external_pinch_gesture_active);
   void ClearCachesOnNextCommit();
   void SetContentSourceId(uint32_t source_id);
   void SetViewportSizeAndScale(
diff --git a/content/renderer/render_frame_proxy.cc b/content/renderer/render_frame_proxy.cc
index d4e8c3f0..5342a18b 100644
--- a/content/renderer/render_frame_proxy.cc
+++ b/content/renderer/render_frame_proxy.cc
@@ -311,8 +311,10 @@
   SynchronizeVisualProperties();
 }
 
-void RenderFrameProxy::OnPageScaleFactorChanged(float page_scale_factor) {
+void RenderFrameProxy::OnPageScaleFactorChanged(float page_scale_factor,
+                                                bool is_pinch_gesture_active) {
   pending_visual_properties_.page_scale_factor = page_scale_factor;
+  pending_visual_properties_.is_pinch_gesture_active = is_pinch_gesture_active;
   SynchronizeVisualProperties();
 }
 
@@ -687,6 +689,8 @@
           pending_visual_properties_.zoom_level ||
       sent_visual_properties_->page_scale_factor !=
           pending_visual_properties_.page_scale_factor ||
+      sent_visual_properties_->is_pinch_gesture_active !=
+          pending_visual_properties_.is_pinch_gesture_active ||
       capture_sequence_number_changed;
 
   if (synchronized_props_changed) {
diff --git a/content/renderer/render_frame_proxy.h b/content/renderer/render_frame_proxy.h
index 947c70a..bebf7d1 100644
--- a/content/renderer/render_frame_proxy.h
+++ b/content/renderer/render_frame_proxy.h
@@ -145,8 +145,9 @@
   void OnZoomLevelChanged(double zoom_level);
 
   // Out-of-process child frames receive a signal from RenderWidget when the
-  // page scale factor has changed.
-  void OnPageScaleFactorChanged(float page_scale_factor);
+  // page scale factor has changed, and/or a pinch-zoom gesture starts/ends.
+  void OnPageScaleFactorChanged(float page_scale_factor,
+                                bool is_pinch_gesture_active);
 
   // Invoked by RenderWidget when a new capture sequence number was set,
   // indicating that surfaces should be synchronized.
@@ -229,6 +230,10 @@
 
   void WasEvicted();
 
+  bool is_pinch_gesture_active_for_testing() {
+    return pending_visual_properties_.is_pinch_gesture_active;
+  }
+
  private:
   RenderFrameProxy(int routing_id);
 
diff --git a/content/renderer/render_view_browsertest.cc b/content/renderer/render_view_browsertest.cc
index e60f863..3bb88dc9 100644
--- a/content/renderer/render_view_browsertest.cc
+++ b/content/renderer/render_view_browsertest.cc
@@ -548,6 +548,54 @@
   }
 };
 
+TEST_F(RenderViewImplTest, IsPinchGestureActivePropagatesToProxies) {
+  LoadHTML(
+      "<body style='min-height:1000px;'>"
+      "  <iframe src='data:text/html,frame 1'></iframe>"
+      "  <iframe src='data:text/html,frame 2'></iframe>"
+      "</body>");
+
+  // Verify child's proxy doesn't think we're pinching.
+  blink::WebFrame* root_web_frame = frame()->GetWebFrame();
+  ASSERT_TRUE(root_web_frame->FirstChild()->IsWebLocalFrame());
+  TestRenderFrame* child_frame_1 =
+      static_cast<TestRenderFrame*>(RenderFrame::FromWebFrame(
+          root_web_frame->FirstChild()->ToWebLocalFrame()));
+  ASSERT_TRUE(child_frame_1);
+  TestRenderFrame* child_frame_2 =
+      static_cast<TestRenderFrame*>(RenderFrame::FromWebFrame(
+          root_web_frame->FirstChild()->NextSibling()->ToWebLocalFrame()));
+  ASSERT_TRUE(child_frame_2);
+  child_frame_1->SwapOut(kProxyRoutingId, true,
+                         ReconstructReplicationStateForTesting(child_frame_1));
+  EXPECT_TRUE(root_web_frame->FirstChild()->IsWebRemoteFrame());
+  RenderFrameProxy* child_proxy_1 = RenderFrameProxy::FromWebFrame(
+      root_web_frame->FirstChild()->ToWebRemoteFrame());
+  ASSERT_TRUE(child_proxy_1);
+  EXPECT_FALSE(child_proxy_1->is_pinch_gesture_active_for_testing());
+
+  // Set the |is_pinch_gesture_active| flag.
+  view()->PageScaleFactorChanged(1.f, true);
+  EXPECT_TRUE(child_proxy_1->is_pinch_gesture_active_for_testing());
+
+  // Create a new remote child, and get its proxy. Swapping out will force
+  // creation and registering of a new RenderFrameProxy, which should pick up
+  // the existing setting.
+  child_frame_2->SwapOut(kProxyRoutingId + 1, true,
+                         ReconstructReplicationStateForTesting(child_frame_2));
+  EXPECT_TRUE(root_web_frame->FirstChild()->NextSibling()->IsWebRemoteFrame());
+  RenderFrameProxy* child_proxy_2 = RenderFrameProxy::FromWebFrame(
+      root_web_frame->FirstChild()->NextSibling()->ToWebRemoteFrame());
+
+  // Verify new child has the flag too.
+  EXPECT_TRUE(child_proxy_2->is_pinch_gesture_active_for_testing());
+
+  // Reset the flag, make sure both children respond.
+  view()->PageScaleFactorChanged(1.f, false);
+  EXPECT_FALSE(child_proxy_1->is_pinch_gesture_active_for_testing());
+  EXPECT_FALSE(child_proxy_2->is_pinch_gesture_active_for_testing());
+}
+
 // Test that we get form state change notifications when input fields change.
 TEST_F(RenderViewImplTest, OnNavStateChanged) {
   view()->set_send_content_state_immediately(true);
@@ -877,10 +925,6 @@
 // continues to receive the original ScreenInfo and not the emualted
 // ScreenInfo.
 TEST_F(RenderViewImplScaleFactorTest, DeviceEmulationWithOOPIF) {
-  // This test should only run with --site-per-process.
-  if (!AreAllSitesIsolatedForTesting())
-    return;
-
   const float device_scale = 2.0f;
   float compositor_dsf =
       compositor_deps_->IsUseZoomForDSFEnabled() ? 1.f : device_scale;
@@ -930,11 +974,6 @@
 // Verify that security origins are replicated properly to RenderFrameProxies
 // when swapping out.
 TEST_F(RenderViewImplTest, OriginReplicationForSwapOut) {
-  // This test should only run with --site-per-process, since origin
-  // replication only happens in that mode.
-  if (!AreAllSitesIsolatedForTesting())
-    return;
-
   LoadHTML(
       "Hello <iframe src='data:text/html,frame 1'></iframe>"
       "<iframe src='data:text/html,frame 2'></iframe>");
@@ -1046,10 +1085,6 @@
 // destroyed along with the proxy.  This protects against races in
 // https://crbug.com/526304 and https://crbug.com/568676.
 TEST_F(RenderViewImplTest, DetachingProxyAlsoDestroysProvisionalFrame) {
-  // This test should only run with --site-per-process.
-  if (!AreAllSitesIsolatedForTesting())
-    return;
-
   LoadHTML("Hello <iframe src='data:text/html,frame 1'></iframe>");
   WebFrame* web_frame = frame()->GetWebFrame();
   TestRenderFrame* child_frame = static_cast<TestRenderFrame*>(
diff --git a/content/renderer/render_widget.cc b/content/renderer/render_widget.cc
index 9fb5ae6..cd7cccd 100644
--- a/content/renderer/render_widget.cc
+++ b/content/renderer/render_widget.cc
@@ -860,10 +860,18 @@
         // the visual properties here. While blink doesn't need to know this
         // page scale factor outside the main frame, the compositor does in
         // order to produce its output at the correct scale.
-        layer_tree_view_->SetExternalPageScaleFactor(params.page_scale_factor);
+        layer_tree_view_->SetExternalPageScaleFactor(
+            params.page_scale_factor, params.is_pinch_gesture_active);
         // Store the value to give to any new RenderFrameProxy that is
         // registered.
         page_scale_factor_from_mainframe_ = params.page_scale_factor;
+        // Similarly, only the main frame knows when a pinch gesture is active,
+        // but this information is needed in subframes so they can throttle
+        // re-rastering in the same manner as the main frame.
+        // |is_pinch_gesture_active| follows the same path to the subframe
+        // compositor(s) as |page_scale_factor|.
+        is_pinch_gesture_active_from_mainframe_ =
+            params.is_pinch_gesture_active;
         // Push the page scale factor down to any child RenderWidgets via our
         // child proxy frames.
         // TODO(danakj): This ends up setting the page scale factor in the
@@ -872,8 +880,10 @@
         // global value per-page, we could instead store it once in the browser
         // (such as in RenderViewHost) and distribute it to each frame-hosted
         // RenderWidget from there.
-        for (auto& child_proxy : render_frame_proxies_)
-          child_proxy.OnPageScaleFactorChanged(params.page_scale_factor);
+        for (auto& child_proxy : render_frame_proxies_) {
+          child_proxy.OnPageScaleFactorChanged(params.page_scale_factor,
+                                               params.is_pinch_gesture_active);
+        }
       }
 
       gfx::Size old_visible_viewport_size = visible_viewport_size_;
@@ -3440,7 +3450,8 @@
   // frame trees). A new RenderFrameProxy means there is a new child
   // RenderWidget in another frame tree. In order for it to hear about
   // the page scale factor we pass along the last seen value here.
-  proxy->OnPageScaleFactorChanged(page_scale_factor_from_mainframe_);
+  proxy->OnPageScaleFactorChanged(page_scale_factor_from_mainframe_,
+                                  is_pinch_gesture_active_from_mainframe_);
 }
 
 void RenderWidget::UnregisterRenderFrameProxy(RenderFrameProxy* proxy) {
@@ -3587,21 +3598,16 @@
   // of which will be via proxy child frame). These will each in turn forward
   // the message to their child RenderWidgets (through their proxy child
   // frames).
-  // TODO(crbug.com/924336): This value is continuously propagated during a
-  // pinch-zoom, causing the child RenderWidgets to re-raster, while the main
-  // frame is able to throttle re-raster to powers of 2. We could find some way
-  // to throttle child RenderWidgets also, perhaps by informing them when the
-  // pinch-zoom gesture is started and stopped.
   DCHECK(!is_frozen_);
   DCHECK(delegate());
 
-  // TODO(wjmaclean): In the next CL, plumb |is_pinch_gesture_active| into the
-  // observer via observer.OnPageScaleFactorChanged() and allow it to trigger
-  // SynchronizeVisualProperties if needed.
-  for (auto& observer : render_frame_proxies_)
-    observer.OnPageScaleFactorChanged(page_scale_factor);
+  for (auto& observer : render_frame_proxies_) {
+    observer.OnPageScaleFactorChanged(page_scale_factor,
+                                      is_pinch_gesture_active);
+  }
   // Store the value to give to any new RenderFrameProxy that is registered.
   page_scale_factor_from_mainframe_ = page_scale_factor;
+  is_pinch_gesture_active_from_mainframe_ = is_pinch_gesture_active;
 }
 
 void RenderWidget::UseSynchronousResizeModeForTesting(bool enable) {
diff --git a/content/renderer/render_widget.h b/content/renderer/render_widget.h
index d0fd4036..a289a6a 100644
--- a/content/renderer/render_widget.h
+++ b/content/renderer/render_widget.h
@@ -1136,10 +1136,11 @@
   // The height of the browser bottom controls.
   float bottom_controls_height_ = 0.f;
 
-  // The last seen page scale factor, which comes from the main frame and is
-  // propagated through the RenderWidget tree. This value is passed to any new
+  // The last seen page scale state, which comes from the main frame and is
+  // propagated through the RenderWidget tree. This state is passed to any new
   // child RenderWidget.
   float page_scale_factor_from_mainframe_ = 1.f;
+  bool is_pinch_gesture_active_from_mainframe_ = false;
 
   // This is initialized to zero and is incremented on each non-same-page
   // navigation commit by RenderFrameImpl. At that time it is sent to the
diff --git a/content/renderer/render_widget_unittest.cc b/content/renderer/render_widget_unittest.cc
index 023f4ede..e25ab23 100644
--- a/content/renderer/render_widget_unittest.cc
+++ b/content/renderer/render_widget_unittest.cc
@@ -15,11 +15,13 @@
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/test/scoped_task_environment.h"
+#include "base/unguessable_token.h"
 #include "build/build_config.h"
 #include "cc/layers/solid_color_layer.h"
 #include "cc/trees/layer_tree_host.h"
 #include "components/viz/common/features.h"
 #include "components/viz/common/surfaces/parent_local_surface_id_allocator.h"
+#include "content/common/frame_replication_state.h"
 #include "content/common/input/input_handler.mojom.h"
 #include "content/common/input/synthetic_web_input_event_builders.h"
 #include "content/common/input_messages.h"
@@ -31,6 +33,7 @@
 #include "content/renderer/compositor/layer_tree_view.h"
 #include "content/renderer/devtools/render_widget_screen_metrics_emulator.h"
 #include "content/renderer/input/widget_input_handler_manager.h"
+#include "content/renderer/render_frame_proxy.h"
 #include "content/renderer/render_widget_delegate.h"
 #include "content/test/fake_compositor_dependencies.h"
 #include "content/test/mock_render_process.h"
@@ -551,6 +554,41 @@
       const blink::WebDeviceEmulationParams& params) override {}
 };
 
+// Tests that the value of VisualProperties::is_pinch_gesture_active is
+// propagated to the LayerTreeHost when properties are synced, but only for
+// subframe widgets.
+TEST_F(RenderWidgetUnittest, ActivePinchGestureUpdatesLayerTreeHost) {
+  auto* layer_tree_host = widget()->layer_tree_view()->layer_tree_host();
+  EXPECT_FALSE(layer_tree_host->is_external_pinch_gesture_active_for_testing());
+  content::VisualProperties visual_properties;
+
+  // Sync visual properties on a child RenderWidget.
+  visual_properties.is_pinch_gesture_active = true;
+  widget()->OnSynchronizeVisualProperties(visual_properties);
+  // We expect the |is_pinch_gesture_active| value to propagate to the
+  // LayerTreeHost for sub-frames. Since GesturePinch events are handled
+  // directly in the main-frame's layer tree (and only there), information about
+  // whether or not we're in a pinch gesture must be communicated separately to
+  // sub-frame layer trees, via SynchronizeVisualProperties. This information
+  // is required to allow sub-frame compositors to throttle rastering while
+  // pinch gestures are active.
+  EXPECT_TRUE(layer_tree_host->is_external_pinch_gesture_active_for_testing());
+  visual_properties.is_pinch_gesture_active = false;
+  widget()->OnSynchronizeVisualProperties(visual_properties);
+  EXPECT_FALSE(layer_tree_host->is_external_pinch_gesture_active_for_testing());
+
+  // Repeat with a 'mainframe' widget.
+  widget()->set_delegate(std::make_unique<StubRenderWidgetDelegate>());
+  visual_properties.is_pinch_gesture_active = true;
+  widget()->OnSynchronizeVisualProperties(visual_properties);
+  // We do not expect the |is_pinch_gesture_active| value to propagate to the
+  // LayerTreeHost for the main-frame. Since GesturePinch events are handled
+  // directly by the layer tree for the main frame, it already knows whether or
+  // not a pinch gesture is active, and so we shouldn't propagate this
+  // information to the layer tree for a main-frame's widget.
+  EXPECT_FALSE(layer_tree_host->is_external_pinch_gesture_active_for_testing());
+}
+
 TEST_F(RenderWidgetPopupUnittest, EmulatingPopupRect) {
   blink::WebRect popup_screen_rect(200, 250, 100, 400);
   widget()->SetWindowRect(popup_screen_rect);
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index 32aeb0d2..3eb36ce 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -1818,7 +1818,6 @@
     "../renderer/media/stream/mock_media_stream_video_source.h",
     "../renderer/media/stream/processed_local_audio_source_unittest.cc",
     "../renderer/media/stream/user_media_client_impl_unittest.cc",
-    "../renderer/media/stream/video_track_adapter_unittest.cc",
     "../renderer/media/stream/webmediaplayer_ms_unittest.cc",
     "../renderer/media/video_capture/video_capture_impl_manager_unittest.cc",
     "../renderer/media/video_capture/video_capture_impl_unittest.cc",
diff --git a/device/fido/get_assertion_handler_unittest.cc b/device/fido/get_assertion_handler_unittest.cc
index 47bb533b..91766cb 100644
--- a/device/fido/get_assertion_handler_unittest.cc
+++ b/device/fido/get_assertion_handler_unittest.cc
@@ -25,6 +25,7 @@
 #include "device/fido/hid/fake_hid_impl_for_testing.h"
 #include "device/fido/make_credential_task.h"
 #include "device/fido/mock_fido_device.h"
+#include "device/fido/scoped_virtual_fido_device.h"
 #include "device/fido/test_callback_receiver.h"
 #include "device/fido/u2f_command_constructor.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -84,6 +85,8 @@
   bool controls_dispatch_ = false;
 };
 
+// FidoGetAssertionHandlerTest allows testing GetAssertionRequestHandler against
+// MockFidoDevices injected via a ScopedFakeFidoDiscoveryFactory.
 class FidoGetAssertionHandlerTest : public ::testing::Test {
  public:
   FidoGetAssertionHandlerTest() {
@@ -649,42 +652,6 @@
       ::testing::UnorderedElementsAre(FidoTransportProtocol::kInternal));
 }
 
-// Tests a scenario where authenticator of incorrect transport type was used to
-// conduct CTAP GetAssertion call.
-//
-// TODO(engedy): This should not happen, instead |allowCredentials| should be
-// filtered to only contain items compatible with the transport actually used to
-// talk to the authenticator.
-TEST_F(FidoGetAssertionHandlerTest, IncorrectTransportType) {
-  // GetAssertion request that expects GetAssertion call for credential
-  // |CredentialType::kPublicKey| to be signed with Cable authenticator.
-  auto request = CreateTestRequestWithCableExtension();
-  request.SetAllowList({
-      {CredentialType::kPublicKey,
-       fido_parsing_utils::Materialize(
-           test_data::kTestGetAssertionCredentialId),
-       {FidoTransportProtocol::kBluetoothLowEnergy}},
-      {CredentialType::kPublicKey,
-       fido_parsing_utils::Materialize(kBogusCredentialId),
-       {FidoTransportProtocol::kUsbHumanInterfaceDevice}},
-  });
-  auto request_handler =
-      CreateGetAssertionHandlerWithRequest(std::move(request));
-  discovery()->WaitForCallToStartAndSimulateSuccess();
-  auto device = MockFidoDevice::MakeCtapWithGetInfoExpectation();
-  // Since transport type of |device| is different from what the relying party
-  // defined in |request| above, this request should fail.
-  device->SetDeviceTransport(FidoTransportProtocol::kUsbHumanInterfaceDevice);
-  device->ExpectCtap2CommandAndRespondWith(
-      CtapRequestCommand::kAuthenticatorGetAssertion,
-      test_data::kTestGetAssertionResponse);
-
-  discovery()->AddDevice(std::move(device));
-
-  scoped_task_environment_.FastForwardUntilNoTasksRemain();
-  EXPECT_FALSE(get_assertion_callback().was_called());
-}
-
 // If a device with transport type kInternal returns a
 // CTAP2_ERR_OPERATION_DENIED error, the request should complete with
 // FidoReturnCode::kUserConsentDenied. Pending authenticators should be
@@ -734,6 +701,43 @@
             get_assertion_callback().status());
 }
 
+// Tests a scenario where authenticator of incorrect transport type was used to
+// conduct CTAP GetAssertion call.
+//
+// TODO(engedy): This should not happen, instead |allowCredentials| should be
+// filtered to only contain items compatible with the transport actually used to
+// talk to the authenticator.
+TEST(GetAssertionRequestHandlerTest, IncorrectTransportType) {
+  base::test::ScopedTaskEnvironment scoped_task_environment{
+      base::test::ScopedTaskEnvironment::MainThreadType::MOCK_TIME};
+  device::test::ScopedVirtualFidoDevice virtual_device;
+  virtual_device.SetSupportedProtocol(device::ProtocolVersion::kCtap);
+  ASSERT_TRUE(virtual_device.mutable_state()->InjectRegistration(
+      fido_parsing_utils::Materialize(test_data::kTestGetAssertionCredentialId),
+      test_data::kRelyingPartyId));
+
+  // Request the correct credential ID, but set a different transport hint.
+  CtapGetAssertionRequest request(test_data::kRelyingPartyId,
+                                  test_data::kClientDataJson);
+  request.SetAllowList({
+      {CredentialType::kPublicKey,
+       fido_parsing_utils::Materialize(
+           test_data::kTestGetAssertionCredentialId),
+       {FidoTransportProtocol::kBluetoothLowEnergy}},
+  });
+
+  TestGetAssertionRequestCallback cb;
+  auto request_handler = std::make_unique<GetAssertionRequestHandler>(
+      nullptr /* connector */,
+      base::flat_set<FidoTransportProtocol>(
+          {FidoTransportProtocol::kUsbHumanInterfaceDevice}),
+      std::move(request), cb.callback());
+  request_handler->SetPlatformAuthenticatorOrMarkUnavailable(base::nullopt);
+
+  scoped_task_environment.FastForwardUntilNoTasksRemain();
+  EXPECT_FALSE(cb.was_called());
+}
+
 #if defined(OS_WIN)
 // Verify that the request handler instantiates a HID device backed
 // FidoDeviceAuthenticator or a WinNativeCrossPlatformAuthenticator, depending
diff --git a/device/fido/get_assertion_task.cc b/device/fido/get_assertion_task.cc
index 2eb337fe..013f181 100644
--- a/device/fido/get_assertion_task.cc
+++ b/device/fido/get_assertion_task.cc
@@ -57,23 +57,28 @@
   }
 }
 
-void GetAssertionTask::GetAssertion() {
-  // If appId extension was used in the request and device is a hybrid U2F/CTAP2
-  // device, then first issue a silent GetAssertionRequest. If no credentials in
-  // allowed credential list are recognized, it's possible that the credential
-  // is registered via U2F. Under these circumstances, the request should be
-  // issued via the U2F protocol. Otherwise, proceed with a normal GetAssertion
-  // request.
-  if (MayFallbackToU2fWithAppIdExtension(*device(), request_)) {
-    request_.SetUserPresenceRequired(false);
-    const auto original_uv_configuration = request_.user_verification();
-    request_.SetUserVerification(UserVerificationRequirement::kDiscouraged);
+CtapGetAssertionRequest GetAssertionTask::NextSilentRequest() {
+  DCHECK(request_.allow_list() &&
+         current_credential_ < request_.allow_list()->size());
+  CtapGetAssertionRequest request = request_;
+  request.SetAllowList({{request_.allow_list()->at(current_credential_)}});
+  request.SetUserPresenceRequired(false);
+  request.SetUserVerification(UserVerificationRequirement::kDiscouraged);
+  return request;
+}
 
+void GetAssertionTask::GetAssertion() {
+  // Silently probe each credential in the allow list to work around
+  // authenticators rejecting lists over a certain size. Also probe silently if
+  // the request may fall back to U2F and the authenticator doesn't recognize
+  // any of the provided credential IDs.
+  if ((request_.allow_list() && request_.allow_list()->size() > 1) ||
+      MayFallbackToU2fWithAppIdExtension(*device(), request_)) {
     sign_operation_ = std::make_unique<Ctap2DeviceOperation<
         CtapGetAssertionRequest, AuthenticatorGetAssertionResponse>>(
-        device(), request_,
+        device(), NextSilentRequest(),
         base::BindOnce(&GetAssertionTask::HandleResponseToSilentRequest,
-                       weak_factory_.GetWeakPtr(), original_uv_configuration),
+                       weak_factory_.GetWeakPtr()),
         base::BindOnce(&ReadCTAPGetAssertionResponse));
     sign_operation_->Start();
     return;
@@ -120,39 +125,55 @@
 }
 
 void GetAssertionTask::HandleResponseToSilentRequest(
-    UserVerificationRequirement original_uv_configuration,
     CtapDeviceResponseCode response_code,
     base::Optional<AuthenticatorGetAssertionResponse> response_data) {
-  DCHECK(!request_.user_presence_required());
-  request_.SetUserPresenceRequired(true);
+  DCHECK(request_.allow_list() && request_.allow_list()->size() > 0);
 
-  if (response_code != CtapDeviceResponseCode::kSuccess) {
-    // An error occurred or no credentials in the allowed list were recognized.
-    // However, as the relying party has provided appId extension, try again
-    // with U2F in case that works.
-    DCHECK(MayFallbackToU2fWithAppIdExtension(*device(), request_));
+  // Credential was recognized by the device. As this authentication was a
+  // silent authentication (i.e. user touch was not provided), try again with
+  // only the matching credential, user presence enforced and with the original
+  // user verification configuration.
+  if (response_code == CtapDeviceResponseCode::kSuccess) {
+    CtapGetAssertionRequest request = request_;
+    request.SetAllowList({{request_.allow_list()->at(current_credential_)}});
+    sign_operation_ = std::make_unique<Ctap2DeviceOperation<
+        CtapGetAssertionRequest, AuthenticatorGetAssertionResponse>>(
+        device(), std::move(request),
+        base::BindOnce(&GetAssertionTask::HandleResponse,
+                       weak_factory_.GetWeakPtr()),
+        base::BindOnce(&ReadCTAPGetAssertionResponse));
+    sign_operation_->Start();
+    return;
+  }
+
+  // Credential was not recognized or an error occurred. Probe the next
+  // credential.
+  if (++current_credential_ < request_.allow_list()->size()) {
+    sign_operation_ = std::make_unique<Ctap2DeviceOperation<
+        CtapGetAssertionRequest, AuthenticatorGetAssertionResponse>>(
+        device(), NextSilentRequest(),
+        base::BindOnce(&GetAssertionTask::HandleResponseToSilentRequest,
+                       weak_factory_.GetWeakPtr()),
+        base::BindOnce(&ReadCTAPGetAssertionResponse));
+    sign_operation_->Start();
+    return;
+  }
+
+  // None of the credentials were recognized. Fall back to U2F or collect a
+  // dummy touch.
+  if (MayFallbackToU2fWithAppIdExtension(*device(), request_)) {
     device()->set_supported_protocol(ProtocolVersion::kU2f);
     U2fSign();
     return;
   }
-
-  // Credential was recognized by the device. As this authentication was a
-  // silent authentication (i.e. user touch was not provided), try again with
-  // user presence enforced and with the original user verification
-  // configuration.
-  DCHECK_EQ(UserVerificationRequirement::kDiscouraged,
-            request_.user_verification());
-  DCHECK_EQ(ProtocolVersion::kCtap, device()->supported_protocol());
-  request_.SetUserVerification(original_uv_configuration);
-
-  sign_operation_ =
-      std::make_unique<Ctap2DeviceOperation<CtapGetAssertionRequest,
-                                            AuthenticatorGetAssertionResponse>>(
-          device(), request_,
-          base::BindOnce(&GetAssertionTask::HandleResponse,
-                         weak_factory_.GetWeakPtr()),
-          base::BindOnce(&ReadCTAPGetAssertionResponse));
-  sign_operation_->Start();
+  dummy_register_operation_ = std::make_unique<Ctap2DeviceOperation<
+      CtapMakeCredentialRequest, AuthenticatorMakeCredentialResponse>>(
+      device(), MakeCredentialTask::GetTouchRequest(device()),
+      base::BindOnce(&GetAssertionTask::HandleDummyMakeCredentialComplete,
+                     weak_factory_.GetWeakPtr()),
+      base::BindOnce(&ReadCTAPMakeCredentialResponse,
+                     device()->DeviceTransport()));
+  dummy_register_operation_->Start();
 }
 
 void GetAssertionTask::HandleDummyMakeCredentialComplete(
diff --git a/device/fido/get_assertion_task.h b/device/fido/get_assertion_task.h
index 87e29f4..96d3e3e 100644
--- a/device/fido/get_assertion_task.h
+++ b/device/fido/get_assertion_task.h
@@ -51,6 +51,8 @@
   void GetAssertion();
   void U2fSign();
 
+  CtapGetAssertionRequest NextSilentRequest();
+
   // HandleResponse is the callback to a CTAP2 assertion request that requested
   // user-presence.
   void HandleResponse(
@@ -58,10 +60,8 @@
       base::Optional<AuthenticatorGetAssertionResponse> response_data);
 
   // HandleResponseToSilentRequest is a callback to a request without user-
-  // presence requested used when this assertion request may require falling
-  // back to U2F.
+  // presence requested used to silently probe credentials from the allow list.
   void HandleResponseToSilentRequest(
-      UserVerificationRequirement original_uv_configuration,
       CtapDeviceResponseCode response_code,
       base::Optional<AuthenticatorGetAssertionResponse> response_data);
 
@@ -75,6 +75,8 @@
   std::unique_ptr<SignOperation> sign_operation_;
   std::unique_ptr<RegisterOperation> dummy_register_operation_;
   GetAssertionTaskCallback callback_;
+  size_t current_credential_ = 0;
+
   base::WeakPtrFactory<GetAssertionTask> weak_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(GetAssertionTask);
diff --git a/device/fido/make_credential_task.cc b/device/fido/make_credential_task.cc
index d1e901e..26b857c 100644
--- a/device/fido/make_credential_task.cc
+++ b/device/fido/make_credential_task.cc
@@ -42,17 +42,16 @@
 
 }  // namespace
 
-MakeCredentialTask::MakeCredentialTask(
-    FidoDevice* device,
-    CtapMakeCredentialRequest request_parameter,
-    MakeCredentialTaskCallback callback)
+MakeCredentialTask::MakeCredentialTask(FidoDevice* device,
+                                       CtapMakeCredentialRequest request,
+                                       MakeCredentialTaskCallback callback)
     : FidoTask(device),
-      request_parameter_(std::move(request_parameter)),
+      request_(std::move(request)),
       callback_(std::move(callback)),
       weak_factory_(this) {
   // The UV parameter should have been made binary by this point because CTAP2
   // only takes a binary value.
-  DCHECK_NE(request_parameter_.user_verification(),
+  DCHECK_NE(request_.user_verification(),
             UserVerificationRequirement::kPreferred);
 }
 
@@ -98,8 +97,8 @@
 
 void MakeCredentialTask::StartTask() {
   if (device()->supported_protocol() == ProtocolVersion::kCtap &&
-      !request_parameter_.is_u2f_only() &&
-      !ShouldUseU2fBecauseCtapRequiresClientPin(device(), request_parameter_)) {
+      !request_.is_u2f_only() &&
+      !ShouldUseU2fBecauseCtapRequiresClientPin(device(), request_)) {
     MakeCredential();
   } else {
     // |device_info| should be present iff the device is CTAP2. This will be
@@ -112,17 +111,112 @@
   }
 }
 
+CtapGetAssertionRequest MakeCredentialTask::NextSilentSignRequest() {
+  DCHECK(request_.exclude_list() &&
+         current_credential_ < request_.exclude_list()->size());
+  CtapGetAssertionRequest request(request_.rp().rp_id(),
+                                  /*client_data_json=*/"");
+  request.SetAllowList({{request_.exclude_list()->at(current_credential_)}});
+  request.SetUserPresenceRequired(false);
+  request.SetUserVerification(UserVerificationRequirement::kDiscouraged);
+  return request;
+}
+
 void MakeCredentialTask::MakeCredential() {
+  // Silently probe each credential in the allow list to work around
+  // authenticators rejecting lists over a certain size.
+  if (request_.exclude_list() && request_.exclude_list()->size() > 1) {
+    silent_sign_operation_ = std::make_unique<Ctap2DeviceOperation<
+        CtapGetAssertionRequest, AuthenticatorGetAssertionResponse>>(
+        device(), NextSilentSignRequest(),
+        base::BindOnce(&MakeCredentialTask::HandleResponseToSilentSignRequest,
+                       weak_factory_.GetWeakPtr()),
+        base::BindOnce(&ReadCTAPGetAssertionResponse));
+    silent_sign_operation_->Start();
+    return;
+  }
+
   register_operation_ = std::make_unique<Ctap2DeviceOperation<
       CtapMakeCredentialRequest, AuthenticatorMakeCredentialResponse>>(
-      device(), std::move(request_parameter_), std::move(callback_),
+      device(), std::move(request_), std::move(callback_),
       base::BindOnce(&ReadCTAPMakeCredentialResponse,
                      device()->DeviceTransport()));
   register_operation_->Start();
 }
 
+void MakeCredentialTask::HandleResponseToSilentSignRequest(
+    CtapDeviceResponseCode response_code,
+    base::Optional<AuthenticatorGetAssertionResponse> response_data) {
+  DCHECK(request_.exclude_list() && request_.exclude_list()->size() > 0);
+
+  // The authenticator recognized a credential from the exclude list. Send the
+  // actual request with only that credential in the exclude list to collect a
+  // touch and and the CTAP2_ERR_CREDENTIAL_EXCLUDED error code.
+  if (response_code == CtapDeviceResponseCode::kSuccess) {
+    CtapMakeCredentialRequest request = request_;
+    request.SetExcludeList(
+        {{request_.exclude_list()->at(current_credential_)}});
+    register_operation_ = std::make_unique<Ctap2DeviceOperation<
+        CtapMakeCredentialRequest, AuthenticatorMakeCredentialResponse>>(
+        device(), std::move(request), std::move(callback_),
+        base::BindOnce(&ReadCTAPMakeCredentialResponse,
+                       device()->DeviceTransport()));
+    register_operation_->Start();
+    return;
+  }
+
+  // The authenticator returned an unexpected error. Collect a touch to take the
+  // authenticator out of the set of active devices.
+  if (response_code != CtapDeviceResponseCode::kCtap2ErrInvalidCredential &&
+      response_code != CtapDeviceResponseCode::kCtap2ErrNoCredentials &&
+      response_code != CtapDeviceResponseCode::kCtap2ErrLimitExceeded &&
+      response_code != CtapDeviceResponseCode::kCtap2ErrRequestTooLarge) {
+    register_operation_ = std::make_unique<Ctap2DeviceOperation<
+        CtapMakeCredentialRequest, AuthenticatorMakeCredentialResponse>>(
+        device(), GetTouchRequest(device()),
+        base::BindOnce(&MakeCredentialTask::HandleResponseToDummyTouch,
+                       weak_factory_.GetWeakPtr()),
+        base::BindOnce(&ReadCTAPMakeCredentialResponse,
+                       device()->DeviceTransport()));
+    register_operation_->Start();
+    return;
+  }
+
+  // The authenticator doesn't recognize this particular credential from the
+  // exclude list. Try the next one.
+  if (++current_credential_ < request_.exclude_list()->size()) {
+    silent_sign_operation_ = std::make_unique<Ctap2DeviceOperation<
+        CtapGetAssertionRequest, AuthenticatorGetAssertionResponse>>(
+        device(), NextSilentSignRequest(),
+        base::BindOnce(&MakeCredentialTask::HandleResponseToSilentSignRequest,
+                       weak_factory_.GetWeakPtr()),
+        base::BindOnce(&ReadCTAPGetAssertionResponse));
+    silent_sign_operation_->Start();
+    return;
+  }
+
+  // None of the credentials from the exclude list were recognized. The actual
+  // register request may proceed but without the exclude list present in case
+  // it exceeds the device's size limit.
+  CtapMakeCredentialRequest request = request_;
+  request.SetExcludeList({});
+  register_operation_ = std::make_unique<Ctap2DeviceOperation<
+      CtapMakeCredentialRequest, AuthenticatorMakeCredentialResponse>>(
+      device(), std::move(request), std::move(callback_),
+      base::BindOnce(&ReadCTAPMakeCredentialResponse,
+                     device()->DeviceTransport()));
+  register_operation_->Start();
+}
+
+void MakeCredentialTask::HandleResponseToDummyTouch(
+    CtapDeviceResponseCode response_code,
+    base::Optional<AuthenticatorMakeCredentialResponse> response_data) {
+  std::move(callback_).Run(CtapDeviceResponseCode::kCtap2ErrOther,
+                           base::nullopt);
+}
+
 void MakeCredentialTask::U2fRegister() {
-  if (!IsConvertibleToU2fRegisterCommand(request_parameter_)) {
+  if (!IsConvertibleToU2fRegisterCommand(request_)) {
     std::move(callback_).Run(CtapDeviceResponseCode::kCtap2ErrOther,
                              base::nullopt);
     return;
@@ -130,7 +224,7 @@
 
   DCHECK_EQ(ProtocolVersion::kU2f, device()->supported_protocol());
   register_operation_ = std::make_unique<U2fRegisterOperation>(
-      device(), std::move(request_parameter_),
+      device(), std::move(request_),
       base::BindOnce(&MakeCredentialTask::MaybeRevertU2fFallback,
                      weak_factory_.GetWeakPtr()));
   register_operation_->Start();
diff --git a/device/fido/make_credential_task.h b/device/fido/make_credential_task.h
index 0b3c922..beda0d5 100644
--- a/device/fido/make_credential_task.h
+++ b/device/fido/make_credential_task.h
@@ -15,7 +15,9 @@
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "base/optional.h"
+#include "device/fido/authenticator_get_assertion_response.h"
 #include "device/fido/authenticator_make_credential_response.h"
+#include "device/fido/ctap_get_assertion_request.h"
 #include "device/fido/ctap_make_credential_request.h"
 #include "device/fido/device_operation.h"
 #include "device/fido/fido_constants.h"
@@ -30,12 +32,14 @@
   using MakeCredentialTaskCallback = base::OnceCallback<void(
       CtapDeviceResponseCode,
       base::Optional<AuthenticatorMakeCredentialResponse>)>;
+  using SignOperation = DeviceOperation<CtapGetAssertionRequest,
+                                        AuthenticatorGetAssertionResponse>;
   using RegisterOperation =
       DeviceOperation<CtapMakeCredentialRequest,
                       AuthenticatorMakeCredentialResponse>;
 
   MakeCredentialTask(FidoDevice* device,
-                     CtapMakeCredentialRequest request_parameter,
+                     CtapMakeCredentialRequest request,
                      MakeCredentialTaskCallback callback);
   ~MakeCredentialTask() override;
 
@@ -48,14 +52,25 @@
   void StartTask() final;
 
   void MakeCredential();
+  CtapGetAssertionRequest NextSilentSignRequest();
+  void HandleResponseToSilentSignRequest(
+      CtapDeviceResponseCode response_code,
+      base::Optional<AuthenticatorGetAssertionResponse> response_data);
+  void HandleResponseToDummyTouch(
+      CtapDeviceResponseCode response_code,
+      base::Optional<AuthenticatorMakeCredentialResponse> response_data);
+
   void U2fRegister();
   void MaybeRevertU2fFallback(
       CtapDeviceResponseCode status,
       base::Optional<AuthenticatorMakeCredentialResponse> response);
 
-  CtapMakeCredentialRequest request_parameter_;
+  CtapMakeCredentialRequest request_;
   std::unique_ptr<RegisterOperation> register_operation_;
+  std::unique_ptr<SignOperation> silent_sign_operation_;
   MakeCredentialTaskCallback callback_;
+  size_t current_credential_ = 0;
+
   base::WeakPtrFactory<MakeCredentialTask> weak_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(MakeCredentialTask);
diff --git a/device/fido/virtual_ctap2_device.cc b/device/fido/virtual_ctap2_device.cc
index 162b79a..cee3f30 100644
--- a/device/fido/virtual_ctap2_device.cc
+++ b/device/fido/virtual_ctap2_device.cc
@@ -552,6 +552,11 @@
   const auto rp_id_hash =
       fido_parsing_utils::CreateSHA256Hash(request.rp().rp_id());
   if (request.exclude_list()) {
+    if (config_.reject_large_allow_and_exclude_lists &&
+        request.exclude_list()->size() > 1) {
+      return CtapDeviceResponseCode::kCtap2ErrLimitExceeded;
+    }
+
     for (const auto& excluded_credential : *request.exclude_list()) {
       if (FindRegistrationData(excluded_credential.id(), rp_id_hash)) {
         if (mutable_state()->simulate_press_callback) {
@@ -699,6 +704,11 @@
       found_registrations;
 
   if (request.allow_list()) {
+    if (config_.reject_large_allow_and_exclude_lists &&
+        request.allow_list()->size() > 1) {
+      return CtapDeviceResponseCode::kCtap2ErrLimitExceeded;
+    }
+
     // An empty allow_list could be considered to be a resident-key request, but
     // some authenticators in practice don't take it that way. Thus this code
     // mirrors that to better reflect reality. CTAP 2.0 leaves it as undefined
diff --git a/device/fido/virtual_ctap2_device.h b/device/fido/virtual_ctap2_device.h
index 419d0a8..a8ecf93 100644
--- a/device/fido/virtual_ctap2_device.h
+++ b/device/fido/virtual_ctap2_device.h
@@ -52,6 +52,10 @@
     // attestedCredentialData to be included in the authenticator data of an
     // GetAssertion response.
     bool return_attested_cred_data_in_get_assertion_response = false;
+    // reject_large_allow_and_exclude_lists causes the authenticator to respond
+    // with an error if an allowList or an excludeList contains more than one
+    // credential ID.
+    bool reject_large_allow_and_exclude_lists = false;
   };
 
   VirtualCtap2Device();
diff --git a/extensions/browser/disable_reason.h b/extensions/browser/disable_reason.h
index ac8d4c612..53040af 100644
--- a/extensions/browser/disable_reason.h
+++ b/extensions/browser/disable_reason.h
@@ -17,6 +17,9 @@
 // Also carefully consider if your reason should sync to other devices, and if
 // so, add it to kKnownSyncableDisableReasons in
 // chrome/browser/extensions/extension_sync_service.cc.
+// Finally, consider whether your disable reason applies to component
+// extensions. Reference/update the existing list of applicable reasons in
+// ExtensionsPrefs::ClearInapplicableDisableReasonsForComponentExtension.
 enum DisableReason {
   DISABLE_NONE = 0,
   DISABLE_USER_ACTION = 1 << 0,
diff --git a/extensions/browser/extension_prefs.cc b/extensions/browser/extension_prefs.cc
index 342885b..2a02a01 100644
--- a/extensions/browser/extension_prefs.cc
+++ b/extensions/browser/extension_prefs.cc
@@ -783,6 +783,22 @@
                        DISABLE_REASON_CLEAR);
 }
 
+void ExtensionPrefs::ClearInapplicableDisableReasonsForComponentExtension(
+    const std::string& component_extension_id) {
+  static constexpr int kAllowDisableReasons =
+      disable_reason::DISABLE_RELOAD |
+      disable_reason::DISABLE_UNSUPPORTED_REQUIREMENT |
+      disable_reason::DISABLE_CORRUPTED;
+
+  // Some disable reasons incorrectly cause component extensions to never
+  // activate on load. See https://crbug.com/946839 for more details on why we
+  // do this.
+  ModifyDisableReasons(
+      component_extension_id,
+      kAllowDisableReasons & GetDisableReasons(component_extension_id),
+      DISABLE_REASON_REPLACE);
+}
+
 void ExtensionPrefs::ModifyDisableReasons(const std::string& extension_id,
                                           int reasons,
                                           DisableReasonChange change) {
diff --git a/extensions/browser/extension_prefs.h b/extensions/browser/extension_prefs.h
index 20cfa62..b22bf759 100644
--- a/extensions/browser/extension_prefs.h
+++ b/extensions/browser/extension_prefs.h
@@ -281,6 +281,10 @@
                              int disable_reasons);
   void ClearDisableReasons(const std::string& extension_id);
 
+  // Clears disable reasons that do not apply to component extensions.
+  void ClearInapplicableDisableReasonsForComponentExtension(
+      const std::string& component_extension_id);
+
   // Gets the set of extensions that have been blacklisted in prefs. This will
   // return only the blocked extensions, not the "greylist" extensions.
   // TODO(oleg): Make method names consistent here, in extension service and in
diff --git a/extensions/browser/extension_registrar.cc b/extensions/browser/extension_registrar.cc
index 1f7782a..ea8e8b37 100644
--- a/extensions/browser/extension_registrar.cc
+++ b/extensions/browser/extension_registrar.cc
@@ -111,6 +111,7 @@
 void ExtensionRegistrar::AddNewExtension(
     scoped_refptr<const Extension> extension) {
   if (extension_prefs_->IsExtensionBlacklisted(extension->id())) {
+    DCHECK(!Manifest::IsComponentLocation(extension->location()));
     // Only prefs is checked for the blacklist. We rely on callers to check the
     // blacklist before calling into here, e.g. CrxInstaller checks before
     // installation then threads through the install and pending install flow
@@ -118,6 +119,7 @@
     // extensions.
     registry_->AddBlacklisted(extension);
   } else if (delegate_->ShouldBlockExtension(extension.get())) {
+    DCHECK(!Manifest::IsComponentLocation(extension->location()));
     registry_->AddBlocked(extension);
   } else if (extension_prefs_->IsExtensionDisabled(extension->id())) {
     registry_->AddDisabled(extension);
diff --git a/gpu/config/gpu_crash_keys.cc b/gpu/config/gpu_crash_keys.cc
index b4f99e66f..a51c27a 100644
--- a/gpu/config/gpu_crash_keys.cc
+++ b/gpu/config/gpu_crash_keys.cc
@@ -24,6 +24,12 @@
     "gpu-gl-context-is-virtual");
 crash_reporter::CrashKeyString<20> seconds_since_last_progress_report(
     "seconds-since-last-progress-report");
+crash_reporter::CrashKeyString<20> seconds_since_last_suspend(
+    "seconds-since-last-suspend");
+crash_reporter::CrashKeyString<20> seconds_since_last_resume(
+    "seconds-since-last-resume");
+crash_reporter::CrashKeyString<20> seconds_since_last_logging(
+    "seconds-since-last-logging");
 crash_reporter::CrashKeyString<20> available_physical_memory_in_mb(
     "available-physical-memory-in-mb");
 
diff --git a/gpu/config/gpu_crash_keys.h b/gpu/config/gpu_crash_keys.h
index 1f1b7e7..e6bee5b 100644
--- a/gpu/config/gpu_crash_keys.h
+++ b/gpu/config/gpu_crash_keys.h
@@ -29,6 +29,9 @@
 extern GPU_EXPORT crash_reporter::CrashKeyString<4> gpu_gl_context_is_virtual;
 extern GPU_EXPORT crash_reporter::CrashKeyString<20>
     seconds_since_last_progress_report;
+extern GPU_EXPORT crash_reporter::CrashKeyString<20> seconds_since_last_suspend;
+extern GPU_EXPORT crash_reporter::CrashKeyString<20> seconds_since_last_resume;
+extern GPU_EXPORT crash_reporter::CrashKeyString<20> seconds_since_last_logging;
 extern GPU_EXPORT crash_reporter::CrashKeyString<20>
     available_physical_memory_in_mb;
 
diff --git a/gpu/ipc/client/command_buffer_proxy_impl.cc b/gpu/ipc/client/command_buffer_proxy_impl.cc
index 04af30b..a5a4c19 100644
--- a/gpu/ipc/client/command_buffer_proxy_impl.cc
+++ b/gpu/ipc/client/command_buffer_proxy_impl.cc
@@ -34,6 +34,7 @@
 #include "mojo/public/cpp/base/shared_memory_utils.h"
 #include "mojo/public/cpp/system/buffer.h"
 #include "mojo/public/cpp/system/platform_handle.h"
+#include "ui/gfx/buffer_format_util.h"
 #include "ui/gfx/geometry/size.h"
 #include "ui/gfx/gpu_fence.h"
 #include "ui/gl/gl_bindings.h"
@@ -435,9 +436,11 @@
     image_fence_sync = GenerateFenceSyncRelease();
 
   DCHECK(gpu::IsImageFromGpuMemoryBufferFormatSupported(
-      gpu_memory_buffer->GetFormat(), capabilities_));
+      gpu_memory_buffer->GetFormat(), capabilities_))
+      << gfx::BufferFormatToString(gpu_memory_buffer->GetFormat());
   DCHECK(gpu::IsImageSizeValidForGpuMemoryBufferFormat(
-      gfx::Size(width, height), gpu_memory_buffer->GetFormat()));
+      gfx::Size(width, height), gpu_memory_buffer->GetFormat()))
+      << gfx::BufferFormatToString(gpu_memory_buffer->GetFormat());
 
   GpuCommandBufferMsg_CreateImage_Params params;
   params.id = new_id;
diff --git a/gpu/ipc/service/gpu_watchdog_thread.cc b/gpu/ipc/service/gpu_watchdog_thread.cc
index f20bda7..45288aed 100644
--- a/gpu/ipc/service/gpu_watchdog_thread.cc
+++ b/gpu/ipc/service/gpu_watchdog_thread.cc
@@ -496,15 +496,12 @@
   base::debug::Alias(&using_high_res_timer);
 #endif
 
-  base::Time current_time = base::Time::Now();
-  base::TimeTicks current_timeticks = base::TimeTicks::Now();
-  base::debug::Alias(&current_time);
-  base::debug::Alias(&current_timeticks);
-
   int32_t awaiting_acknowledge =
       base::subtle::NoBarrier_Load(&awaiting_acknowledge_);
   base::debug::Alias(&awaiting_acknowledge);
 
+  base::TimeTicks before_logging_timeticks = base::TimeTicks::Now();
+
   // Don't log the message to stderr in release builds because the buffer
   // may be full.
   std::string message = base::StringPrintf(
@@ -515,10 +512,27 @@
     handler(logging::LOG_ERROR, __FILE__, __LINE__, 0, message);
   DLOG(ERROR) << message;
 
+  base::Time current_time = base::Time::Now();
+  base::TimeTicks current_timeticks = base::TimeTicks::Now();
+  base::debug::Alias(&current_time);
+  base::debug::Alias(&current_timeticks);
+
+  int64_t since_last_logging =
+      (current_timeticks - before_logging_timeticks).InSeconds();
+  crash_keys::seconds_since_last_logging.Set(
+      base::NumberToString(since_last_logging));
   int64_t since_last_progress_report =
       (current_timeticks - last_reported_progress_timeticks_).InSeconds();
   crash_keys::seconds_since_last_progress_report.Set(
       base::NumberToString(since_last_progress_report));
+  int64_t since_last_suspend =
+      (current_timeticks - last_suspend_timeticks_).InSeconds();
+  crash_keys::seconds_since_last_suspend.Set(
+      base::NumberToString(since_last_suspend));
+  int64_t since_last_resume =
+      (current_timeticks - last_resume_timeticks_).InSeconds();
+  crash_keys::seconds_since_last_resume.Set(
+      base::NumberToString(since_last_resume));
 
   int64_t available_physical_memory =
       base::SysInfo::AmountOfAvailablePhysicalMemory() >> 20;
@@ -573,10 +587,12 @@
 }
 
 void GpuWatchdogThread::OnSuspend() {
+  last_suspend_timeticks_ = base::TimeTicks::Now();
   power_suspend_ref_ = suspension_counter_.Take();
 }
 
 void GpuWatchdogThread::OnResume() {
+  last_resume_timeticks_ = base::TimeTicks::Now();
   power_suspend_ref_.reset();
 }
 
diff --git a/gpu/ipc/service/gpu_watchdog_thread.h b/gpu/ipc/service/gpu_watchdog_thread.h
index 6bb08e4..b9388fe 100644
--- a/gpu/ipc/service/gpu_watchdog_thread.h
+++ b/gpu/ipc/service/gpu_watchdog_thread.h
@@ -188,6 +188,8 @@
   base::TimeTicks check_timeticks_;
 
   base::TimeTicks last_reported_progress_timeticks_;
+  base::TimeTicks last_suspend_timeticks_;
+  base::TimeTicks last_resume_timeticks_;
 
 #if defined(USE_X11)
   XDisplay* display_;
diff --git a/ios/web/navigation/history_state_operations_inttest.mm b/ios/web/navigation/history_state_operations_inttest.mm
index 3aa0f331..0acc4fc 100644
--- a/ios/web/navigation/history_state_operations_inttest.mm
+++ b/ios/web/navigation/history_state_operations_inttest.mm
@@ -475,7 +475,8 @@
 }
 
 // Regression test for crbug.com/788464.
-TEST_P(HistoryStateOperationsTest, ReplaceStateThenReload) {
+// TODO(crbug.com/950263): Investigate culprit and reenable.
+TEST_P(HistoryStateOperationsTest, DISABLED_ReplaceStateThenReload) {
   GURL url = web::test::HttpServer::MakeUrl(
       "http://ios/testing/data/http_server_files/"
       "onload_replacestate_reload.html");
diff --git a/media/gpu/image_processor_test.cc b/media/gpu/image_processor_test.cc
index 82f7b19..4349a2e 100644
--- a/media/gpu/image_processor_test.cc
+++ b/media/gpu/image_processor_test.cc
@@ -30,6 +30,8 @@
     FILE_PATH_LITERAL("bear_320x192.i420.yuv");
 constexpr const base::FilePath::CharType* kNV12Image =
     FILE_PATH_LITERAL("bear_320x192.nv12.yuv");
+constexpr const base::FilePath::CharType* kRGBAImage =
+    FILE_PATH_LITERAL("bear_320x192.rgba");
 constexpr const base::FilePath::CharType* kYV12Image =
     FILE_PATH_LITERAL("bear_320x192.yv12.yuv");
 
@@ -60,8 +62,13 @@
     // TODO(crbug.com/917951): Select more appropriate number of buffers.
     constexpr size_t kNumBuffers = 1;
     LOG_ASSERT(output_image.IsMetadataLoaded());
-    auto vf_validator = test::VideoFrameValidator::Create(
-        {output_image.Checksum()}, output_image.PixelFormat());
+    // TODO(crbug.com/944823): Use VideoFrameValidator for RGB formats.
+    std::unique_ptr<test::VideoFrameValidator> vf_validator;
+    if (IsYuvPlanar(input_image.PixelFormat()) &&
+        IsYuvPlanar(output_image.PixelFormat())) {
+      vf_validator = test::VideoFrameValidator::Create(
+          {output_image.Checksum()}, output_image.PixelFormat());
+    }
     std::vector<std::unique_ptr<test::VideoFrameProcessor>> frame_processors;
     frame_processors.push_back(std::move(vf_validator));
     auto ip_client = test::ImageProcessorClient::Create(
@@ -89,10 +96,12 @@
 
 // I420->NV12
 // YV12->NV12
+// RGBA->NV12
 INSTANTIATE_TEST_SUITE_P(
     ConvertToNV12,
     ImageProcessorSimpleParamTest,
     ::testing::Values(std::make_tuple(kI420Image, kNV12Image),
+                      std::make_tuple(kRGBAImage, kNV12Image),
                       std::make_tuple(kYV12Image, kNV12Image)));
 
 #if defined(OS_CHROMEOS)
diff --git a/media/gpu/test/image.cc b/media/gpu/test/image.cc
index 6ed45d03..4f04310 100644
--- a/media/gpu/test/image.cc
+++ b/media/gpu/test/image.cc
@@ -38,6 +38,8 @@
     return PIXEL_FORMAT_NV12;
   } else if (pixel_format == "YV12") {
     return PIXEL_FORMAT_YV12;
+  } else if (pixel_format == "RGBA") {
+    return PIXEL_FORMAT_RGB32;
   } else {
     VLOG(2) << pixel_format << " is not supported.";
     return PIXEL_FORMAT_UNKNOWN;
diff --git a/net/base/network_interfaces_fuchsia.cc b/net/base/network_interfaces_fuchsia.cc
index 300966e..f98a911 100644
--- a/net/base/network_interfaces_fuchsia.cc
+++ b/net/base/network_interfaces_fuchsia.cc
@@ -69,10 +69,10 @@
 
 IPAddress FuchsiaIpAddressToIPAddress(const fuchsia::net::IpAddress& addr) {
   if (addr.is_ipv4()) {
-    return IPAddress(addr.ipv4().addr.data(), addr.ipv4().addr.count());
+    return IPAddress(addr.ipv4().addr.data(), addr.ipv4().addr.size());
   }
   if (addr.is_ipv6()) {
-    return IPAddress(addr.ipv6().addr.data(), addr.ipv6().addr.count());
+    return IPAddress(addr.ipv6().addr.data(), addr.ipv6().addr.size());
   }
   return IPAddress();
 }
diff --git a/remoting/signaling/grpc_support/grpc_async_executor.cc b/remoting/signaling/grpc_support/grpc_async_executor.cc
index 03bfbf4..866d9b7 100644
--- a/remoting/signaling/grpc_support/grpc_async_executor.cc
+++ b/remoting/signaling/grpc_support/grpc_async_executor.cc
@@ -4,6 +4,9 @@
 
 #include "remoting/signaling/grpc_support/grpc_async_executor.h"
 
+#include <algorithm>
+#include <utility>
+
 #include "base/bind.h"
 #include "base/callback.h"
 #include "base/no_destructor.h"
@@ -90,15 +93,21 @@
 GrpcAsyncExecutor::~GrpcAsyncExecutor() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   VLOG(0) << "# of pending RPCs at destruction: " << pending_requests_.size();
-  for (auto* pending_request : pending_requests_) {
-    pending_request->CancelRequest();
+  for (auto& pending_request : pending_requests_) {
+    // If the sequence itself is being destroyed, pending tasks will be dropped
+    // in arbitrary order without checking the weak ptr. If the dequeue task is
+    // destroyed earlier than the executor itself, then |pending_request| will
+    // already be destroyed.
+    if (pending_request) {
+      pending_request->CancelRequest();
+    }
   }
 }
 
 void GrpcAsyncExecutor::ExecuteRpc(std::unique_ptr<GrpcAsyncRequest> request) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  DCHECK(pending_requests_.find(request.get()) == pending_requests_.end());
   auto* unowned_request = request.get();
+  DCHECK(FindRequest(unowned_request) == pending_requests_.end());
   auto task = std::make_unique<DispatchTask>();
   task->caller_sequence_task_runner = base::SequencedTaskRunnerHandle::Get();
   task->callback =
@@ -120,7 +129,12 @@
   unowned_request->Start(
       run_task_cb, CompletionQueueDispatcher::GetInstance()->completion_queue(),
       task.release());
-  pending_requests_.insert(unowned_request);
+
+  // You might think that we can invert the ownership and make GrpcAsyncExecutor
+  // own the request instead, but this doesn't work because the gRPC completion
+  // queue (which runs on a different thread) expects the client context to be
+  // alive when you try to pop out a completed/dead event.
+  pending_requests_.push_back(unowned_request->GetGrpcAsyncRequestWeakPtr());
 }
 
 void GrpcAsyncExecutor::OnDequeue(std::unique_ptr<GrpcAsyncRequest> request,
@@ -129,12 +143,14 @@
 
   if (!request->OnDequeue(operation_succeeded)) {
     VLOG(0) << "Dequeuing RPC: " << request.get();
-    DCHECK(pending_requests_.find(request.get()) != pending_requests_.end());
-    pending_requests_.erase(request.get());
+    auto iter = FindRequest(request.get());
+    DCHECK(iter != pending_requests_.end());
+    pending_requests_.erase(iter);
     return;
   }
 
   VLOG(0) << "Re-enqueuing RPC: " << request.get();
+  DCHECK(FindRequest(request.get()) != pending_requests_.end());
   auto* unowned_request = request.get();
   auto task = std::make_unique<DispatchTask>();
   task->caller_sequence_task_runner = base::SequencedTaskRunnerHandle::Get();
@@ -157,4 +173,13 @@
   std::move(closure).Run();
 }
 
+GrpcAsyncExecutor::PendingRequestListIter GrpcAsyncExecutor::FindRequest(
+    GrpcAsyncRequest* request) {
+  return std::find_if(
+      pending_requests_.begin(), pending_requests_.end(),
+      [request](const base::WeakPtr<GrpcAsyncRequest>& current_request) {
+        return current_request.get() == request;
+      });
+}
+
 }  // namespace remoting
diff --git a/remoting/signaling/grpc_support/grpc_async_executor.h b/remoting/signaling/grpc_support/grpc_async_executor.h
index 7583a1d..444a950 100644
--- a/remoting/signaling/grpc_support/grpc_async_executor.h
+++ b/remoting/signaling/grpc_support/grpc_async_executor.h
@@ -5,11 +5,10 @@
 #ifndef REMOTING_SIGNALING_GRPC_SUPPORT_GRPC_ASYNC_EXECUTOR_H_
 #define REMOTING_SIGNALING_GRPC_SUPPORT_GRPC_ASYNC_EXECUTOR_H_
 
+#include <list>
 #include <memory>
-#include <utility>
 
 #include "base/callback_forward.h"
-#include "base/containers/flat_set.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "base/sequence_checker.h"
@@ -30,6 +29,9 @@
   void ExecuteRpc(std::unique_ptr<GrpcAsyncRequest> request) override;
 
  private:
+  using PendingRequestList = std::list<base::WeakPtr<GrpcAsyncRequest>>;
+  using PendingRequestListIter = PendingRequestList::iterator;
+
   void OnDequeue(std::unique_ptr<GrpcAsyncRequest> request,
                  bool operation_succeeded);
 
@@ -38,11 +40,13 @@
   void PostTaskToRunClosure(base::OnceClosure closure);
   void RunClosure(base::OnceClosure closure);
 
+  PendingRequestListIter FindRequest(GrpcAsyncRequest* request);
+
   SEQUENCE_CHECKER(sequence_checker_);
 
   // Keep the list of pending requests so that we can cancel them at
   // destruction.
-  base::flat_set<GrpcAsyncRequest*> pending_requests_;
+  PendingRequestList pending_requests_;
 
   base::WeakPtrFactory<GrpcAsyncExecutor> weak_factory_;
   DISALLOW_COPY_AND_ASSIGN(GrpcAsyncExecutor);
diff --git a/remoting/signaling/grpc_support/grpc_async_request.cc b/remoting/signaling/grpc_support/grpc_async_request.cc
index 75566bc..5b110f0 100644
--- a/remoting/signaling/grpc_support/grpc_async_request.cc
+++ b/remoting/signaling/grpc_support/grpc_async_request.cc
@@ -8,8 +8,8 @@
 
 namespace remoting {
 
-GrpcAsyncRequest::GrpcAsyncRequest(
-    std::unique_ptr<grpc::ClientContext> context) {
+GrpcAsyncRequest::GrpcAsyncRequest(std::unique_ptr<grpc::ClientContext> context)
+    : grpc_async_request_weak_factory_(this) {
   context_ = std::move(context);
 }
 
@@ -21,4 +21,8 @@
   OnRequestCanceled();
 }
 
+base::WeakPtr<GrpcAsyncRequest> GrpcAsyncRequest::GetGrpcAsyncRequestWeakPtr() {
+  return grpc_async_request_weak_factory_.GetWeakPtr();
+}
+
 }  // namespace remoting
diff --git a/remoting/signaling/grpc_support/grpc_async_request.h b/remoting/signaling/grpc_support/grpc_async_request.h
index 83b7a50..1181ae4 100644
--- a/remoting/signaling/grpc_support/grpc_async_request.h
+++ b/remoting/signaling/grpc_support/grpc_async_request.h
@@ -11,6 +11,7 @@
 #include "base/callback_forward.h"
 #include "base/macros.h"
 #include "base/memory/scoped_refptr.h"
+#include "base/memory/weak_ptr.h"
 #include "base/single_thread_task_runner.h"
 #include "third_party/grpc/src/include/grpcpp/support/status.h"
 
@@ -68,6 +69,8 @@
 
   grpc::ClientContext* context() { return context_.get(); }
 
+  base::WeakPtr<GrpcAsyncRequest> GetGrpcAsyncRequestWeakPtr();
+
  protected:
   // Called after CancelRequest() is called.
   virtual void OnRequestCanceled() = 0;
@@ -77,6 +80,7 @@
  private:
   std::unique_ptr<grpc::ClientContext> context_;
 
+  base::WeakPtrFactory<GrpcAsyncRequest> grpc_async_request_weak_factory_;
   DISALLOW_COPY_AND_ASSIGN(GrpcAsyncRequest);
 };
 
diff --git a/third_party/blink/public/BUILD.gn b/third_party/blink/public/BUILD.gn
index c9b5544a..9b623ee 100644
--- a/third_party/blink/public/BUILD.gn
+++ b/third_party/blink/public/BUILD.gn
@@ -395,7 +395,6 @@
     "web/modules/mediastream/media_stream_video_sink.h",
     "web/modules/mediastream/media_stream_video_source.h",
     "web/modules/mediastream/media_stream_video_track.h",
-    "web/modules/mediastream/video_track_adapter.h",
     "web/modules/mediastream/video_track_adapter_settings.h",
     "web/modules/mediastream/web_media_stream_utils.h",
     "web/modules/service_worker/web_service_worker_context_client.h",
diff --git a/third_party/blink/public/common/frame/sandbox_flags.h b/third_party/blink/public/common/frame/sandbox_flags.h
index 4555905..19925ce 100644
--- a/third_party/blink/public/common/frame/sandbox_flags.h
+++ b/third_party/blink/public/common/frame/sandbox_flags.h
@@ -10,8 +10,7 @@
 namespace blink {
 
 // See http://www.whatwg.org/specs/web-apps/current-work/#attr-iframe-sandbox
-// for a list of the sandbox flags.  This enum should be kept in sync with
-// Source/core/frame/SandboxFlags.h, as enforced in SandboxFlags.cpp.
+// for a list of the sandbox flags.
 enum class WebSandboxFlags : int {
   kNone = 0,
   kNavigation = 1,
@@ -20,17 +19,24 @@
   kForms = 1 << 3,
   kScripts = 1 << 4,
   kTopNavigation = 1 << 5,
+  // See https://www.w3.org/Bugs/Public/show_bug.cgi?id=12393
   kPopups = 1 << 6,
   kAutomaticFeatures = 1 << 7,
   kPointerLock = 1 << 8,
   kDocumentDomain = 1 << 9,
+  // See
+  // https://w3c.github.io/screen-orientation/#dfn-sandboxed-orientation-lock-browsing-context-flag.
   kOrientationLock = 1 << 10,
   kPropagatesToAuxiliaryBrowsingContexts = 1 << 11,
   kModals = 1 << 12,
+  // See
+  // https://w3c.github.io/presentation-api/#sandboxing-and-the-allow-presentation-keyword
   kPresentationController = 1 << 13,
+  // See https://github.com/WICG/interventions/issues/42.
   kTopNavigationByUserActivation = 1 << 14,
+  // See https://crbug.com/539938
   kDownloads = 1 << 15,
-  kAll = -1
+  kAll = -1  // Mask with all bits set to 1.
 };
 
 inline constexpr WebSandboxFlags operator&(WebSandboxFlags a,
diff --git a/third_party/blink/public/mojom/payments/payment_request.mojom b/third_party/blink/public/mojom/payments/payment_request.mojom
index 8f118931..034d0e2 100644
--- a/third_party/blink/public/mojom/payments/payment_request.mojom
+++ b/third_party/blink/public/mojom/payments/payment_request.mojom
@@ -217,7 +217,7 @@
        PaymentOptions options);
 
   // Shows the user interface with the payment details.
-  Show(bool is_user_gesture);
+  Show(bool is_user_gesture, bool wait_for_updated_details);
 
   // Updates the payment details in response to new shipping address or shipping
   // option.
diff --git a/third_party/blink/public/platform/web_resource_timing_info.h b/third_party/blink/public/platform/web_resource_timing_info.h
index 250ae8e..02a7c0a 100644
--- a/third_party/blink/public/platform/web_resource_timing_info.h
+++ b/third_party/blink/public/platform/web_resource_timing_info.h
@@ -13,9 +13,14 @@
 #include "third_party/blink/public/platform/web_url_load_timing.h"
 #include "third_party/blink/public/platform/web_vector.h"
 
+#if INSIDE_BLINK
+#include "third_party/blink/renderer/platform/cross_thread_copier.h"  // nogncheck
+#endif
+
 namespace blink {
 
 // The browser-side equivalent to this struct is content::ServerTimingInfo.
+// Note: Please update operator==() whenever a new field is added.
 // TODO(dcheng): Migrate this struct over to Mojo so it doesn't need to be
 // duplicated in //content and //third_party/blink.
 struct WebServerTimingInfo {
@@ -24,6 +29,11 @@
                       const WebString& description)
       : name(name), duration(duration), description(description) {}
 
+#if INSIDE_BLINK
+  bool operator==(const WebServerTimingInfo&) const;
+  bool operator!=(const WebServerTimingInfo&) const;
+#endif
+
   WebString name;
   double duration;
   WebString description;
@@ -33,9 +43,15 @@
 // to be passed between processes. This is currently used to send timing
 // information about cross-process iframes for window.performance. The
 // browser-side equivalent to this struct is content::ResourceTimingInfo.
+// Note: Please update operator==() and CrossThreadCopier whenever a new field
+// is added.
 // TODO(dcheng): Migrate this struct over to Mojo so it doesn't need to be
 // duplicated in //content and //third_party/blink.
 struct WebResourceTimingInfo {
+#if INSIDE_BLINK
+  PLATFORM_EXPORT bool operator==(const WebResourceTimingInfo&) const;
+#endif
+
   // The name to associate with the performance entry. For iframes, this is
   // typically the initial URL of the iframe resource.
   WebString name;
@@ -76,6 +92,15 @@
   WebVector<WebServerTimingInfo> server_timing;
 };
 
+#if INSIDE_BLINK
+template <>
+struct CrossThreadCopier<WebResourceTimingInfo> {
+  STATIC_ONLY(CrossThreadCopier);
+  typedef WebResourceTimingInfo Type;
+  PLATFORM_EXPORT static Type Copy(const WebResourceTimingInfo&);
+};
+#endif
+
 }  // namespace blink
 
 #endif
diff --git a/third_party/blink/public/platform/web_url_load_timing.h b/third_party/blink/public/platform/web_url_load_timing.h
index 6aca8fa..a5ab4972 100644
--- a/third_party/blink/public/platform/web_url_load_timing.h
+++ b/third_party/blink/public/platform/web_url_load_timing.h
@@ -37,6 +37,7 @@
 
 #if INSIDE_BLINK
 #include "base/memory/scoped_refptr.h"
+#include "third_party/blink/renderer/platform/cross_thread_copier.h"  // nogncheck
 #endif
 
 namespace blink {
@@ -119,12 +120,23 @@
   BLINK_PLATFORM_EXPORT WebURLLoadTiming& operator=(
       scoped_refptr<ResourceLoadTiming>);
   BLINK_PLATFORM_EXPORT operator scoped_refptr<ResourceLoadTiming>() const;
+  BLINK_PLATFORM_EXPORT WebURLLoadTiming DeepCopy() const;
+  BLINK_PLATFORM_EXPORT bool operator==(const WebURLLoadTiming&) const;
 #endif
 
  private:
   WebPrivatePtr<ResourceLoadTiming> private_;
 };
 
+#if INSIDE_BLINK
+template <>
+struct CrossThreadCopier<WebURLLoadTiming> {
+  STATIC_ONLY(CrossThreadCopier);
+  typedef WebURLLoadTiming Type;
+  PLATFORM_EXPORT static Type Copy(const WebURLLoadTiming&);
+};
+#endif
+
 }  // namespace blink
 
 #endif
diff --git a/third_party/blink/public/web/web_navigation_params.h b/third_party/blink/public/web/web_navigation_params.h
index 265633c..d83236c 100644
--- a/third_party/blink/public/web/web_navigation_params.h
+++ b/third_party/blink/public/web/web_navigation_params.h
@@ -79,8 +79,8 @@
   // Whether the navigation is a result of client redirect.
   bool is_client_redirect = false;
 
-  // Whether the navigation initiator frame has the |kSandboxDownloads| bit set
-  // in its sandbox flags set.
+  // Whether the navigation initiator frame has the
+  // |WebSandboxFlags::kDownloads| bit set in its sandbox flags set.
   bool initiator_frame_has_download_sandbox_flag = false;
 
   // Whether the navigation initiator frame is an ad frame.
diff --git a/third_party/blink/renderer/core/clipboard/data_object_item.h b/third_party/blink/renderer/core/clipboard/data_object_item.h
index 5b051db..e75fe30 100644
--- a/third_party/blink/renderer/core/clipboard/data_object_item.h
+++ b/third_party/blink/renderer/core/clipboard/data_object_item.h
@@ -64,8 +64,10 @@
   static DataObjectItem* CreateFromClipboard(const String& type,
                                              uint64_t sequence_number);
 
-  DataObjectItem(ItemKind, const String& type);
-  DataObjectItem(ItemKind, const String& type, uint64_t sequence_number);
+  explicit DataObjectItem(ItemKind, const String& type);
+  explicit DataObjectItem(ItemKind,
+                          const String& type,
+                          uint64_t sequence_number);
 
   ItemKind Kind() const { return kind_; }
   String GetType() const { return type_; }
diff --git a/third_party/blink/renderer/core/clipboard/data_transfer.h b/third_party/blink/renderer/core/clipboard/data_transfer.h
index 6cfbb54b..f1e4461 100644
--- a/third_party/blink/renderer/core/clipboard/data_transfer.h
+++ b/third_party/blink/renderer/core/clipboard/data_transfer.h
@@ -72,7 +72,9 @@
                               DataTransferAccessPolicy,
                               DataObject*);
 
-  DataTransfer(DataTransferType, DataTransferAccessPolicy, DataObject*);
+  explicit DataTransfer(DataTransferType,
+                        DataTransferAccessPolicy,
+                        DataObject*);
   ~DataTransfer() override;
 
   bool IsForCopyAndPaste() const { return transfer_type_ == kCopyAndPaste; }
diff --git a/third_party/blink/renderer/core/clipboard/data_transfer_item.h b/third_party/blink/renderer/core/clipboard/data_transfer_item.h
index 62255e4..c930a457 100644
--- a/third_party/blink/renderer/core/clipboard/data_transfer_item.h
+++ b/third_party/blink/renderer/core/clipboard/data_transfer_item.h
@@ -50,7 +50,7 @@
   DEFINE_WRAPPERTYPEINFO();
 
  public:
-  DataTransferItem(DataTransfer*, DataObjectItem*);
+  explicit DataTransferItem(DataTransfer*, DataObjectItem*);
 
   String kind() const;
   String type() const;
diff --git a/third_party/blink/renderer/core/clipboard/data_transfer_item_list.h b/third_party/blink/renderer/core/clipboard/data_transfer_item_list.h
index 1d6b18b..0c0df5f 100644
--- a/third_party/blink/renderer/core/clipboard/data_transfer_item_list.h
+++ b/third_party/blink/renderer/core/clipboard/data_transfer_item_list.h
@@ -49,7 +49,7 @@
   DEFINE_WRAPPERTYPEINFO();
 
  public:
-  DataTransferItemList(DataTransfer*, DataObject*);
+  explicit DataTransferItemList(DataTransfer*, DataObject*);
 
   uint32_t length() const;
   DataTransferItem* item(uint32_t index);
diff --git a/third_party/blink/renderer/core/dom/document.cc b/third_party/blink/renderer/core/dom/document.cc
index 52412cef..b561dfd 100644
--- a/third_party/blink/renderer/core/dom/document.cc
+++ b/third_party/blink/renderer/core/dom/document.cc
@@ -4177,7 +4177,7 @@
   }
 
   if (http_refresh_type == kHttpRefreshFromMetaTag &&
-      IsSandboxed(kSandboxAutomaticFeatures)) {
+      IsSandboxed(WebSandboxFlags::kAutomaticFeatures)) {
     String message =
         "Refused to execute the redirect specified via '<meta "
         "http-equiv='refresh' content='...'>'. The document is sandboxed, and "
@@ -5228,7 +5228,7 @@
   // browsing context.
 
   if (!GetSecurityOrigin()->CanAccessCookies()) {
-    if (IsSandboxed(kSandboxOrigin))
+    if (IsSandboxed(WebSandboxFlags::kOrigin))
       exception_state.ThrowSecurityError(
           "The document is sandboxed and lacks the 'allow-same-origin' flag.");
     else if (Url().ProtocolIs("data"))
@@ -5259,7 +5259,7 @@
   // browsing context.
 
   if (!GetSecurityOrigin()->CanAccessCookies()) {
-    if (IsSandboxed(kSandboxOrigin))
+    if (IsSandboxed(WebSandboxFlags::kOrigin))
       exception_state.ThrowSecurityError(
           "The document is sandboxed and lacks the 'allow-same-origin' flag.");
     else if (Url().ProtocolIs("data"))
@@ -5308,7 +5308,7 @@
     return;
   }
 
-  if (IsSandboxed(kSandboxDocumentDomain)) {
+  if (IsSandboxed(WebSandboxFlags::kDocumentDomain)) {
     exception_state.ThrowSecurityError(
         "Assignment is forbidden for sandboxed iframes.");
     return;
@@ -6197,7 +6197,7 @@
                                mojom::ConsoleMessageLevel::kError,
                                "Error with Feature-Policy header: " + message));
   }
-  if (GetSandboxFlags() != kSandboxNone &&
+  if (GetSandboxFlags() != WebSandboxFlags::kNone &&
       RuntimeEnabledFeatures::FeaturePolicyForSandboxEnabled()) {
     // The sandbox flags might have come from CSP header or the browser; in such
     // cases the sandbox is not part of the container policy. They are added
@@ -6347,8 +6347,9 @@
     // Instead, force a Document loaded from a MHTML archive to be sandboxed,
     // providing exceptions only for creating new windows.
     sandbox_flags |=
-        kSandboxAll &
-        ~(kSandboxPopups | kSandboxPropagatesToAuxiliaryBrowsingContexts);
+        (WebSandboxFlags::kAll &
+         ~(WebSandboxFlags::kPopups |
+           WebSandboxFlags::kPropagatesToAuxiliaryBrowsingContexts));
   }
   // In the common case, create the security context from the currently
   // loading URL with a fresh content security policy.
@@ -6392,7 +6393,7 @@
     }
   }
 
-  if (IsSandboxed(kSandboxOrigin)) {
+  if (IsSandboxed(WebSandboxFlags::kOrigin)) {
     DCHECK(!initializer.ContextDocument());
     scoped_refptr<SecurityOrigin> sandboxed_origin =
         initializer.OriginToCommit() ? initializer.OriginToCommit()
@@ -6473,7 +6474,7 @@
     GetMutableSecurityOrigin()->SetOpaqueOriginIsPotentiallyTrustworthy(true);
 
   ParsedFeaturePolicy declared_policy = {};
-  if (GetSandboxFlags() != kSandboxNone &&
+  if (GetSandboxFlags() != WebSandboxFlags::kNone &&
       RuntimeEnabledFeatures::FeaturePolicyForSandboxEnabled()) {
     // If any sandbox flags are enforced above they should also be added as
     // part of a declared policy to properly initialize the sandbox feature
@@ -6560,7 +6561,7 @@
   // However, there is an exception for cases when the script should bypass the
   // main world's CSP (such as for privileged isolated worlds). See
   // https://crbug.com/811528.
-  if (IsSandboxed(kSandboxScripts) &&
+  if (IsSandboxed(WebSandboxFlags::kScripts) &&
       !ContentSecurityPolicy::ShouldBypassMainWorld(this)) {
     // FIXME: This message should be moved off the console once a solution to
     // https://bugs.webkit.org/show_bug.cgi?id=103274 exists.
@@ -7378,7 +7379,7 @@
 
 bool Document::IsSecureContext() const {
   bool is_secure = secure_context_state_ == SecureContextState::kSecure;
-  if (GetSandboxFlags() != kSandboxNone) {
+  if (GetSandboxFlags() != WebSandboxFlags::kNone) {
     UseCounter::Count(
         *this, is_secure
                    ? WebFeature::kSecureContextCheckForSandboxedOriginPassed
diff --git a/third_party/blink/renderer/core/dom/document_init.cc b/third_party/blink/renderer/core/dom/document_init.cc
index d8c9e39..6e81911 100644
--- a/third_party/blink/renderer/core/dom/document_init.cc
+++ b/third_party/blink/renderer/core/dom/document_init.cc
@@ -102,7 +102,7 @@
   // so that the blocked document appears to be a normal cross-origin document's
   // load per CSP spec: https://www.w3.org/TR/CSP3/#directive-frame-ancestors.
   if (loader->WasBlockedAfterCSP()) {
-    flags |= kSandboxOrigin;
+    flags |= WebSandboxFlags::kOrigin;
   }
 
   return flags;
diff --git a/third_party/blink/renderer/core/dom/document_test.cc b/third_party/blink/renderer/core/dom/document_test.cc
index 415d791..7aa7675d 100644
--- a/third_party/blink/renderer/core/dom/document_test.cc
+++ b/third_party/blink/renderer/core/dom/document_test.cc
@@ -596,12 +596,12 @@
   scoped_refptr<SecurityOrigin> origin =
       SecurityOrigin::CreateFromString("http://example.test");
   GetDocument().SetSecurityOrigin(origin);
-  SandboxFlags mask = kSandboxNavigation;
+  SandboxFlags mask = WebSandboxFlags::kNavigation;
   GetDocument().EnforceSandboxFlags(mask);
   EXPECT_EQ(origin, GetDocument().GetSecurityOrigin());
   EXPECT_FALSE(GetDocument().GetSecurityOrigin()->IsPotentiallyTrustworthy());
 
-  mask |= kSandboxOrigin;
+  mask |= WebSandboxFlags::kOrigin;
   GetDocument().EnforceSandboxFlags(mask);
   EXPECT_TRUE(GetDocument().GetSecurityOrigin()->IsOpaque());
   EXPECT_FALSE(GetDocument().GetSecurityOrigin()->IsPotentiallyTrustworthy());
@@ -879,7 +879,7 @@
   scoped_refptr<SecurityOrigin> origin =
       SecurityOrigin::CreateFromString("https://test.com");
   GetDocument().SetSecurityOrigin(origin);
-  SandboxFlags mask = kSandboxOrigin;
+  SandboxFlags mask = WebSandboxFlags::kOrigin;
   GetDocument().EnforceSandboxFlags(mask);
   GetDocument().SetURL(KURL("https://test.com/foobar/document"));
 
@@ -1077,7 +1077,7 @@
 INSTANTIATE_TEST_SUITE_P(, IsolatedWorldCSPTest, testing::Values(true, false));
 
 TEST_F(DocumentTest, CanExecuteScriptsWithSandboxAndIsolatedWorld) {
-  constexpr SandboxFlags kSandboxMask = kSandboxScripts;
+  constexpr SandboxFlags kSandboxMask = WebSandboxFlags::kScripts;
   GetDocument().EnforceSandboxFlags(kSandboxMask);
   // With FeaturePolicyForSandbox, all the sandbox flags must be explicitly
   // converted to equivalent feature policies. Since sandbox is enforced above,
diff --git a/third_party/blink/renderer/core/editing/caret_display_item_client.cc b/third_party/blink/renderer/core/editing/caret_display_item_client.cc
index eb0d5c20..30253cf 100644
--- a/third_party/blink/renderer/core/editing/caret_display_item_client.cc
+++ b/third_party/blink/renderer/core/editing/caret_display_item_client.cc
@@ -58,17 +58,17 @@
   if (!layout_object)
     return nullptr;
 
+  auto* caret_layout_object = DynamicTo<LayoutBlock>(layout_object);
   // if caretNode is a block and caret is inside it then caret should be painted
   // by that block
-  bool painted_by_block =
-      layout_object->IsLayoutBlock() && CaretRendersInsideNode(node);
+  bool painted_by_block = caret_layout_object && CaretRendersInsideNode(node);
   // TODO(yoichio): This function is called at least
   // DocumentLifeCycle::LayoutClean but caretRendersInsideNode above can
   // layout. Thus |node->layoutObject()| can be changed then this is bad
   // design. We should make caret painting algorithm clean.
   CHECK_EQ(layout_object, node->GetLayoutObject())
       << "Layout tree should not changed";
-  return painted_by_block ? ToLayoutBlock(layout_object)
+  return painted_by_block ? caret_layout_object
                           : layout_object->ContainingBlock();
 }
 
diff --git a/third_party/blink/renderer/core/editing/caret_display_item_client_test.cc b/third_party/blink/renderer/core/editing/caret_display_item_client_test.cc
index 6d4de7a..97832e6 100644
--- a/third_party/blink/renderer/core/editing/caret_display_item_client_test.cc
+++ b/third_party/blink/renderer/core/editing/caret_display_item_client_test.cc
@@ -81,7 +81,7 @@
 
   Text* text = AppendTextNode("Hello, World!");
   UpdateAllLifecyclePhasesForCaretTest();
-  const auto* block = ToLayoutBlock(GetDocument().body()->GetLayoutObject());
+  const auto* block = To<LayoutBlock>(GetDocument().body()->GetLayoutObject());
 
   // Focus the body. Should invalidate the new caret.
   GetDocument().View()->SetTracksPaintInvalidations(true);
@@ -232,8 +232,8 @@
   auto* block_element1 = AppendBlock("Block1");
   auto* block_element2 = AppendBlock("Block2");
   UpdateAllLifecyclePhasesForCaretTest();
-  auto* block1 = ToLayoutBlock(block_element1->GetLayoutObject());
-  auto* block2 = ToLayoutBlock(block_element2->GetLayoutObject());
+  auto* block1 = To<LayoutBlock>(block_element1->GetLayoutObject());
+  auto* block2 = To<LayoutBlock>(block_element2->GetLayoutObject());
 
   // Set caret into block2.
   GetDocument().body()->focus();
@@ -295,7 +295,7 @@
   Text* text = AppendTextNode("Hello, World!");
   GetDocument().body()->focus();
   UpdateAllLifecyclePhasesForCaretTest();
-  const auto* block = ToLayoutBlock(GetDocument().body()->GetLayoutObject());
+  const auto* block = To<LayoutBlock>(GetDocument().body()->GetLayoutObject());
 
   LayoutRect caret_visual_rect = GetCaretDisplayItemClient().VisualRect();
   EXPECT_EQ(1, caret_visual_rect.Width());
@@ -345,7 +345,7 @@
   GetDocument().GetPage()->GetFocusController().SetFocused(true);
   auto* container = GetDocument().getElementById("container");
   auto* editor = GetDocument().getElementById("editor");
-  auto* editor_block = ToLayoutBlock(editor->GetLayoutObject());
+  auto* editor_block = To<LayoutBlock>(editor->GetLayoutObject());
   Selection().SetSelectionAndEndTyping(
       SelectionInDOMTree::Builder().Collapse(Position(editor, 0)).Build());
   UpdateAllLifecyclePhasesForCaretTest();
diff --git a/third_party/blink/renderer/core/editing/frame_selection_test.cc b/third_party/blink/renderer/core/editing/frame_selection_test.cc
index cd580127..24cf1038 100644
--- a/third_party/blink/renderer/core/editing/frame_selection_test.cc
+++ b/third_party/blink/renderer/core/editing/frame_selection_test.cc
@@ -130,7 +130,7 @@
       SelectionInDOMTree::Builder().Collapse(Position(text, 0)).Build());
   UpdateAllLifecyclePhasesForTest();
   EXPECT_TRUE(Selection().ComputeVisibleSelectionInDOMTree().IsCaret());
-  EXPECT_TRUE(ToLayoutBlock(GetDocument().body()->GetLayoutObject())
+  EXPECT_TRUE(To<LayoutBlock>(GetDocument().body()->GetLayoutObject())
                   ->ShouldPaintCursorCaret());
 
   unsigned start_count = LayoutCount();
diff --git a/third_party/blink/renderer/core/editing/iterators/text_iterator.cc b/third_party/blink/renderer/core/editing/iterators/text_iterator.cc
index 447d7c83..e04a90b 100644
--- a/third_party/blink/renderer/core/editing/iterators/text_iterator.cc
+++ b/third_party/blink/renderer/core/editing/iterators/text_iterator.cc
@@ -716,7 +716,7 @@
       node_->GetLayoutObject()->Style()->Visibility() !=
           EVisibility::kVisible ||
       (node_->GetLayoutObject()->IsLayoutBlockFlow() &&
-       !ToLayoutBlock(node_->GetLayoutObject())->Size().Height() &&
+       !To<LayoutBlock>(node_->GetLayoutObject())->Size().Height() &&
        !IsHTMLBodyElement(*node_)))
     return false;
 
diff --git a/third_party/blink/renderer/core/editing/text_offset_mapping.cc b/third_party/blink/renderer/core/editing/text_offset_mapping.cc
index 3e9cdcbe..8143a8e2 100644
--- a/third_party/blink/renderer/core/editing/text_offset_mapping.cc
+++ b/third_party/blink/renderer/core/editing/text_offset_mapping.cc
@@ -91,9 +91,10 @@
 // |LayoutBlockFlow|.
 const LayoutBlockFlow* ComputeInlineContentsAsBlockFlow(
     const LayoutObject& layout_object) {
-  const LayoutBlock* const block = layout_object.IsLayoutBlock()
-                                       ? &ToLayoutBlock(layout_object)
-                                       : layout_object.ContainingBlock();
+  const LayoutBlock* block = DynamicTo<LayoutBlock>(layout_object);
+  if (!block)
+    block = layout_object.ContainingBlock();
+
   DCHECK(block) << layout_object;
   if (!block->IsLayoutBlockFlow())
     return nullptr;
diff --git a/third_party/blink/renderer/core/editing/visible_units.cc b/third_party/blink/renderer/core/editing/visible_units.cc
index 2efc1db..84e6a01 100644
--- a/third_party/blink/renderer/core/editing/visible_units.cc
+++ b/third_party/blink/renderer/core/editing/visible_units.cc
@@ -934,7 +934,7 @@
   if (layout_object->IsLayoutBlockFlow() ||
       layout_object->IsFlexibleBoxIncludingNG() ||
       layout_object->IsLayoutGrid()) {
-    if (ToLayoutBlock(layout_object)->LogicalHeight() ||
+    if (To<LayoutBlock>(layout_object)->LogicalHeight() ||
         anchor_node->GetDocument().body() == anchor_node) {
       if (!HasRenderedNonAnonymousDescendantsWithHeight(layout_object))
         return position.AtFirstEditingPositionForNode();
diff --git a/third_party/blink/renderer/core/execution_context/remote_security_context.cc b/third_party/blink/renderer/core/execution_context/remote_security_context.cc
index 6f210cbf..d8138e2 100644
--- a/third_party/blink/renderer/core/execution_context/remote_security_context.cc
+++ b/third_party/blink/renderer/core/execution_context/remote_security_context.cc
@@ -41,7 +41,7 @@
 }
 
 void RemoteSecurityContext::ResetSandboxFlags() {
-  sandbox_flags_ = kSandboxNone;
+  sandbox_flags_ = WebSandboxFlags::kNone;
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/execution_context/security_context.cc b/third_party/blink/renderer/core/execution_context/security_context.cc
index bd632ed..1ee2e2b 100644
--- a/third_party/blink/renderer/core/execution_context/security_context.cc
+++ b/third_party/blink/renderer/core/execution_context/security_context.cc
@@ -49,7 +49,7 @@
 }
 
 SecurityContext::SecurityContext()
-    : sandbox_flags_(kSandboxNone),
+    : sandbox_flags_(WebSandboxFlags::kNone),
       address_space_(mojom::IPAddressSpace::kPublic),
       insecure_request_policy_(kLeaveInsecureRequestsAlone),
       require_safe_types_(false) {}
@@ -70,34 +70,34 @@
   content_security_policy_ = content_security_policy;
 }
 
-bool SecurityContext::IsSandboxed(SandboxFlag mask) const {
+bool SecurityContext::IsSandboxed(WebSandboxFlags mask) const {
   if (RuntimeEnabledFeatures::FeaturePolicyForSandboxEnabled()) {
     switch (mask) {
-      case kSandboxAll:
+      case WebSandboxFlags::kAll:
         NOTREACHED();
         break;
-      case kSandboxTopNavigation:
+      case WebSandboxFlags::kTopNavigation:
         return !feature_policy_->IsFeatureEnabled(
             mojom::FeaturePolicyFeature::kTopNavigation);
-      case kSandboxForms:
+      case WebSandboxFlags::kForms:
         return !feature_policy_->IsFeatureEnabled(
             mojom::FeaturePolicyFeature::kFormSubmission);
-      case kSandboxScripts:
+      case WebSandboxFlags::kScripts:
         return !feature_policy_->IsFeatureEnabled(
             mojom::FeaturePolicyFeature::kScript);
-      case kSandboxPopups:
+      case WebSandboxFlags::kPopups:
         return !feature_policy_->IsFeatureEnabled(
             mojom::FeaturePolicyFeature::kPopups);
-      case kSandboxPointerLock:
+      case WebSandboxFlags::kPointerLock:
         return !feature_policy_->IsFeatureEnabled(
             mojom::FeaturePolicyFeature::kPointerLock);
-      case kSandboxOrientationLock:
+      case WebSandboxFlags::kOrientationLock:
         return !feature_policy_->IsFeatureEnabled(
             mojom::FeaturePolicyFeature::kOrientationLock);
-      case kSandboxModals:
+      case WebSandboxFlags::kModals:
         return !feature_policy_->IsFeatureEnabled(
             mojom::FeaturePolicyFeature::kModals);
-      case kSandboxPresentationController:
+      case WebSandboxFlags::kPresentationController:
         return !feature_policy_->IsFeatureEnabled(
             mojom::FeaturePolicyFeature::kPresentation);
       default:
@@ -105,18 +105,18 @@
         break;
     }
   }
-  return sandbox_flags_ & mask;
+  return (sandbox_flags_ & mask) != WebSandboxFlags::kNone;
 }
 
-void SecurityContext::EnforceSandboxFlags(SandboxFlags mask) {
+void SecurityContext::EnforceSandboxFlags(WebSandboxFlags mask) {
   ApplySandboxFlags(mask);
 }
 
-void SecurityContext::ApplySandboxFlags(SandboxFlags mask,
+void SecurityContext::ApplySandboxFlags(WebSandboxFlags mask,
                                         bool is_potentially_trustworthy) {
   sandbox_flags_ |= mask;
 
-  if (IsSandboxed(kSandboxOrigin) && GetSecurityOrigin() &&
+  if (IsSandboxed(WebSandboxFlags::kOrigin) && GetSecurityOrigin() &&
       !GetSecurityOrigin()->IsOpaque()) {
     scoped_refptr<SecurityOrigin> security_origin =
         GetSecurityOrigin()->DeriveNewOpaqueOrigin();
diff --git a/third_party/blink/renderer/core/exported/local_frame_client_impl.cc b/third_party/blink/renderer/core/exported/local_frame_client_impl.cc
index fdc1982..3493baa 100644
--- a/third_party/blink/renderer/core/exported/local_frame_client_impl.cc
+++ b/third_party/blink/renderer/core/exported/local_frame_client_impl.cc
@@ -564,7 +564,7 @@
         frame->Client()->Opener() == ToCoreFrame(web_frame_);
     navigation_info->initiator_frame_has_download_sandbox_flag =
         frame->GetSecurityContext() &&
-        frame->GetSecurityContext()->IsSandboxed(kSandboxDownloads);
+        frame->GetSecurityContext()->IsSandboxed(WebSandboxFlags::kDownloads);
     navigation_info->initiator_frame_is_ad = frame->IsAdSubframe();
   }
 
diff --git a/third_party/blink/renderer/core/exported/web_frame_test.cc b/third_party/blink/renderer/core/exported/web_frame_test.cc
index ca2ee4f..6bc5842 100644
--- a/third_party/blink/renderer/core/exported/web_frame_test.cc
+++ b/third_party/blink/renderer/core/exported/web_frame_test.cc
@@ -8670,7 +8670,7 @@
 
   Document* document = web_view->MainFrameImpl()->GetFrame()->GetDocument();
   LayoutBlock* container =
-      ToLayoutBlock(document->getElementById("container")->GetLayoutObject());
+      To<LayoutBlock>(document->getElementById("container")->GetLayoutObject());
   LayoutBox* percent_height_in_anonymous =
       ToLayoutBox(document->getElementById("percent-height-in-anonymous")
                       ->GetLayoutObject());
diff --git a/third_party/blink/renderer/core/frame/csp/content_security_policy.cc b/third_party/blink/renderer/core/frame/csp/content_security_policy.cc
index 27ec352c..50a865f 100644
--- a/third_party/blink/renderer/core/frame/csp/content_security_policy.cc
+++ b/third_party/blink/renderer/core/frame/csp/content_security_policy.cc
@@ -152,7 +152,7 @@
       override_inline_style_allowed_(false),
       script_hash_algorithms_used_(kContentSecurityPolicyHashAlgorithmNone),
       style_hash_algorithms_used_(kContentSecurityPolicyHashAlgorithmNone),
-      sandbox_mask_(0),
+      sandbox_mask_(WebSandboxFlags::kNone),
       treat_as_public_address_(false),
       require_trusted_types_(false),
       insecure_request_policy_(kLeaveInsecureRequestsAlone) {}
@@ -191,7 +191,7 @@
 
   // Set mixed content checking and sandbox flags, then dump all the parsing
   // error messages, then poke at histograms.
-  if (sandbox_mask_ != kSandboxNone) {
+  if (sandbox_mask_ != WebSandboxFlags::kNone) {
     Count(WebFeature::kSandboxViaCSP);
     delegate_->SetSandboxFlags(sandbox_mask_);
   }
diff --git a/third_party/blink/renderer/core/frame/csp/content_security_policy.h b/third_party/blink/renderer/core/frame/csp/content_security_policy.h
index fd052b2..7a2d6bd 100644
--- a/third_party/blink/renderer/core/frame/csp/content_security_policy.h
+++ b/third_party/blink/renderer/core/frame/csp/content_security_policy.h
@@ -73,7 +73,7 @@
 class SourceLocation;
 enum class ResourceType : uint8_t;
 
-typedef int SandboxFlags;
+using SandboxFlags = WebSandboxFlags;
 typedef HeapVector<Member<CSPDirectiveList>> CSPDirectiveListVector;
 typedef HeapVector<Member<ConsoleMessage>> ConsoleMessageVector;
 typedef std::pair<String, ContentSecurityPolicyHeaderType> CSPHeaderAndType;
diff --git a/third_party/blink/renderer/core/frame/csp/content_security_policy_test.cc b/third_party/blink/renderer/core/frame/csp/content_security_policy_test.cc
index 3ec5854..339210c 100644
--- a/third_party/blink/renderer/core/frame/csp/content_security_policy_test.cc
+++ b/third_party/blink/renderer/core/frame/csp/content_security_policy_test.cc
@@ -1593,7 +1593,7 @@
   EXPECT_EQ(kLeaveInsecureRequestsAlone, csp->GetInsecureRequestPolicy());
   EXPECT_FALSE(csp->HasHeaderDeliveredPolicy());
   EXPECT_FALSE(csp->SupportsWasmEval());
-  EXPECT_EQ(kSandboxNone, csp->GetSandboxMask());
+  EXPECT_EQ(WebSandboxFlags::kNone, csp->GetSandboxMask());
   EXPECT_FALSE(
       csp->HasPolicyFromSource(kContentSecurityPolicyHeaderSourceHTTP));
 }
diff --git a/third_party/blink/renderer/core/frame/dom_window.cc b/third_party/blink/renderer/core/frame/dom_window.cc
index 791af2f..8b4e29f 100644
--- a/third_party/blink/renderer/core/frame/dom_window.cc
+++ b/third_party/blink/renderer/core/frame/dom_window.cc
@@ -251,18 +251,19 @@
   KURL target_url = local_dom_window
                         ? local_dom_window->document()->Url()
                         : KURL(NullURL(), target_origin->ToString());
-  if (GetFrame()->GetSecurityContext()->IsSandboxed(kSandboxOrigin) ||
-      accessing_window->document()->IsSandboxed(kSandboxOrigin)) {
+  if (GetFrame()->GetSecurityContext()->IsSandboxed(WebSandboxFlags::kOrigin) ||
+      accessing_window->document()->IsSandboxed(WebSandboxFlags::kOrigin)) {
     message = "Blocked a frame at \"" +
               SecurityOrigin::Create(active_url)->ToString() +
               "\" from accessing a frame at \"" +
               SecurityOrigin::Create(target_url)->ToString() + "\". ";
-    if (GetFrame()->GetSecurityContext()->IsSandboxed(kSandboxOrigin) &&
-        accessing_window->document()->IsSandboxed(kSandboxOrigin))
+    if (GetFrame()->GetSecurityContext()->IsSandboxed(
+            WebSandboxFlags::kOrigin) &&
+        accessing_window->document()->IsSandboxed(WebSandboxFlags::kOrigin))
       return "Sandbox access violation: " + message +
              " Both frames are sandboxed and lack the \"allow-same-origin\" "
              "flag.";
-    if (GetFrame()->GetSecurityContext()->IsSandboxed(kSandboxOrigin))
+    if (GetFrame()->GetSecurityContext()->IsSandboxed(WebSandboxFlags::kOrigin))
       return "Sandbox access violation: " + message +
              " The frame being accessed is sandboxed and lacks the "
              "\"allow-same-origin\" flag.";
diff --git a/third_party/blink/renderer/core/frame/frame_owner.h b/third_party/blink/renderer/core/frame/frame_owner.h
index 810b82e..c31b88e 100644
--- a/third_party/blink/renderer/core/frame/frame_owner.h
+++ b/third_party/blink/renderer/core/frame/frame_owner.h
@@ -92,7 +92,9 @@
   Frame* ContentFrame() const override { return nullptr; }
   void SetContentFrame(Frame&) override {}
   void ClearContentFrame() override {}
-  SandboxFlags GetSandboxFlags() const override { return kSandboxNone; }
+  SandboxFlags GetSandboxFlags() const override {
+    return WebSandboxFlags::kNone;
+  }
   void AddResourceTiming(const ResourceTimingInfo&) override {}
   void DispatchLoad() override {}
   bool CanRenderFallbackContent() const override { return false; }
diff --git a/third_party/blink/renderer/core/frame/local_dom_window.cc b/third_party/blink/renderer/core/frame/local_dom_window.cc
index 46315a0..5fc087b 100644
--- a/third_party/blink/renderer/core/frame/local_dom_window.cc
+++ b/third_party/blink/renderer/core/frame/local_dom_window.cc
@@ -245,7 +245,8 @@
     document = DOMImplementation::createDocument(
         mime_type, init,
         init.GetFrame() ? init.GetFrame()->InViewSourceMode() : false);
-    if (document->IsPluginDocument() && document->IsSandboxed(kSandboxPlugins))
+    if (document->IsPluginDocument() &&
+        document->IsSandboxed(WebSandboxFlags::kPlugins))
       document = MakeGarbageCollected<SinkDocument>(init);
   }
 
@@ -667,7 +668,7 @@
   if (!GetFrame())
     return;
 
-  if (document()->IsSandboxed(kSandboxModals)) {
+  if (document()->IsSandboxed(WebSandboxFlags::kModals)) {
     UseCounter::Count(document(), WebFeature::kDialogInSandboxedContext);
     GetFrameConsole()->AddMessage(ConsoleMessage::Create(
         mojom::ConsoleMessageSource::kSecurity,
@@ -697,7 +698,7 @@
   if (!GetFrame())
     return false;
 
-  if (document()->IsSandboxed(kSandboxModals)) {
+  if (document()->IsSandboxed(WebSandboxFlags::kModals)) {
     UseCounter::Count(document(), WebFeature::kDialogInSandboxedContext);
     GetFrameConsole()->AddMessage(ConsoleMessage::Create(
         mojom::ConsoleMessageSource::kSecurity,
@@ -729,7 +730,7 @@
   if (!GetFrame())
     return String();
 
-  if (document()->IsSandboxed(kSandboxModals)) {
+  if (document()->IsSandboxed(WebSandboxFlags::kModals)) {
     UseCounter::Count(document(), WebFeature::kDialogInSandboxedContext);
     GetFrameConsole()->AddMessage(ConsoleMessage::Create(
         mojom::ConsoleMessageSource::kSecurity,
diff --git a/third_party/blink/renderer/core/frame/local_frame.cc b/third_party/blink/renderer/core/frame/local_frame.cc
index cf4c2653..4e3a44a 100644
--- a/third_party/blink/renderer/core/frame/local_frame.cc
+++ b/third_party/blink/renderer/core/frame/local_frame.cc
@@ -1073,7 +1073,7 @@
                       WebFeature::kOpenerNavigationWithoutGesture);
   }
 
-  if (GetSecurityContext()->IsSandboxed(kSandboxNavigation)) {
+  if (GetSecurityContext()->IsSandboxed(WebSandboxFlags::kNavigation)) {
     if (!target_frame.Tree().IsDescendantOf(this) &&
         !target_frame.IsMainFrame()) {
       PrintNavigationErrorMessage(
@@ -1088,8 +1088,8 @@
     // 'allow-popups' flag is specified, or if the
     if (target_frame.IsMainFrame() && target_frame != Tree().Top() &&
         GetSecurityContext()->IsSandboxed(
-            kSandboxPropagatesToAuxiliaryBrowsingContexts) &&
-        (GetSecurityContext()->IsSandboxed(kSandboxPopups) ||
+            WebSandboxFlags::kPropagatesToAuxiliaryBrowsingContexts) &&
+        (GetSecurityContext()->IsSandboxed(WebSandboxFlags::kPopups) ||
          target_frame.Client()->Opener() != this)) {
       PrintNavigationErrorMessage(
           target_frame,
@@ -1102,9 +1102,9 @@
     // Top navigation is forbidden unless opted-in. allow-top-navigation or
     // allow-top-navigation-by-user-activation will also skips origin checks.
     if (target_frame == Tree().Top()) {
-      if (GetSecurityContext()->IsSandboxed(kSandboxTopNavigation) &&
+      if (GetSecurityContext()->IsSandboxed(WebSandboxFlags::kTopNavigation) &&
           GetSecurityContext()->IsSandboxed(
-              kSandboxTopNavigationByUserActivation)) {
+              WebSandboxFlags::kTopNavigationByUserActivation)) {
         PrintNavigationErrorMessage(
             target_frame,
             "The frame attempting navigation of the top-level window is "
@@ -1113,9 +1113,9 @@
         return false;
       }
 
-      if (GetSecurityContext()->IsSandboxed(kSandboxTopNavigation) &&
+      if (GetSecurityContext()->IsSandboxed(WebSandboxFlags::kTopNavigation) &&
           !GetSecurityContext()->IsSandboxed(
-              kSandboxTopNavigationByUserActivation) &&
+              WebSandboxFlags::kTopNavigationByUserActivation) &&
           !LocalFrame::HasTransientUserActivation(this)) {
         // With only 'allow-top-navigation-by-user-activation' (but not
         // 'allow-top-navigation'), top navigation requires a user gesture.
diff --git a/third_party/blink/renderer/core/frame/mhtml_loading_test.cc b/third_party/blink/renderer/core/frame/mhtml_loading_test.cc
index da856d3..bfa6c37 100644
--- a/third_party/blink/renderer/core/frame/mhtml_loading_test.cc
+++ b/third_party/blink/renderer/core/frame/mhtml_loading_test.cc
@@ -119,8 +119,9 @@
 
   // Full sandboxing with the exception to new top-level windows should be
   // turned on.
-  EXPECT_EQ(kSandboxAll & ~(kSandboxPopups |
-                            kSandboxPropagatesToAuxiliaryBrowsingContexts),
+  EXPECT_EQ(WebSandboxFlags::kAll &
+                ~(WebSandboxFlags::kPopups |
+                  WebSandboxFlags::kPropagatesToAuxiliaryBrowsingContexts),
             document->GetSandboxFlags());
 
   // MHTML document should be loaded into unique origin.
@@ -138,8 +139,9 @@
   Document* child_document = child_frame->GetDocument();
   ASSERT_TRUE(child_document);
 
-  EXPECT_EQ(kSandboxAll & ~(kSandboxPopups |
-                            kSandboxPropagatesToAuxiliaryBrowsingContexts),
+  EXPECT_EQ(WebSandboxFlags::kAll &
+                ~(WebSandboxFlags::kPopups |
+                  WebSandboxFlags::kPropagatesToAuxiliaryBrowsingContexts),
             child_document->GetSandboxFlags());
 
   // MHTML document should be loaded into unique origin.
@@ -163,8 +165,9 @@
 
   // Full sandboxing with the exception to new top-level windows should be
   // turned on.
-  EXPECT_EQ(kSandboxAll & ~(kSandboxPopups |
-                            kSandboxPropagatesToAuxiliaryBrowsingContexts),
+  EXPECT_EQ(WebSandboxFlags::kAll &
+                ~(WebSandboxFlags::kPopups |
+                  WebSandboxFlags::kPropagatesToAuxiliaryBrowsingContexts),
             document->GetSandboxFlags());
 
   // MHTML document should be loaded into unique origin.
diff --git a/third_party/blink/renderer/core/frame/remote_frame.cc b/third_party/blink/renderer/core/frame/remote_frame.cc
index 116006c..b28e5292 100644
--- a/third_party/blink/renderer/core/frame/remote_frame.cc
+++ b/third_party/blink/renderer/core/frame/remote_frame.cc
@@ -108,13 +108,13 @@
     is_opener_navigation = frame->Client()->Opener() == this;
     initiator_frame_has_download_sandbox_flag =
         frame->GetSecurityContext() &&
-        frame->GetSecurityContext()->IsSandboxed(kSandboxDownloads);
+        frame->GetSecurityContext()->IsSandboxed(WebSandboxFlags::kDownloads);
     initiator_frame_is_ad = frame->IsAdSubframe();
   }
 
   bool current_frame_has_download_sandbox_flag =
       GetSecurityContext() &&
-      GetSecurityContext()->IsSandboxed(kSandboxDownloads);
+      GetSecurityContext()->IsSandboxed(WebSandboxFlags::kDownloads);
   bool has_download_sandbox_flag = initiator_frame_has_download_sandbox_flag ||
                                    current_frame_has_download_sandbox_flag;
 
diff --git a/third_party/blink/renderer/core/frame/sandbox_flags.cc b/third_party/blink/renderer/core/frame/sandbox_flags.cc
index 12a9250..05de213 100644
--- a/third_party/blink/renderer/core/frame/sandbox_flags.cc
+++ b/third_party/blink/renderer/core/frame/sandbox_flags.cc
@@ -45,15 +45,17 @@
 const SandboxFlagFeaturePolicyPairs& SandboxFlagsWithFeaturePolicies() {
   DEFINE_STATIC_LOCAL(
       SandboxFlagFeaturePolicyPairs, array,
-      ({{kSandboxTopNavigation, mojom::FeaturePolicyFeature::kTopNavigation},
-        {kSandboxForms, mojom::FeaturePolicyFeature::kFormSubmission},
-        {kSandboxScripts, mojom::FeaturePolicyFeature::kScript},
-        {kSandboxPopups, mojom::FeaturePolicyFeature::kPopups},
-        {kSandboxPointerLock, mojom::FeaturePolicyFeature::kPointerLock},
-        {kSandboxModals, mojom::FeaturePolicyFeature::kModals},
-        {kSandboxOrientationLock,
+      ({{WebSandboxFlags::kTopNavigation,
+         mojom::FeaturePolicyFeature::kTopNavigation},
+        {WebSandboxFlags::kForms, mojom::FeaturePolicyFeature::kFormSubmission},
+        {WebSandboxFlags::kScripts, mojom::FeaturePolicyFeature::kScript},
+        {WebSandboxFlags::kPopups, mojom::FeaturePolicyFeature::kPopups},
+        {WebSandboxFlags::kPointerLock,
+         mojom::FeaturePolicyFeature::kPointerLock},
+        {WebSandboxFlags::kModals, mojom::FeaturePolicyFeature::kModals},
+        {WebSandboxFlags::kOrientationLock,
          mojom::FeaturePolicyFeature::kOrientationLock},
-        {kSandboxPresentationController,
+        {WebSandboxFlags::kPresentationController,
          mojom::FeaturePolicyFeature::kPresentation}}));
   return array;
 }
@@ -63,8 +65,8 @@
 // are always removed from the set of sandbox flags set for a sandboxed
 // <iframe> (those sandbox flags are now contained in the |ContainerPolicy|).
 SandboxFlags SandboxFlagsImplementedByFeaturePolicy() {
-  DEFINE_STATIC_LOCAL(SandboxFlags, mask, (kSandboxNone));
-  if (mask == kSandboxNone) {
+  DEFINE_STATIC_LOCAL(SandboxFlags, mask, (WebSandboxFlags::kNone));
+  if (mask == WebSandboxFlags::kNone) {
     for (const auto& pair : SandboxFlagsWithFeaturePolicies())
       mask |= pair.first;
   }
@@ -75,7 +77,7 @@
                                 String& invalid_tokens_error_message) {
   // http://www.w3.org/TR/html5/the-iframe-element.html#attr-iframe-sandbox
   // Parse the unordered set of unique space-separated tokens.
-  SandboxFlags flags = kSandboxAll;
+  SandboxFlags flags = WebSandboxFlags::kAll;
   unsigned length = policy.size();
   unsigned number_of_token_errors = 0;
   StringBuilder token_errors;
@@ -84,34 +86,34 @@
     // Turn off the corresponding sandbox flag if it's set as "allowed".
     String sandbox_token(policy[index]);
     if (EqualIgnoringASCIICase(sandbox_token, "allow-same-origin")) {
-      flags &= ~kSandboxOrigin;
+      flags = flags & ~WebSandboxFlags::kOrigin;
     } else if (EqualIgnoringASCIICase(sandbox_token, "allow-forms")) {
-      flags &= ~kSandboxForms;
+      flags = flags & ~WebSandboxFlags::kForms;
     } else if (EqualIgnoringASCIICase(sandbox_token, "allow-scripts")) {
-      flags &= ~kSandboxScripts;
-      flags &= ~kSandboxAutomaticFeatures;
+      flags = flags & ~WebSandboxFlags::kScripts;
+      flags = flags & ~WebSandboxFlags::kAutomaticFeatures;
     } else if (EqualIgnoringASCIICase(sandbox_token, "allow-top-navigation")) {
-      flags &= ~kSandboxTopNavigation;
+      flags = flags & ~WebSandboxFlags::kTopNavigation;
     } else if (EqualIgnoringASCIICase(sandbox_token, "allow-popups")) {
-      flags &= ~kSandboxPopups;
+      flags = flags & ~WebSandboxFlags::kPopups;
     } else if (EqualIgnoringASCIICase(sandbox_token, "allow-pointer-lock")) {
-      flags &= ~kSandboxPointerLock;
+      flags = flags & ~WebSandboxFlags::kPointerLock;
     } else if (EqualIgnoringASCIICase(sandbox_token,
                                       "allow-orientation-lock")) {
-      flags &= ~kSandboxOrientationLock;
+      flags = flags & ~WebSandboxFlags::kOrientationLock;
     } else if (EqualIgnoringASCIICase(sandbox_token,
                                       "allow-popups-to-escape-sandbox")) {
-      flags &= ~kSandboxPropagatesToAuxiliaryBrowsingContexts;
+      flags = flags & ~WebSandboxFlags::kPropagatesToAuxiliaryBrowsingContexts;
     } else if (EqualIgnoringASCIICase(sandbox_token, "allow-modals")) {
-      flags &= ~kSandboxModals;
+      flags = flags & ~WebSandboxFlags::kModals;
     } else if (EqualIgnoringASCIICase(sandbox_token, "allow-presentation")) {
-      flags &= ~kSandboxPresentationController;
+      flags = flags & ~WebSandboxFlags::kPresentationController;
     } else if (EqualIgnoringASCIICase(
                    sandbox_token, "allow-top-navigation-by-user-activation")) {
-      flags &= ~kSandboxTopNavigationByUserActivation;
+      flags = flags & ~WebSandboxFlags::kTopNavigationByUserActivation;
     } else if (EqualIgnoringASCIICase(
                    sandbox_token, "allow-downloads-without-user-activation")) {
-      flags &= ~kSandboxDownloads;
+      flags = flags & ~WebSandboxFlags::kDownloads;
     } else {
       token_errors.Append(token_errors.IsEmpty() ? "'" : ", '");
       token_errors.Append(sandbox_token);
@@ -142,31 +144,9 @@
     SandboxFlags sandbox_flags,
     ParsedFeaturePolicy& parsed_feature_policy) {
   for (const auto& pair : SandboxFlagsWithFeaturePolicies()) {
-    if (sandbox_flags & pair.first)
+    if ((sandbox_flags & pair.first) != WebSandboxFlags::kNone)
       DisallowFeatureIfNotPresent(pair.second, parsed_feature_policy);
   }
 }
 
-STATIC_ASSERT_ENUM(WebSandboxFlags::kNone, kSandboxNone);
-STATIC_ASSERT_ENUM(WebSandboxFlags::kNavigation, kSandboxNavigation);
-STATIC_ASSERT_ENUM(WebSandboxFlags::kPlugins, kSandboxPlugins);
-STATIC_ASSERT_ENUM(WebSandboxFlags::kOrigin, kSandboxOrigin);
-STATIC_ASSERT_ENUM(WebSandboxFlags::kForms, kSandboxForms);
-STATIC_ASSERT_ENUM(WebSandboxFlags::kScripts, kSandboxScripts);
-STATIC_ASSERT_ENUM(WebSandboxFlags::kTopNavigation, kSandboxTopNavigation);
-STATIC_ASSERT_ENUM(WebSandboxFlags::kPopups, kSandboxPopups);
-STATIC_ASSERT_ENUM(WebSandboxFlags::kAutomaticFeatures,
-                   kSandboxAutomaticFeatures);
-STATIC_ASSERT_ENUM(WebSandboxFlags::kPointerLock, kSandboxPointerLock);
-STATIC_ASSERT_ENUM(WebSandboxFlags::kDocumentDomain, kSandboxDocumentDomain);
-STATIC_ASSERT_ENUM(WebSandboxFlags::kOrientationLock, kSandboxOrientationLock);
-STATIC_ASSERT_ENUM(WebSandboxFlags::kPropagatesToAuxiliaryBrowsingContexts,
-                   kSandboxPropagatesToAuxiliaryBrowsingContexts);
-STATIC_ASSERT_ENUM(WebSandboxFlags::kModals, kSandboxModals);
-STATIC_ASSERT_ENUM(WebSandboxFlags::kPresentationController,
-                   kSandboxPresentationController);
-STATIC_ASSERT_ENUM(WebSandboxFlags::kTopNavigationByUserActivation,
-                   kSandboxTopNavigationByUserActivation);
-STATIC_ASSERT_ENUM(WebSandboxFlags::kDownloads, kSandboxDownloads);
-
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/frame/sandbox_flags.h b/third_party/blink/renderer/core/frame/sandbox_flags.h
index 59bfc85d..b8689f0 100644
--- a/third_party/blink/renderer/core/frame/sandbox_flags.h
+++ b/third_party/blink/renderer/core/frame/sandbox_flags.h
@@ -30,42 +30,15 @@
 #include <vector>
 
 #include "third_party/blink/public/common/feature_policy/feature_policy.h"
+#include "third_party/blink/public/common/frame/sandbox_flags.h"
 #include "third_party/blink/renderer/core/dom/space_split_string.h"
 #include "third_party/blink/renderer/platform/wtf/forward.h"
 
 namespace blink {
 
-enum SandboxFlag {
-  // See http://www.whatwg.org/specs/web-apps/current-work/#attr-iframe-sandbox
-  // for a list of the sandbox flags.
-  kSandboxNone = 0,
-  kSandboxNavigation = 1,
-  kSandboxPlugins = 1 << 1,
-  kSandboxOrigin = 1 << 2,
-  kSandboxForms = 1 << 3,
-  kSandboxScripts = 1 << 4,
-  kSandboxTopNavigation = 1 << 5,
-  // See https://www.w3.org/Bugs/Public/show_bug.cgi?id=12393
-  kSandboxPopups = 1 << 6,
-  kSandboxAutomaticFeatures = 1 << 7,
-  kSandboxPointerLock = 1 << 8,
-  kSandboxDocumentDomain = 1 << 9,
-  // See
-  // https://w3c.github.io/screen-orientation/#dfn-sandboxed-orientation-lock-browsing-context-flag.
-  kSandboxOrientationLock = 1 << 10,
-  kSandboxPropagatesToAuxiliaryBrowsingContexts = 1 << 11,
-  kSandboxModals = 1 << 12,
-  // See
-  // https://w3c.github.io/presentation-api/#sandboxing-and-the-allow-presentation-keyword
-  kSandboxPresentationController = 1 << 13,
-  // See https://github.com/WICG/interventions/issues/42.
-  kSandboxTopNavigationByUserActivation = 1 << 14,
-  // See https://crbug.com/539938
-  kSandboxDownloads = 1 << 15,
-  kSandboxAll = -1  // Mask with all bits set to 1.
-};
-
-typedef int SandboxFlags;
+// TODO(ekaramad): Get rid of these.
+using SandboxFlag = WebSandboxFlags;
+using SandboxFlags = WebSandboxFlags;
 
 SandboxFlags ParseSandboxPolicy(const SpaceSplitString& policy,
                                 String& invalid_tokens_error_message);
diff --git a/third_party/blink/renderer/core/html/forms/html_form_control_element.cc b/third_party/blink/renderer/core/html/forms/html_form_control_element.cc
index 217c7dc..88268ef 100644
--- a/third_party/blink/renderer/core/html/forms/html_form_control_element.cc
+++ b/third_party/blink/renderer/core/html/forms/html_form_control_element.cc
@@ -216,7 +216,7 @@
 static bool ShouldAutofocusOnAttach(const HTMLFormControlElement* element) {
   if (!element->IsAutofocusable())
     return false;
-  if (element->GetDocument().IsSandboxed(kSandboxAutomaticFeatures)) {
+  if (element->GetDocument().IsSandboxed(WebSandboxFlags::kAutomaticFeatures)) {
     // FIXME: This message should be moved off the console once a solution to
     // https://bugs.webkit.org/show_bug.cgi?id=103274 exists.
     element->GetDocument().AddConsoleMessage(ConsoleMessage::Create(
diff --git a/third_party/blink/renderer/core/html/forms/html_form_element.cc b/third_party/blink/renderer/core/html/forms/html_form_element.cc
index 1497714a..4f8ad23 100644
--- a/third_party/blink/renderer/core/html/forms/html_form_element.cc
+++ b/third_party/blink/renderer/core/html/forms/html_form_element.cc
@@ -275,7 +275,7 @@
     return;
   }
 
-  if (GetDocument().IsSandboxed(kSandboxForms)) {
+  if (GetDocument().IsSandboxed(WebSandboxFlags::kForms)) {
     GetDocument().AddConsoleMessage(ConsoleMessage::Create(
         mojom::ConsoleMessageSource::kSecurity,
         mojom::ConsoleMessageLevel::kError,
@@ -465,7 +465,7 @@
   DCHECK(submission->Form());
   if (submission->Action().IsEmpty())
     return;
-  if (GetDocument().IsSandboxed(kSandboxForms)) {
+  if (GetDocument().IsSandboxed(WebSandboxFlags::kForms)) {
     // FIXME: This message should be moved off the console once a solution to
     // https://bugs.webkit.org/show_bug.cgi?id=103274 exists.
     GetDocument().AddConsoleMessage(ConsoleMessage::Create(
diff --git a/third_party/blink/renderer/core/html/html_anchor_element.cc b/third_party/blink/renderer/core/html/html_anchor_element.cc
index d8f2d8f..2db03f4 100644
--- a/third_party/blink/renderer/core/html/html_anchor_element.cc
+++ b/third_party/blink/renderer/core/html/html_anchor_element.cc
@@ -406,7 +406,7 @@
                   kBlockingDownloadsInAdFrameWithoutUserActivation))
         return;
     }
-    if (GetDocument().IsSandboxed(kSandboxDownloads)) {
+    if (GetDocument().IsSandboxed(WebSandboxFlags::kDownloads)) {
       if (!has_gesture) {
         UseCounter::Count(GetDocument(),
                           WebFeature::kDownloadInSandboxWithoutUserGesture);
diff --git a/third_party/blink/renderer/core/html/html_frame_element_base.cc b/third_party/blink/renderer/core/html/html_frame_element_base.cc
index 9124cd0a4..bcd8df0 100644
--- a/third_party/blink/renderer/core/html/html_frame_element_base.cc
+++ b/third_party/blink/renderer/core/html/html_frame_element_base.cc
@@ -138,7 +138,7 @@
 scoped_refptr<const SecurityOrigin>
 HTMLFrameElementBase::GetOriginForFeaturePolicy() const {
   // Sandboxed frames have a unique origin.
-  if (GetSandboxFlags() & kSandboxOrigin)
+  if ((GetSandboxFlags() & WebSandboxFlags::kOrigin) != WebSandboxFlags::kNone)
     return SecurityOrigin::CreateUniqueOpaque();
 
   // If the frame will inherit its origin from the owner, then use the owner's
diff --git a/third_party/blink/renderer/core/html/html_frame_owner_element.cc b/third_party/blink/renderer/core/html/html_frame_owner_element.cc
index 682b5a5f2..64eeea9 100644
--- a/third_party/blink/renderer/core/html/html_frame_owner_element.cc
+++ b/third_party/blink/renderer/core/html/html_frame_owner_element.cc
@@ -145,7 +145,7 @@
     : HTMLElement(tag_name, document),
       content_frame_(nullptr),
       embedded_content_view_(nullptr),
-      sandbox_flags_(kSandboxNone),
+      sandbox_flags_(WebSandboxFlags::kNone),
       should_lazy_load_children_(DoesParentAllowLazyLoadingChildren(document)) {
 }
 
diff --git a/third_party/blink/renderer/core/html/html_iframe_element.cc b/third_party/blink/renderer/core/html/html_iframe_element.cc
index 06a85f3c..517043c 100644
--- a/third_party/blink/renderer/core/html/html_iframe_element.cc
+++ b/third_party/blink/renderer/core/html/html_iframe_element.cc
@@ -151,15 +151,15 @@
         RuntimeEnabledFeatures::FeaturePolicyForSandboxEnabled();
     SandboxFlags current_flags =
         value.IsNull()
-            ? kSandboxNone
+            ? WebSandboxFlags::kNone
             : ParseSandboxPolicy(sandbox_->TokenSet(), invalid_tokens);
     // With FeaturePolicyForSandbox, sandbox flags are represented as part of
     // the container policies. However, not all sandbox flags are yet converted
     // and for now the residue will stay around in the stored flags.
     // (see https://crbug.com/812381).
     SandboxFlags sandbox_to_set = current_flags;
-    sandbox_flags_converted_to_feature_policies_ = kSandboxNone;
-    if (feature_policy_for_sandbox && current_flags != kSandboxNone) {
+    sandbox_flags_converted_to_feature_policies_ = WebSandboxFlags::kNone;
+    if (feature_policy_for_sandbox && current_flags != WebSandboxFlags::kNone) {
       // The part of sandbox which will be mapped to feature policies.
       sandbox_flags_converted_to_feature_policies_ = current_flags;
       // Residue sandbox which will not be mapped to feature policies.
@@ -288,9 +288,10 @@
     // If the frame is sandboxed at all, then warn if feature policy attributes
     // will override the sandbox attributes.
     // TODO(ekaramad): Add similar messages for all the converted sandbox flags.
-    if (messages &&
-        (sandbox_flags_converted_to_feature_policies_ & kSandboxNavigation)) {
-      if (!(sandbox_flags_converted_to_feature_policies_ & kSandboxForms) &&
+    if (messages && (sandbox_flags_converted_to_feature_policies_ &
+                     WebSandboxFlags::kNavigation) != WebSandboxFlags::kNone) {
+      if ((sandbox_flags_converted_to_feature_policies_ &
+           WebSandboxFlags::kForms) == WebSandboxFlags::kNone &&
           IsFeatureDeclared(mojom::FeaturePolicyFeature::kFormSubmission,
                             container_policy)) {
         messages->push_back(
diff --git a/third_party/blink/renderer/core/html/html_iframe_element.h b/third_party/blink/renderer/core/html/html_iframe_element.h
index f0b7471..caede62 100644
--- a/third_party/blink/renderer/core/html/html_iframe_element.h
+++ b/third_party/blink/renderer/core/html/html_iframe_element.h
@@ -97,7 +97,8 @@
   // This represents a subset of sandbox flags set through 'sandbox' attribute
   // that will be converted to feature policies as part of the container
   // policies.
-  SandboxFlags sandbox_flags_converted_to_feature_policies_ = kSandboxNone;
+  SandboxFlags sandbox_flags_converted_to_feature_policies_ =
+      WebSandboxFlags::kNone;
 
   network::mojom::ReferrerPolicy referrer_policy_;
 };
diff --git a/third_party/blink/renderer/core/html/html_plugin_element.cc b/third_party/blink/renderer/core/html/html_plugin_element.cc
index e87d363..140ee22 100644
--- a/third_party/blink/renderer/core/html/html_plugin_element.cc
+++ b/third_party/blink/renderer/core/html/html_plugin_element.cc
@@ -713,7 +713,7 @@
 
 bool HTMLPlugInElement::AllowedToLoadPlugin(const KURL& url,
                                             const String& mime_type) {
-  if (GetDocument().IsSandboxed(kSandboxPlugins)) {
+  if (GetDocument().IsSandboxed(WebSandboxFlags::kPlugins)) {
     GetDocument().AddConsoleMessage(
         ConsoleMessage::Create(mojom::ConsoleMessageSource::kSecurity,
                                mojom::ConsoleMessageLevel::kError,
diff --git a/third_party/blink/renderer/core/html/media/autoplay_policy.cc b/third_party/blink/renderer/core/html/media/autoplay_policy.cc
index 77733386..d4545d6 100644
--- a/third_party/blink/renderer/core/html/media/autoplay_policy.cc
+++ b/third_party/blink/renderer/core/html/media/autoplay_policy.cc
@@ -399,7 +399,7 @@
 }
 
 bool AutoplayPolicy::ShouldAutoplay() {
-  if (element_->GetDocument().IsSandboxed(kSandboxAutomaticFeatures))
+  if (element_->GetDocument().IsSandboxed(WebSandboxFlags::kAutomaticFeatures))
     return false;
   return element_->can_autoplay_ && element_->paused_ && element_->Autoplay();
 }
diff --git a/third_party/blink/renderer/core/inspector/inspector_css_agent.cc b/third_party/blink/renderer/core/inspector/inspector_css_agent.cc
index 0f3a2bc7..71c1b496 100644
--- a/third_party/blink/renderer/core/inspector/inspector_css_agent.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_css_agent.cc
@@ -1958,7 +1958,8 @@
     Document* owner_document) {
   DCHECK(page_style_sheet);
 
-  if (!page_style_sheet->ownerNode() && page_style_sheet->href().IsEmpty())
+  if (!page_style_sheet->ownerNode() && page_style_sheet->href().IsEmpty() &&
+      !page_style_sheet->IsConstructed())
     return protocol::CSS::StyleSheetOriginEnum::UserAgent;
 
   if (page_style_sheet->ownerNode() &&
diff --git a/third_party/blink/renderer/core/inspector/inspector_network_agent.cc b/third_party/blink/renderer/core/inspector/inspector_network_agent.cc
index 193d19b..9ff24c3 100644
--- a/third_party/blink/renderer/core/inspector/inspector_network_agent.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_network_agent.cc
@@ -509,7 +509,7 @@
 
 static std::unique_ptr<protocol::Network::Response>
 BuildObjectForResourceResponse(const ResourceResponse& response,
-                               Resource* cached_resource = nullptr,
+                               const Resource* cached_resource = nullptr,
                                bool* is_empty = nullptr) {
   if (response.IsNull())
     return nullptr;
@@ -920,7 +920,7 @@
     uint64_t identifier,
     DocumentLoader* loader,
     const ResourceResponse& response,
-    Resource* cached_resource) {
+    const Resource* cached_resource) {
   String request_id = IdentifiersFactory::RequestId(loader, identifier);
   bool is_not_modified = response.HttpStatusCode() == 304;
 
diff --git a/third_party/blink/renderer/core/inspector/inspector_network_agent.h b/third_party/blink/renderer/core/inspector/inspector_network_agent.h
index b6614057..71244f7 100644
--- a/third_party/blink/renderer/core/inspector/inspector_network_agent.h
+++ b/third_party/blink/renderer/core/inspector/inspector_network_agent.h
@@ -114,7 +114,7 @@
   void DidReceiveResourceResponse(uint64_t identifier,
                                   DocumentLoader*,
                                   const ResourceResponse&,
-                                  Resource*);
+                                  const Resource*);
   void DidReceiveData(uint64_t identifier,
                       DocumentLoader*,
                       const char* data,
diff --git a/third_party/blink/renderer/core/inspector/inspector_page_agent.cc b/third_party/blink/renderer/core/inspector/inspector_page_agent.cc
index fd0800fe..7fa1292 100644
--- a/third_party/blink/renderer/core/inspector/inspector_page_agent.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_page_agent.cc
@@ -178,7 +178,7 @@
 
 }  // namespace
 
-static bool PrepareResourceBuffer(Resource* cached_resource,
+static bool PrepareResourceBuffer(const Resource* cached_resource,
                                   bool* has_zero_size) {
   if (!cached_resource)
     return false;
@@ -197,7 +197,7 @@
   return true;
 }
 
-static bool HasTextContent(Resource* cached_resource) {
+static bool HasTextContent(const Resource* cached_resource) {
   ResourceType type = cached_resource->GetType();
   return type == ResourceType::kCSSStyleSheet ||
          type == ResourceType::kXSLStyleSheet ||
@@ -310,7 +310,7 @@
 }
 
 // static
-bool InspectorPageAgent::CachedResourceContent(Resource* cached_resource,
+bool InspectorPageAgent::CachedResourceContent(const Resource* cached_resource,
                                                String* result,
                                                bool* base64_encoded) {
   bool has_zero_size;
diff --git a/third_party/blink/renderer/core/inspector/inspector_page_agent.h b/third_party/blink/renderer/core/inspector/inspector_page_agent.h
index 79d30c96..d7dfd32 100644
--- a/third_party/blink/renderer/core/inspector/inspector_page_agent.h
+++ b/third_party/blink/renderer/core/inspector/inspector_page_agent.h
@@ -89,7 +89,7 @@
   };
 
   static HeapVector<Member<Document>> ImportsForFrame(LocalFrame*);
-  static bool CachedResourceContent(Resource*,
+  static bool CachedResourceContent(const Resource*,
                                     String* result,
                                     bool* base64_encoded);
   static bool SharedBufferContent(scoped_refptr<const SharedBuffer>,
diff --git a/third_party/blink/renderer/core/inspector/inspector_trace_events.cc b/third_party/blink/renderer/core/inspector/inspector_trace_events.cc
index 162e924..16bb86e 100644
--- a/third_party/blink/renderer/core/inspector/inspector_trace_events.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_trace_events.cc
@@ -133,7 +133,7 @@
     uint64_t identifier,
     DocumentLoader* loader,
     const ResourceResponse& response,
-    Resource*) {
+    const Resource*) {
   LocalFrame* frame = loader ? loader->GetFrame() : nullptr;
   TRACE_EVENT_INSTANT1("devtools.timeline", "ResourceReceiveResponse",
                        TRACE_EVENT_SCOPE_THREAD, "data",
diff --git a/third_party/blink/renderer/core/inspector/inspector_trace_events.h b/third_party/blink/renderer/core/inspector/inspector_trace_events.h
index 566e160..978e2af1 100644
--- a/third_party/blink/renderer/core/inspector/inspector_trace_events.h
+++ b/third_party/blink/renderer/core/inspector/inspector_trace_events.h
@@ -98,7 +98,7 @@
   void DidReceiveResourceResponse(uint64_t identifier,
                                   DocumentLoader*,
                                   const ResourceResponse&,
-                                  Resource*);
+                                  const Resource*);
   void DidReceiveData(uint64_t identifier,
                       DocumentLoader*,
                       const char* data,
diff --git a/third_party/blink/renderer/core/inspector/network_resources_data.cc b/third_party/blink/renderer/core/inspector/network_resources_data.cc
index 93c79f1..14a3a4cc95 100644
--- a/third_party/blink/renderer/core/inspector/network_resources_data.cc
+++ b/third_party/blink/renderer/core/inspector/network_resources_data.cc
@@ -132,7 +132,7 @@
 }
 
 void NetworkResourcesData::ResourceData::SetResource(
-    Resource* cached_resource) {
+    const Resource* cached_resource) {
   cached_resource_ = cached_resource;
 }
 
@@ -329,7 +329,7 @@
 }
 
 void NetworkResourcesData::AddResource(const String& request_id,
-                                       Resource* cached_resource) {
+                                       const Resource* cached_resource) {
   ResourceData* resource_data = ResourceDataForRequestId(request_id);
   if (!resource_data)
     return;
diff --git a/third_party/blink/renderer/core/inspector/network_resources_data.h b/third_party/blink/renderer/core/inspector/network_resources_data.h
index 7c6e373..a533843 100644
--- a/third_party/blink/renderer/core/inspector/network_resources_data.h
+++ b/third_party/blink/renderer/core/inspector/network_resources_data.h
@@ -140,8 +140,8 @@
       buffer_ = std::move(buffer);
     }
 
-    Resource* CachedResource() const { return cached_resource_.Get(); }
-    void SetResource(Resource*);
+    const Resource* CachedResource() const { return cached_resource_.Get(); }
+    void SetResource(const Resource*);
 
     XHRReplayData* XhrReplayData() const { return xhr_replay_data_.Get(); }
     void SetXHRReplayData(XHRReplayData* xhr_replay_data) {
@@ -201,7 +201,7 @@
     int64_t pending_encoded_data_length_;
 
     scoped_refptr<SharedBuffer> buffer_;
-    WeakMember<Resource> cached_resource_;
+    WeakMember<const Resource> cached_resource_;
     scoped_refptr<BlobDataHandle> downloaded_file_blob_;
     Vector<AtomicString> certificate_;
     scoped_refptr<EncodedFormData> post_data_;
@@ -234,7 +234,7 @@
                             const char* data,
                             uint64_t data_length);
   void MaybeDecodeDataToContent(const String& request_id);
-  void AddResource(const String& request_id, Resource*);
+  void AddResource(const String& request_id, const Resource*);
   ResourceData const* Data(const String& request_id);
   void Clear(const String& preserved_loader_id = String());
 
diff --git a/third_party/blink/renderer/core/layout/flexible_box_algorithm.cc b/third_party/blink/renderer/core/layout/flexible_box_algorithm.cc
index 427cece..e532266 100644
--- a/third_party/blink/renderer/core/layout/flexible_box_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/flexible_box_algorithm.cc
@@ -489,7 +489,7 @@
   // when percentages are involved, so for now don't apply min-height: auto
   // in such cases.
   if (IsColumnFlow() && child.IsFlexibleBox() &&
-      ToLayoutBlock(child).HasPercentHeightDescendants())
+      To<LayoutBlock>(child).HasPercentHeightDescendants())
     return false;
 
   return !child.ShouldApplySizeContainment() &&
diff --git a/third_party/blink/renderer/core/layout/layout_block.cc b/third_party/blink/renderer/core/layout/layout_block.cc
index 66ebd6d..02f9a1d7 100644
--- a/third_party/blink/renderer/core/layout/layout_block.cc
+++ b/third_party/blink/renderer/core/layout/layout_block.cc
@@ -875,7 +875,7 @@
         relayout_children || height_available_to_children_changed_;
     if (!update_child_needs_layout) {
       if (!positioned_object->IsLayoutNGObject() ||
-          ToLayoutBlock(positioned_object)
+          To<LayoutBlock>(positioned_object)
               ->IsLegacyInitiatedOutOfFlowLayout()) {
         update_child_needs_layout |=
             NeedsLayoutDueToStaticPosition(positioned_object);
@@ -1928,10 +1928,10 @@
         first_line_block->IsFloatingOrOutOfFlowPositioned() || !parent_block ||
         !parent_block->BehavesLikeBlockContainer())
       break;
-    SECURITY_DCHECK(parent_block->IsLayoutBlock());
-    if (ToLayoutBlock(parent_block)->FirstChild() != first_line_block)
+    auto* parent_layout_block = DynamicTo<LayoutBlock>(parent_block);
+    if (parent_layout_block->FirstChild() != first_line_block)
       break;
-    first_line_block = ToLayoutBlock(parent_block);
+    first_line_block = parent_layout_block;
   }
 
   if (!has_pseudo)
@@ -1946,7 +1946,7 @@
   for (LayoutObject* child = FirstChild();
        child && !child->IsFloatingOrOutOfFlowPositioned() &&
        child->IsLayoutBlockFlow();
-       child = ToLayoutBlock(child)->FirstChild()) {
+       child = To<LayoutBlock>(child)->FirstChild()) {
     if (child->ChildrenInline())
       return ToLayoutBlockFlow(child);
   }
@@ -2058,16 +2058,17 @@
 bool LayoutBlock::HasMarginBeforeQuirk(const LayoutBox* child) const {
   // If the child has the same directionality as we do, then we can just return
   // its margin quirk.
+  auto* child_layout_block = DynamicTo<LayoutBlock>(child);
   if (!child->IsWritingModeRoot()) {
-    return child->IsLayoutBlock() ? ToLayoutBlock(child)->HasMarginBeforeQuirk()
-                                  : child->StyleRef().HasMarginBeforeQuirk();
+    return child_layout_block ? child_layout_block->HasMarginBeforeQuirk()
+                              : child->StyleRef().HasMarginBeforeQuirk();
   }
 
   // The child has a different directionality. If the child is parallel, then
   // it's just flipped relative to us. We can use the opposite edge.
   if (child->IsHorizontalWritingMode() == IsHorizontalWritingMode()) {
-    return child->IsLayoutBlock() ? ToLayoutBlock(child)->HasMarginAfterQuirk()
-                                  : child->StyleRef().HasMarginAfterQuirk();
+    return child_layout_block ? child_layout_block->HasMarginAfterQuirk()
+                              : child->StyleRef().HasMarginAfterQuirk();
   }
 
   // The child is perpendicular to us and box sides are never quirky in
@@ -2079,16 +2080,17 @@
 bool LayoutBlock::HasMarginAfterQuirk(const LayoutBox* child) const {
   // If the child has the same directionality as we do, then we can just return
   // its margin quirk.
+  auto* child_layout_block = DynamicTo<LayoutBlock>(child);
   if (!child->IsWritingModeRoot()) {
-    return child->IsLayoutBlock() ? ToLayoutBlock(child)->HasMarginAfterQuirk()
-                                  : child->StyleRef().HasMarginAfterQuirk();
+    return child_layout_block ? child_layout_block->HasMarginAfterQuirk()
+                              : child->StyleRef().HasMarginAfterQuirk();
   }
 
   // The child has a different directionality. If the child is parallel, then
   // it's just flipped relative to us. We can use the opposite edge.
   if (child->IsHorizontalWritingMode() == IsHorizontalWritingMode()) {
-    return child->IsLayoutBlock() ? ToLayoutBlock(child)->HasMarginBeforeQuirk()
-                                  : child->StyleRef().HasMarginBeforeQuirk();
+    return child_layout_block ? child_layout_block->HasMarginBeforeQuirk()
+                              : child->StyleRef().HasMarginBeforeQuirk();
   }
 
   // The child is perpendicular to us and box sides are never quirky in
diff --git a/third_party/blink/renderer/core/layout/layout_block.h b/third_party/blink/renderer/core/layout/layout_block.h
index aa9a33e..1658619 100644
--- a/third_party/blink/renderer/core/layout/layout_block.h
+++ b/third_party/blink/renderer/core/layout/layout_block.h
@@ -27,6 +27,7 @@
 #include <memory>
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/layout/layout_box.h"
+#include "third_party/blink/renderer/platform/wtf/casting.h"
 #include "third_party/blink/renderer/platform/wtf/list_hash_set.h"
 
 namespace blink {
@@ -595,6 +596,13 @@
 
 DEFINE_LAYOUT_OBJECT_TYPE_CASTS(LayoutBlock, IsLayoutBlock());
 
+template <>
+struct DowncastTraits<LayoutBlock> {
+  static bool AllowFrom(const LayoutObject& object) {
+    return object.IsLayoutBlock();
+  }
+};
+
 }  // namespace blink
 
 #endif  // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_LAYOUT_BLOCK_H_
diff --git a/third_party/blink/renderer/core/layout/layout_block_flow.cc b/third_party/blink/renderer/core/layout/layout_block_flow.cc
index cba8982..f0298d4 100644
--- a/third_party/blink/renderer/core/layout/layout_block_flow.cc
+++ b/third_party/blink/renderer/core/layout/layout_block_flow.cc
@@ -1394,8 +1394,9 @@
   LayoutBlockFlow* parent_block_flow = ToLayoutBlockFlow(Parent());
   bool sibling_float_may_intrude = false;
   LayoutObject* prev = PreviousSibling();
-  while (prev && (!prev->IsBox() || !prev->IsLayoutBlock() ||
-                  ToLayoutBlock(prev)->CreatesNewFormattingContext())) {
+  auto* prev_layout_block = DynamicTo<LayoutBlock>(prev);
+  while (prev && (!prev->IsBox() || !prev_layout_block ||
+                  prev_layout_block->CreatesNewFormattingContext())) {
     if (prev->IsFloating())
       sibling_float_may_intrude = true;
     prev = prev->PreviousSibling();
@@ -1409,7 +1410,7 @@
   bool parent_floats_may_intrude =
       !sibling_float_may_intrude &&
       (!prev || ToLayoutBlockFlow(prev)->IsSelfCollapsingBlock() ||
-       ToLayoutBlock(prev)->LogicalTop() > LogicalTop()) &&
+       prev_layout_block->LogicalTop() > LogicalTop()) &&
       parent_block_flow->LowestFloatLogicalBottom() > LogicalTop();
   if (sibling_float_may_intrude || parent_floats_may_intrude)
     AddIntrudingFloats(parent_block_flow,
@@ -2165,8 +2166,8 @@
   // we're looking at current values.
   if (grandchild_box->NeedsLayout()) {
     grandchild_box->ComputeAndSetBlockDirectionMargins(this);
-    if (grandchild_box->IsLayoutBlock()) {
-      LayoutBlock* grandchild_block = ToLayoutBlock(grandchild_box);
+    auto* grandchild_block = DynamicTo<LayoutBlock>(grandchild_box);
+    if (grandchild_block) {
       grandchild_block->SetHasMarginBeforeQuirk(
           grandchild_box->StyleRef().HasMarginBeforeQuirk());
       grandchild_block->SetHasMarginAfterQuirk(
@@ -3194,10 +3195,9 @@
   // insertion in a way that isn't sufficient for us, and can only cause trouble
   // at this point.
   LayoutBox::AddChild(new_child, before_child);
-
-  if (made_boxes_non_inline && Parent() && IsAnonymousBlock() &&
-      Parent()->IsLayoutBlock()) {
-    ToLayoutBlock(Parent())->RemoveLeftoverAnonymousBlock(this);
+  auto* parent_layout_block = DynamicTo<LayoutBlock>(Parent());
+  if (made_boxes_non_inline && IsAnonymousBlock() && parent_layout_block) {
+    parent_layout_block->RemoveLeftoverAnonymousBlock(this);
     // |this| may be dead now.
   }
 }
@@ -3611,8 +3611,9 @@
 
 void LayoutBlockFlow::ChildBecameNonInline(LayoutObject*) {
   MakeChildrenNonInline();
-  if (IsAnonymousBlock() && Parent() && Parent()->IsLayoutBlock())
-    ToLayoutBlock(Parent())->RemoveLeftoverAnonymousBlock(this);
+  auto* parent_layout_block = DynamicTo<LayoutBlock>(Parent());
+  if (IsAnonymousBlock() && parent_layout_block)
+    parent_layout_block->RemoveLeftoverAnonymousBlock(this);
   // |this| may be dead here
 }
 
@@ -4611,9 +4612,10 @@
       children_layout_overflow_changed = true;
       // TODO(chrishtr): should this be IsBox()? Non-blocks can be
       // inline and have line box wrappers.
-      if (layout_object->IsLayoutBlock()) {
+      auto* layout_block_object = DynamicTo<LayoutBlock>(layout_object);
+      if (layout_block_object) {
         if (InlineBox* inline_box_wrapper =
-                ToLayoutBlock(layout_object)->InlineBoxWrapper())
+                layout_block_object->InlineBoxWrapper())
           line_boxes.insert(&inline_box_wrapper->Root());
       }
     }
diff --git a/third_party/blink/renderer/core/layout/layout_box.cc b/third_party/blink/renderer/core/layout/layout_box.cc
index 99e86f50..525e0cf 100644
--- a/third_party/blink/renderer/core/layout/layout_box.cc
+++ b/third_party/blink/renderer/core/layout/layout_box.cc
@@ -3887,8 +3887,8 @@
       bool has_perpendicular_containing_block =
           cb->IsHorizontalWritingMode() != IsHorizontalWritingMode();
       LayoutUnit stretched_height(-1);
-      if (cb->IsLayoutBlock()) {
-        LayoutBlock* block = ToLayoutBlock(cb);
+      auto* block = DynamicTo<LayoutBlock>(cb);
+      if (block) {
         block->AddPercentHeightDescendant(const_cast<LayoutBox*>(this));
         if (block->IsFlexItem()) {
           const LayoutFlexibleBox* flex_box =
@@ -3933,7 +3933,7 @@
                 logical_height,
                 available_height - BorderAndPaddingLogicalHeight());
           }
-          ToLayoutBlock(cb)->AddPercentHeightDescendant(
+          To<LayoutBlock>(cb)->AddPercentHeightDescendant(
               const_cast<LayoutBox*>(this));
           cb = cb->ContainingBlock();
         }
@@ -4026,10 +4026,11 @@
   // FIXME: Check logicalTop/logicalBottom here to correctly handle vertical
   // writing-mode.
   // https://bugs.webkit.org/show_bug.cgi?id=46500
-  if (IsLayoutBlock() && IsOutOfFlowPositioned() &&
+  auto* curr_layout_block = DynamicTo<LayoutBlock>(this);
+  if (curr_layout_block && IsOutOfFlowPositioned() &&
       StyleRef().Height().IsAuto() &&
       !(StyleRef().Top().IsAuto() || StyleRef().Bottom().IsAuto())) {
-    LayoutBlock* block = const_cast<LayoutBlock*>(ToLayoutBlock(this));
+    LayoutBlock* block = const_cast<LayoutBlock*>(curr_layout_block);
     LogicalExtentComputedValues computed_values;
     block->ComputeLogicalHeight(block->LogicalHeight(), LayoutUnit(),
                                 computed_values);
diff --git a/third_party/blink/renderer/core/layout/layout_box_model_object.cc b/third_party/blink/renderer/core/layout/layout_box_model_object.cc
index be5c65d..c73402e 100644
--- a/third_party/blink/renderer/core/layout/layout_box_model_object.cc
+++ b/third_party/blink/renderer/core/layout/layout_box_model_object.cc
@@ -1375,7 +1375,7 @@
   // moves (!fullRemoveInsert) so the positioned layoutObject maps don't become
   // stale. It would be too slow to do the map lookup on each call.
   DCHECK(!full_remove_insert || !IsLayoutBlock() ||
-         !ToLayoutBlock(this)->HasPositionedObjects());
+         !To<LayoutBlock>(this)->HasPositionedObjects());
 
   DCHECK_EQ(this, child->Parent());
   DCHECK(!before_child || to_box_model_object == before_child->Parent());
@@ -1418,8 +1418,8 @@
   // This condition is rarely hit since this function is usually called on
   // anonymous blocks which can no longer carry positioned objects (see r120761)
   // or when fullRemoveInsert is false.
-  if (full_remove_insert && IsLayoutBlock()) {
-    LayoutBlock* block = ToLayoutBlock(this);
+  auto* block = DynamicTo<LayoutBlock>(this);
+  if (full_remove_insert && block) {
     block->RemovePositionedObjects(nullptr);
     block->RemoveFromPercentHeightContainer();
     if (block->IsLayoutBlockFlow())
diff --git a/third_party/blink/renderer/core/layout/layout_fieldset.cc b/third_party/blink/renderer/core/layout/layout_fieldset.cc
index f0c42eb..91dba64 100644
--- a/third_party/blink/renderer/core/layout/layout_fieldset.cc
+++ b/third_party/blink/renderer/core/layout/layout_fieldset.cc
@@ -146,7 +146,7 @@
     if (fieldset.IsLayoutNGFieldset()) {
       // If there is a rendered legend, it will be found inside the anonymous
       // fieldset wrapper.
-      parent = ToLayoutBlock(fieldset.FirstChild());
+      parent = To<LayoutBlock>(fieldset.FirstChild());
       if (!parent)
         return nullptr;
     }
diff --git a/third_party/blink/renderer/core/layout/layout_flexible_box.cc b/third_party/blink/renderer/core/layout/layout_flexible_box.cc
index 40674ea..d7ead52f 100644
--- a/third_party/blink/renderer/core/layout/layout_flexible_box.cc
+++ b/third_party/blink/renderer/core/layout/layout_flexible_box.cc
@@ -1434,8 +1434,9 @@
         relayout_children && !relaid_out_children_.Contains(child);
     // TODO(dgrogan): Broaden the NG part of this check once NG types other
     // than Mixin derivatives are cached.
-    if (child->IsLayoutBlock() &&
-        ToLayoutBlock(*child).HasPercentHeightDescendants() &&
+    auto* child_layout_block = DynamicTo<LayoutBlock>(child);
+    if (child_layout_block &&
+        child_layout_block->HasPercentHeightDescendants() &&
         !CanAvoidLayoutForNGChild(*child)) {
       // Have to force another relayout even though the child is sized
       // correctly, because its descendants are not sized correctly yet. Our
@@ -1637,8 +1638,8 @@
     // https://webkit.org/b/87905.
     bool child_needs_relayout =
         flex_item.cross_axis_size != child.LogicalHeight();
-    if (child.IsLayoutBlock() &&
-        ToLayoutBlock(child).HasPercentHeightDescendants() &&
+    auto* child_block = DynamicTo<LayoutBlock>(child);
+    if (child_block && child_block->HasPercentHeightDescendants() &&
         !CanAvoidLayoutForNGChild(child)) {
       // Have to force another relayout even though the child is sized
       // correctly, because its descendants are not sized correctly yet. Our
diff --git a/third_party/blink/renderer/core/layout/layout_list_item.cc b/third_party/blink/renderer/core/layout/layout_list_item.cc
index 68020a82..4835bc8 100644
--- a/third_party/blink/renderer/core/layout/layout_list_item.cc
+++ b/third_party/blink/renderer/core/layout/layout_list_item.cc
@@ -522,10 +522,11 @@
     bool found_self_painting_layer = false;
     do {
       object = object->ParentBox();
-      if (object->IsLayoutBlock()) {
+      auto* layout_block_object = DynamicTo<LayoutBlock>(object);
+      if (layout_block_object) {
         if (!found_self_painting_layer)
-          ToLayoutBlock(object)->AddContentsVisualOverflow(marker_rect);
-        ToLayoutBlock(object)->AddLayoutOverflow(marker_rect);
+          layout_block_object->AddContentsVisualOverflow(marker_rect);
+        layout_block_object->AddLayoutOverflow(marker_rect);
       }
 
       if (object->HasOverflowClip())
diff --git a/third_party/blink/renderer/core/layout/layout_multi_column_flow_thread.cc b/third_party/blink/renderer/core/layout/layout_multi_column_flow_thread.cc
index 31e226b..7049960a 100644
--- a/third_party/blink/renderer/core/layout/layout_multi_column_flow_thread.cc
+++ b/third_party/blink/renderer/core/layout/layout_multi_column_flow_thread.cc
@@ -1343,15 +1343,14 @@
                        ColumnGap(*multicol_style, LayoutUnit()));
 
   if (MultiColumnBlockFlow()->ShouldApplySizeContainment()) {
-    LayoutUnit size = gap_extra;
-    if (!multicol_style->HasAutoColumnWidth())
-      size += LayoutUnit(multicol_style->ColumnWidth()) * column_count;
-    max_preferred_logical_width_ = min_preferred_logical_width_ = size;
+    min_preferred_logical_width_ = max_preferred_logical_width_ = LayoutUnit();
     ClearPreferredLogicalWidthsDirty();
-    return;
+  } else {
+    // Calculate and set new min_preferred_logical_width_ and
+    // max_preferred_logical_width_.
+    LayoutFlowThread::ComputePreferredLogicalWidths();
   }
 
-  LayoutFlowThread::ComputePreferredLogicalWidths();
   LayoutUnit column_width;
   if (multicol_style->HasAutoColumnWidth()) {
     min_preferred_logical_width_ =
diff --git a/third_party/blink/renderer/core/layout/layout_object.cc b/third_party/blink/renderer/core/layout/layout_object.cc
index 12561842..b8659aed 100644
--- a/third_party/blink/renderer/core/layout/layout_object.cc
+++ b/third_party/blink/renderer/core/layout/layout_object.cc
@@ -153,10 +153,7 @@
   while (container && container->IsAnonymousBlock())
     container = container->ContainingBlock(skip_info);
 
-  if (!container || !container->IsLayoutBlock())
-    return nullptr;  // This can still happen in case of an orphaned tree
-
-  return ToLayoutBlock(container);
+  return DynamicTo<LayoutBlock>(container);
 }
 
 }  // namespace
@@ -515,9 +512,9 @@
     if (parent->IsAnonymous() && parent->Parent()->IsLayoutNGFieldset())
       parent = parent->Parent();
   }
-  return parent && parent->IsLayoutBlock() &&
-         IsHTMLFieldSetElement(parent->GetNode()) &&
-         LayoutFieldset::FindInFlowLegend(*ToLayoutBlock(parent)) == this;
+  const auto* parent_layout_block = DynamicTo<LayoutBlock>(parent);
+  return parent_layout_block && IsHTMLFieldSetElement(parent->GetNode()) &&
+         LayoutFieldset::FindInFlowLegend(*parent_layout_block) == this;
 }
 
 LayoutObject* LayoutObject::NextInPreOrderAfterChildren() const {
@@ -1070,8 +1067,9 @@
 void LayoutObject::CheckBlockPositionedObjectsNeedLayout() {
   DCHECK(!NeedsLayout());
 
-  if (IsLayoutBlock())
-    ToLayoutBlock(this)->CheckPositionedObjectsNeedLayout();
+  auto* layout_block = DynamicTo<LayoutBlock>(this);
+  if (layout_block)
+    layout_block->CheckPositionedObjectsNeedLayout();
 }
 #endif
 
@@ -1163,9 +1161,8 @@
 }
 
 const LayoutBlock* LayoutObject::InclusiveContainingBlock() const {
-  if (IsLayoutBlock())
-    return ToLayoutBlock(this);
-  return ContainingBlock();
+  auto* layout_block = DynamicTo<LayoutBlock>(this);
+  return layout_block ? layout_block : ContainingBlock();
 }
 
 LayoutBlock* LayoutObject::ContainingBlock(AncestorSkipInfo* skip_info) const {
@@ -1190,10 +1187,7 @@
     }
   }
 
-  if (!object || !object->IsLayoutBlock())
-    return nullptr;  // This can still happen in case of an orphaned tree
-
-  return ToLayoutBlock(object);
+  return DynamicTo<LayoutBlock>(object);
 }
 
 FloatRect LayoutObject::AbsoluteBoundingBoxFloatRect(
@@ -1990,7 +1984,7 @@
     } else {
       // An anonymous block must be made to wrap this inline.
       LayoutBlock* block =
-          ToLayoutBlock(object->Parent())->CreateAnonymousBlock();
+          To<LayoutBlock>(object->Parent())->CreateAnonymousBlock();
       LayoutObjectChildList* childlist = object->Parent()->VirtualChildren();
       childlist->InsertChildNode(object->Parent(), block, object);
       block->Children()->AppendChildNode(
@@ -2421,13 +2415,11 @@
   LayoutObject* object = Parent();
   Element* viewport_defining_element = GetDocument().ViewportDefiningElement();
   while (object) {
-    if (object->IsLayoutBlock()) {
-      LayoutBlock* block = ToLayoutBlock(object);
-      if (block->HasOverflowClip() ||
-          block->GetNode() == viewport_defining_element) {
-        block->SetScrollAnchorDisablingStyleChanged(true);
-        return;
-      }
+    auto* block = DynamicTo<LayoutBlock>(object);
+    if (block && (block->HasOverflowClip() ||
+                  block->GetNode() == viewport_defining_element)) {
+      block->SetScrollAnchorDisablingStyleChanged(true);
+      return;
     }
     object = object->Parent();
   }
@@ -2536,7 +2528,7 @@
   if (BehavesLikeBlockContainer() && (diff.NeedsFullPaintInvalidation() ||
                                       diff.TextDecorationOrColorChanged())) {
     if (auto* first_line_container =
-            ToLayoutBlock(this)->NearestInnerBlockWithFirstLine())
+            To<LayoutBlock>(this)->NearestInnerBlockWithFirstLine())
       first_line_container->SetShouldDoFullPaintInvalidationForFirstLine();
   }
 
@@ -3555,7 +3547,7 @@
 
   if (layout_object_for_first_line_style->BehavesLikeBlockContainer()) {
     if (const LayoutBlock* first_line_block =
-            ToLayoutBlock(layout_object_for_first_line_style)
+            To<LayoutBlock>(layout_object_for_first_line_style)
                 ->EnclosingFirstLineStyleBlock()) {
       if (type == kCached)
         return first_line_block->GetCachedPseudoStyle(kPseudoIdFirstLine,
@@ -4107,7 +4099,7 @@
   // not a block, we need to get the selection state from the containing block
   // to tell if we have any selected node children.
   LayoutBlock* block =
-      IsLayoutBlock() ? ToLayoutBlock(this) : ContainingBlock();
+      IsLayoutBlock() ? To<LayoutBlock>(this) : ContainingBlock();
   if (!block)
     return;
   if (!block->IsSelected())
diff --git a/third_party/blink/renderer/core/layout/layout_object_test.cc b/third_party/blink/renderer/core/layout/layout_object_test.cc
index 8076911..c4d21205 100644
--- a/third_party/blink/renderer/core/layout/layout_object_test.cc
+++ b/third_party/blink/renderer/core/layout/layout_object_test.cc
@@ -242,7 +242,7 @@
 
   LayoutObject* overflow_clip_object =
       GetLayoutObjectByElementId("overflow-clip-layer");
-  LayoutBlock* columns = ToLayoutBlock(GetLayoutObjectByElementId("columns"));
+  LayoutBlock* columns = To<LayoutBlock>(GetLayoutObjectByElementId("columns"));
   EXPECT_EQ(columns->Layer(), overflow_clip_object->PaintingLayer());
 }
 
diff --git a/third_party/blink/renderer/core/layout/layout_ruby_base.cc b/third_party/blink/renderer/core/layout/layout_ruby_base.cc
index c6e45b4..3ce97f4a 100644
--- a/third_party/blink/renderer/core/layout/layout_ruby_base.cc
+++ b/third_party/blink/renderer/core/layout/layout_ruby_base.cc
@@ -89,7 +89,7 @@
     LayoutObject* last_child = to_base->LastChild();
     if (last_child && last_child->IsAnonymousBlock() &&
         last_child->ChildrenInline()) {
-      to_block = ToLayoutBlock(last_child);
+      to_block = To<LayoutBlock>(last_child);
     } else {
       to_block = to_base->CreateAnonymousBlock();
       to_base->Children()->AppendChildNode(to_base, to_block);
diff --git a/third_party/blink/renderer/core/layout/layout_scrollbar.cc b/third_party/blink/renderer/core/layout/layout_scrollbar.cc
index 16c939e0..5f7d9854 100644
--- a/third_party/blink/renderer/core/layout/layout_scrollbar.cc
+++ b/third_party/blink/renderer/core/layout/layout_scrollbar.cc
@@ -190,8 +190,9 @@
         IntRect(Location(), IntSize(is_horizontal ? Width() : new_thickness,
                                     is_horizontal ? new_thickness : Height())));
     if (LayoutBox* box = GetScrollableArea()->GetLayoutBox()) {
-      if (box->IsLayoutBlock())
-        ToLayoutBlock(box)->NotifyScrollbarThicknessChanged();
+      auto* layout_block = DynamicTo<LayoutBlock>(box);
+      if (layout_block)
+        layout_block->NotifyScrollbarThicknessChanged();
       box->SetChildNeedsLayout();
       // LayoutNG may attempt to reuse line-box fragments. It will do this even
       // if the |LayoutObject::ChildNeedsLayout| is true (set above).
diff --git a/third_party/blink/renderer/core/layout/layout_table_cell_test.cc b/third_party/blink/renderer/core/layout/layout_table_cell_test.cc
index cc6f7c7..a628cb92 100644
--- a/third_party/blink/renderer/core/layout/layout_table_cell_test.cc
+++ b/third_party/blink/renderer/core/layout/layout_table_cell_test.cc
@@ -142,7 +142,7 @@
   UpdateAllLifecyclePhasesForTest();
 
   // Check that overflow was calculated on the cell.
-  auto* input_block = ToLayoutBlock(cell->GetLayoutObject());
+  auto* input_block = To<LayoutBlock>(cell->GetLayoutObject());
   LayoutRect rect = input_block->LocalVisualRect();
   EXPECT_EQ(LayoutRect(-1, -1, 24, 24), rect);
 }
diff --git a/third_party/blink/renderer/core/layout/layout_text_control.cc b/third_party/blink/renderer/core/layout/layout_text_control.cc
index 12a3c45..d30a5332 100644
--- a/third_party/blink/renderer/core/layout/layout_text_control.cc
+++ b/third_party/blink/renderer/core/layout/layout_text_control.cc
@@ -53,7 +53,7 @@
   if (!inner_editor)
     return;
   LayoutBlock* inner_editor_layout_object =
-      ToLayoutBlock(inner_editor->GetLayoutObject());
+      To<LayoutBlock>(inner_editor->GetLayoutObject());
   if (inner_editor_layout_object) {
     inner_editor->SetNeedsStyleRecalc(
         kSubtreeStyleChange,
@@ -334,7 +334,7 @@
     return LayoutUnit(-1);
 
   LayoutBlock* inner_editor_layout_object =
-      ToLayoutBlock(inner_editor->GetLayoutObject());
+      To<LayoutBlock>(inner_editor->GetLayoutObject());
   const SimpleFontData* font_data =
       inner_editor_layout_object->Style(true)->GetFont().PrimaryFont();
   DCHECK(font_data);
diff --git a/third_party/blink/renderer/core/layout/ng/layout_ng_fieldset.cc b/third_party/blink/renderer/core/layout/ng/layout_ng_fieldset.cc
index 78e976c0..98179479 100644
--- a/third_party/blink/renderer/core/layout/ng/layout_ng_fieldset.cc
+++ b/third_party/blink/renderer/core/layout/ng/layout_ng_fieldset.cc
@@ -15,7 +15,7 @@
 
 void LayoutNGFieldset::AddChild(LayoutObject* new_child,
                                 LayoutObject* before_child) {
-  LayoutBlock* fieldset_content = ToLayoutBlock(FirstChild());
+  LayoutBlock* fieldset_content = To<LayoutBlock>(FirstChild());
   if (!fieldset_content) {
     // We wrap everything inside an anonymous child, which will take care of the
     // fieldset contents. This parent will only be responsible for the fieldset
diff --git a/third_party/blink/renderer/core/layout/ng/ng_block_node.cc b/third_party/blink/renderer/core/layout/ng/ng_block_node.cc
index 75f72d4..f74eb42 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_block_node.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_block_node.cc
@@ -288,13 +288,11 @@
 }
 
 void NGBlockNode::PrepareForLayout() {
-  if (box_->IsLayoutBlock()) {
-    LayoutBlock* block = ToLayoutBlock(box_);
-    if (block->HasOverflowClip()) {
-      DCHECK(block->GetScrollableArea());
-      if (block->GetScrollableArea()->ShouldPerformScrollAnchoring())
-        block->GetScrollableArea()->GetScrollAnchor()->NotifyBeforeLayout();
-    }
+  auto* block = DynamicTo<LayoutBlock>(box_);
+  if (block && block->HasOverflowClip()) {
+    DCHECK(block->GetScrollableArea());
+    if (block->GetScrollableArea()->ShouldPerformScrollAnchoring())
+      block->GetScrollableArea()->GetScrollAnchor()->NotifyBeforeLayout();
   }
 
   // TODO(layoutng) Can UpdateMarkerTextIfNeeded call be moved
@@ -485,7 +483,7 @@
 }
 
 NGLayoutInputNode NGBlockNode::FirstChild() const {
-  auto* block = ToLayoutBlock(box_);
+  auto* block = To<LayoutBlock>(box_);
   auto* child = GetLayoutObjectForFirstChildNode(block);
   if (!child)
     return nullptr;
@@ -497,13 +495,13 @@
 NGBlockNode NGBlockNode::GetRenderedLegend() const {
   if (!IsFieldsetContainer())
     return nullptr;
-  return NGBlockNode(LayoutFieldset::FindInFlowLegend(*ToLayoutBlock(box_)));
+  return NGBlockNode(LayoutFieldset::FindInFlowLegend(*To<LayoutBlock>(box_)));
 }
 
 NGBlockNode NGBlockNode::GetFieldsetContent() const {
   if (!IsFieldsetContainer())
     return nullptr;
-  auto* child = GetLayoutObjectForFirstChildNode(ToLayoutBlock(box_));
+  auto* child = GetLayoutObjectForFirstChildNode(To<LayoutBlock>(box_));
   if (!child)
     return nullptr;
   return NGBlockNode(ToLayoutBox(child));
@@ -602,7 +600,7 @@
                              offset_from_start);
   }
 
-  LayoutBlock* block = ToLayoutBlockOrNull(box_);
+  LayoutBlock* block = DynamicTo<LayoutBlock>(box_);
   if (LIKELY(block && IsLastFragment(physical_fragment))) {
     LayoutUnit intrinsic_block_size = layout_result.IntrinsicBlockSize();
     if (UNLIKELY(constraint_space.HasBlockFragmentation())) {
@@ -792,10 +790,9 @@
 }
 
 bool NGBlockNode::UseLogicalBottomMarginEdgeForInlineBlockBaseline() const {
-  LayoutBox* layout_box = GetLayoutBox();
-  return layout_box->IsLayoutBlock() &&
-         ToLayoutBlock(layout_box)
-             ->UseLogicalBottomMarginEdgeForInlineBlockBaseline();
+  auto* layout_box = DynamicTo<LayoutBlock>(GetLayoutBox());
+  return layout_box &&
+         layout_box->UseLogicalBottomMarginEdgeForInlineBlockBaseline();
 }
 
 scoped_refptr<const NGLayoutResult> NGBlockNode::LayoutAtomicInline(
@@ -840,7 +837,7 @@
   // we need to be at a formatting context boundary, since NG and legacy don't
   // cooperate on e.g. margin collapsing.
   DCHECK(!box_->IsLayoutBlock() ||
-         ToLayoutBlock(box_)->CreatesNewFormattingContext());
+         To<LayoutBlock>(box_)->CreatesNewFormattingContext());
 
   scoped_refptr<const NGLayoutResult> layout_result =
       box_->GetCachedLayoutResult();
diff --git a/third_party/blink/renderer/core/loader/appcache/application_cache_host.cc b/third_party/blink/renderer/core/loader/appcache/application_cache_host.cc
index 3b5e8e0..a66cb62 100644
--- a/third_party/blink/renderer/core/loader/appcache/application_cache_host.cc
+++ b/third_party/blink/renderer/core/loader/appcache/application_cache_host.cc
@@ -134,7 +134,7 @@
 
   LocalFrame* frame = document_loader_->GetFrame();
   Document* document = frame->GetDocument();
-  if (document->IsSandboxed(kSandboxOrigin)) {
+  if (document->IsSandboxed(WebSandboxFlags::kOrigin)) {
     // Prevent sandboxes from establishing application caches.
     SelectCacheWithoutManifest();
     return;
diff --git a/third_party/blink/renderer/core/loader/base_fetch_context.cc b/third_party/blink/renderer/core/loader/base_fetch_context.cc
index 23310e60..32ad63e 100644
--- a/third_party/blink/renderer/core/loader/base_fetch_context.cc
+++ b/third_party/blink/renderer/core/loader/base_fetch_context.cc
@@ -173,11 +173,11 @@
         }
       }
 
-      request.AddHttpHeaderField("Sec-Fetch-Dest", destination_value);
-      request.AddHttpHeaderField(
+      request.SetHttpHeaderField("Sec-Fetch-Dest", destination_value);
+      request.SetHttpHeaderField(
           "Sec-Fetch-Mode",
           FetchRequestModeToString(request.GetFetchRequestMode()));
-      request.AddHttpHeaderField("Sec-Fetch-Site", site_value);
+      request.SetHttpHeaderField("Sec-Fetch-Site", site_value);
       // We don't set `Sec-Fetch-User` for subresource requests.
     }
   }
diff --git a/third_party/blink/renderer/core/loader/frame_loader.cc b/third_party/blink/renderer/core/loader/frame_loader.cc
index 8a9ad38..c3cf805 100644
--- a/third_party/blink/renderer/core/loader/frame_loader.cc
+++ b/third_party/blink/renderer/core/loader/frame_loader.cc
@@ -192,7 +192,7 @@
     : frame_(frame),
       progress_tracker_(MakeGarbageCollected<ProgressTracker>(frame)),
       in_restore_scroll_(false),
-      forced_sandbox_flags_(kSandboxNone),
+      forced_sandbox_flags_(WebSandboxFlags::kNone),
       dispatching_did_clear_window_object_in_main_world_(false),
       detached_(false),
       virtual_time_pauser_(
@@ -713,7 +713,9 @@
     if (!javascript_url_is_allowed)
       return false;
 
-    if (frame_->Owner() && frame_->Owner()->GetSandboxFlags() & kSandboxOrigin)
+    if (frame_->Owner() &&
+        ((frame_->Owner()->GetSandboxFlags() & WebSandboxFlags::kOrigin) !=
+         WebSandboxFlags::kNone))
       return false;
 
     frame_->GetDocument()->ProcessJavaScriptUrl(
@@ -1526,7 +1528,8 @@
   // be considered when deciding whether to reuse it.
   // Spec:
   // https://html.spec.whatwg.org/C/#initialise-the-document-object
-  if (csp && (csp->GetSandboxMask() & kSandboxOrigin)) {
+  if (csp && (csp->GetSandboxMask() & WebSandboxFlags::kOrigin) !=
+                 WebSandboxFlags::kNone) {
     return false;
   }
 
diff --git a/third_party/blink/renderer/core/loader/resource_load_observer_for_frame.cc b/third_party/blink/renderer/core/loader/resource_load_observer_for_frame.cc
index a6c2965..ff28994 100644
--- a/third_party/blink/renderer/core/loader/resource_load_observer_for_frame.cc
+++ b/third_party/blink/renderer/core/loader/resource_load_observer_for_frame.cc
@@ -62,7 +62,7 @@
     uint64_t identifier,
     const ResourceRequest& request,
     const ResourceResponse& response,
-    Resource* resource,
+    const Resource* resource,
     ResponseSource response_source) {
   LocalFrame& frame = frame_or_imported_document_->GetFrame();
   DocumentLoader& document_loader =
diff --git a/third_party/blink/renderer/core/loader/resource_load_observer_for_frame.h b/third_party/blink/renderer/core/loader/resource_load_observer_for_frame.h
index 91bc2af..8558965 100644
--- a/third_party/blink/renderer/core/loader/resource_load_observer_for_frame.h
+++ b/third_party/blink/renderer/core/loader/resource_load_observer_for_frame.h
@@ -36,7 +36,7 @@
   void DidReceiveResponse(uint64_t identifier,
                           const ResourceRequest& request,
                           const ResourceResponse& response,
-                          Resource* resource,
+                          const Resource* resource,
                           ResponseSource) override;
   void DidReceiveData(uint64_t identifier,
                       base::span<const char> chunk) override;
diff --git a/third_party/blink/renderer/core/loader/resource_load_observer_for_worker.cc b/third_party/blink/renderer/core/loader/resource_load_observer_for_worker.cc
index d38e0b0..fe031bd9 100644
--- a/third_party/blink/renderer/core/loader/resource_load_observer_for_worker.cc
+++ b/third_party/blink/renderer/core/loader/resource_load_observer_for_worker.cc
@@ -40,7 +40,7 @@
     uint64_t identifier,
     const ResourceRequest& request,
     const ResourceResponse& response,
-    Resource* resource,
+    const Resource* resource,
     ResponseSource) {
   if (response.HasMajorCertificateErrors()) {
     WebMixedContentContextType context_type =
diff --git a/third_party/blink/renderer/core/loader/resource_load_observer_for_worker.h b/third_party/blink/renderer/core/loader/resource_load_observer_for_worker.h
index 262327b..59a2329 100644
--- a/third_party/blink/renderer/core/loader/resource_load_observer_for_worker.h
+++ b/third_party/blink/renderer/core/loader/resource_load_observer_for_worker.h
@@ -33,7 +33,7 @@
   void DidReceiveResponse(uint64_t identifier,
                           const ResourceRequest& request,
                           const ResourceResponse& response,
-                          Resource* resource,
+                          const Resource* resource,
                           ResponseSource) override;
   void DidReceiveData(uint64_t identifier,
                       base::span<const char> chunk) override;
diff --git a/third_party/blink/renderer/core/page/chrome_client.cc b/third_party/blink/renderer/core/page/chrome_client.cc
index 8bf9ac4..ef95959 100644
--- a/third_party/blink/renderer/core/page/chrome_client.cc
+++ b/third_party/blink/renderer/core/page/chrome_client.cc
@@ -256,7 +256,7 @@
     return false;
   }
 
-  if (frame->GetDocument()->IsSandboxed(kSandboxModals)) {
+  if (frame->GetDocument()->IsSandboxed(WebSandboxFlags::kModals)) {
     UseCounter::Count(frame->GetDocument(),
                       WebFeature::kDialogInSandboxedContext);
     frame->Console().AddMessage(ConsoleMessage::Create(
diff --git a/third_party/blink/renderer/core/page/chrome_client_impl_test.cc b/third_party/blink/renderer/core/page/chrome_client_impl_test.cc
index 1814c9f..7ba626a 100644
--- a/third_party/blink/renderer/core/page/chrome_client_impl_test.cc
+++ b/third_party/blink/renderer/core/page/chrome_client_impl_test.cc
@@ -95,7 +95,7 @@
   request.SetNavigationPolicy(kNavigationPolicyNewForegroundTab);
   WebWindowFeatures features;
   EXPECT_EQ(nullptr, chrome_client_impl_->CreateWindow(
-                         frame, request, features, kSandboxNone,
+                         frame, request, features, WebSandboxFlags::kNone,
                          FeaturePolicy::FeatureState(), ""));
 }
 
diff --git a/third_party/blink/renderer/core/page/create_window.cc b/third_party/blink/renderer/core/page/create_window.cc
index c6ec294..dab84de 100644
--- a/third_party/blink/renderer/core/page/create_window.cc
+++ b/third_party/blink/renderer/core/page/create_window.cc
@@ -240,7 +240,7 @@
                     LocalFrame::HasTransientUserActivation(&opener_frame));
 
   // Sandboxed frames cannot open new auxiliary browsing contexts.
-  if (opener_frame.GetDocument()->IsSandboxed(kSandboxPopups)) {
+  if (opener_frame.GetDocument()->IsSandboxed(WebSandboxFlags::kPopups)) {
     // FIXME: This message should be moved off the console once a solution to
     // https://bugs.webkit.org/show_bug.cgi?id=103274 exists.
     opener_frame.GetDocument()->AddConsoleMessage(ConsoleMessage::Create(
@@ -253,12 +253,12 @@
   }
 
   bool propagate_sandbox = opener_frame.GetDocument()->IsSandboxed(
-      kSandboxPropagatesToAuxiliaryBrowsingContexts);
+      WebSandboxFlags::kPropagatesToAuxiliaryBrowsingContexts);
   const SandboxFlags sandbox_flags =
       propagate_sandbox ? opener_frame.GetDocument()->GetSandboxFlags()
-                        : kSandboxNone;
+                        : WebSandboxFlags::kNone;
   bool not_sandboxed =
-      opener_frame.GetDocument()->GetSandboxFlags() == kSandboxNone;
+      opener_frame.GetDocument()->GetSandboxFlags() == WebSandboxFlags::kNone;
   FeaturePolicy::FeatureState opener_feature_state =
       (not_sandboxed || propagate_sandbox)
           ? opener_frame.GetDocument()->GetFeaturePolicy()->GetFeatureState()
diff --git a/third_party/blink/renderer/core/page/pointer_lock_controller.cc b/third_party/blink/renderer/core/page/pointer_lock_controller.cc
index e8a11d6b..cbc18b3 100644
--- a/third_party/blink/renderer/core/page/pointer_lock_controller.cc
+++ b/third_party/blink/renderer/core/page/pointer_lock_controller.cc
@@ -53,7 +53,7 @@
                       WebFeature::kElementRequestPointerLockInShadow);
   }
 
-  if (target->GetDocument().IsSandboxed(kSandboxPointerLock)) {
+  if (target->GetDocument().IsSandboxed(WebSandboxFlags::kPointerLock)) {
     // FIXME: This message should be moved off the console once a solution to
     // https://bugs.webkit.org/show_bug.cgi?id=103274 exists.
     target->GetDocument().AddConsoleMessage(ConsoleMessage::Create(
diff --git a/third_party/blink/renderer/core/probe/core_probes.pidl b/third_party/blink/renderer/core/probe/core_probes.pidl
index 18117db..5279eee 100644
--- a/third_party/blink/renderer/core/probe/core_probes.pidl
+++ b/third_party/blink/renderer/core/probe/core_probes.pidl
@@ -94,7 +94,7 @@
   void WillSendRequest(CoreProbeSink*, uint64_t identifier, DocumentLoader*, const KURL& fetch_context_url, const ResourceRequest&, const ResourceResponse& redirect_response, const FetchInitiatorInfo&, ResourceType);
   void WillSendNavigationRequest(CoreProbeSink*, uint64_t identifier, DocumentLoader*, const KURL&, const AtomicString& http_method, EncodedFormData*);
   void MarkResourceAsCached(LocalFrame*, DocumentLoader*, uint64_t identifier);
-  void DidReceiveResourceResponse(CoreProbeSink*, uint64_t identifier, DocumentLoader*, const ResourceResponse&, Resource*);
+  void DidReceiveResourceResponse(CoreProbeSink*, uint64_t identifier, DocumentLoader*, const ResourceResponse&, const Resource*);
   void DidReceiveData(CoreProbeSink*, uint64_t identifier, DocumentLoader*, const char* data, uint64_t data_length);
   void DidReceiveBlob(CoreProbeSink*, uint64_t identifier, DocumentLoader*, BlobDataHandle*);
   void DidReceiveEncodedDataLength(CoreProbeSink*, DocumentLoader* loader, uint64_t identifier, size_t encoded_data_length);
diff --git a/third_party/blink/renderer/core/svg/graphics/svg_image.cc b/third_party/blink/renderer/core/svg/graphics/svg_image.cc
index 41c7d3f..aeb45d3 100644
--- a/third_party/blink/renderer/core/svg/graphics/svg_image.cc
+++ b/third_party/blink/renderer/core/svg/graphics/svg_image.cc
@@ -795,7 +795,7 @@
   }
 
   FrameLoader& loader = frame->Loader();
-  loader.ForceSandboxFlags(kSandboxAll);
+  loader.ForceSandboxFlags(WebSandboxFlags::kAll);
 
   // SVG Images will always synthesize a viewBox, if it's not available, and
   // thus never see scrollbars.
diff --git a/third_party/blink/renderer/modules/BUILD.gn b/third_party/blink/renderer/modules/BUILD.gn
index e894279..eb6f43aa 100644
--- a/third_party/blink/renderer/modules/BUILD.gn
+++ b/third_party/blink/renderer/modules/BUILD.gn
@@ -323,6 +323,7 @@
     "mediastream/media_stream_video_capturer_source_test.cc",
     "mediastream/mock_mojo_media_stream_dispatcher_host.cc",
     "mediastream/mock_mojo_media_stream_dispatcher_host.h",
+    "mediastream/video_track_adapter_unittest.cc",
     "notifications/notification_data_test.cc",
     "notifications/notification_image_loader_test.cc",
     "notifications/notification_resources_loader_test.cc",
diff --git a/third_party/blink/renderer/modules/cache_storage/global_cache_storage.cc b/third_party/blink/renderer/modules/cache_storage/global_cache_storage.cc
index 7e274de..6692cff 100644
--- a/third_party/blink/renderer/modules/cache_storage/global_cache_storage.cc
+++ b/third_party/blink/renderer/modules/cache_storage/global_cache_storage.cc
@@ -43,7 +43,7 @@
   CacheStorage* Caches(T& fetching_scope, ExceptionState& exception_state) {
     ExecutionContext* context = fetching_scope.GetExecutionContext();
     if (!context->GetSecurityOrigin()->CanAccessCacheStorage()) {
-      if (context->GetSecurityContext().IsSandboxed(kSandboxOrigin)) {
+      if (context->GetSecurityContext().IsSandboxed(WebSandboxFlags::kOrigin)) {
         exception_state.ThrowSecurityError(
             "Cache storage is disabled because the context is sandboxed and "
             "lacks the 'allow-same-origin' flag.");
diff --git a/third_party/blink/renderer/modules/clipboard/clipboard_promise.h b/third_party/blink/renderer/modules/clipboard/clipboard_promise.h
index 1ee1ea4..0e37b458 100644
--- a/third_party/blink/renderer/modules/clipboard/clipboard_promise.h
+++ b/third_party/blink/renderer/modules/clipboard/clipboard_promise.h
@@ -25,7 +25,7 @@
   USING_GARBAGE_COLLECTED_MIXIN(ClipboardPromise);
 
  public:
-  ClipboardPromise(ScriptState*);
+  explicit ClipboardPromise(ScriptState*);
   virtual ~ClipboardPromise();
 
   // Creates promise to execute Clipboard API functions off the main thread.
diff --git a/third_party/blink/renderer/modules/clipboard/clipboard_writer.h b/third_party/blink/renderer/modules/clipboard/clipboard_writer.h
index 05e5a9b5..8c082a5 100644
--- a/third_party/blink/renderer/modules/clipboard/clipboard_writer.h
+++ b/third_party/blink/renderer/modules/clipboard/clipboard_writer.h
@@ -39,7 +39,7 @@
   void DidFail(FileErrorCode) override;
 
  protected:
-  ClipboardWriter(ClipboardPromise* promise);
+  explicit ClipboardWriter(ClipboardPromise* promise);
 
   virtual void DecodeOnBackgroundThread(
       scoped_refptr<base::SingleThreadTaskRunner> task_runner,
diff --git a/third_party/blink/renderer/modules/encryptedmedia/BUILD.gn b/third_party/blink/renderer/modules/encryptedmedia/BUILD.gn
index 356f6d7..8aeb053 100644
--- a/third_party/blink/renderer/modules/encryptedmedia/BUILD.gn
+++ b/third_party/blink/renderer/modules/encryptedmedia/BUILD.gn
@@ -22,6 +22,8 @@
     "media_key_status_map.h",
     "media_key_system_access.cc",
     "media_key_system_access.h",
+    "media_key_system_access_initializer_base.cc",
+    "media_key_system_access_initializer_base.h",
     "media_keys.cc",
     "media_keys.h",
     "media_keys_controller.cc",
diff --git a/third_party/blink/renderer/modules/encryptedmedia/media_key_system_access_initializer_base.cc b/third_party/blink/renderer/modules/encryptedmedia/media_key_system_access_initializer_base.cc
new file mode 100644
index 0000000..bc00ec6
--- /dev/null
+++ b/third_party/blink/renderer/modules/encryptedmedia/media_key_system_access_initializer_base.cc
@@ -0,0 +1,232 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/modules/encryptedmedia/media_key_system_access_initializer_base.h"
+
+#include "services/metrics/public/cpp/ukm_builders.h"
+#include "services/metrics/public/cpp/ukm_recorder.h"
+#include "third_party/blink/renderer/core/dom/document.h"
+#include "third_party/blink/renderer/core/inspector/console_message.h"
+#include "third_party/blink/renderer/modules/encryptedmedia/encrypted_media_utils.h"
+#include "third_party/blink/renderer/platform/histogram.h"
+#include "third_party/blink/renderer/platform/network/parsed_content_type.h"
+#include "third_party/blink/renderer/platform/wtf/wtf_size_t.h"
+
+namespace blink {
+
+namespace {
+
+static WebVector<WebEncryptedMediaInitDataType> ConvertInitDataTypes(
+    const Vector<String>& init_data_types) {
+  WebVector<WebEncryptedMediaInitDataType> result(init_data_types.size());
+  for (wtf_size_t i = 0; i < init_data_types.size(); ++i)
+    result[i] = EncryptedMediaUtils::ConvertToInitDataType(init_data_types[i]);
+  return result;
+}
+
+static WebMediaKeySystemMediaCapability::EncryptionScheme
+ConvertEncryptionScheme(const String& encryption_scheme) {
+  if (encryption_scheme == "cenc")
+    return WebMediaKeySystemMediaCapability::EncryptionScheme::kCenc;
+  if (encryption_scheme == "cbcs")
+    return WebMediaKeySystemMediaCapability::EncryptionScheme::kCbcs;
+
+  NOTREACHED();
+  return WebMediaKeySystemMediaCapability::EncryptionScheme::kNotSpecified;
+}
+
+static WebVector<WebMediaKeySystemMediaCapability> ConvertCapabilities(
+    const HeapVector<Member<MediaKeySystemMediaCapability>>& capabilities) {
+  WebVector<WebMediaKeySystemMediaCapability> result(capabilities.size());
+  for (wtf_size_t i = 0; i < capabilities.size(); ++i) {
+    const WebString& content_type = capabilities[i]->contentType();
+    result[i].content_type = content_type;
+    ParsedContentType type(content_type);
+    if (type.IsValid() && !type.GetParameters().HasDuplicatedNames()) {
+      // From
+      // http://w3c.github.io/encrypted-media/#get-supported-capabilities-for-audio-video-type
+      // "If the user agent does not recognize one or more parameters,
+      // continue to the next iteration." There is no way to enumerate the
+      // parameters, so only look up "codecs" if a single parameter is
+      // present. Chromium expects "codecs" to be provided, so this capability
+      // will be skipped if codecs is not the only parameter specified.
+      result[i].mime_type = type.MimeType();
+      if (type.GetParameters().ParameterCount() == 1u)
+        result[i].codecs = type.ParameterValueForName("codecs");
+    }
+    result[i].robustness = capabilities[i]->robustness();
+
+    // From
+    // https://github.com/WICG/encrypted-media-encryption-scheme/blob/master/explainer.md
+    // "Asking for "any" encryption scheme is unrealistic. Defining null as
+    // "any scheme" is convenient for backward compatibility, though.
+    // Applications which ignore this feature by leaving encryptionScheme null
+    // get the same user agent behavior they did before this feature existed."
+    result[i].encryption_scheme =
+        capabilities[i]->hasEncryptionScheme()
+            ? ConvertEncryptionScheme(capabilities[i]->encryptionScheme())
+            : WebMediaKeySystemMediaCapability::EncryptionScheme::kNotSpecified;
+  }
+  return result;
+}
+
+static WebVector<WebEncryptedMediaSessionType> ConvertSessionTypes(
+    const Vector<String>& session_types) {
+  WebVector<WebEncryptedMediaSessionType> result(session_types.size());
+  for (wtf_size_t i = 0; i < session_types.size(); ++i)
+    result[i] = EncryptedMediaUtils::ConvertToSessionType(session_types[i]);
+  return result;
+}
+
+}  // namespace
+
+MediaKeySystemAccessInitializerBase::MediaKeySystemAccessInitializerBase(
+    ExecutionContext* execution_context,
+    const String& key_system,
+    const HeapVector<Member<MediaKeySystemConfiguration>>&
+        supported_configurations)
+    : ContextLifecycleObserver(execution_context),
+      key_system_(key_system),
+      supported_configurations_(supported_configurations.size()) {
+  for (wtf_size_t i = 0; i < supported_configurations.size(); ++i) {
+    const MediaKeySystemConfiguration* config = supported_configurations[i];
+    WebMediaKeySystemConfiguration web_config;
+
+    DCHECK(config->hasInitDataTypes());
+    web_config.init_data_types = ConvertInitDataTypes(config->initDataTypes());
+
+    DCHECK(config->hasAudioCapabilities());
+    web_config.audio_capabilities =
+        ConvertCapabilities(config->audioCapabilities());
+
+    DCHECK(config->hasVideoCapabilities());
+    web_config.video_capabilities =
+        ConvertCapabilities(config->videoCapabilities());
+
+    DCHECK(config->hasDistinctiveIdentifier());
+    web_config.distinctive_identifier =
+        EncryptedMediaUtils::ConvertToMediaKeysRequirement(
+            config->distinctiveIdentifier());
+
+    DCHECK(config->hasPersistentState());
+    web_config.persistent_state =
+        EncryptedMediaUtils::ConvertToMediaKeysRequirement(
+            config->persistentState());
+
+    if (config->hasSessionTypes()) {
+      web_config.session_types = ConvertSessionTypes(config->sessionTypes());
+    } else {
+      // From the spec
+      // (http://w3c.github.io/encrypted-media/#idl-def-mediakeysystemconfiguration):
+      // If this member is not present when the dictionary is passed to
+      // requestMediaKeySystemAccess(), the dictionary will be treated
+      // as if this member is set to [ "temporary" ].
+      WebVector<WebEncryptedMediaSessionType> session_types(
+          static_cast<size_t>(1));
+      session_types[0] = WebEncryptedMediaSessionType::kTemporary;
+      web_config.session_types = session_types;
+    }
+
+    // If |label| is not present, it will be a null string.
+    web_config.label = config->label();
+    supported_configurations_[i] = web_config;
+  }
+
+  CheckVideoCapabilityRobustness();
+}
+
+const SecurityOrigin* MediaKeySystemAccessInitializerBase::GetSecurityOrigin()
+    const {
+  return IsExecutionContextValid() ? GetExecutionContext()->GetSecurityOrigin()
+                                   : nullptr;
+}
+
+void MediaKeySystemAccessInitializerBase::Trace(blink::Visitor* visitor) {
+  EncryptedMediaRequest::Trace(visitor);
+  ContextLifecycleObserver::Trace(visitor);
+}
+
+bool MediaKeySystemAccessInitializerBase::IsExecutionContextValid() const {
+  // isContextDestroyed() is called to see if the context is in the
+  // process of being destroyed. If it is true, assume the context is no
+  // longer valid as it is about to be destroyed anyway.
+  ExecutionContext* context = GetExecutionContext();
+  return context && !context->IsContextDestroyed();
+}
+
+void MediaKeySystemAccessInitializerBase::CheckVideoCapabilityRobustness()
+    const {
+  const char kWidevineKeySystem[] = "com.widevine.alpha";
+  const char kWidevineHwSecureAllRobustness[] = "HW_SECURE_ALL";
+
+  // Reported to UKM. Existing values must not change and new values must be
+  // added at the end of the list.
+  enum KeySystemForUkm {
+    kClearKey = 0,
+    kWidevine = 1,
+  };
+
+  // Only check for widevine key system for now.
+  if (KeySystem() != kWidevineKeySystem)
+    return;
+
+  bool has_video_capabilities = false;
+  bool has_empty_robustness = false;
+  bool has_hw_secure_all = false;
+
+  for (const auto& config : supported_configurations_) {
+    for (const auto& capability : config.video_capabilities) {
+      has_video_capabilities = true;
+      if (capability.robustness.IsEmpty()) {
+        has_empty_robustness = true;
+      } else if (capability.robustness == kWidevineHwSecureAllRobustness) {
+        has_hw_secure_all = true;
+      }
+
+      if (has_empty_robustness && has_hw_secure_all)
+        break;
+    }
+
+    if (has_empty_robustness && has_hw_secure_all)
+      break;
+  }
+
+  if (has_video_capabilities) {
+    DEFINE_THREAD_SAFE_STATIC_LOCAL(
+        EnumerationHistogram, empty_robustness_histogram,
+        ("Media.EME.Widevine.VideoCapability.HasEmptyRobustness", 2));
+    empty_robustness_histogram.Count(has_empty_robustness);
+  }
+
+  if (has_empty_robustness) {
+    // TODO(xhwang): Write a best practice doc explaining details about risks of
+    // using an empty robustness here, and provide the link to the doc in this
+    // message. See http://crbug.com/720013
+    GetExecutionContext()->AddConsoleMessage(ConsoleMessage::Create(
+        mojom::ConsoleMessageSource::kJavaScript,
+        mojom::ConsoleMessageLevel::kWarning,
+        "It is recommended that a robustness level be specified. Not "
+        "specifying the robustness level could result in unexpected "
+        "behavior."));
+  }
+
+  if (!IsExecutionContextValid())
+    return;
+
+  Document* document = To<Document>(GetExecutionContext());
+  if (!document)
+    return;
+
+  ukm::builders::Media_EME_RequestMediaKeySystemAccess builder(
+      document->UkmSourceID());
+  builder.SetKeySystem(KeySystemForUkm::kWidevine);
+  builder.SetVideoCapabilities(static_cast<int>(has_video_capabilities));
+  builder.SetVideoCapabilities_HasEmptyRobustness(
+      static_cast<int>(has_empty_robustness));
+  builder.SetVideoCapabilities_HasHwSecureAllRobustness(
+      static_cast<int>(has_hw_secure_all));
+  builder.Record(document->UkmRecorder());
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/modules/encryptedmedia/media_key_system_access_initializer_base.h b/third_party/blink/renderer/modules/encryptedmedia/media_key_system_access_initializer_base.h
new file mode 100644
index 0000000..c1a2aaa
--- /dev/null
+++ b/third_party/blink/renderer/modules/encryptedmedia/media_key_system_access_initializer_base.h
@@ -0,0 +1,58 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_ENCRYPTEDMEDIA_MEDIA_KEY_SYSTEM_ACCESS_INITIALIZER_BASE_H_
+#define THIRD_PARTY_BLINK_RENDERER_MODULES_ENCRYPTEDMEDIA_MEDIA_KEY_SYSTEM_ACCESS_INITIALIZER_BASE_H_
+
+#include "third_party/blink/public/platform/web_media_key_system_configuration.h"
+#include "third_party/blink/public/platform/web_vector.h"
+#include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
+#include "third_party/blink/renderer/core/execution_context/context_lifecycle_observer.h"
+#include "third_party/blink/renderer/modules/encryptedmedia/media_key_system_configuration.h"
+#include "third_party/blink/renderer/platform/encrypted_media_request.h"
+#include "third_party/blink/renderer/platform/heap/member.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+class MediaKeySystemAccessInitializerBase : public EncryptedMediaRequest,
+                                            public ContextLifecycleObserver {
+  USING_GARBAGE_COLLECTED_MIXIN(MediaKeySystemAccessInitializerBase);
+
+ public:
+  MediaKeySystemAccessInitializerBase(
+      ExecutionContext* execution_context,
+      const String& key_system,
+      const HeapVector<Member<MediaKeySystemConfiguration>>&
+          supported_configurations);
+  ~MediaKeySystemAccessInitializerBase() override = default;
+
+  // EncryptedMediaRequest implementation.
+  WebString KeySystem() const override { return key_system_; }
+  const WebVector<WebMediaKeySystemConfiguration>& SupportedConfigurations()
+      const override {
+    return supported_configurations_;
+  }
+  const SecurityOrigin* GetSecurityOrigin() const override;
+
+  void Trace(blink::Visitor* visitor) override;
+
+ protected:
+  // Returns true if the ExecutionContext is valid, false otherwise.
+  bool IsExecutionContextValid() const;
+
+  // For widevine key system, generate warning and report to UMA if
+  // |m_supportedConfigurations| contains any video capability with empty
+  // robustness string.
+  void CheckVideoCapabilityRobustness() const;
+
+  const String key_system_;
+  WebVector<WebMediaKeySystemConfiguration> supported_configurations_;
+
+  DISALLOW_COPY_AND_ASSIGN(MediaKeySystemAccessInitializerBase);
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_ENCRYPTEDMEDIA_MEDIA_KEY_SYSTEM_ACCESS_INITIALIZER_BASE_H_
diff --git a/third_party/blink/renderer/modules/encryptedmedia/navigator_request_media_key_system_access.cc b/third_party/blink/renderer/modules/encryptedmedia/navigator_request_media_key_system_access.cc
index ee5079d..ccb50dd 100644
--- a/third_party/blink/renderer/modules/encryptedmedia/navigator_request_media_key_system_access.cc
+++ b/third_party/blink/renderer/modules/encryptedmedia/navigator_request_media_key_system_access.cc
@@ -8,8 +8,6 @@
 
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
-#include "services/metrics/public/cpp/ukm_builders.h"
-#include "services/metrics/public/cpp/ukm_recorder.h"
 #include "third_party/blink/public/mojom/feature_policy/feature_policy.mojom-blink.h"
 #include "third_party/blink/public/platform/web_encrypted_media_client.h"
 #include "third_party/blink/public/platform/web_encrypted_media_request.h"
@@ -27,11 +25,11 @@
 #include "third_party/blink/renderer/modules/encryptedmedia/encrypted_media_utils.h"
 #include "third_party/blink/renderer/modules/encryptedmedia/media_key_session.h"
 #include "third_party/blink/renderer/modules/encryptedmedia/media_key_system_access.h"
+#include "third_party/blink/renderer/modules/encryptedmedia/media_key_system_access_initializer_base.h"
 #include "third_party/blink/renderer/modules/encryptedmedia/media_keys_controller.h"
 #include "third_party/blink/renderer/platform/bindings/script_state.h"
 #include "third_party/blink/renderer/platform/bindings/v8_throw_exception.h"
 #include "third_party/blink/renderer/platform/encrypted_media_request.h"
-#include "third_party/blink/renderer/platform/histogram.h"
 #include "third_party/blink/renderer/platform/network/mime/content_type.h"
 #include "third_party/blink/renderer/platform/network/parsed_content_type.h"
 #include "third_party/blink/renderer/platform/runtime_enabled_features.h"
@@ -47,71 +45,10 @@
     "applied to the current document. See https://goo.gl/EuHzyv for more "
     "details.";
 
-static WebVector<WebEncryptedMediaInitDataType> ConvertInitDataTypes(
-    const Vector<String>& init_data_types) {
-  WebVector<WebEncryptedMediaInitDataType> result(init_data_types.size());
-  for (wtf_size_t i = 0; i < init_data_types.size(); ++i)
-    result[i] = EncryptedMediaUtils::ConvertToInitDataType(init_data_types[i]);
-  return result;
-}
-
-static WebMediaKeySystemMediaCapability::EncryptionScheme
-ConvertEncryptionScheme(const String& encryption_scheme) {
-  if (encryption_scheme == "cenc")
-    return WebMediaKeySystemMediaCapability::EncryptionScheme::kCenc;
-  if (encryption_scheme == "cbcs")
-    return WebMediaKeySystemMediaCapability::EncryptionScheme::kCbcs;
-
-  NOTREACHED();
-  return WebMediaKeySystemMediaCapability::EncryptionScheme::kNotSpecified;
-}
-
-static WebVector<WebMediaKeySystemMediaCapability> ConvertCapabilities(
-    const HeapVector<Member<MediaKeySystemMediaCapability>>& capabilities) {
-  WebVector<WebMediaKeySystemMediaCapability> result(capabilities.size());
-  for (wtf_size_t i = 0; i < capabilities.size(); ++i) {
-    const WebString& content_type = capabilities[i]->contentType();
-    result[i].content_type = content_type;
-    ParsedContentType type(content_type);
-    if (type.IsValid() && !type.GetParameters().HasDuplicatedNames()) {
-      // From
-      // http://w3c.github.io/encrypted-media/#get-supported-capabilities-for-audio-video-type
-      // "If the user agent does not recognize one or more parameters,
-      // continue to the next iteration." There is no way to enumerate the
-      // parameters, so only look up "codecs" if a single parameter is
-      // present. Chromium expects "codecs" to be provided, so this capability
-      // will be skipped if codecs is not the only parameter specified.
-      result[i].mime_type = type.MimeType();
-      if (type.GetParameters().ParameterCount() == 1u)
-        result[i].codecs = type.ParameterValueForName("codecs");
-    }
-    result[i].robustness = capabilities[i]->robustness();
-
-    // From
-    // https://github.com/WICG/encrypted-media-encryption-scheme/blob/master/explainer.md
-    // "Asking for "any" encryption scheme is unrealistic. Defining null as
-    // "any scheme" is convenient for backward compatibility, though.
-    // Applications which ignore this feature by leaving encryptionScheme null
-    // get the same user agent behavior they did before this feature existed."
-    result[i].encryption_scheme =
-        capabilities[i]->hasEncryptionScheme()
-            ? ConvertEncryptionScheme(capabilities[i]->encryptionScheme())
-            : WebMediaKeySystemMediaCapability::EncryptionScheme::kNotSpecified;
-  }
-  return result;
-}
-
-static WebVector<WebEncryptedMediaSessionType> ConvertSessionTypes(
-    const Vector<String>& session_types) {
-  WebVector<WebEncryptedMediaSessionType> result(session_types.size());
-  for (wtf_size_t i = 0; i < session_types.size(); ++i)
-    result[i] = EncryptedMediaUtils::ConvertToSessionType(session_types[i]);
-  return result;
-}
-
 // This class allows capabilities to be checked and a MediaKeySystemAccess
 // object to be created asynchronously.
-class MediaKeySystemAccessInitializer final : public EncryptedMediaRequest {
+class MediaKeySystemAccessInitializer final
+    : public MediaKeySystemAccessInitializerBase {
  public:
   MediaKeySystemAccessInitializer(
       ScriptState*,
@@ -121,12 +58,6 @@
   ~MediaKeySystemAccessInitializer() override = default;
 
   // EncryptedMediaRequest implementation.
-  WebString KeySystem() const override { return key_system_; }
-  const WebVector<WebMediaKeySystemConfiguration>& SupportedConfigurations()
-      const override {
-    return supported_configurations_;
-  }
-  const SecurityOrigin* GetSecurityOrigin() const override;
   void RequestSucceeded(
       std::unique_ptr<WebContentDecryptionModuleAccess>) override;
   void RequestNotSupported(const WebString& error_message) override;
@@ -135,22 +66,11 @@
 
   void Trace(blink::Visitor* visitor) override {
     visitor->Trace(resolver_);
-    EncryptedMediaRequest::Trace(visitor);
+    MediaKeySystemAccessInitializerBase::Trace(visitor);
   }
 
  private:
-  // Returns true if the ExecutionContext is valid, false otherwise.
-  bool IsExecutionContextValid() const;
-
-  // For widevine key system, generate warning and report to UMA if
-  // |m_supportedConfigurations| contains any video capability with empty
-  // robustness string.
-  void CheckVideoCapabilityRobustness() const;
-
   Member<ScriptPromiseResolver> resolver_;
-  const String key_system_;
-  WebVector<WebMediaKeySystemConfiguration> supported_configurations_;
-
   DISALLOW_COPY_AND_ASSIGN(MediaKeySystemAccessInitializer);
 };
 
@@ -159,62 +79,10 @@
     const String& key_system,
     const HeapVector<Member<MediaKeySystemConfiguration>>&
         supported_configurations)
-    : resolver_(MakeGarbageCollected<ScriptPromiseResolver>(script_state)),
-      key_system_(key_system),
-      supported_configurations_(supported_configurations.size()) {
-  for (wtf_size_t i = 0; i < supported_configurations.size(); ++i) {
-    const MediaKeySystemConfiguration* config = supported_configurations[i];
-    WebMediaKeySystemConfiguration web_config;
-
-    DCHECK(config->hasInitDataTypes());
-    web_config.init_data_types = ConvertInitDataTypes(config->initDataTypes());
-
-    DCHECK(config->hasAudioCapabilities());
-    web_config.audio_capabilities =
-        ConvertCapabilities(config->audioCapabilities());
-
-    DCHECK(config->hasVideoCapabilities());
-    web_config.video_capabilities =
-        ConvertCapabilities(config->videoCapabilities());
-
-    DCHECK(config->hasDistinctiveIdentifier());
-    web_config.distinctive_identifier =
-        EncryptedMediaUtils::ConvertToMediaKeysRequirement(
-            config->distinctiveIdentifier());
-
-    DCHECK(config->hasPersistentState());
-    web_config.persistent_state =
-        EncryptedMediaUtils::ConvertToMediaKeysRequirement(
-            config->persistentState());
-
-    if (config->hasSessionTypes()) {
-      web_config.session_types = ConvertSessionTypes(config->sessionTypes());
-    } else {
-      // From the spec
-      // (http://w3c.github.io/encrypted-media/#idl-def-mediakeysystemconfiguration):
-      // If this member is not present when the dictionary is passed to
-      // requestMediaKeySystemAccess(), the dictionary will be treated
-      // as if this member is set to [ "temporary" ].
-      WebVector<WebEncryptedMediaSessionType> session_types(
-          static_cast<size_t>(1));
-      session_types[0] = WebEncryptedMediaSessionType::kTemporary;
-      web_config.session_types = session_types;
-    }
-
-    // If |label| is not present, it will be a null string.
-    web_config.label = config->label();
-    supported_configurations_[i] = web_config;
-  }
-
-  CheckVideoCapabilityRobustness();
-}
-
-const SecurityOrigin* MediaKeySystemAccessInitializer::GetSecurityOrigin()
-    const {
-  return IsExecutionContextValid()
-             ? resolver_->GetExecutionContext()->GetSecurityOrigin()
-             : nullptr;
-}
+    : MediaKeySystemAccessInitializerBase(ExecutionContext::From(script_state),
+                                          key_system,
+                                          supported_configurations),
+      resolver_(MakeGarbageCollected<ScriptPromiseResolver>(script_state)) {}
 
 void MediaKeySystemAccessInitializer::RequestSucceeded(
     std::unique_ptr<WebContentDecryptionModuleAccess> access) {
@@ -240,84 +108,6 @@
   resolver_.Clear();
 }
 
-bool MediaKeySystemAccessInitializer::IsExecutionContextValid() const {
-  // isContextDestroyed() is called to see if the context is in the
-  // process of being destroyed. If it is true, assume the context is no
-  // longer valid as it is about to be destroyed anyway.
-  ExecutionContext* context = resolver_->GetExecutionContext();
-  return context && !context->IsContextDestroyed();
-}
-
-void MediaKeySystemAccessInitializer::CheckVideoCapabilityRobustness() const {
-  const char kWidevineKeySystem[] = "com.widevine.alpha";
-  const char kWidevineHwSecureAllRobustness[] = "HW_SECURE_ALL";
-
-  // Reported to UKM. Existing values must not change and new values must be
-  // added at the end of the list.
-  enum KeySystemForUkm {
-    kClearKey = 0,
-    kWidevine = 1,
-  };
-
-  // Only check for widevine key system for now.
-  if (KeySystem() != kWidevineKeySystem)
-    return;
-
-  bool has_video_capabilities = false;
-  bool has_empty_robustness = false;
-  bool has_hw_secure_all = false;
-
-  for (const auto& config : supported_configurations_) {
-    for (const auto& capability : config.video_capabilities) {
-      has_video_capabilities = true;
-      if (capability.robustness.IsEmpty()) {
-        has_empty_robustness = true;
-      } else if (capability.robustness == kWidevineHwSecureAllRobustness) {
-        has_hw_secure_all = true;
-      }
-
-      if (has_empty_robustness && has_hw_secure_all)
-        break;
-    }
-
-    if (has_empty_robustness && has_hw_secure_all)
-      break;
-  }
-
-  if (has_video_capabilities) {
-    DEFINE_THREAD_SAFE_STATIC_LOCAL(
-        EnumerationHistogram, empty_robustness_histogram,
-        ("Media.EME.Widevine.VideoCapability.HasEmptyRobustness", 2));
-    empty_robustness_histogram.Count(has_empty_robustness);
-  }
-
-  if (has_empty_robustness) {
-    // TODO(xhwang): Write a best practice doc explaining details about risks of
-    // using an empty robustness here, and provide the link to the doc in this
-    // message. See http://crbug.com/720013
-    resolver_->GetExecutionContext()->AddConsoleMessage(ConsoleMessage::Create(
-        mojom::ConsoleMessageSource::kJavaScript,
-        mojom::ConsoleMessageLevel::kWarning,
-        "It is recommended that a robustness level be specified. Not "
-        "specifying the robustness level could result in unexpected "
-        "behavior."));
-  }
-
-  Document* document = To<Document>(resolver_->GetExecutionContext());
-  if (!document)
-    return;
-
-  ukm::builders::Media_EME_RequestMediaKeySystemAccess builder(
-      document->UkmSourceID());
-  builder.SetKeySystem(KeySystemForUkm::kWidevine);
-  builder.SetVideoCapabilities(static_cast<int>(has_video_capabilities));
-  builder.SetVideoCapabilities_HasEmptyRobustness(
-      static_cast<int>(has_empty_robustness));
-  builder.SetVideoCapabilities_HasHwSecureAllRobustness(
-      static_cast<int>(has_hw_secure_all));
-  builder.Record(document->UkmRecorder());
-}
-
 }  // namespace
 
 ScriptPromise NavigatorRequestMediaKeySystemAccess::requestMediaKeySystemAccess(
diff --git a/third_party/blink/renderer/modules/exported/web_embedded_worker_impl.cc b/third_party/blink/renderer/modules/exported/web_embedded_worker_impl.cc
index 2fcbd13c..0839428 100644
--- a/third_party/blink/renderer/modules/exported/web_embedded_worker_impl.cc
+++ b/third_party/blink/renderer/modules/exported/web_embedded_worker_impl.cc
@@ -376,8 +376,8 @@
   const HttpsState starter_https_state = document->GetHttpsState();
 
   auto* worker_clients = MakeGarbageCollected<WorkerClients>();
-  ProvideIndexedDBClientToWorker(worker_clients,
-                                 IndexedDBClient::Create(*worker_clients));
+  ProvideIndexedDBClientToWorker(
+      worker_clients, MakeGarbageCollected<IndexedDBClient>(*worker_clients));
 
   ProvideContentSettingsClientToWorker(worker_clients,
                                        std::move(content_settings_client_));
diff --git a/third_party/blink/renderer/modules/imagecapture/image_capture.cc b/third_party/blink/renderer/modules/imagecapture/image_capture.cc
index bf755d1..122e66ff 100644
--- a/third_party/blink/renderer/modules/imagecapture/image_capture.cc
+++ b/third_party/blink/renderer/modules/imagecapture/image_capture.cc
@@ -700,7 +700,7 @@
   photo_settings_->setImageWidth(photo_state->width->current);
   // TODO(mcasas): collect the remaining two entries https://crbug.com/732521.
 
-  photo_capabilities_ = PhotoCapabilities::Create();
+  photo_capabilities_ = MakeGarbageCollected<PhotoCapabilities>();
   photo_capabilities_->SetRedEyeReduction(photo_state->red_eye_reduction);
   // TODO(mcasas): Remove the explicit MediaSettingsRange::create() when
   // mojo::StructTraits supports garbage-collected mappings,
diff --git a/third_party/blink/renderer/modules/imagecapture/photo_capabilities.cc b/third_party/blink/renderer/modules/imagecapture/photo_capabilities.cc
index 7da6e62..4c2beb9 100644
--- a/third_party/blink/renderer/modules/imagecapture/photo_capabilities.cc
+++ b/third_party/blink/renderer/modules/imagecapture/photo_capabilities.cc
@@ -6,11 +6,6 @@
 
 namespace blink {
 
-// static
-PhotoCapabilities* PhotoCapabilities::Create() {
-  return MakeGarbageCollected<PhotoCapabilities>();
-}
-
 Vector<String> PhotoCapabilities::fillLightMode() const {
   Vector<String> fill_light_modes;
   for (const auto& mode : fill_light_modes_) {
diff --git a/third_party/blink/renderer/modules/imagecapture/photo_capabilities.h b/third_party/blink/renderer/modules/imagecapture/photo_capabilities.h
index 802b653..f1c0095d 100644
--- a/third_party/blink/renderer/modules/imagecapture/photo_capabilities.h
+++ b/third_party/blink/renderer/modules/imagecapture/photo_capabilities.h
@@ -17,8 +17,6 @@
   DEFINE_WRAPPERTYPEINFO();
 
  public:
-  static PhotoCapabilities* Create();
-
   PhotoCapabilities() = default;
   ~PhotoCapabilities() override = default;
 
diff --git a/third_party/blink/renderer/modules/indexeddb/global_indexed_db.cc b/third_party/blink/renderer/modules/indexeddb/global_indexed_db.cc
index ea3e68c..2bfd373 100644
--- a/third_party/blink/renderer/modules/indexeddb/global_indexed_db.cc
+++ b/third_party/blink/renderer/modules/indexeddb/global_indexed_db.cc
@@ -37,7 +37,7 @@
 
   IDBFactory* IdbFactory(T& fetching_scope) {
     if (!idb_factory_)
-      idb_factory_ = IDBFactory::Create();
+      idb_factory_ = MakeGarbageCollected<IDBFactory>();
     return idb_factory_;
   }
 
diff --git a/third_party/blink/renderer/modules/indexeddb/idb_cursor.cc b/third_party/blink/renderer/modules/indexeddb/idb_cursor.cc
index 7e97b25..c63514a 100644
--- a/third_party/blink/renderer/modules/indexeddb/idb_cursor.cc
+++ b/third_party/blink/renderer/modules/indexeddb/idb_cursor.cc
@@ -43,15 +43,6 @@
 
 namespace blink {
 
-IDBCursor* IDBCursor::Create(std::unique_ptr<WebIDBCursor> backend,
-                             mojom::IDBCursorDirection direction,
-                             IDBRequest* request,
-                             const Source& source,
-                             IDBTransaction* transaction) {
-  return MakeGarbageCollected<IDBCursor>(std::move(backend), direction, request,
-                                         source, transaction);
-}
-
 IDBCursor::IDBCursor(std::unique_ptr<WebIDBCursor> backend,
                      mojom::IDBCursorDirection direction,
                      IDBRequest* request,
diff --git a/third_party/blink/renderer/modules/indexeddb/idb_cursor.h b/third_party/blink/renderer/modules/indexeddb/idb_cursor.h
index f5a1ba8..6df0b0a9 100644
--- a/third_party/blink/renderer/modules/indexeddb/idb_cursor.h
+++ b/third_party/blink/renderer/modules/indexeddb/idb_cursor.h
@@ -52,12 +52,6 @@
 
   static mojom::IDBCursorDirection StringToDirection(const String& mode_string);
 
-  static IDBCursor* Create(std::unique_ptr<WebIDBCursor>,
-                           mojom::IDBCursorDirection,
-                           IDBRequest*,
-                           const Source&,
-                           IDBTransaction*);
-
   IDBCursor(std::unique_ptr<WebIDBCursor>,
             mojom::IDBCursorDirection,
             IDBRequest*,
diff --git a/third_party/blink/renderer/modules/indexeddb/idb_cursor_with_value.cc b/third_party/blink/renderer/modules/indexeddb/idb_cursor_with_value.cc
index 7df7371..101e265f 100644
--- a/third_party/blink/renderer/modules/indexeddb/idb_cursor_with_value.cc
+++ b/third_party/blink/renderer/modules/indexeddb/idb_cursor_with_value.cc
@@ -30,16 +30,6 @@
 
 namespace blink {
 
-IDBCursorWithValue* IDBCursorWithValue::Create(
-    std::unique_ptr<WebIDBCursor> backend,
-    mojom::IDBCursorDirection direction,
-    IDBRequest* request,
-    const Source& source,
-    IDBTransaction* transaction) {
-  return MakeGarbageCollected<IDBCursorWithValue>(std::move(backend), direction,
-                                                  request, source, transaction);
-}
-
 IDBCursorWithValue::IDBCursorWithValue(std::unique_ptr<WebIDBCursor> backend,
                                        mojom::IDBCursorDirection direction,
                                        IDBRequest* request,
diff --git a/third_party/blink/renderer/modules/indexeddb/idb_cursor_with_value.h b/third_party/blink/renderer/modules/indexeddb/idb_cursor_with_value.h
index 4c0deb78..559640e 100644
--- a/third_party/blink/renderer/modules/indexeddb/idb_cursor_with_value.h
+++ b/third_party/blink/renderer/modules/indexeddb/idb_cursor_with_value.h
@@ -41,12 +41,6 @@
   DEFINE_WRAPPERTYPEINFO();
 
  public:
-  static IDBCursorWithValue* Create(std::unique_ptr<WebIDBCursor>,
-                                    mojom::IDBCursorDirection,
-                                    IDBRequest*,
-                                    const Source&,
-                                    IDBTransaction*);
-
   IDBCursorWithValue(std::unique_ptr<WebIDBCursor>,
                      mojom::IDBCursorDirection,
                      IDBRequest*,
diff --git a/third_party/blink/renderer/modules/indexeddb/idb_database.cc b/third_party/blink/renderer/modules/indexeddb/idb_database.cc
index cc2f5293..3fad0b87 100644
--- a/third_party/blink/renderer/modules/indexeddb/idb_database.cc
+++ b/third_party/blink/renderer/modules/indexeddb/idb_database.cc
@@ -92,14 +92,6 @@
 const char IDBDatabase::kDatabaseClosedErrorMessage[] =
     "The database connection is closed.";
 
-IDBDatabase* IDBDatabase::Create(ExecutionContext* context,
-                                 std::unique_ptr<WebIDBDatabase> database,
-                                 IDBDatabaseCallbacks* callbacks,
-                                 v8::Isolate* isolate) {
-  return MakeGarbageCollected<IDBDatabase>(context, std::move(database),
-                                           callbacks, isolate);
-}
-
 IDBDatabase::IDBDatabase(ExecutionContext* context,
                          std::unique_ptr<WebIDBDatabase> backend,
                          IDBDatabaseCallbacks* callbacks,
@@ -210,8 +202,8 @@
       }
 
       observer->Callback()->InvokeAndReportException(
-          observer, IDBObserverChanges::Create(this, nullptr, observations,
-                                               map_entry.second));
+          observer, MakeGarbageCollected<IDBObserverChanges>(
+                        this, nullptr, observations, map_entry.second));
     }
   }
 }
@@ -308,8 +300,8 @@
       base::AdoptRef(new IDBObjectStoreMetadata(
           name, object_store_id, key_path, auto_increment,
           WebIDBDatabase::kMinimumIndexId));
-  IDBObjectStore* object_store =
-      IDBObjectStore::Create(store_metadata, version_change_transaction_.Get());
+  auto* object_store = MakeGarbageCollected<IDBObjectStore>(
+      store_metadata, version_change_transaction_.Get());
   version_change_transaction_->ObjectStoreCreated(name, object_store);
   metadata_.object_stores.Set(object_store_id, std::move(store_metadata));
   ++metadata_.max_object_store_id;
diff --git a/third_party/blink/renderer/modules/indexeddb/idb_database.h b/third_party/blink/renderer/modules/indexeddb/idb_database.h
index 6e86526..2020565 100644
--- a/third_party/blink/renderer/modules/indexeddb/idb_database.h
+++ b/third_party/blink/renderer/modules/indexeddb/idb_database.h
@@ -63,11 +63,6 @@
   DEFINE_WRAPPERTYPEINFO();
 
  public:
-  static IDBDatabase* Create(ExecutionContext*,
-                             std::unique_ptr<WebIDBDatabase>,
-                             IDBDatabaseCallbacks*,
-                             v8::Isolate*);
-
   IDBDatabase(ExecutionContext*,
               std::unique_ptr<WebIDBDatabase>,
               IDBDatabaseCallbacks*,
diff --git a/third_party/blink/renderer/modules/indexeddb/idb_database_callbacks.cc b/third_party/blink/renderer/modules/indexeddb/idb_database_callbacks.cc
index 88d4cb7..cf324f4f 100644
--- a/third_party/blink/renderer/modules/indexeddb/idb_database_callbacks.cc
+++ b/third_party/blink/renderer/modules/indexeddb/idb_database_callbacks.cc
@@ -30,10 +30,6 @@
 
 namespace blink {
 
-IDBDatabaseCallbacks* IDBDatabaseCallbacks::Create() {
-  return MakeGarbageCollected<IDBDatabaseCallbacks>();
-}
-
 IDBDatabaseCallbacks::IDBDatabaseCallbacks() : database_(nullptr) {}
 
 IDBDatabaseCallbacks::~IDBDatabaseCallbacks() = default;
diff --git a/third_party/blink/renderer/modules/indexeddb/idb_database_callbacks.h b/third_party/blink/renderer/modules/indexeddb/idb_database_callbacks.h
index 1da199a..13a020c 100644
--- a/third_party/blink/renderer/modules/indexeddb/idb_database_callbacks.h
+++ b/third_party/blink/renderer/modules/indexeddb/idb_database_callbacks.h
@@ -41,8 +41,6 @@
 class MODULES_EXPORT IDBDatabaseCallbacks
     : public GarbageCollectedFinalized<IDBDatabaseCallbacks> {
  public:
-  static IDBDatabaseCallbacks* Create();
-
   IDBDatabaseCallbacks();
   virtual ~IDBDatabaseCallbacks();
   void Trace(blink::Visitor*);
diff --git a/third_party/blink/renderer/modules/indexeddb/idb_factory.cc b/third_party/blink/renderer/modules/indexeddb/idb_factory.cc
index f352f44..1e35a4b 100644
--- a/third_party/blink/renderer/modules/indexeddb/idb_factory.cc
+++ b/third_party/blink/renderer/modules/indexeddb/idb_factory.cc
@@ -316,7 +316,7 @@
                       WebFeature::kFileAccessedDatabase);
   }
 
-  IDBDatabaseCallbacks* database_callbacks = IDBDatabaseCallbacks::Create();
+  auto* database_callbacks = MakeGarbageCollected<IDBDatabaseCallbacks>();
   int64_t transaction_id = IDBDatabase::NextTransactionId();
 
   auto transaction_backend = std::make_unique<WebIDBTransactionImpl>(
@@ -325,7 +325,7 @@
       transaction_id);
   mojom::blink::IDBTransactionAssociatedRequest transaction_request =
       transaction_backend->CreateRequest();
-  IDBOpenDBRequest* request = IDBOpenDBRequest::Create(
+  auto* request = MakeGarbageCollected<IDBOpenDBRequest>(
       script_state, database_callbacks, std::move(transaction_backend),
       transaction_id, version, std::move(metrics));
 
@@ -393,7 +393,7 @@
                       WebFeature::kFileAccessedDatabase);
   }
 
-  IDBOpenDBRequest* request = IDBOpenDBRequest::Create(
+  auto* request = MakeGarbageCollected<IDBOpenDBRequest>(
       script_state, nullptr, /*IDBTransactionAssociatedPtr=*/nullptr, 0,
       IDBDatabaseMetadata::kDefaultVersion, std::move(metrics));
 
diff --git a/third_party/blink/renderer/modules/indexeddb/idb_factory.h b/third_party/blink/renderer/modules/indexeddb/idb_factory.h
index 825ff1d6..461aa05 100644
--- a/third_party/blink/renderer/modules/indexeddb/idb_factory.h
+++ b/third_party/blink/renderer/modules/indexeddb/idb_factory.h
@@ -46,12 +46,6 @@
   DEFINE_WRAPPERTYPEINFO();
 
  public:
-  static IDBFactory* Create() { return MakeGarbageCollected<IDBFactory>(); }
-  static IDBFactory* CreateForTest(
-      std::unique_ptr<WebIDBFactory> web_idb_factory) {
-    return MakeGarbageCollected<IDBFactory>(std::move(web_idb_factory));
-  }
-
   IDBFactory();
   IDBFactory(std::unique_ptr<WebIDBFactory>);
 
diff --git a/third_party/blink/renderer/modules/indexeddb/idb_factory_test.cc b/third_party/blink/renderer/modules/indexeddb/idb_factory_test.cc
index c50291e8..bbe65a2 100644
--- a/third_party/blink/renderer/modules/indexeddb/idb_factory_test.cc
+++ b/third_party/blink/renderer/modules/indexeddb/idb_factory_test.cc
@@ -61,7 +61,7 @@
   auto web_factory = std::make_unique<MockWebIDBFactory>();
   std::unique_ptr<WebIDBCallbacks> callbacks;
   web_factory->SetCallbacksPointer(&callbacks);
-  IDBFactory* factory = IDBFactory::CreateForTest(std::move(web_factory));
+  auto* factory = MakeGarbageCollected<IDBFactory>(std::move(web_factory));
 
   DummyExceptionStateForTesting exception_state;
   ScriptPromise promise =
@@ -94,7 +94,7 @@
   auto web_factory = std::make_unique<MockWebIDBFactory>();
   std::unique_ptr<WebIDBCallbacks> callbacks;
   web_factory->SetCallbacksPointer(&callbacks);
-  IDBFactory* factory = IDBFactory::CreateForTest(std::move(web_factory));
+  auto* factory = MakeGarbageCollected<IDBFactory>(std::move(web_factory));
 
   DummyExceptionStateForTesting exception_state;
   ScriptPromise promise =
diff --git a/third_party/blink/renderer/modules/indexeddb/idb_index.h b/third_party/blink/renderer/modules/indexeddb/idb_index.h
index 751a75b8..221839a1 100644
--- a/third_party/blink/renderer/modules/indexeddb/idb_index.h
+++ b/third_party/blink/renderer/modules/indexeddb/idb_index.h
@@ -48,13 +48,6 @@
   DEFINE_WRAPPERTYPEINFO();
 
  public:
-  static IDBIndex* Create(scoped_refptr<IDBIndexMetadata> metadata,
-                          IDBObjectStore* object_store,
-                          IDBTransaction* transaction) {
-    return MakeGarbageCollected<IDBIndex>(std::move(metadata), object_store,
-                                          transaction);
-  }
-
   IDBIndex(scoped_refptr<IDBIndexMetadata>, IDBObjectStore*, IDBTransaction*);
   ~IDBIndex() override;
 
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 d1175be..3e04775 100644
--- a/third_party/blink/renderer/modules/indexeddb/idb_object_store.cc
+++ b/third_party/blink/renderer/modules/indexeddb/idb_object_store.cc
@@ -694,17 +694,6 @@
 // cursor success handlers are kept alive.
 class IndexPopulator final : public NativeEventListener {
  public:
-  static IndexPopulator* Create(
-      ScriptState* script_state,
-      IDBDatabase* database,
-      int64_t transaction_id,
-      int64_t object_store_id,
-      scoped_refptr<const IDBIndexMetadata> index_metadata) {
-    return MakeGarbageCollected<IndexPopulator>(script_state, database,
-                                                transaction_id, object_store_id,
-                                                std::move(index_metadata));
-  }
-
   IndexPopulator(ScriptState* script_state,
                  IDBDatabase* database,
                  int64_t transaction_id,
@@ -843,7 +832,8 @@
   scoped_refptr<IDBIndexMetadata> index_metadata =
       base::AdoptRef(new IDBIndexMetadata(
           name, index_id, key_path, options->unique(), options->multiEntry()));
-  IDBIndex* index = IDBIndex::Create(index_metadata, this, transaction_.Get());
+  auto* index =
+      MakeGarbageCollected<IDBIndex>(index_metadata, this, transaction_.Get());
   index_map_.Set(name, index);
   metadata_->indexes.Set(index_id, index_metadata);
 
@@ -858,7 +848,7 @@
 
   // This is kept alive by being the success handler of the request, which is in
   // turn kept alive by the owning transaction.
-  IndexPopulator* index_populator = IndexPopulator::Create(
+  auto* index_populator = MakeGarbageCollected<IndexPopulator>(
       script_state, transaction()->db(), transaction_->Id(), Id(),
       std::move(index_metadata));
   index_request->setOnsuccess(index_populator);
@@ -896,8 +886,8 @@
   scoped_refptr<IDBIndexMetadata> index_metadata =
       Metadata().indexes.at(index_id);
   DCHECK(index_metadata.get());
-  IDBIndex* index =
-      IDBIndex::Create(std::move(index_metadata), this, transaction_.Get());
+  auto* index = MakeGarbageCollected<IDBIndex>(std::move(index_metadata), this,
+                                               transaction_.Get());
   index_map_.Set(name, index);
   return index;
 }
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 a91472d4..8581ce9 100644
--- a/third_party/blink/renderer/modules/indexeddb/idb_object_store.h
+++ b/third_party/blink/renderer/modules/indexeddb/idb_object_store.h
@@ -51,12 +51,6 @@
   DEFINE_WRAPPERTYPEINFO();
 
  public:
-  static IDBObjectStore* Create(scoped_refptr<IDBObjectStoreMetadata> metadata,
-                                IDBTransaction* transaction) {
-    return MakeGarbageCollected<IDBObjectStore>(std::move(metadata),
-                                                transaction);
-  }
-
   IDBObjectStore(scoped_refptr<IDBObjectStoreMetadata>, IDBTransaction*);
   ~IDBObjectStore() override = default;
 
diff --git a/third_party/blink/renderer/modules/indexeddb/idb_observation.cc b/third_party/blink/renderer/modules/indexeddb/idb_observation.cc
index 6abddba..030bc57 100644
--- a/third_party/blink/renderer/modules/indexeddb/idb_observation.cc
+++ b/third_party/blink/renderer/modules/indexeddb/idb_observation.cc
@@ -65,14 +65,6 @@
   }
 }
 
-IDBObservation* IDBObservation::Create(int64_t object_store_id,
-                                       mojom::IDBOperationType type,
-                                       IDBKeyRange* key_range,
-                                       std::unique_ptr<IDBValue> value) {
-  return MakeGarbageCollected<IDBObservation>(object_store_id, type, key_range,
-                                              std::move(value));
-}
-
 IDBObservation::IDBObservation(int64_t object_store_id,
                                mojom::IDBOperationType type,
                                IDBKeyRange* key_range,
diff --git a/third_party/blink/renderer/modules/indexeddb/idb_observation.h b/third_party/blink/renderer/modules/indexeddb/idb_observation.h
index 8964fd9e..831fae9 100644
--- a/third_party/blink/renderer/modules/indexeddb/idb_observation.h
+++ b/third_party/blink/renderer/modules/indexeddb/idb_observation.h
@@ -25,11 +25,6 @@
  public:
   static mojom::IDBOperationType StringToOperationType(const String&);
 
-  static IDBObservation* Create(int64_t object_store_id,
-                                mojom::IDBOperationType type,
-                                IDBKeyRange* key_range,
-                                std::unique_ptr<IDBValue> value);
-
   IDBObservation(int64_t object_store_id,
                  mojom::IDBOperationType type,
                  IDBKeyRange* key_range,
diff --git a/third_party/blink/renderer/modules/indexeddb/idb_observer_changes.cc b/third_party/blink/renderer/modules/indexeddb/idb_observer_changes.cc
index cbf4b733..f8b1e48 100644
--- a/third_party/blink/renderer/modules/indexeddb/idb_observer_changes.cc
+++ b/third_party/blink/renderer/modules/indexeddb/idb_observer_changes.cc
@@ -28,15 +28,6 @@
   return ScriptValue::From(script_state, map);
 }
 
-IDBObserverChanges* IDBObserverChanges::Create(
-    IDBDatabase* database,
-    IDBTransaction* transaction,
-    const Vector<Persistent<IDBObservation>>& observations,
-    const Vector<int32_t>& observation_indices) {
-  return MakeGarbageCollected<IDBObserverChanges>(
-      database, transaction, observations, observation_indices);
-}
-
 IDBObserverChanges::IDBObserverChanges(
     IDBDatabase* database,
     IDBTransaction* transaction,
diff --git a/third_party/blink/renderer/modules/indexeddb/idb_observer_changes.h b/third_party/blink/renderer/modules/indexeddb/idb_observer_changes.h
index aa7a3a0..874589d 100644
--- a/third_party/blink/renderer/modules/indexeddb/idb_observer_changes.h
+++ b/third_party/blink/renderer/modules/indexeddb/idb_observer_changes.h
@@ -20,12 +20,6 @@
   DEFINE_WRAPPERTYPEINFO();
 
  public:
-  static IDBObserverChanges* Create(
-      IDBDatabase*,
-      IDBTransaction*,
-      const Vector<Persistent<IDBObservation>>& observations,
-      const Vector<int32_t>& observation_indices);
-
   IDBObserverChanges(IDBDatabase*,
                      IDBTransaction*,
                      const Vector<Persistent<IDBObservation>>& observations,
diff --git a/third_party/blink/renderer/modules/indexeddb/idb_open_db_request.cc b/third_party/blink/renderer/modules/indexeddb/idb_open_db_request.cc
index c71ac004..5b2d5db3 100644
--- a/third_party/blink/renderer/modules/indexeddb/idb_open_db_request.cc
+++ b/third_party/blink/renderer/modules/indexeddb/idb_open_db_request.cc
@@ -38,18 +38,6 @@
 
 namespace blink {
 
-IDBOpenDBRequest* IDBOpenDBRequest::Create(
-    ScriptState* script_state,
-    IDBDatabaseCallbacks* callbacks,
-    std::unique_ptr<WebIDBTransaction> transaction_backend,
-    int64_t transaction_id,
-    int64_t version,
-    IDBRequest::AsyncTraceState metrics) {
-  return MakeGarbageCollected<IDBOpenDBRequest>(
-      script_state, callbacks, std::move(transaction_backend), transaction_id,
-      version, std::move(metrics));
-}
-
 IDBOpenDBRequest::IDBOpenDBRequest(
     ScriptState* script_state,
     IDBDatabaseCallbacks* callbacks,
@@ -111,9 +99,9 @@
 
   DCHECK(database_callbacks_);
 
-  IDBDatabase* idb_database =
-      IDBDatabase::Create(GetExecutionContext(), std::move(backend),
-                          database_callbacks_.Release(), isolate_);
+  auto* idb_database = MakeGarbageCollected<IDBDatabase>(
+      GetExecutionContext(), std::move(backend), database_callbacks_.Release(),
+      isolate_);
   idb_database->SetMetadata(metadata);
 
   if (old_version == IDBDatabaseMetadata::kNoVersion) {
@@ -153,9 +141,9 @@
   } else {
     DCHECK(backend.get());
     DCHECK(database_callbacks_);
-    idb_database =
-        IDBDatabase::Create(GetExecutionContext(), std::move(backend),
-                            database_callbacks_.Release(), isolate_);
+    idb_database = MakeGarbageCollected<IDBDatabase>(
+        GetExecutionContext(), std::move(backend),
+        database_callbacks_.Release(), isolate_);
     SetResult(IDBAny::Create(idb_database));
   }
   idb_database->SetMetadata(metadata);
diff --git a/third_party/blink/renderer/modules/indexeddb/idb_open_db_request.h b/third_party/blink/renderer/modules/indexeddb/idb_open_db_request.h
index 2db74510..57fa3f5 100644
--- a/third_party/blink/renderer/modules/indexeddb/idb_open_db_request.h
+++ b/third_party/blink/renderer/modules/indexeddb/idb_open_db_request.h
@@ -39,14 +39,6 @@
   DEFINE_WRAPPERTYPEINFO();
 
  public:
-  static IDBOpenDBRequest* Create(
-      ScriptState*,
-      IDBDatabaseCallbacks*,
-      std::unique_ptr<WebIDBTransaction> transaction_backend,
-      int64_t transaction_id,
-      int64_t version,
-      IDBRequest::AsyncTraceState metrics);
-
   IDBOpenDBRequest(ScriptState*,
                    IDBDatabaseCallbacks*,
                    std::unique_ptr<WebIDBTransaction> transaction_backend,
diff --git a/third_party/blink/renderer/modules/indexeddb/idb_request.cc b/third_party/blink/renderer/modules/indexeddb/idb_request.cc
index bb5fdaa7..7ca6c4f 100644
--- a/third_party/blink/renderer/modules/indexeddb/idb_request.cc
+++ b/third_party/blink/renderer/modules/indexeddb/idb_request.cc
@@ -458,12 +458,14 @@
 
   switch (cursor_type_) {
     case indexed_db::kCursorKeyOnly:
-      cursor = IDBCursor::Create(std::move(backend), cursor_direction_, this,
-                                 source, transaction_.Get());
+      cursor =
+          MakeGarbageCollected<IDBCursor>(std::move(backend), cursor_direction_,
+                                          this, source, transaction_.Get());
       break;
     case indexed_db::kCursorKeyAndValue:
-      cursor = IDBCursorWithValue::Create(std::move(backend), cursor_direction_,
-                                          this, source, transaction_.Get());
+      cursor = MakeGarbageCollected<IDBCursorWithValue>(
+          std::move(backend), cursor_direction_, this, source,
+          transaction_.Get());
       break;
     default:
       NOTREACHED();
diff --git a/third_party/blink/renderer/modules/indexeddb/idb_request_test.cc b/third_party/blink/renderer/modules/indexeddb/idb_request_test.cc
index f3e048a9..454fde5 100644
--- a/third_party/blink/renderer/modules/indexeddb/idb_request_test.cc
+++ b/third_party/blink/renderer/modules/indexeddb/idb_request_test.cc
@@ -181,9 +181,9 @@
       V8TestingScope& scope,
       std::unique_ptr<MockWebIDBDatabase> database_backend,
       std::unique_ptr<MockWebIDBTransaction> transaction_backend) {
-    db_ = IDBDatabase::Create(
+    db_ = MakeGarbageCollected<IDBDatabase>(
         scope.GetExecutionContext(), std::move(database_backend),
-        IDBDatabaseCallbacks::Create(), scope.GetIsolate());
+        MakeGarbageCollected<IDBDatabaseCallbacks>(), scope.GetIsolate());
 
     HashSet<String> transaction_scope = {"store"};
     transaction_ = IDBTransaction::CreateNonVersionChange(
@@ -193,7 +193,7 @@
     IDBKeyPath store_key_path("primaryKey");
     scoped_refptr<IDBObjectStoreMetadata> store_metadata = base::AdoptRef(
         new IDBObjectStoreMetadata("store", kStoreId, store_key_path, true, 1));
-    store_ = IDBObjectStore::Create(store_metadata, transaction_);
+    store_ = MakeGarbageCollected<IDBObjectStore>(store_metadata, transaction_);
   }
 
   WebURLLoaderMockFactory* url_loader_mock_factory_;
@@ -374,7 +374,8 @@
   const int64_t kVersion = 1;
   const int64_t kOldVersion = 0;
   const IDBDatabaseMetadata metadata;
-  Persistent<IDBDatabaseCallbacks> callbacks = IDBDatabaseCallbacks::Create();
+  Persistent<IDBDatabaseCallbacks> callbacks =
+      MakeGarbageCollected<IDBDatabaseCallbacks>();
 
   {
     mojom::blink::IDBDatabaseAssociatedPtr ptr;
@@ -386,7 +387,7 @@
     auto transaction_backend = std::make_unique<MockWebIDBTransaction>(
         scope.GetExecutionContext()->GetTaskRunner(TaskType::kDatabaseAccess),
         kTransactionId);
-    IDBOpenDBRequest* request = IDBOpenDBRequest::Create(
+    auto* request = MakeGarbageCollected<IDBOpenDBRequest>(
         scope.GetScriptState(), callbacks, std::move(transaction_backend),
         kTransactionId, kVersion, IDBRequest::AsyncTraceState());
     EXPECT_EQ(request->readyState(), "pending");
@@ -408,7 +409,7 @@
     auto transaction_backend = std::make_unique<MockWebIDBTransaction>(
         scope.GetExecutionContext()->GetTaskRunner(TaskType::kDatabaseAccess),
         kTransactionId);
-    IDBOpenDBRequest* request = IDBOpenDBRequest::Create(
+    auto* request = MakeGarbageCollected<IDBOpenDBRequest>(
         scope.GetScriptState(), callbacks, std::move(transaction_backend),
         kTransactionId, kVersion, IDBRequest::AsyncTraceState());
     EXPECT_EQ(request->readyState(), "pending");
diff --git a/third_party/blink/renderer/modules/indexeddb/idb_transaction.cc b/third_party/blink/renderer/modules/indexeddb/idb_transaction.cc
index 97dadd3..a16a2be 100644
--- a/third_party/blink/renderer/modules/indexeddb/idb_transaction.cc
+++ b/third_party/blink/renderer/modules/indexeddb/idb_transaction.cc
@@ -193,8 +193,8 @@
       database_->Metadata().object_stores.at(object_store_id);
   DCHECK(object_store_metadata.get());
 
-  IDBObjectStore* object_store =
-      IDBObjectStore::Create(std::move(object_store_metadata), this);
+  auto* object_store = MakeGarbageCollected<IDBObjectStore>(
+      std::move(object_store_metadata), this);
   DCHECK(!object_store_map_.Contains(name));
   object_store_map_.Set(name, object_store);
 
diff --git a/third_party/blink/renderer/modules/indexeddb/idb_transaction_test.cc b/third_party/blink/renderer/modules/indexeddb/idb_transaction_test.cc
index b6cd7dd2..cc1a1f3 100644
--- a/third_party/blink/renderer/modules/indexeddb/idb_transaction_test.cc
+++ b/third_party/blink/renderer/modules/indexeddb/idb_transaction_test.cc
@@ -64,9 +64,6 @@
 
 class FakeIDBDatabaseCallbacks final : public IDBDatabaseCallbacks {
  public:
-  static FakeIDBDatabaseCallbacks* Create() {
-    return MakeGarbageCollected<FakeIDBDatabaseCallbacks>();
-  }
   FakeIDBDatabaseCallbacks() = default;
   void OnVersionChange(int64_t old_version, int64_t new_version) override {}
   void OnForcedClose() override {}
@@ -92,9 +89,9 @@
       V8TestingScope& scope,
       std::unique_ptr<MockWebIDBDatabase> database_backend,
       std::unique_ptr<MockWebIDBTransaction> transaction_backend) {
-    db_ = IDBDatabase::Create(
+    db_ = MakeGarbageCollected<IDBDatabase>(
         scope.GetExecutionContext(), std::move(database_backend),
-        FakeIDBDatabaseCallbacks::Create(), scope.GetIsolate());
+        MakeGarbageCollected<FakeIDBDatabaseCallbacks>(), scope.GetIsolate());
 
     HashSet<String> transaction_scope = {"store"};
     transaction_ = IDBTransaction::CreateNonVersionChange(
@@ -104,7 +101,7 @@
     IDBKeyPath store_key_path("primaryKey");
     scoped_refptr<IDBObjectStoreMetadata> store_metadata = base::AdoptRef(
         new IDBObjectStoreMetadata("store", kStoreId, store_key_path, true, 1));
-    store_ = IDBObjectStore::Create(store_metadata, transaction_);
+    store_ = MakeGarbageCollected<IDBObjectStore>(store_metadata, transaction_);
   }
 
   WebURLLoaderMockFactory* url_loader_mock_factory_;
diff --git a/third_party/blink/renderer/modules/indexeddb/indexed_db_client.cc b/third_party/blink/renderer/modules/indexeddb/indexed_db_client.cc
index f2f3785..723c11b 100644
--- a/third_party/blink/renderer/modules/indexeddb/indexed_db_client.cc
+++ b/third_party/blink/renderer/modules/indexeddb/indexed_db_client.cc
@@ -17,14 +17,6 @@
 
 namespace blink {
 
-IndexedDBClient* IndexedDBClient::Create(LocalFrame& frame) {
-  return MakeGarbageCollected<IndexedDBClient>(frame);
-}
-
-IndexedDBClient* IndexedDBClient::Create(WorkerClients& worker_clients) {
-  return MakeGarbageCollected<IndexedDBClient>(worker_clients);
-}
-
 IndexedDBClient::IndexedDBClient(LocalFrame& frame)
     : Supplement<LocalFrame>(frame) {}
 
diff --git a/third_party/blink/renderer/modules/indexeddb/indexed_db_client.h b/third_party/blink/renderer/modules/indexeddb/indexed_db_client.h
index 948d42d..77e52b5 100644
--- a/third_party/blink/renderer/modules/indexeddb/indexed_db_client.h
+++ b/third_party/blink/renderer/modules/indexeddb/indexed_db_client.h
@@ -52,9 +52,6 @@
  public:
   static const char kSupplementName[];
 
-  static IndexedDBClient* Create(LocalFrame&);
-  static IndexedDBClient* Create(WorkerClients&);
-
   explicit IndexedDBClient(LocalFrame&);
   explicit IndexedDBClient(WorkerClients&);
 
diff --git a/third_party/blink/renderer/modules/indexeddb/indexed_db_database_callbacks_impl.cc b/third_party/blink/renderer/modules/indexeddb/indexed_db_database_callbacks_impl.cc
index 22aa44a..c92e2f13 100644
--- a/third_party/blink/renderer/modules/indexeddb/indexed_db_database_callbacks_impl.cc
+++ b/third_party/blink/renderer/modules/indexeddb/indexed_db_database_callbacks_impl.cc
@@ -53,9 +53,9 @@
       value = std::make_unique<IDBValue>(scoped_refptr<SharedBuffer>(),
                                          Vector<WebBlobInfo>());
     }
-    observations.emplace_back(
-        IDBObservation::Create(observation->object_store_id, observation->type,
-                               key_range, std::move(value)));
+    observations.emplace_back(MakeGarbageCollected<IDBObservation>(
+        observation->object_store_id, observation->type, key_range,
+        std::move(value)));
   }
 
   std::unordered_map<int32_t, Vector<int32_t>> observation_index_map;
diff --git a/third_party/blink/renderer/modules/indexeddb/inspector_indexed_db_agent.cc b/third_party/blink/renderer/modules/indexeddb/inspector_indexed_db_agent.cc
index d0d970f..3c4bf94 100644
--- a/third_party/blink/renderer/modules/indexeddb/inspector_indexed_db_agent.cc
+++ b/third_party/blink/renderer/modules/indexeddb/inspector_indexed_db_agent.cc
@@ -111,13 +111,6 @@
 
 class GetDatabaseNamesCallback final : public NativeEventListener {
  public:
-  static GetDatabaseNamesCallback* Create(
-      std::unique_ptr<RequestDatabaseNamesCallback> request_callback,
-      const String& security_origin) {
-    return MakeGarbageCollected<GetDatabaseNamesCallback>(
-        std::move(request_callback), security_origin);
-  }
-
   GetDatabaseNamesCallback(
       std::unique_ptr<RequestDatabaseNamesCallback> request_callback,
       const String& security_origin)
@@ -154,13 +147,6 @@
 
 class DeleteCallback final : public NativeEventListener {
  public:
-  static DeleteCallback* Create(
-      std::unique_ptr<DeleteDatabaseCallback> request_callback,
-      const String& security_origin) {
-    return MakeGarbageCollected<DeleteCallback>(std::move(request_callback),
-                                                security_origin);
-  }
-
   DeleteCallback(std::unique_ptr<DeleteDatabaseCallback> request_callback,
                  const String& security_origin)
       : request_callback_(std::move(request_callback)),
@@ -787,7 +773,7 @@
   }
   idb_request->addEventListener(
       event_type_names::kSuccess,
-      GetDatabaseNamesCallback::Create(
+      MakeGarbageCollected<GetDatabaseNamesCallback>(
           std::move(request_callback),
           document->GetSecurityOrigin()->ToRawString()),
       false);
@@ -970,12 +956,6 @@
 
 class DeleteObjectStoreEntriesListener final : public NativeEventListener {
  public:
-  static DeleteObjectStoreEntriesListener* Create(
-      std::unique_ptr<DeleteObjectStoreEntriesCallback> request_callback) {
-    return MakeGarbageCollected<DeleteObjectStoreEntriesListener>(
-        std::move(request_callback));
-  }
-
   DeleteObjectStoreEntriesListener(
       std::unique_ptr<DeleteObjectStoreEntriesCallback> request_callback)
       : request_callback_(std::move(request_callback)) {}
@@ -1035,7 +1015,8 @@
         idb_object_store->deleteFunction(script_state, idb_key_range_.Get());
     idb_request->addEventListener(
         event_type_names::kSuccess,
-        DeleteObjectStoreEntriesListener::Create(std::move(request_callback_)),
+        MakeGarbageCollected<DeleteObjectStoreEntriesListener>(
+            std::move(request_callback_)),
         false);
   }
 
@@ -1070,12 +1051,6 @@
 
 class ClearObjectStoreListener final : public NativeEventListener {
  public:
-  static ClearObjectStoreListener* Create(
-      std::unique_ptr<ClearObjectStoreCallback> request_callback) {
-    return MakeGarbageCollected<ClearObjectStoreListener>(
-        std::move(request_callback));
-  }
-
   ClearObjectStoreListener(
       std::unique_ptr<ClearObjectStoreCallback> request_callback)
       : request_callback_(std::move(request_callback)) {}
@@ -1138,7 +1113,9 @@
     }
     idb_transaction->addEventListener(
         event_type_names::kComplete,
-        ClearObjectStoreListener::Create(std::move(request_callback_)), false);
+        MakeGarbageCollected<ClearObjectStoreListener>(
+            std::move(request_callback_)),
+        false);
   }
 
   ClearObjectStoreCallback* GetRequestCallback() override {
@@ -1196,8 +1173,9 @@
   }
   idb_request->addEventListener(
       event_type_names::kSuccess,
-      DeleteCallback::Create(std::move(request_callback),
-                             document->GetSecurityOrigin()->ToRawString()),
+      MakeGarbageCollected<DeleteCallback>(
+          std::move(request_callback),
+          document->GetSecurityOrigin()->ToRawString()),
       false);
 }
 
diff --git a/third_party/blink/renderer/modules/mediastream/BUILD.gn b/third_party/blink/renderer/modules/mediastream/BUILD.gn
index 3ee2db8..dbfe955 100644
--- a/third_party/blink/renderer/modules/mediastream/BUILD.gn
+++ b/third_party/blink/renderer/modules/mediastream/BUILD.gn
@@ -48,6 +48,7 @@
     "user_media_request.cc",
     "user_media_request.h",
     "video_track_adapter.cc",
+    "video_track_adapter.h",
     "video_track_adapter_settings.cc",
     "web_media_stream_utils.cc",
   ]
diff --git a/third_party/blink/renderer/modules/mediastream/media_stream_video_source.cc b/third_party/blink/renderer/modules/mediastream/media_stream_video_source.cc
index e0a5467f..ac1efc2 100644
--- a/third_party/blink/renderer/modules/mediastream/media_stream_video_source.cc
+++ b/third_party/blink/renderer/modules/mediastream/media_stream_video_source.cc
@@ -20,7 +20,7 @@
 #include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/public/web/modules/mediastream/media_stream_constraints_util_video_device.h"
 #include "third_party/blink/public/web/modules/mediastream/media_stream_video_track.h"
-#include "third_party/blink/public/web/modules/mediastream/video_track_adapter.h"
+#include "third_party/blink/renderer/modules/mediastream/video_track_adapter.h"
 
 namespace blink {
 
diff --git a/third_party/blink/renderer/modules/mediastream/video_track_adapter.cc b/third_party/blink/renderer/modules/mediastream/video_track_adapter.cc
index 83f8f2cb..a45f52f 100644
--- a/third_party/blink/renderer/modules/mediastream/video_track_adapter.cc
+++ b/third_party/blink/renderer/modules/mediastream/video_track_adapter.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 "third_party/blink/public/web/modules/mediastream/video_track_adapter.h"
+#include "third_party/blink/renderer/modules/mediastream/video_track_adapter.h"
 
 #include <algorithm>
 #include <cmath>
@@ -488,7 +488,7 @@
 }
 
 VideoTrackAdapter::~VideoTrackAdapter() {
-  DCHECK(adapters_.empty());
+  DCHECK(adapters_.IsEmpty());
 }
 
 void VideoTrackAdapter::AddTrack(const MediaStreamVideoTrack* track,
@@ -671,7 +671,7 @@
 
 void VideoTrackAdapter::RemoveTrackOnIO(const MediaStreamVideoTrack* track) {
   DCHECK(io_task_runner_->BelongsToCurrentThread());
-  for (auto it = adapters_.begin(); it != adapters_.end(); ++it) {
+  for (auto* it = adapters_.begin(); it != adapters_.end(); ++it) {
     (*it)->RemoveCallbacks(track);
     if ((*it)->IsEmpty()) {
       adapters_.erase(it);
@@ -687,7 +687,7 @@
 
   VideoFrameResolutionAdapter::VideoTrackCallbacks track_callbacks;
   // Remove the track.
-  for (auto it = adapters_.begin(); it != adapters_.end(); ++it) {
+  for (auto* it = adapters_.begin(); it != adapters_.end(); ++it) {
     track_callbacks = (*it)->RemoveAndGetCallbacks(track);
     if (track_callbacks.frame_callback.is_null())
       continue;
@@ -721,7 +721,7 @@
       frame->natural_size().height() == source_frame_size_->width()) {
     is_device_rotated = true;
   }
-  if (adapters_.empty()) {
+  if (adapters_.IsEmpty()) {
     renderer_task_runner_->PostTask(
         FROM_HERE,
         base::BindOnce(frame_dropped_cb_,
diff --git a/third_party/blink/public/web/modules/mediastream/video_track_adapter.h b/third_party/blink/renderer/modules/mediastream/video_track_adapter.h
similarity index 90%
rename from third_party/blink/public/web/modules/mediastream/video_track_adapter.h
rename to third_party/blink/renderer/modules/mediastream/video_track_adapter.h
index 088ea41..76a6710 100644
--- a/third_party/blink/public/web/modules/mediastream/video_track_adapter.h
+++ b/third_party/blink/renderer/modules/mediastream/video_track_adapter.h
@@ -2,15 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef THIRD_PARTY_BLINK_PUBLIC_WEB_MODULES_MEDIASTREAM_VIDEO_TRACK_ADAPTER_H_
-#define THIRD_PARTY_BLINK_PUBLIC_WEB_MODULES_MEDIASTREAM_VIDEO_TRACK_ADAPTER_H_
+#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_MEDIASTREAM_VIDEO_TRACK_ADAPTER_H_
+#define THIRD_PARTY_BLINK_RENDERER_MODULES_MEDIASTREAM_VIDEO_TRACK_ADAPTER_H_
 
 #include <stdint.h>
 
-#include <vector>
-
+#include "base/callback.h"
 #include "base/macros.h"
-#include "base/memory/ref_counted.h"
 #include "base/optional.h"
 #include "base/single_thread_task_runner.h"
 #include "base/time/time.h"
@@ -18,7 +16,8 @@
 #include "third_party/blink/public/platform/modules/mediastream/media_stream_types.h"
 #include "third_party/blink/public/platform/web_common.h"
 #include "third_party/blink/public/web/modules/mediastream/media_stream_video_track.h"
-#include "ui/gfx/geometry/size.h"
+#include "third_party/blink/renderer/platform/wtf/thread_safe_ref_counted.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
 
 namespace blink {
 
@@ -34,9 +33,9 @@
 // Adaptations is done by wrapping the original media::VideoFrame in a new
 // media::VideoFrame with a new visible_rect and natural_size.
 class BLINK_EXPORT VideoTrackAdapter
-    : public base::RefCountedThreadSafe<VideoTrackAdapter> {
+    : public WTF::ThreadSafeRefCounted<VideoTrackAdapter> {
  public:
-  using OnMutedCallback = base::Callback<void(bool mute_state)>;
+  using OnMutedCallback = base::RepeatingCallback<void(bool mute_state)>;
 
   VideoTrackAdapter(
       scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
@@ -90,7 +89,7 @@
 
  private:
   virtual ~VideoTrackAdapter();
-  friend class base::RefCountedThreadSafe<VideoTrackAdapter>;
+  friend class WTF::ThreadSafeRefCounted<VideoTrackAdapter>;
 
   void AddTrackOnIO(const MediaStreamVideoTrack* track,
                     VideoCaptureDeliverFrameCB frame_callback,
@@ -127,7 +126,7 @@
   // It does the resolution adaptation and delivers frames to all registered
   // tracks.
   class VideoFrameResolutionAdapter;
-  using FrameAdapters = std::vector<scoped_refptr<VideoFrameResolutionAdapter>>;
+  using FrameAdapters = WTF::Vector<scoped_refptr<VideoFrameResolutionAdapter>>;
   FrameAdapters adapters_;
 
   // Set to true if frame monitoring has been started. It is only accessed on
@@ -152,4 +151,4 @@
 
 }  // namespace blink
 
-#endif  // THIRD_PARTY_BLINK_PUBLIC_WEB_MODULES_MEDIASTREAM_VIDEO_TRACK_ADAPTER_H_
+#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_MEDIASTREAM_VIDEO_TRACK_ADAPTER_H_
diff --git a/content/renderer/media/stream/video_track_adapter_unittest.cc b/third_party/blink/renderer/modules/mediastream/video_track_adapter_unittest.cc
similarity index 97%
rename from content/renderer/media/stream/video_track_adapter_unittest.cc
rename to third_party/blink/renderer/modules/mediastream/video_track_adapter_unittest.cc
index 1fae622..4afe674 100644
--- a/content/renderer/media/stream/video_track_adapter_unittest.cc
+++ b/third_party/blink/renderer/modules/mediastream/video_track_adapter_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 "third_party/blink/public/web/modules/mediastream/video_track_adapter.h"
+#include "third_party/blink/renderer/modules/mediastream/video_track_adapter.h"
 
 #include <limits>
 
@@ -10,10 +10,7 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/web/modules/mediastream/video_track_adapter_settings.h"
 
-namespace content {
-
-using blink::VideoTrackAdapter;
-using blink::VideoTrackAdapterSettings;
+namespace blink {
 
 // Most VideoTrackAdapter functionality is tested in MediaStreamVideoSourceTest.
 // These tests focus on the computation of cropped frame sizes in edge cases
@@ -192,4 +189,4 @@
   EXPECT_EQ(desired_size.height(), kInputHeight);
 }
 
-}  // namespace content
+}  // namespace blink
diff --git a/third_party/blink/renderer/modules/modules_initializer.cc b/third_party/blink/renderer/modules/modules_initializer.cc
index e8b2c295..bb7c448 100644
--- a/third_party/blink/renderer/modules/modules_initializer.cc
+++ b/third_party/blink/renderer/modules/modules_initializer.cc
@@ -178,7 +178,7 @@
   ProvidePushControllerTo(frame, client->PushClient());
   ProvideUserMediaTo(
       frame, std::make_unique<UserMediaClient>(client->UserMediaClient()));
-  ProvideIndexedDBClientTo(frame, IndexedDBClient::Create(frame));
+  ProvideIndexedDBClientTo(frame, MakeGarbageCollected<IndexedDBClient>(frame));
   ProvideLocalFileSystemTo(frame, std::make_unique<LocalFileSystemClient>());
   NavigatorContentUtils::ProvideTo(
       *frame.DomWindow()->navigator(),
@@ -201,7 +201,7 @@
 void ModulesInitializer::ProvideIndexedDBClientToWorker(
     WorkerClients& worker_clients) const {
   ::blink::ProvideIndexedDBClientToWorker(
-      &worker_clients, IndexedDBClient::Create(worker_clients));
+      &worker_clients, MakeGarbageCollected<IndexedDBClient>(worker_clients));
 }
 
 MediaControls* ModulesInitializer::CreateMediaControls(
diff --git a/third_party/blink/renderer/modules/payments/BUILD.gn b/third_party/blink/renderer/modules/payments/BUILD.gn
index 8c22515..d9fa9400 100644
--- a/third_party/blink/renderer/modules/payments/BUILD.gn
+++ b/third_party/blink/renderer/modules/payments/BUILD.gn
@@ -49,6 +49,8 @@
     "payment_state_resolver.h",
     "payments_validators.cc",
     "payments_validators.h",
+    "update_payment_details_function.cc",
+    "update_payment_details_function.h",
   ]
 
   deps = [
diff --git a/third_party/blink/renderer/modules/payments/payment_request.cc b/third_party/blink/renderer/modules/payments/payment_request.cc
index 3895841..83fb32e 100644
--- a/third_party/blink/renderer/modules/payments/payment_request.cc
+++ b/third_party/blink/renderer/modules/payments/payment_request.cc
@@ -54,6 +54,7 @@
 #include "third_party/blink/renderer/modules/payments/payment_shipping_option.h"
 #include "third_party/blink/renderer/modules/payments/payment_validation_errors.h"
 #include "third_party/blink/renderer/modules/payments/payments_validators.h"
+#include "third_party/blink/renderer/modules/payments/update_payment_details_function.h"
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
 #include "third_party/blink/renderer/platform/bindings/script_state.h"
 #include "third_party/blink/renderer/platform/mojo/mojo_helper.h"
@@ -210,10 +211,6 @@
 namespace blink {
 namespace {
 
-// If the website does not call complete() 60 seconds after show() has been
-// resolved, then behave as if the website called complete("fail").
-constexpr TimeDelta kCompleteTimeout = TimeDelta::FromSeconds(60);
-
 // Validates ShippingOption or PaymentItem, which happen to have identical
 // fields, except for "id", which is present only in ShippingOption.
 template <typename T>
@@ -791,6 +788,11 @@
 PaymentRequest::~PaymentRequest() = default;
 
 ScriptPromise PaymentRequest::show(ScriptState* script_state) {
+  return show(script_state, ScriptPromise());
+}
+
+ScriptPromise PaymentRequest::show(ScriptState* script_state,
+                                   ScriptPromise details_promise) {
   if (!script_state->ContextIsValid() || !LocalDOMWindow::From(script_state) ||
       !LocalDOMWindow::From(script_state)->GetFrame()) {
     return ScriptPromise::RejectWithDOMException(
@@ -820,7 +822,22 @@
                                            "Page popups are suppressed"));
   }
 
-  payment_provider_->Show(is_user_gesture);
+  is_waiting_for_show_promise_to_resolve_ = !details_promise.IsEmpty();
+  payment_provider_->Show(is_user_gesture,
+                          is_waiting_for_show_promise_to_resolve_);
+  if (is_waiting_for_show_promise_to_resolve_) {
+    // If the website does not calculate the final shopping cart contents within
+    // 10 seconds, abort payment.
+    update_payment_details_timer_.StartOneShot(TimeDelta::FromSeconds(10),
+                                               FROM_HERE);
+    details_promise.Then(
+        UpdatePaymentDetailsFunction::CreateFunction(
+            script_state, this,
+            UpdatePaymentDetailsFunction::ResolveType::kFulfill),
+        UpdatePaymentDetailsFunction::CreateFunction(
+            script_state, this,
+            UpdatePaymentDetailsFunction::ResolveType::kReject));
+  }
 
   accept_resolver_ = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
   return accept_resolver_->Promise();
@@ -1038,8 +1055,13 @@
 
 void PaymentRequest::OnUpdatePaymentDetails(
     const ScriptValue& details_script_value) {
-  if (!GetPendingAcceptPromiseResolver() || !payment_provider_)
+  ScriptPromiseResolver* resolver = GetPendingAcceptPromiseResolver();
+  if (!resolver || !payment_provider_ ||
+      !update_payment_details_timer_.IsActive()) {
     return;
+  }
+
+  update_payment_details_timer_.Stop();
 
   PaymentDetailsUpdate* details = PaymentDetailsUpdate::Create();
   ExceptionState exception_state(v8::Isolate::GetCurrent(),
@@ -1048,7 +1070,6 @@
   V8PaymentDetailsUpdate::ToImpl(details_script_value.GetIsolate(),
                                  details_script_value.V8Value(), details,
                                  exception_state);
-  ScriptPromiseResolver* resolver = GetPendingAcceptPromiseResolver();
   if (exception_state.HadException()) {
     resolver->Reject(exception_state.GetException());
     ClearResolversAndCloseMojoConnection();
@@ -1069,10 +1090,36 @@
   if (!options_->requestShipping())
     validated_details->shipping_options = base::nullopt;
 
+  if (is_waiting_for_show_promise_to_resolve_) {
+    is_waiting_for_show_promise_to_resolve_ = false;
+
+    if (!validated_details->total) {
+      resolver->Reject(
+          DOMException::Create(DOMExceptionCode::kInvalidStateError,
+                               "Must specify 'total' when resolving the "
+                               "promise passed into PaymentRequest.show()"));
+      ClearResolversAndCloseMojoConnection();
+      return;
+    }
+
+    if (!validated_details->error.IsEmpty()) {
+      resolver->Reject(
+          DOMException::Create(DOMExceptionCode::kInvalidStateError,
+                               "Cannot specify 'error' when resolving the "
+                               "promise passed into PaymentRequest.show()"));
+      ClearResolversAndCloseMojoConnection();
+      return;
+    }
+  }
+
   payment_provider_->UpdateWith(std::move(validated_details));
 }
 
 void PaymentRequest::OnUpdatePaymentDetailsFailure(const String& error) {
+  if (!payment_provider_)
+    return;
+  if (update_payment_details_timer_.IsActive())
+    update_payment_details_timer_.Stop();
   ScriptPromiseResolver* resolver = GetPendingAcceptPromiseResolver();
   if (resolver) {
     resolver->Reject(
@@ -1108,6 +1155,11 @@
   OnCompleteTimeout(nullptr);
 }
 
+void PaymentRequest::OnUpdatePaymentDetailsTimeoutForTesting() {
+  update_payment_details_timer_.Stop();
+  OnUpdatePaymentDetailsTimeout(nullptr);
+}
+
 PaymentRequest::PaymentRequest(
     ExecutionContext* execution_context,
     const HeapVector<Member<PaymentMethodData>>& method_data,
@@ -1120,7 +1172,12 @@
       complete_timer_(
           execution_context->GetTaskRunner(TaskType::kMiscPlatformAPI),
           this,
-          &PaymentRequest::OnCompleteTimeout) {
+          &PaymentRequest::OnCompleteTimeout),
+      update_payment_details_timer_(
+          execution_context->GetTaskRunner(TaskType::kMiscPlatformAPI),
+          this,
+          &PaymentRequest::OnUpdatePaymentDetailsTimeout),
+      is_waiting_for_show_promise_to_resolve_(false) {
   DCHECK(GetExecutionContext()->IsSecureContext());
 
   if (!AllowedToUsePaymentRequest(execution_context)) {
@@ -1307,7 +1364,9 @@
     return;
   }
 
-  complete_timer_.StartOneShot(kCompleteTimeout, FROM_HERE);
+  // If the website does not call complete() 60 seconds after show() has been
+  // resolved, then behave as if the website called complete("fail").
+  complete_timer_.StartOneShot(TimeDelta::FromSeconds(60), FROM_HERE);
 
   if (retry_resolver_) {
     DCHECK(payment_response_);
@@ -1515,6 +1574,14 @@
   ClearResolversAndCloseMojoConnection();
 }
 
+void PaymentRequest::OnUpdatePaymentDetailsTimeout(TimerBase*) {
+  OnUpdatePaymentDetailsFailure(
+      is_waiting_for_show_promise_to_resolve_
+          ? "Timed out waiting for a PaymentRequest.show(promise) to resolve."
+          : "Timed out waiting for a "
+            "PaymentRequestUpdateEvent.updateWith(promise) to resolve.");
+}
+
 void PaymentRequest::ClearResolversAndCloseMojoConnection() {
   complete_timer_.Stop();
   complete_resolver_.Clear();
@@ -1537,15 +1604,21 @@
     PaymentRequestUpdateEvent* event) {
   event->SetTarget(event_target);
   event->SetPaymentRequest(this);
+
+  // If the website does not calculate the updated shopping cart contents
+  // within 60 seconds, abort payment.
+  update_payment_details_timer_.StartOneShot(TimeDelta::FromSeconds(60),
+                                             FROM_HERE);
+
   event_target->DispatchEvent(*event);
   if (!event->is_waiting_for_update()) {
     // DispatchEvent runs synchronously. The method is_waiting_for_update()
-    // returns true if the merchant called event.updateWith() within the event
-    // handler. Calling this method is optional. If the method is not called,
-    // the renderer sends a message to the browser to re-enable UI interactions.
+    // returns false if the merchant did not call event.updateWith() within the
+    // event handler, which is optional, so the renderer sends a message to the
+    // browser to re-enable UI interactions.
     const String& message = String::Format(
-        "No updateWith() call in '%s' event handler. User "
-        "may see outdated line items and total.",
+        "No updateWith() call in '%s' event handler. User may see outdated "
+        "line items and total.",
         event->type().Ascii().data());
     GetExecutionContext()->AddConsoleMessage(
         ConsoleMessage::Create(mojom::ConsoleMessageSource::kJavaScript,
diff --git a/third_party/blink/renderer/modules/payments/payment_request.h b/third_party/blink/renderer/modules/payments/payment_request.h
index b7f7cc2..49a5dde27 100644
--- a/third_party/blink/renderer/modules/payments/payment_request.h
+++ b/third_party/blink/renderer/modules/payments/payment_request.h
@@ -67,6 +67,7 @@
   ~PaymentRequest() override;
 
   ScriptPromise show(ScriptState*);
+  ScriptPromise show(ScriptState*, ScriptPromise details_promise);
   ScriptPromise abort(ScriptState*);
 
   const String& id() const { return id_; }
@@ -100,6 +101,7 @@
   void Trace(blink::Visitor*) override;
 
   void OnCompleteTimeoutForTesting();
+  void OnUpdatePaymentDetailsTimeoutForTesting();
 
   enum {
     // Implementation defined constants controlling the allowed list length
@@ -132,6 +134,7 @@
   void WarnNoFavicon() override;
 
   void OnCompleteTimeout(TimerBase*);
+  void OnUpdatePaymentDetailsTimeout(TimerBase*);
 
   // Clears the promise resolvers and closes the Mojo connection.
   void ClearResolversAndCloseMojoConnection();
@@ -163,6 +166,8 @@
   payments::mojom::blink::PaymentRequestPtr payment_provider_;
   mojo::Binding<payments::mojom::blink::PaymentRequestClient> client_binding_;
   TaskRunnerTimer<PaymentRequest> complete_timer_;
+  TaskRunnerTimer<PaymentRequest> update_payment_details_timer_;
+  bool is_waiting_for_show_promise_to_resolve_;
 
   DISALLOW_COPY_AND_ASSIGN(PaymentRequest);
 };
diff --git a/third_party/blink/renderer/modules/payments/payment_request.idl b/third_party/blink/renderer/modules/payments/payment_request.idl
index e2a1c83d..97b3655 100644
--- a/third_party/blink/renderer/modules/payments/payment_request.idl
+++ b/third_party/blink/renderer/modules/payments/payment_request.idl
@@ -14,7 +14,7 @@
     Exposed=Window,
     ActiveScriptWrappable
 ] interface PaymentRequest : EventTarget {
-    [CallWith=ScriptState, NewObject] Promise<PaymentResponse> show();
+    [CallWith=ScriptState, NewObject] Promise<PaymentResponse> show(optional Promise<PaymentDetailsUpdate> detailsPromise);
     [CallWith=ScriptState, NewObject] Promise<void> abort();
     [CallWith=ScriptState, HighEntropy, Measure, NewObject] Promise<boolean> canMakePayment();
     [CallWith=ScriptState, HighEntropy, Measure, NewObject, RuntimeEnabled=PaymentRequestHasEnrolledInstrument] Promise<boolean> hasEnrolledInstrument();
diff --git a/third_party/blink/renderer/modules/payments/payment_request_delegate.h b/third_party/blink/renderer/modules/payments/payment_request_delegate.h
index 49dd9ad..91abd7d 100644
--- a/third_party/blink/renderer/modules/payments/payment_request_delegate.h
+++ b/third_party/blink/renderer/modules/payments/payment_request_delegate.h
@@ -13,11 +13,21 @@
 
 class ScriptValue;
 
+// The interface for updating the payment details (shopping cart, shipping
+// options, total) in response to shipping address or option change, or through
+// the promise passed into the PaymentRequest.show() method.
 class MODULES_EXPORT PaymentRequestDelegate : public GarbageCollectedMixin {
  public:
+  // Updates the payment details in response to a change in, e.g., shipping
+  // address. This stops the spinner in the UI.
   virtual void OnUpdatePaymentDetails(
       const ScriptValue& details_script_value) = 0;
+
+  // Called when the merchant failed to update the payment details in response
+  // to a change in, e.g., shipping address. This will abort the payment.
   virtual void OnUpdatePaymentDetailsFailure(const String& error) = 0;
+
+  // Whether the PaymentRequest.show() or PaymentResponse.retry() is ongoing.
   virtual bool IsInteractive() const = 0;
 
  protected:
diff --git a/third_party/blink/renderer/modules/payments/payment_request_test.cc b/third_party/blink/renderer/modules/payments/payment_request_test.cc
index 2be918d..8f6e899b502 100644
--- a/third_party/blink/renderer/modules/payments/payment_request_test.cc
+++ b/third_party/blink/renderer/modules/payments/payment_request_test.cc
@@ -370,6 +370,8 @@
   request->show(scope.GetScriptState())
       .Then(funcs.ExpectNoCall(), funcs.ExpectCall(&error_message));
 
+  static_cast<payments::mojom::blink::PaymentRequestClient*>(request)
+      ->OnShippingAddressChange(BuildPaymentAddressForTest());
   request->OnUpdatePaymentDetailsFailure("oops");
 
   v8::MicrotasksScope::PerformCheckpoint(scope.GetScriptState()->GetIsolate());
@@ -405,6 +407,8 @@
   request->show(scope.GetScriptState())
       .Then(funcs.ExpectNoCall(), funcs.ExpectCall());
 
+  static_cast<payments::mojom::blink::PaymentRequestClient*>(request)
+      ->OnShippingAddressChange(BuildPaymentAddressForTest());
   request->OnUpdatePaymentDetails(
       ScriptValue::From(scope.GetScriptState(), "NotPaymentDetails"));
 }
@@ -421,6 +425,8 @@
   request->show(scope.GetScriptState())
       .Then(funcs.ExpectNoCall(), funcs.ExpectCall());
 
+  static_cast<payments::mojom::blink::PaymentRequestClient*>(request)
+      ->OnShippingAddressChange(BuildPaymentAddressForTest());
   request->OnUpdatePaymentDetails(
       ScriptValue::From(
           scope.GetScriptState(),
@@ -446,6 +452,8 @@
   EXPECT_TRUE(request->shippingOption().IsNull());
   request->show(scope.GetScriptState())
       .Then(funcs.ExpectNoCall(), funcs.ExpectNoCall());
+  static_cast<payments::mojom::blink::PaymentRequestClient*>(request)
+      ->OnShippingAddressChange(BuildPaymentAddressForTest());
   String detail_with_shipping_options =
       "{\"total\": {\"label\": \"Total\", \"amount\": {\"currency\": \"USD\", "
       "\"value\": \"5.00\"}},"
@@ -460,6 +468,8 @@
                                        scope.GetExceptionState())));
   EXPECT_FALSE(scope.GetExceptionState().HadException());
   EXPECT_EQ("standardShippingOption", request->shippingOption());
+  static_cast<payments::mojom::blink::PaymentRequestClient*>(request)
+      ->OnShippingAddressChange(BuildPaymentAddressForTest());
   String detail_without_shipping_options =
       "{\"total\": {\"label\": \"Total\", \"amount\": {\"currency\": \"USD\", "
       "\"value\": \"5.00\"}}}";
@@ -519,6 +529,8 @@
   EXPECT_FALSE(scope.GetExceptionState().HadException());
   request->show(scope.GetScriptState())
       .Then(funcs.ExpectNoCall(), funcs.ExpectNoCall());
+  static_cast<payments::mojom::blink::PaymentRequestClient*>(request)
+      ->OnShippingAddressChange(BuildPaymentAddressForTest());
   String detail =
       "{\"total\": {\"label\": \"Total\", \"amount\": {\"currency\": \"USD\", "
       "\"value\": \"5.00\"}},"
diff --git a/third_party/blink/renderer/modules/payments/payment_request_update_event.cc b/third_party/blink/renderer/modules/payments/payment_request_update_event.cc
index 44fafee..7af3e8c 100644
--- a/third_party/blink/renderer/modules/payments/payment_request_update_event.cc
+++ b/third_party/blink/renderer/modules/payments/payment_request_update_event.cc
@@ -11,71 +11,11 @@
 #include "third_party/blink/renderer/core/dom/dom_exception.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
 #include "third_party/blink/renderer/modules/payments/payment_request_delegate.h"
+#include "third_party/blink/renderer/modules/payments/update_payment_details_function.h"
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
 
 namespace blink {
-namespace {
-
-// Reject the payment request if the page does not resolve the promise from
-// updateWith within 60 seconds.
-constexpr TimeDelta kAbortTimeout = TimeDelta::FromSeconds(60);
-
-}  // anonymous namespace
-
-class PaymentRequestUpdateEvent::UpdatePaymentDetailsFunction final
-    : public ScriptFunction {
- public:
-  enum class ResolveType {
-    kFulfilled,
-    kRejected,
-  };
-
-  static v8::Local<v8::Function> CreateFunction(
-      ScriptState* script_state,
-      ResolveType resolve_type,
-      PaymentRequestUpdateEvent* event) {
-    UpdatePaymentDetailsFunction* self =
-        MakeGarbageCollected<UpdatePaymentDetailsFunction>(script_state,
-                                                           resolve_type, event);
-    return self->BindToV8Function();
-  }
-
-  UpdatePaymentDetailsFunction(ScriptState* script_state,
-                               ResolveType resolve_type,
-                               PaymentRequestUpdateEvent* event)
-      : ScriptFunction(script_state),
-        resolve_type_(resolve_type),
-        event_(event) {
-    DCHECK(event_);
-  }
-
-  void Trace(blink::Visitor* visitor) override {
-    visitor->Trace(event_);
-    ScriptFunction::Trace(visitor);
-  }
-
- private:
-  ScriptValue Call(ScriptValue value) override {
-    if (!event_ || !event_->request_)
-      return ScriptValue();
-
-    event_->abort_timer_.Stop();
-    if (resolve_type_ == ResolveType::kFulfilled) {
-      event_->request_->OnUpdatePaymentDetails(value);
-    } else if (resolve_type_ == ResolveType::kRejected) {
-      event_->request_->OnUpdatePaymentDetailsFailure(
-          ToCoreString(value.V8Value()
-                           ->ToString(GetScriptState()->GetContext())
-                           .ToLocalChecked()));
-    }
-    event_->SetPaymentRequest(nullptr);
-    return ScriptValue();
-  }
-
-  ResolveType resolve_type_;
-  Member<PaymentRequestUpdateEvent> event_;
-};
 
 PaymentRequestUpdateEvent::~PaymentRequestUpdateEvent() = default;
 
@@ -122,15 +62,12 @@
   stopImmediatePropagation();
   wait_for_update_ = true;
 
-  DCHECK(!abort_timer_.IsActive());
-  abort_timer_.StartOneShot(kAbortTimeout, FROM_HERE);
-
   promise.Then(UpdatePaymentDetailsFunction::CreateFunction(
-                   script_state,
-                   UpdatePaymentDetailsFunction::ResolveType::kFulfilled, this),
+                   script_state, request_,
+                   UpdatePaymentDetailsFunction::ResolveType::kFulfill),
                UpdatePaymentDetailsFunction::CreateFunction(
-                   script_state,
-                   UpdatePaymentDetailsFunction::ResolveType::kRejected, this));
+                   script_state, request_,
+                   UpdatePaymentDetailsFunction::ResolveType::kReject));
 }
 
 void PaymentRequestUpdateEvent::Trace(blink::Visitor* visitor) {
@@ -138,27 +75,10 @@
   Event::Trace(visitor);
 }
 
-void PaymentRequestUpdateEvent::OnUpdateEventTimeoutForTesting() {
-  OnUpdateEventTimeout(nullptr);
-}
-
 PaymentRequestUpdateEvent::PaymentRequestUpdateEvent(
     ExecutionContext* execution_context,
     const AtomicString& type,
     const PaymentRequestUpdateEventInit* init)
-    : Event(type, init),
-      wait_for_update_(false),
-      abort_timer_(execution_context->GetTaskRunner(TaskType::kUserInteraction),
-                   this,
-                   &PaymentRequestUpdateEvent::OnUpdateEventTimeout) {}
-
-void PaymentRequestUpdateEvent::OnUpdateEventTimeout(TimerBase*) {
-  if (!request_)
-    return;
-  abort_timer_.Stop();
-  request_->OnUpdatePaymentDetailsFailure(
-      "Timed out waiting for a response to a '" + type() + "' event");
-  SetPaymentRequest(nullptr);
-}
+    : Event(type, init), wait_for_update_(false) {}
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/payments/payment_request_update_event.h b/third_party/blink/renderer/modules/payments/payment_request_update_event.h
index 24dea88..f3e5e046 100644
--- a/third_party/blink/renderer/modules/payments/payment_request_update_event.h
+++ b/third_party/blink/renderer/modules/payments/payment_request_update_event.h
@@ -46,20 +46,11 @@
 
   void Trace(blink::Visitor*) override;
 
-  void OnUpdateEventTimeoutForTesting();
-
  private:
-  // This class is declared here because it requires privileges to access some
-  // PaymentRequestUpdateEvent's member fields, such as request_, abort_timer_.
-  class UpdatePaymentDetailsFunction;
-
-  void OnUpdateEventTimeout(TimerBase*);
-
   // True after event.updateWith() was called.
   bool wait_for_update_;
 
   Member<PaymentRequestDelegate> request_;
-  TaskRunnerTimer<PaymentRequestUpdateEvent> abort_timer_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/payments/payment_request_update_event_test.cc b/third_party/blink/renderer/modules/payments/payment_request_update_event_test.cc
index ddd7fab1..ca4d769 100644
--- a/third_party/blink/renderer/modules/payments/payment_request_update_event_test.cc
+++ b/third_party/blink/renderer/modules/payments/payment_request_update_event_test.cc
@@ -151,12 +151,14 @@
   request->show(scope.GetScriptState())
       .Then(funcs.ExpectNoCall(), funcs.ExpectCall(&error_message));
 
-  event->OnUpdateEventTimeoutForTesting();
+  static_cast<payments::mojom::blink::PaymentRequestClient*>(request)
+      ->OnShippingAddressChange(BuildPaymentAddressForTest());
+  request->OnUpdatePaymentDetailsTimeoutForTesting();
 
   v8::MicrotasksScope::PerformCheckpoint(scope.GetScriptState()->GetIsolate());
   EXPECT_EQ(
-      "AbortError: Timed out waiting for a response to a "
-      "'shippingaddresschange' event",
+      "AbortError: Timed out waiting for a "
+      "PaymentRequestUpdateEvent.updateWith(promise) to resolve.",
       error_message);
 
   event->updateWith(
@@ -165,7 +167,9 @@
           ->Promise(),
       scope.GetExceptionState());
 
-  EXPECT_FALSE(scope.GetExceptionState().HadException());
+  EXPECT_TRUE(scope.GetExceptionState().HadException());
+  EXPECT_EQ("PaymentRequest is no longer interactive",
+            scope.GetExceptionState().Message());
 }
 
 TEST(PaymentRequestUpdateEventTest, OptionChangeUpdateWithTimeout) {
@@ -185,12 +189,14 @@
   request->show(scope.GetScriptState())
       .Then(funcs.ExpectNoCall(), funcs.ExpectCall(&error_message));
 
-  event->OnUpdateEventTimeoutForTesting();
+  static_cast<payments::mojom::blink::PaymentRequestClient*>(request)
+      ->OnShippingAddressChange(BuildPaymentAddressForTest());
+  request->OnUpdatePaymentDetailsTimeoutForTesting();
 
   v8::MicrotasksScope::PerformCheckpoint(scope.GetScriptState()->GetIsolate());
   EXPECT_EQ(
-      "AbortError: Timed out waiting for a response to a "
-      "'shippingoptionchange' event",
+      "AbortError: Timed out waiting for a "
+      "PaymentRequestUpdateEvent.updateWith(promise) to resolve.",
       error_message);
 
   event->updateWith(
@@ -199,7 +205,9 @@
           ->Promise(),
       scope.GetExceptionState());
 
-  EXPECT_FALSE(scope.GetExceptionState().HadException());
+  EXPECT_TRUE(scope.GetExceptionState().HadException());
+  EXPECT_EQ("PaymentRequest is no longer interactive",
+            scope.GetExceptionState().Message());
 }
 
 TEST(PaymentRequestUpdateEventTest, AddressChangePromiseTimeout) {
@@ -215,21 +223,23 @@
   event->SetTrusted(true);
   event->SetPaymentRequest(request);
   event->SetEventPhase(Event::kCapturingPhase);
-  auto* payment_details =
-      MakeGarbageCollected<ScriptPromiseResolver>(scope.GetScriptState());
   String error_message;
   request->show(scope.GetScriptState())
       .Then(funcs.ExpectNoCall(), funcs.ExpectCall(&error_message));
+  static_cast<payments::mojom::blink::PaymentRequestClient*>(request)
+      ->OnShippingAddressChange(BuildPaymentAddressForTest());
+  auto* payment_details =
+      MakeGarbageCollected<ScriptPromiseResolver>(scope.GetScriptState());
   event->updateWith(scope.GetScriptState(), payment_details->Promise(),
                     scope.GetExceptionState());
   EXPECT_FALSE(scope.GetExceptionState().HadException());
 
-  event->OnUpdateEventTimeoutForTesting();
+  request->OnUpdatePaymentDetailsTimeoutForTesting();
 
   v8::MicrotasksScope::PerformCheckpoint(scope.GetScriptState()->GetIsolate());
   EXPECT_EQ(
-      "AbortError: Timed out waiting for a response to a "
-      "'shippingaddresschange' event",
+      "AbortError: Timed out waiting for a "
+      "PaymentRequestUpdateEvent.updateWith(promise) to resolve.",
       error_message);
 
   payment_details->Resolve("foo");
@@ -248,21 +258,23 @@
   event->SetTrusted(true);
   event->SetPaymentRequest(request);
   event->SetEventPhase(Event::kCapturingPhase);
-  auto* payment_details =
-      MakeGarbageCollected<ScriptPromiseResolver>(scope.GetScriptState());
   String error_message;
   request->show(scope.GetScriptState())
       .Then(funcs.ExpectNoCall(), funcs.ExpectCall(&error_message));
+  static_cast<payments::mojom::blink::PaymentRequestClient*>(request)
+      ->OnShippingAddressChange(BuildPaymentAddressForTest());
+  auto* payment_details =
+      MakeGarbageCollected<ScriptPromiseResolver>(scope.GetScriptState());
   event->updateWith(scope.GetScriptState(), payment_details->Promise(),
                     scope.GetExceptionState());
   EXPECT_FALSE(scope.GetExceptionState().HadException());
 
-  event->OnUpdateEventTimeoutForTesting();
+  request->OnUpdatePaymentDetailsTimeoutForTesting();
 
   v8::MicrotasksScope::PerformCheckpoint(scope.GetScriptState()->GetIsolate());
   EXPECT_EQ(
-      "AbortError: Timed out waiting for a response to a "
-      "'shippingoptionchange' event",
+      "AbortError: Timed out waiting for a "
+      "PaymentRequestUpdateEvent.updateWith(promise) to resolve.",
       error_message);
 
   payment_details->Resolve("foo");
diff --git a/third_party/blink/renderer/modules/payments/payment_test_helper.cc b/third_party/blink/renderer/modules/payments/payment_test_helper.cc
index 2b206afb..e4126f5a 100644
--- a/third_party/blink/renderer/modules/payments/payment_test_helper.cc
+++ b/third_party/blink/renderer/modules/payments/payment_test_helper.cc
@@ -205,6 +205,13 @@
   return result;
 }
 
+payments::mojom::blink::PaymentAddressPtr BuildPaymentAddressForTest() {
+  payments::mojom::blink::PaymentAddressPtr result =
+      payments::mojom::blink::PaymentAddress::New();
+  result->country = "US";
+  return result;
+}
+
 void MakePaymentRequestOriginSecure(Document& document) {
   document.SetSecurityOrigin(
       SecurityOrigin::Create(KURL("https://www.example.com/")));
diff --git a/third_party/blink/renderer/modules/payments/payment_test_helper.h b/third_party/blink/renderer/modules/payments/payment_test_helper.h
index b9bd4fad..1042389 100644
--- a/third_party/blink/renderer/modules/payments/payment_test_helper.h
+++ b/third_party/blink/renderer/modules/payments/payment_test_helper.h
@@ -85,6 +85,8 @@
 
 payments::mojom::blink::PaymentResponsePtr BuildPaymentResponseForTest();
 
+payments::mojom::blink::PaymentAddressPtr BuildPaymentAddressForTest();
+
 void MakePaymentRequestOriginSecure(Document&);
 
 class PaymentRequestMockFunctionScope {
diff --git a/third_party/blink/renderer/modules/payments/update_payment_details_function.cc b/third_party/blink/renderer/modules/payments/update_payment_details_function.cc
new file mode 100644
index 0000000..3cdf2fb
--- /dev/null
+++ b/third_party/blink/renderer/modules/payments/update_payment_details_function.cc
@@ -0,0 +1,57 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/modules/payments/update_payment_details_function.h"
+
+#include "third_party/blink/renderer/bindings/core/v8/script_value.h"
+#include "third_party/blink/renderer/modules/payments/payment_request_delegate.h"
+
+namespace blink {
+
+// static
+v8::Local<v8::Function> UpdatePaymentDetailsFunction::CreateFunction(
+    ScriptState* script_state,
+    PaymentRequestDelegate* delegate,
+    ResolveType resolve_type) {
+  UpdatePaymentDetailsFunction* self =
+      MakeGarbageCollected<UpdatePaymentDetailsFunction>(script_state, delegate,
+                                                         resolve_type);
+  return self->BindToV8Function();
+}
+
+UpdatePaymentDetailsFunction::UpdatePaymentDetailsFunction(
+    ScriptState* script_state,
+    PaymentRequestDelegate* delegate,
+    ResolveType resolve_type)
+    : ScriptFunction(script_state),
+      delegate_(delegate),
+      resolve_type_(resolve_type) {
+  DCHECK(delegate_);
+}
+
+void UpdatePaymentDetailsFunction::Trace(blink::Visitor* visitor) {
+  visitor->Trace(delegate_);
+  ScriptFunction::Trace(visitor);
+}
+
+ScriptValue UpdatePaymentDetailsFunction::Call(ScriptValue value) {
+  if (!delegate_)
+    return ScriptValue();
+
+  switch (resolve_type_) {
+    case ResolveType::kFulfill:
+      delegate_->OnUpdatePaymentDetails(value);
+      break;
+    case ResolveType::kReject:
+      delegate_->OnUpdatePaymentDetailsFailure(
+          ToCoreString(value.V8Value()
+                           ->ToString(GetScriptState()->GetContext())
+                           .ToLocalChecked()));
+      break;
+  }
+  delegate_ = nullptr;
+  return ScriptValue();
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/modules/payments/update_payment_details_function.h b/third_party/blink/renderer/modules/payments/update_payment_details_function.h
new file mode 100644
index 0000000..11d32ba1
--- /dev/null
+++ b/third_party/blink/renderer/modules/payments/update_payment_details_function.h
@@ -0,0 +1,41 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_PAYMENTS_UPDATE_PAYMENT_DETAILS_FUNCTION_H_
+#define THIRD_PARTY_BLINK_RENDERER_MODULES_PAYMENTS_UPDATE_PAYMENT_DETAILS_FUNCTION_H_
+
+#include "third_party/blink/renderer/bindings/core/v8/script_function.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h"
+
+namespace blink {
+
+class PaymentRequestDelegate;
+class ScriptState;
+class ScriptValue;
+
+class UpdatePaymentDetailsFunction : public ScriptFunction {
+ public:
+  enum class ResolveType {
+    kFulfill,
+    kReject,
+  };
+
+  static v8::Local<v8::Function> CreateFunction(ScriptState*,
+                                                PaymentRequestDelegate*,
+                                                ResolveType);
+
+  UpdatePaymentDetailsFunction(ScriptState*,
+                               PaymentRequestDelegate*,
+                               ResolveType);
+  void Trace(blink::Visitor*) override;
+  ScriptValue Call(ScriptValue) override;
+
+ private:
+  Member<PaymentRequestDelegate> delegate_;
+  ResolveType resolve_type_;
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_PAYMENTS_UPDATE_PAYMENT_DETAILS_FUNCTION_H_
diff --git a/third_party/blink/renderer/modules/presentation/presentation_request.cc b/third_party/blink/renderer/modules/presentation/presentation_request.cc
index e967c09..33c4fc8 100644
--- a/third_party/blink/renderer/modules/presentation/presentation_request.cc
+++ b/third_party/blink/renderer/modules/presentation/presentation_request.cc
@@ -59,7 +59,7 @@
     const Vector<String>& urls,
     ExceptionState& exception_state) {
   if (To<Document>(execution_context)
-          ->IsSandboxed(kSandboxPresentationController)) {
+          ->IsSandboxed(WebSandboxFlags::kPresentationController)) {
     exception_state.ThrowSecurityError(
         "The document is sandboxed and lacks the 'allow-presentation' flag.");
     return nullptr;
diff --git a/third_party/blink/renderer/modules/screen_orientation/screen_orientation.cc b/third_party/blink/renderer/modules/screen_orientation/screen_orientation.cc
index d4f68b30..59c16d8 100644
--- a/third_party/blink/renderer/modules/screen_orientation/screen_orientation.cc
+++ b/third_party/blink/renderer/modules/screen_orientation/screen_orientation.cc
@@ -161,7 +161,7 @@
     return promise;
   }
 
-  if (document->IsSandboxed(kSandboxOrientationLock)) {
+  if (document->IsSandboxed(WebSandboxFlags::kOrientationLock)) {
     DOMException* exception =
         DOMException::Create(DOMExceptionCode::kSecurityError,
                              "The document is sandboxed and lacks the "
diff --git a/third_party/blink/renderer/modules/service_worker/navigator_service_worker.cc b/third_party/blink/renderer/modules/service_worker/navigator_service_worker.cc
index 7f43d54..eb0a2d7f 100644
--- a/third_party/blink/renderer/modules/service_worker/navigator_service_worker.cc
+++ b/third_party/blink/renderer/modules/service_worker/navigator_service_worker.cc
@@ -90,7 +90,7 @@
            ->GetSecurityOrigin()
            ->CanAccessServiceWorkers()) {
     String error_message;
-    if (frame->GetSecurityContext()->IsSandboxed(kSandboxOrigin)) {
+    if (frame->GetSecurityContext()->IsSandboxed(WebSandboxFlags::kOrigin)) {
       error_message =
           "Service worker is disabled because the context is sandboxed and "
           "lacks the 'allow-same-origin' flag.";
diff --git a/third_party/blink/renderer/modules/storage/dom_window_storage.cc b/third_party/blink/renderer/modules/storage/dom_window_storage.cc
index a03053b..80046d5 100644
--- a/third_party/blink/renderer/modules/storage/dom_window_storage.cc
+++ b/third_party/blink/renderer/modules/storage/dom_window_storage.cc
@@ -64,7 +64,7 @@
   DCHECK(document);
   String access_denied_message = "Access is denied for this document.";
   if (!document->GetSecurityOrigin()->CanAccessSessionStorage()) {
-    if (document->IsSandboxed(kSandboxOrigin))
+    if (document->IsSandboxed(WebSandboxFlags::kOrigin))
       exception_state.ThrowSecurityError(
           "The document is sandboxed and lacks the 'allow-same-origin' flag.");
     else if (document->Url().ProtocolIs("data"))
@@ -123,7 +123,7 @@
   DCHECK(document);
   String access_denied_message = "Access is denied for this document.";
   if (!document->GetSecurityOrigin()->CanAccessLocalStorage()) {
-    if (document->IsSandboxed(kSandboxOrigin))
+    if (document->IsSandboxed(WebSandboxFlags::kOrigin))
       exception_state.ThrowSecurityError(
           "The document is sandboxed and lacks the 'allow-same-origin' flag.");
     else if (document->Url().ProtocolIs("data"))
diff --git a/third_party/blink/renderer/platform/BUILD.gn b/third_party/blink/renderer/platform/BUILD.gn
index 9384159..4bb9e4a25 100644
--- a/third_party/blink/renderer/platform/BUILD.gn
+++ b/third_party/blink/renderer/platform/BUILD.gn
@@ -551,6 +551,7 @@
     "exported/web_network_state_notifier.cc",
     "exported/web_prerender.cc",
     "exported/web_prerendering_support.cc",
+    "exported/web_resource_timing_info.cc",
     "exported/web_rtc_answer_options.cc",
     "exported/web_rtc_ice_candidate.cc",
     "exported/web_rtc_offer_options.cc",
@@ -2125,6 +2126,7 @@
 
     # Tests migrated from the web/tests directory.
     "exported/web_image_test.cc",
+    "exported/web_resource_timing_info_test.cc",
     "exported/web_url_request_test.cc",
     "exported/web_url_response_test.cc",
     "timer_perf_test.cc",
diff --git a/third_party/blink/renderer/platform/exported/web_resource_timing_info.cc b/third_party/blink/renderer/platform/exported/web_resource_timing_info.cc
new file mode 100644
index 0000000..fc4a988
--- /dev/null
+++ b/third_party/blink/renderer/platform/exported/web_resource_timing_info.cc
@@ -0,0 +1,92 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/public/platform/web_resource_timing_info.h"
+
+#include "third_party/blink/public/platform/web_url_load_timing.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+namespace {
+
+bool IsSameServerTimingInfo(const WebVector<WebServerTimingInfo>& lhs,
+                            const WebVector<WebServerTimingInfo>& rhs) {
+  if (lhs.size() != rhs.size())
+    return false;
+  for (size_t i = 0; i < lhs.size(); ++i) {
+    if (lhs[i] != rhs[i])
+      return false;
+  }
+  return true;
+}
+
+}  // namespace
+
+bool WebServerTimingInfo::operator==(const WebServerTimingInfo& other) const {
+  return name == other.name && duration == other.duration &&
+         description == other.description;
+}
+
+bool WebServerTimingInfo::operator!=(const WebServerTimingInfo& other) const {
+  return !(*this == other);
+}
+
+bool WebResourceTimingInfo::operator==(
+    const WebResourceTimingInfo& other) const {
+  return name == other.name && start_time == other.start_time &&
+         alpn_negotiated_protocol == other.alpn_negotiated_protocol &&
+         connection_info == other.connection_info && timing == other.timing &&
+         last_redirect_end_time == other.last_redirect_end_time &&
+         response_end == other.response_end &&
+         transfer_size == other.transfer_size &&
+         encoded_body_size == other.encoded_body_size &&
+         decoded_body_size == other.decoded_body_size &&
+         did_reuse_connection == other.did_reuse_connection &&
+         is_secure_context == other.is_secure_context &&
+         allow_timing_details == other.allow_timing_details &&
+         allow_redirect_details == other.allow_redirect_details &&
+         allow_negative_values == other.allow_negative_values &&
+         IsSameServerTimingInfo(server_timing, other.server_timing);
+}
+
+CrossThreadCopier<WebResourceTimingInfo>::Type CrossThreadCopier<
+    WebResourceTimingInfo>::Copy(const WebResourceTimingInfo& info) {
+  WebResourceTimingInfo copy;
+
+  copy.name = String(info.name).IsolatedCopy();
+  copy.start_time = info.start_time;
+
+  copy.alpn_negotiated_protocol =
+      String(info.alpn_negotiated_protocol).IsolatedCopy();
+  copy.connection_info = String(info.connection_info).IsolatedCopy();
+
+  if (!info.timing.IsNull())
+    copy.timing = CrossThreadCopier<WebURLLoadTiming>::Copy(info.timing);
+
+  copy.last_redirect_end_time = info.last_redirect_end_time;
+  copy.response_end = info.response_end;
+
+  copy.transfer_size = info.transfer_size;
+  copy.encoded_body_size = info.encoded_body_size;
+  copy.decoded_body_size = info.decoded_body_size;
+
+  copy.did_reuse_connection = info.did_reuse_connection;
+  copy.is_secure_context = info.is_secure_context;
+
+  copy.allow_timing_details = info.allow_timing_details;
+  copy.allow_redirect_details = info.allow_redirect_details;
+
+  copy.allow_negative_values = info.allow_negative_values;
+  for (auto& entry : info.server_timing) {
+    WebServerTimingInfo entry_copy(String(entry.name).IsolatedCopy(),
+                                   entry.duration,
+                                   String(entry.description).IsolatedCopy());
+    copy.server_timing.emplace_back(std::move(entry_copy));
+  }
+
+  return copy;
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/platform/exported/web_resource_timing_info_test.cc b/third_party/blink/renderer/platform/exported/web_resource_timing_info_test.cc
new file mode 100644
index 0000000..c0e61fac
--- /dev/null
+++ b/third_party/blink/renderer/platform/exported/web_resource_timing_info_test.cc
@@ -0,0 +1,128 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/public/platform/web_resource_timing_info.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/cross_thread_functional.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_load_timing.h"
+#include "third_party/blink/renderer/platform/scheduler/public/post_cross_thread_task.h"
+#include "third_party/blink/renderer/platform/scheduler/public/thread.h"
+#include "third_party/blink/renderer/platform/testing/testing_platform_support_with_mock_scheduler.h"
+
+namespace blink {
+
+namespace {
+
+scoped_refptr<ResourceLoadTiming> CreateResourceLoadTiming(
+    const base::TimeTicks pseudo_time) {
+  auto timing = ResourceLoadTiming::Create();
+  timing->SetRequestTime(pseudo_time);
+  timing->SetProxyStart(pseudo_time);
+  timing->SetProxyEnd(pseudo_time);
+  timing->SetDnsStart(pseudo_time);
+  timing->SetDnsEnd(pseudo_time);
+  timing->SetConnectStart(pseudo_time);
+  timing->SetConnectEnd(pseudo_time);
+  timing->SetWorkerStart(pseudo_time);
+  timing->SetWorkerReady(pseudo_time);
+  timing->SetSendStart(pseudo_time);
+  timing->SetSendEnd(pseudo_time);
+  timing->SetReceiveHeadersStart(pseudo_time);
+  timing->SetReceiveHeadersEnd(pseudo_time);
+  timing->SetSslStart(pseudo_time);
+  timing->SetSslStart(pseudo_time);
+  timing->SetSslEnd(pseudo_time);
+  timing->SetPushEnd(pseudo_time);
+  return timing;
+}
+
+WebResourceTimingInfo CreateWebResourceTimingInfo(
+    const base::TimeTicks pseudo_time) {
+  WebVector<WebServerTimingInfo> server_timing;
+  server_timing.emplace_back(WebServerTimingInfo("server_timing_name", 1.0,
+                                                 "server_timing_description"));
+
+  WebResourceTimingInfo info = {
+      .name = "name",
+      .start_time = pseudo_time,
+
+      .alpn_negotiated_protocol = "protocol",
+      .connection_info = "info",
+
+      .timing = CreateResourceLoadTiming(pseudo_time),
+      .last_redirect_end_time = pseudo_time,
+      .response_end = pseudo_time,
+
+      .transfer_size = 1,
+      .encoded_body_size = 2,
+      .decoded_body_size = 3,
+
+      .did_reuse_connection = true,
+      .is_secure_context = true,
+
+      .allow_timing_details = true,
+      .allow_redirect_details = true,
+
+      .allow_negative_values = true,
+
+      .server_timing = server_timing,
+  };
+  return info;
+}
+
+void CheckWebResourceTimingInfoOnThread(const WebResourceTimingInfo& info,
+                                        const base::TimeTicks pseudo_time) {
+  WebResourceTimingInfo expected = CreateWebResourceTimingInfo(pseudo_time);
+  EXPECT_EQ(expected, info);
+
+  EXPECT_EQ("name", info.name);
+  EXPECT_EQ(pseudo_time, info.start_time);
+
+  EXPECT_EQ("protocol", info.alpn_negotiated_protocol);
+  EXPECT_EQ("info", info.connection_info);
+
+  WebURLLoadTiming expected_timing(CreateResourceLoadTiming(pseudo_time));
+  EXPECT_EQ(expected_timing, info.timing);
+  EXPECT_EQ(pseudo_time, info.last_redirect_end_time);
+  EXPECT_EQ(pseudo_time, info.response_end);
+
+  EXPECT_EQ(1u, info.transfer_size);
+  EXPECT_EQ(2u, info.encoded_body_size);
+  EXPECT_EQ(3u, info.decoded_body_size);
+
+  EXPECT_TRUE(info.did_reuse_connection);
+  EXPECT_TRUE(info.is_secure_context);
+
+  EXPECT_TRUE(info.allow_timing_details);
+  EXPECT_TRUE(info.allow_redirect_details);
+
+  EXPECT_TRUE(info.allow_negative_values);
+
+  EXPECT_EQ(1u, info.server_timing.size());
+
+  auto& entry = info.server_timing[0];
+  EXPECT_EQ("server_timing_name", entry.name);
+  EXPECT_EQ(1.0, entry.duration);
+  EXPECT_EQ("server_timing_description", entry.description);
+}
+
+}  // namespace
+
+TEST(WebResourceTimingInfoTest, CrossThreadCopy) {
+  ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler>
+      platform;
+
+  base::TimeTicks pseudo_time = TimeTicks::Now();
+  WebResourceTimingInfo info = CreateWebResourceTimingInfo(pseudo_time);
+
+  std::unique_ptr<Thread> thread = Platform::Current()->CreateThread(
+      ThreadCreationParams(WebThreadType::kTestThread)
+          .SetThreadNameForTest("TestThread"));
+  PostCrossThreadTask(
+      *thread->GetTaskRunner(), FROM_HERE,
+      CrossThreadBind(&CheckWebResourceTimingInfoOnThread, info, pseudo_time));
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/platform/exported/web_url_load_timing.cc b/third_party/blink/renderer/platform/exported/web_url_load_timing.cc
index 82042f97..421bf0e 100644
--- a/third_party/blink/renderer/platform/exported/web_url_load_timing.cc
+++ b/third_party/blink/renderer/platform/exported/web_url_load_timing.cc
@@ -196,4 +196,17 @@
   return private_.Get();
 }
 
+WebURLLoadTiming WebURLLoadTiming::DeepCopy() const {
+  return private_->DeepCopy();
+}
+
+CrossThreadCopier<WebURLLoadTiming>::Type
+CrossThreadCopier<WebURLLoadTiming>::Copy(const WebURLLoadTiming& timing) {
+  return timing.DeepCopy();
+}
+
+bool WebURLLoadTiming::operator==(const WebURLLoadTiming& other) const {
+  return *private_ == *other.private_;
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_fetcher_test.cc b/third_party/blink/renderer/platform/loader/fetch/resource_fetcher_test.cc
index 714d8df..299aa53 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource_fetcher_test.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/resource_fetcher_test.cc
@@ -114,7 +114,7 @@
     void DidReceiveResponse(uint64_t identifier,
                             const ResourceRequest& request,
                             const ResourceResponse& response,
-                            Resource* resource,
+                            const Resource* resource,
                             ResponseSource source) override {}
     void DidReceiveData(uint64_t identifier,
                         base::span<const char> chunk) override {}
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_load_observer.h b/third_party/blink/renderer/platform/loader/fetch/resource_load_observer.h
index a51666bd..e074443 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource_load_observer.h
+++ b/third_party/blink/renderer/platform/loader/fetch/resource_load_observer.h
@@ -49,11 +49,10 @@
   // the memory cache |request| and |resource->GetResourceRequest()| don't
   // match. |response| may not yet be set to |resource| when this function is
   // called.
-  // TODO(yhirano): Use const* Resource.
   virtual void DidReceiveResponse(uint64_t identifier,
                                   const ResourceRequest& request,
                                   const ResourceResponse& response,
-                                  Resource* resource,
+                                  const Resource* resource,
                                   ResponseSource) = 0;
 
   // Called when a response body chunk is received.
diff --git a/third_party/blink/renderer/platform/wtf/text/string_utf8_adaptor.h b/third_party/blink/renderer/platform/wtf/text/string_utf8_adaptor.h
index de0c404..76b9df3 100644
--- a/third_party/blink/renderer/platform/wtf/text/string_utf8_adaptor.h
+++ b/third_party/blink/renderer/platform/wtf/text/string_utf8_adaptor.h
@@ -46,8 +46,6 @@
   DISALLOW_NEW();
 
  public:
-  using value_type = const char;
-
   StringUTF8Adaptor(const String& string,
                     UTF8ConversionMode mode = kLenientUTF8Conversion);
   ~StringUTF8Adaptor();
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index dc2a98d9..7c4e8ab 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -487,7 +487,6 @@
 crbug.com/881057 [ Mac ] external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/contain/contain-paint-clip-005.html [ Failure ]
 crbug.com/880802 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/contain/contain-paint-ignored-cases-ruby-stacking-and-clipping-001.html [ Failure ]
 crbug.com/881057 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/contain/contain-paint-stacking-context-001b.html [ Failure ]
-crbug.com/863454 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/contain/contain-size-multicol-003.html [ Failure ]
 
 # [css-align]
 
diff --git a/third_party/blink/web_tests/external/wpt/fetch/sec-metadata/iframe.tentative.https.sub.html b/third_party/blink/web_tests/external/wpt/fetch/sec-metadata/iframe.tentative.https.sub.html
index 461d9f2..b916e3a 100644
--- a/third_party/blink/web_tests/external/wpt/fetch/sec-metadata/iframe.tentative.https.sub.html
+++ b/third_party/blink/web_tests/external/wpt/fetch/sec-metadata/iframe.tentative.https.sub.html
@@ -65,21 +65,21 @@
   create_test("{{host}}:{{ports[https][0]}}", USER, {
     "dest": "nested-document",
     "site": "same-origin",
-    "user": "?T",
+    "user": "?1",
     "mode": "nested-navigate"
   });
 
   create_test("{{hosts[][www]}}:{{ports[https][0]}}", USER, {
     "dest": "nested-document",
     "site": "same-site",
-    "user": "?T",
+    "user": "?1",
     "mode": "nested-navigate"
   });
 
   create_test("{{hosts[alt][www]}}:{{ports[https][0]}}", USER, {
     "dest": "nested-document",
     "site": "cross-site",
-    "user": "?T",
+    "user": "?1",
     "mode": "nested-navigate"
   });
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/fetch/sec-metadata/window-open.tentative.https.sub.html b/third_party/blink/web_tests/external/wpt/fetch/sec-metadata/window-open.tentative.https.sub.html
index 0be9f2c..df13d55c 100644
--- a/third_party/blink/web_tests/external/wpt/fetch/sec-metadata/window-open.tentative.https.sub.html
+++ b/third_party/blink/web_tests/external/wpt/fetch/sec-metadata/window-open.tentative.https.sub.html
@@ -143,7 +143,7 @@
         assert_header_equals(e.data, {
           "dest": "document",
           "site": "same-origin",
-          "user": "?T",
+          "user": "?1",
           "mode": "navigate",
         });
         t.done();
@@ -165,7 +165,7 @@
         assert_header_equals(e.data, {
           "dest": "document",
           "site": "same-site",
-          "user": "?T",
+          "user": "?1",
           "mode": "navigate",
         });
         t.done();
@@ -187,7 +187,7 @@
         assert_header_equals(e.data, {
           "dest": "document",
           "site": "cross-site",
-          "user": "?T",
+          "user": "?1",
           "mode": "navigate",
         });
         t.done();
diff --git a/third_party/blink/web_tests/http/tests/devtools/elements/styles-4/cssom-constructed-expected.txt b/third_party/blink/web_tests/http/tests/devtools/elements/styles-4/cssom-constructed-expected.txt
new file mode 100644
index 0000000..443ff82
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/devtools/elements/styles-4/cssom-constructed-expected.txt
@@ -0,0 +1,19 @@
+Tests that constructed stylesheets appear properly.
+
+[expanded] 
+element.style { ()
+
+[expanded] 
+div { ()
+    color: red;
+
+[expanded] 
+div { (user agent stylesheet)
+    display: block;
+
+======== Inherited from html ========
+[expanded] 
+html { (user agent stylesheet)
+/-- overloaded --/     color: -internal-root-color;
+
+
diff --git a/third_party/blink/web_tests/http/tests/devtools/elements/styles-4/cssom-constructed.js b/third_party/blink/web_tests/http/tests/devtools/elements/styles-4/cssom-constructed.js
new file mode 100644
index 0000000..57d0084
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/devtools/elements/styles-4/cssom-constructed.js
@@ -0,0 +1,24 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+(async function() {
+  TestRunner.addResult(`Tests that constructed stylesheets appear properly.\n`);
+  await TestRunner.loadModule('elements_test_runner');
+  await TestRunner.showPanel('elements');
+  await TestRunner.loadHTML(`
+      <div id="inspected">Text</div>
+    `);
+  await TestRunner.evaluateInPagePromise(`
+      const s = new CSSStyleSheet();
+      s.replaceSync('div {color: red}');
+      document.adoptedStyleSheets = [s];
+  `);
+
+  ElementsTestRunner.selectNodeAndWaitForStyles('inspected', dump);
+
+  function dump() {
+    ElementsTestRunner.dumpSelectedElementStyles(true);
+    TestRunner.completeTest();
+  }
+})();
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index dccb1ff..5badbd820 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -13886,10 +13886,11 @@
 </histogram>
 
 <histogram name="Bookmarks.Count.OnProfileLoad" units="bookmarks"
-    expires_after="2019-03-24">
+    expires_after="2019-09-24">
   <owner>supertri@chromium.org</owner>
   <owner>isherman@chromium.org</owner>
   <owner>aidanday@google.com</owner>
+  <owner>mamir@chromium.org</owner>
   <summary>
     The total number of bookmarks a user has saved. Recorded when a profile is
     opened - precisely, when bookmarks are loaded into storage from disk. The
@@ -13898,6 +13899,40 @@
   </summary>
 </histogram>
 
+<histogram name="Bookmarks.Count.OnProfileLoad.DuplicateUrl" units="bookmarks"
+    expires_after="2019-09-24">
+  <owner>mamir@chromium.org</owner>
+  <owner>mastiz@chromium.org</owner>
+  <summary>
+    The number of bookmarks a user has saved with a URL that is also present in
+    at least one other bookmark. This excludes folders (which don't have a URL).
+    Recorded when bookmarks are loaded into storage from disk if there is at
+    least one duplicate.
+  </summary>
+</histogram>
+
+<histogram name="Bookmarks.Count.OnProfileLoad.EmptyTitle" units="bookmarks"
+    expires_after="2019-09-24">
+  <owner>mamir@chromium.org</owner>
+  <owner>mastiz@chromium.org</owner>
+  <summary>
+    The number of bookmarks a user has saved with an empty title (strictly
+    speaking, i.e. does not count whitespace-only titles), including folders and
+    excluding the root. Recorded when bookmarks are loaded into storage from
+    disk if there is at least one node with an empty title.
+  </summary>
+</histogram>
+
+<histogram name="Bookmarks.DuplicateAndEmptyTitleDetectionTime" units="ms"
+    expires_after="2019-09-24">
+  <owner>mamir@chromium.org</owner>
+  <owner>mastiz@chromium.org</owner>
+  <summary>
+    Time to compute values to be logged for UMA metrics
+    Bookmarks.Count.OnProfileLoad.*.
+  </summary>
+</histogram>
+
 <histogram name="Bookmarks.EntryPoint" enum="BookmarksEntryPoint">
   <owner>ianwen@chromium.org</owner>
   <summary>How users add a new bookmark.</summary>
diff --git a/ui/compositor/paint_cache.cc b/ui/compositor/paint_cache.cc
index 71b2a0e..eec101fb 100644
--- a/ui/compositor/paint_cache.cc
+++ b/ui/compositor/paint_cache.cc
@@ -15,8 +15,10 @@
 
 bool PaintCache::UseCache(const PaintContext& context,
                           const gfx::Size& size_in_context) {
-  if (!paint_op_buffer_)
+  if (!paint_op_buffer_ ||
+      context.device_scale_factor() != device_scale_factor_) {
     return false;
+  }
   DCHECK(context.list_);
   context.list_->StartPaint();
   context.list_->push<cc::DrawRecordOp>(paint_op_buffer_);
@@ -25,8 +27,10 @@
   return true;
 }
 
-void PaintCache::SetPaintOpBuffer(sk_sp<cc::PaintOpBuffer> paint_op_buffer) {
+void PaintCache::SetPaintOpBuffer(sk_sp<cc::PaintOpBuffer> paint_op_buffer,
+                                  float device_scale_factor) {
   paint_op_buffer_ = std::move(paint_op_buffer);
+  device_scale_factor_ = device_scale_factor;
 }
 
 }  // namespace ui
diff --git a/ui/compositor/paint_cache.h b/ui/compositor/paint_cache.h
index 04ae875..1863e202 100644
--- a/ui/compositor/paint_cache.h
+++ b/ui/compositor/paint_cache.h
@@ -36,12 +36,19 @@
   // Only PaintRecorder can modify these.
   friend PaintRecorder;
 
-  void SetPaintOpBuffer(sk_sp<cc::PaintOpBuffer> paint_op_buffer);
+  void SetPaintOpBuffer(sk_sp<cc::PaintOpBuffer> paint_op_buffer,
+                        float device_scale_factor);
 
   // Stored in an sk_sp because PaintOpBuffer requires this to append the cached
   // items into it.
   sk_sp<cc::PaintOpBuffer> paint_op_buffer_;
 
+  // This allows paint cache to be device scale factor aware. If a request for
+  // a cache entry is made that does not match the stored cache entry's DSF,
+  // then we can reject it instead of returning the incorrect cache entry.
+  // See https://crbug.com/834114 for details.
+  float device_scale_factor_ = 0.f;
+
   DISALLOW_COPY_AND_ASSIGN(PaintCache);
 };
 
diff --git a/ui/compositor/paint_recorder.cc b/ui/compositor/paint_recorder.cc
index f85d3cb..173c88bc 100644
--- a/ui/compositor/paint_recorder.cc
+++ b/ui/compositor/paint_recorder.cc
@@ -74,7 +74,8 @@
   if (cache_) {
     local_list_->EndPaintOfUnpaired(gfx::Rect());
     local_list_->Finalize();
-    cache_->SetPaintOpBuffer(local_list_->ReleaseAsRecord());
+    cache_->SetPaintOpBuffer(local_list_->ReleaseAsRecord(),
+                             context_.device_scale_factor());
     cache_->UseCache(context_, recording_size_);
   } else {
     gfx::Rect bounds_in_layer = context_.ToLayerSpaceBounds(recording_size_);
diff --git a/ui/display/manager/display_manager.cc b/ui/display/manager/display_manager.cc
index 03816a0..11eee291 100644
--- a/ui/display/manager/display_manager.cc
+++ b/ui/display/manager/display_manager.cc
@@ -1343,7 +1343,6 @@
         return true;
       }
     }
-    return false;
   }
   // Mirror mode should remain unchanged as long as there are more than one
   // connected displays.
@@ -2231,6 +2230,13 @@
   if (num_connected_displays_ <= 1)
     return;
 
+  // The display prefs have just been loaded and we're waiting for the
+  // reconfiguration of the displays to apply the newly loaded prefs. We should
+  // not overwrite the newly-loaded external display mirror configs.
+  // https://crbug.com/936884.
+  if (should_restore_mirror_mode_from_display_prefs_)
+    return;
+
   // External displays mirrored because of forced tablet mode mirroring should
   // not be considered candidates for restoring their mirrored state.
   // https://crbug.com/919994.
diff --git a/ui/login/BUILD.gn b/ui/login/BUILD.gn
new file mode 100644
index 0000000..3d66b87
--- /dev/null
+++ b/ui/login/BUILD.gn
@@ -0,0 +1,8 @@
+# Copyright 2019 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//third_party/closure_compiler/compile_js.gni")
+
+js_library("display_manager_types") {
+}
diff --git a/ui/login/display_manager_types.js b/ui/login/display_manager_types.js
index c50024e..389ead2 100644
--- a/ui/login/display_manager_types.js
+++ b/ui/login/display_manager_types.js
@@ -7,6 +7,18 @@
  * Closure compiler type definitions used by display_manager.js .
  */
 
+/**
+ * @typedef {{
+ *   commonScreenSize: (boolean|undefined),
+ *   enableDebuggingAllowed: (boolean|undefined),
+ *   enterDemoModeAllowed: (boolean|undefined),
+ *   noAnimatedTransition: (boolean|undefined),
+ *   postponeEnrollmentAllowed: (boolean|undefined),
+ *   resetAllowed: (boolean|undefined),
+ *   startEnrollmentAllowed: (boolean|undefined),
+ *   toggleKioskAllowed: (boolean|undefined),
+ * }}
+ */
 var DisplayManagerScreenAttributes = {};
 
 /**
diff --git a/ui/shell_dialogs/select_file_dialog_mac.mm b/ui/shell_dialogs/select_file_dialog_mac.mm
index 21c7e89..0d8913e 100644
--- a/ui/shell_dialogs/select_file_dialog_mac.mm
+++ b/ui/shell_dialogs/select_file_dialog_mac.mm
@@ -268,10 +268,10 @@
     // this by never hiding extensions in that case.
     base::FilePath::StringType penultimate_extension =
         default_path.RemoveFinalExtension().FinalExtension();
-    if (!penultimate_extension.empty() &&
-        penultimate_extension.length() <= 5U) {
+    if (!penultimate_extension.empty()) {
       [dialog setExtensionHidden:NO];
     } else {
+      [dialog setExtensionHidden:YES];
       [dialog setCanSelectHiddenExtension:YES];
     }
   } else {
diff --git a/ui/shell_dialogs/select_file_dialog_mac_unittest.mm b/ui/shell_dialogs/select_file_dialog_mac_unittest.mm
index 4f71b81..8249888f 100644
--- a/ui/shell_dialogs/select_file_dialog_mac_unittest.mm
+++ b/ui/shell_dialogs/select_file_dialog_mac_unittest.mm
@@ -463,12 +463,14 @@
 TEST_F(SelectFileDialogMacTest, MultipleExtension) {
   const std::string fake_path_normal = "/fake_directory/filename.tar";
   const std::string fake_path_multiple = "/fake_directory/filename.tar.gz";
+  const std::string fake_path_long = "/fake_directory/example.com-123.json";
   FileDialogArguments args(GetDefaultArguments());
 
   args.default_path = base::FilePath(FILE_PATH_LITERAL(fake_path_normal));
   SelectFileWithParams(args);
   NSSavePanel* panel = GetPanel();
   EXPECT_TRUE([panel canSelectHiddenExtension]);
+  EXPECT_TRUE([panel isExtensionHidden]);
 
   ResetDialog();
   args.default_path = base::FilePath(FILE_PATH_LITERAL(fake_path_multiple));
@@ -476,6 +478,13 @@
   panel = GetPanel();
   EXPECT_FALSE([panel canSelectHiddenExtension]);
   EXPECT_FALSE([panel isExtensionHidden]);
+
+  ResetDialog();
+  args.default_path = base::FilePath(FILE_PATH_LITERAL(fake_path_long));
+  SelectFileWithParams(args);
+  panel = GetPanel();
+  EXPECT_FALSE([panel canSelectHiddenExtension]);
+  EXPECT_FALSE([panel isExtensionHidden]);
 }
 
 // Test to ensure lifetime is sound if a reference to the panel outlives the