diff --git a/BUILD.gn b/BUILD.gn
index 036f1213..6943ee4 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -744,7 +744,10 @@
   }
 
   if (use_atk) {
-    deps += [ "//tools/accessibility/inspect:ax_dump_tree" ]
+    deps += [
+      "//tools/accessibility/inspect:ax_dump_events",
+      "//tools/accessibility/inspect:ax_dump_tree",
+    ]
   }
 }
 
diff --git a/DEPS b/DEPS
index 28e1e044..6738eba1 100644
--- a/DEPS
+++ b/DEPS
@@ -105,11 +105,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': '6bdfebeb1594e90da280776e5f1f1b86364bb207',
+  'skia_revision': '0191ed8e87dca709c1248a685248e0a694bce80a',
   # 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': '3dea982408acf085f75be3efa73184efb6b44802',
+  'v8_revision': '345f55139f62a25e371d310b6e6d4c7d422062fc',
   # 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.
@@ -117,7 +117,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ANGLE
   # and whatever else without interference from each other.
-  'angle_revision': '95277a300f52bf89b7a8c14ada10e4dd3c5962b5',
+  'angle_revision': '0fdb956d9c241f67bfab8a81256147020d9a93f9',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling build tools
   # and whatever else without interference from each other.
@@ -129,7 +129,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
-  'pdfium_revision': 'debba6d2124e780ce627be8e058a7ce5d81d5910',
+  'pdfium_revision': 'c3099d1c694251a654edc6cb72df8adb5e2268ab',
   # 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.
@@ -165,7 +165,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': '934a29ea903e9da0745770fe046764e2d6b61bdc',
+  'catapult_revision': '874b57542941b7e9d5a099bdc5bcc0ee1c5b10ea',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -213,7 +213,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'spv_tools_revision': '5f599e700e931b66102d5816797691fc8b24cc01',
+  'spv_tools_revision': '0cd3e599ae3b3179c26b0e0756f0b488a9d82b7e',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -584,7 +584,7 @@
 
   # Build tools for Chrome OS. Note: This depends on third_party/pyelftools.
   'src/third_party/chromite': {
-      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '151945a7459ce7d5cecda7b05fb8dff7f8a7b1a2',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '2426fe64bffb099a84645fdfb326a813307248db',
       'condition': 'checkout_linux',
   },
 
@@ -609,7 +609,7 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'f9b4845975ffaf4b7bfe7b0b6ae0198b8a729bbd',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '1aa405fd859a3bd625b0d61184d6e4a3cf95c0b4',
 
   'src/third_party/devtools-node-modules':
     Var('chromium_git') + '/external/github.com/ChromeDevTools/devtools-node-modules' + '@' + Var('devtools_node_modules_revision'),
@@ -943,7 +943,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' +  'b98184a9ff697b5706fb331675354a36c7c0fa41',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' +  'c42c66b2dd053527068cab84c7af44df08b00475',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + 'ac0d98b5cee6c024b0cffeb4f8f45b6fc5ccdb78',
@@ -1098,7 +1098,7 @@
     Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + '7ca87fb1d3da3b3d2060886e8c58e726d74c8219',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '6d800030ab7efa0c0cb50efee737320d5f1c1d2e',
+    Var('webrtc_git') + '/src.git' + '@' + 'd574123c5035901107e4dd56a3cd0968f174a881',
 
   'src/third_party/xdg-utils': {
       'url': Var('chromium_git') + '/chromium/deps/xdg-utils.git' + '@' + 'd80274d5869b17b8c9067a1022e4416ee7ed5e0d',
@@ -1129,7 +1129,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@892036cb879fab76a3c3b8ee2c68af9c41064db8',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@cb44953479a132369f22beb6cf0e5af15767e300',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/WATCHLISTS b/WATCHLISTS
index 20d38133c..665fa6d 100644
--- a/WATCHLISTS
+++ b/WATCHLISTS
@@ -1089,6 +1089,9 @@
                   '|chrome/common/media_galleries/'\
                   '|chrome/test/data/extensions/api_test/media_galleries/'
     },
+    'media_gpu': {
+      'filepath': 'media/gpu/',
+    },
     'media_mojo': {
       'filepath': 'media/mojo/'
     },
@@ -1609,9 +1612,6 @@
     'usb': {
       'filepath': '/usb/',
     },
-    'v4l2': {
-      'filepath': 'media/gpu/v4l2',
-    },
     'vaapi': {
       'filepath': 'media/gpu/vaapi',
     },
@@ -2221,6 +2221,7 @@
     'media_controls': ['steimel+watch-mediacontrols@chromium.org'],
     'media_galleries': ['thestig@chromium.org',
                         'tommycli@chromium.org'],
+    'media_gpu': ['hiroh+watch@chromium.org'],
     'media_mojo': ['alokp+watch@chromium.org',
                    'xhwang+watch@chromium.org'],
     'media_recorder': ['emircan+watch+mediarecorder@chromium.org',
@@ -2407,7 +2408,6 @@
     'ui_resources': ['oshima+watch@chromium.org'],
     'ui_strings': ['srahim+watch@chromium.org'],
     'usb': ['mattreynolds+watch@chromium.org'],
-    'v4l2': ['hiroh+watch@chromium.org'],
     'vaapi': ['vaapi-reviews@chromium.org'],
     'version_assembly': ['caitkp+watch@chromium.org',
                          'gab+watch@chromium.org'],
diff --git a/android_webview/tools/system_webview_shell/test/data/webexposed/not-webview-exposed.txt b/android_webview/tools/system_webview_shell/test/data/webexposed/not-webview-exposed.txt
index c581be7..0d76763 100644
--- a/android_webview/tools/system_webview_shell/test/data/webexposed/not-webview-exposed.txt
+++ b/android_webview/tools/system_webview_shell/test/data/webexposed/not-webview-exposed.txt
@@ -18,7 +18,7 @@
 interface PaymentAddress
 interface PaymentRequest : EventTarget
 interface PaymentRequestUpdateEvent : Event
-interface PaymentResponse
+interface PaymentResponse : EventTarget
 
 # not yet supported in webview and chrome on android in general, crbug.com/154571
 interface SharedWorker : EventTarget
diff --git a/ash/app_list/views/app_list_drag_and_drop_host.h b/ash/app_list/views/app_list_drag_and_drop_host.h
index 4c57f230..3ec93435 100644
--- a/ash/app_list/views/app_list_drag_and_drop_host.h
+++ b/ash/app_list/views/app_list_drag_and_drop_host.h
@@ -41,11 +41,14 @@
   // |scale_factor| with its origin at |origin_in_screen_coordinates|.
   // Use |replaced_view| to find the screen which is used.
   // The proxy will be created without any visibility animation effect.
+  // Sets background blur with specified blur radius to the dragged icon if
+  // |blur_radius| is larger than 0.
   virtual void CreateDragIconProxyByLocationWithNoAnimation(
       const gfx::Point& origin_in_screen_coordinates,
       const gfx::ImageSkia& icon,
       views::View* replaced_view,
-      float scale_factor) = 0;
+      float scale_factor,
+      int blur_radius) = 0;
 
   // Updates the screen location of the Drag icon proxy.
   virtual void UpdateDragIconProxy(
diff --git a/ash/app_list/views/app_list_item_view.cc b/ash/app_list/views/app_list_item_view.cc
index 5069d473..24d77842 100644
--- a/ash/app_list/views/app_list_item_view.cc
+++ b/ash/app_list/views/app_list_item_view.cc
@@ -27,7 +27,6 @@
 #include "ui/gfx/canvas.h"
 #include "ui/gfx/font_list.h"
 #include "ui/gfx/geometry/point.h"
-#include "ui/gfx/geometry/size_conversions.h"
 #include "ui/gfx/geometry/vector2d.h"
 #include "ui/gfx/image/canvas_image_source.h"
 #include "ui/gfx/image/image_skia_operations.h"
@@ -81,41 +80,92 @@
 // The shadow color of icon.
 constexpr SkColor kIconShadowColor = SkColorSetA(SK_ColorBLACK, 31);
 
-// The class clips the provided icon image.
-class ClippedIconImageSource : public gfx::CanvasImageSource {
+// The class clips the provided folder icon image.
+class ClippedFolderIconImageSource : public gfx::CanvasImageSource {
  public:
-  ClippedIconImageSource(const gfx::ImageSkia& image,
-                         const gfx::Size& clipped_size)
-      : gfx::CanvasImageSource(clipped_size, false),
-        image_(image),
-        clipped_size_(clipped_size) {}
-  ~ClippedIconImageSource() override = default;
+  explicit ClippedFolderIconImageSource(const gfx::ImageSkia& image)
+      : gfx::CanvasImageSource(AppListConfig::instance().folder_icon_size(),
+                               false),
+        image_(image) {}
+  ~ClippedFolderIconImageSource() override = default;
 
   void Draw(gfx::Canvas* canvas) override {
     // Draw the unclipped icon on the center of the canvas with a circular mask.
     gfx::Path circular_mask;
-    circular_mask.addCircle(SkFloatToScalar(clipped_size_.width() / 2),
-                            SkFloatToScalar(clipped_size_.height() / 2),
-                            SkIntToScalar(clipped_size_.width() / 2));
+    circular_mask.addCircle(SkFloatToScalar(size_.width() / 2),
+                            SkFloatToScalar(size_.height() / 2),
+                            SkIntToScalar(size_.width() / 2));
 
     cc::PaintFlags flags;
     flags.setStyle(cc::PaintFlags::kFill_Style);
     flags.setAntiAlias(true);
-    canvas->DrawImageInPath(
-        image_, (clipped_size_.width() - image_.size().width()) / 2,
-        (clipped_size_.height() - image_.size().height()) / 2, circular_mask,
-        flags);
+    canvas->DrawImageInPath(image_, (size_.width() - image_.size().width()) / 2,
+                            (size_.height() - image_.size().height()) / 2,
+                            circular_mask, flags);
   }
 
  private:
   const gfx::ImageSkia image_;
-  const gfx::Size clipped_size_;
 
-  DISALLOW_COPY_AND_ASSIGN(ClippedIconImageSource);
+  DISALLOW_COPY_AND_ASSIGN(ClippedFolderIconImageSource);
 };
 
 }  // namespace
 
+// ImageView for the item icon.
+class AppListItemView::IconImageView : public views::ImageView {
+ public:
+  IconImageView() {
+    SetPaintToLayer();
+    layer()->SetFillsBoundsOpaquely(false);
+    set_can_process_events_within_subtree(false);
+    SetVerticalAlignment(views::ImageView::LEADING);
+  }
+  ~IconImageView() override = default;
+
+  // views::View:
+  void OnBoundsChanged(const gfx::Rect& previous_bounds) override {
+    if (icon_mask_)
+      icon_mask_->layer()->SetBounds(GetLocalBounds());
+  }
+
+  // ui::LayerOwner:
+  std::unique_ptr<ui::Layer> RecreateLayer() override {
+    std::unique_ptr<ui::Layer> old_layer = views::View::RecreateLayer();
+
+    // ui::Layer::Clone() does not copy mask layer, so set it explicitly here.
+    SetRoundedRectMaskLayer(mask_corner_radius_, mask_insets_);
+    return old_layer;
+  }
+
+  // Sets a rounded rect mask layer with |corner_radius| and |insets| to clip
+  // the icon.
+  void SetRoundedRectMaskLayer(int corner_radius, const gfx::Insets& insets) {
+    icon_mask_ = views::Painter::CreatePaintedLayer(
+        views::Painter::CreateSolidRoundRectPainter(SK_ColorBLACK,
+                                                    corner_radius, insets));
+    icon_mask_->layer()->SetFillsBoundsOpaquely(false);
+    icon_mask_->layer()->SetBounds(GetLocalBounds());
+    layer()->SetMaskLayer(icon_mask_->layer());
+
+    // Save the attributes in case the layer is recreated.
+    mask_corner_radius_ = corner_radius;
+    mask_insets_ = insets;
+  }
+
+ private:
+  // The owner of a mask layer to clip the icon into circle.
+  std::unique_ptr<ui::LayerOwner> icon_mask_;
+
+  // The corner radius of mask layer.
+  int mask_corner_radius_;
+
+  // The insets of the mask layer.
+  gfx::Insets mask_insets_;
+
+  DISALLOW_COPY_AND_ASSIGN(IconImageView);
+};
+
 // static
 const char AppListItemView::kViewClassName[] = "ui/app_list/AppListItemView";
 
@@ -134,17 +184,21 @@
       item_weak_(item),
       delegate_(delegate),
       apps_grid_view_(apps_grid_view),
-      icon_(new views::ImageView),
+      icon_(new IconImageView),
       title_(new views::Label),
       progress_bar_(new views::ProgressBar),
       is_new_style_launcher_enabled_(features::IsNewStyleLauncherEnabled()),
       weak_ptr_factory_(this) {
   SetFocusBehavior(FocusBehavior::ALWAYS);
 
-  icon_->SetPaintToLayer();
-  icon_->layer()->SetFillsBoundsOpaquely(false);
-  icon_->set_can_process_events_within_subtree(false);
-  icon_->SetVerticalAlignment(views::ImageView::LEADING);
+  if (is_new_style_launcher_enabled_ && is_folder_) {
+    // Set background blur for folder icon and use mask layer to clip it into
+    // circle.
+    icon_->layer()->SetBackgroundBlur(AppListConfig::instance().blur_radius());
+    icon_->SetRoundedRectMaskLayer(
+        AppListConfig::instance().folder_icon_radius(),
+        gfx::Insets(AppListConfig::instance().folder_icon_insets()));
+  }
 
   if (is_new_style_launcher_enabled_ && !is_in_folder_ && !is_folder_) {
     // To display shadow for icon while not affecting the icon's bounds, icon
@@ -178,9 +232,7 @@
   AddChildView(title_);
   AddChildView(progress_bar_);
 
-  SetIcon(
-      item->icon(), is_folder_ /* clip */,
-      is_folder_ ? AppListConfig::instance().folder_icon_size() : gfx::Size());
+  SetIcon(item->icon());
   SetItemName(base::UTF8ToUTF16(item->GetDisplayName()),
               base::UTF8ToUTF16(item->name()));
   SetItemIsInstalling(item->is_installing());
@@ -199,9 +251,7 @@
     item_weak_->RemoveObserver(this);
 }
 
-void AppListItemView::SetIcon(const gfx::ImageSkia& icon,
-                              bool clip,
-                              const gfx::Size& clipped_size) {
+void AppListItemView::SetIcon(const gfx::ImageSkia& icon) {
   // Clear icon and bail out if item icon is empty.
   if (icon.isNull()) {
     icon_->SetImage(nullptr);
@@ -215,21 +265,16 @@
       is_folder_ ? AppListConfig::instance().folder_unclipped_icon_size()
                  : AppListConfig::instance().grid_icon_size());
 
-  gfx::ImageSkia clipped =
-      clip ? gfx::CanvasImageSource::MakeImageSkia<ClippedIconImageSource>(
-                 resized, clipped_size)
-           : icon;
-
   if (shadow_animator_)
-    shadow_animator_->SetOriginalImage(clipped);
+    shadow_animator_->SetOriginalImage(resized);
   else
-    icon_->SetImage(clipped);
+    icon_->SetImage(resized);
 
   if (icon_shadow_) {
     // Create a shadow for the shown icon.
     gfx::ImageSkia shadowed =
         gfx::ImageSkiaOperations::CreateImageWithDropShadow(
-            clipped, gfx::ShadowValues(
+            resized, gfx::ShadowValues(
                          1, gfx::ShadowValue(gfx::Vector2d(), kIconShadowBlur,
                                              kIconShadowColor)));
     icon_shadow_->SetImage(shadowed);
@@ -529,6 +574,7 @@
   const gfx::Rect icon_bounds =
       GetIconBoundsForTargetViewBounds(rect, icon_->GetImage().size());
   icon_->SetBoundsRect(icon_bounds);
+
   if (icon_shadow_) {
     const gfx::Rect icon_shadow_bounds =
         GetIconBoundsForTargetViewBounds(rect, icon_shadow_->GetImage().size());
@@ -696,12 +742,15 @@
 
 void AppListItemView::AnimationProgressed(const gfx::Animation* animation) {
   if (is_folder_) {
-    SetIcon(item_weak_->icon(), true /* clip */,
-            gfx::ToRoundedSize(gfx::Tween::SizeValueBetween(
-                animation->GetCurrentValue(),
-                gfx::SizeF(AppListConfig::instance().folder_icon_size()),
-                gfx::SizeF(
-                    AppListConfig::instance().folder_unclipped_icon_size()))));
+    // Animate the folder icon via changing mask layer's corner radius and
+    // insets.
+    const double progress = animation->GetCurrentValue();
+    const int corner_radius = gfx::Tween::IntValueBetween(
+        progress, AppListConfig::instance().folder_icon_dimension() / 2,
+        AppListConfig::instance().folder_unclipped_icon_dimension() / 2);
+    const int insets = gfx::Tween::IntValueBetween(
+        progress, AppListConfig::instance().folder_icon_insets(), 0);
+    icon_->SetRoundedRectMaskLayer(corner_radius, gfx::Insets(insets));
     return;
   }
 
@@ -740,8 +789,8 @@
   if (!is_folder_)
     return icon_->GetImage();
 
-  return gfx::CanvasImageSource::MakeImageSkia<ClippedIconImageSource>(
-      icon_->GetImage(), AppListConfig::instance().folder_icon_size());
+  return gfx::CanvasImageSource::MakeImageSkia<ClippedFolderIconImageSource>(
+      icon_->GetImage());
 }
 
 void AppListItemView::SetIconVisible(bool visible) {
@@ -807,22 +856,7 @@
 
 void AppListItemView::ItemIconChanged() {
   DCHECK(item_weak_);
-  if (!is_folder_) {
-    SetIcon(item_weak_->icon(), false /* clip */, gfx::Size());
-    return;
-  }
-
-  gfx::Size clipped_size = AppListConfig::instance().folder_icon_size();
-  if (dragged_view_hover_animation_ &&
-      dragged_view_hover_animation_->is_animating()) {
-    // The icon is updated before the folder icon animation ended, so clip the
-    // icon to the size in progress here.
-    clipped_size = gfx::ToRoundedSize(gfx::Tween::SizeValueBetween(
-        dragged_view_hover_animation_->GetCurrentValue(),
-        gfx::SizeF(AppListConfig::instance().folder_icon_size()),
-        gfx::SizeF(AppListConfig::instance().folder_unclipped_icon_size())));
-  }
-  SetIcon(item_weak_->icon(), true /* clip */, clipped_size);
+  SetIcon(item_weak_->icon());
 }
 
 void AppListItemView::ItemNameChanged() {
diff --git a/ash/app_list/views/app_list_item_view.h b/ash/app_list/views/app_list_item_view.h
index b4e08fc..f29c586 100644
--- a/ash/app_list/views/app_list_item_view.h
+++ b/ash/app_list/views/app_list_item_view.h
@@ -53,11 +53,8 @@
                   bool is_in_folder);
   ~AppListItemView() override;
 
-  // Sets the icon of this image. Clips the icon into |clipped_size| if |clip|
-  // is true.
-  void SetIcon(const gfx::ImageSkia& icon,
-               bool clip,
-               const gfx::Size& clipped_size);
+  // Sets the icon of this image.
+  void SetIcon(const gfx::ImageSkia& icon);
 
   void SetItemName(const base::string16& display_name,
                    const base::string16& full_name);
@@ -135,6 +132,8 @@
   void OnDraggedViewExit();
 
  private:
+  class IconImageView;
+
   enum UIState {
     UI_STATE_NORMAL,              // Normal UI (icon + label)
     UI_STATE_DRAGGING,            // Dragging UI (scaled icon only)
@@ -225,7 +224,7 @@
 
   AppListViewDelegate* delegate_;            // Unowned.
   AppsGridView* apps_grid_view_;             // Parent view, owns this.
-  views::ImageView* icon_;                   // Strongly typed child view.
+  IconImageView* icon_;                      // Strongly typed child view.
   views::Label* title_;                      // Strongly typed child view.
   views::ProgressBar* progress_bar_;         // Strongly typed child view.
   views::ImageView* icon_shadow_ = nullptr;  // Strongly typed child view.
diff --git a/ash/app_list/views/app_list_view.cc b/ash/app_list/views/app_list_view.cc
index 598657e..068a5c442 100644
--- a/ash/app_list/views/app_list_view.cc
+++ b/ash/app_list/views/app_list_view.cc
@@ -80,9 +80,6 @@
 // closed app list.
 constexpr int kAppListBezelMargin = 50;
 
-// The blur radius of the app list background.
-constexpr int kAppListBlurRadius = 30;
-
 // The size of app info dialog in fullscreen app list.
 constexpr int kAppInfoDialogWidth = 512;
 constexpr int kAppInfoDialogHeight = 384;
@@ -500,12 +497,13 @@
   SetBackgroundShieldColor();
   if (is_background_blur_enabled_ && !IsHomeLauncherEnabledInTabletMode()) {
     app_list_background_shield_mask_ = views::Painter::CreatePaintedLayer(
-        views::Painter::CreateSolidRoundRectPainter(SK_ColorBLACK,
-                                                    kAppListBlurRadius));
+        views::Painter::CreateSolidRoundRectPainter(
+            SK_ColorBLACK, AppListConfig::instance().blur_radius()));
     app_list_background_shield_mask_->layer()->SetFillsBoundsOpaquely(false);
     app_list_background_shield_->layer()->SetMaskLayer(
         app_list_background_shield_mask_->layer());
-    app_list_background_shield_->layer()->SetBackgroundBlur(kAppListBlurRadius);
+    app_list_background_shield_->layer()->SetBackgroundBlur(
+        AppListConfig::instance().blur_radius());
   }
   AddChildView(app_list_background_shield_);
   app_list_main_view_ = new AppListMainView(delegate_, this);
diff --git a/ash/app_list/views/apps_grid_view.cc b/ash/app_list/views/apps_grid_view.cc
index 9c61515..e8a6b32 100644
--- a/ash/app_list/views/apps_grid_view.cc
+++ b/ash/app_list/views/apps_grid_view.cc
@@ -1992,7 +1992,10 @@
   DCHECK(!IsDraggingForReparentInRootLevelGridView());
   drag_and_drop_host_->CreateDragIconProxyByLocationWithNoAnimation(
       drag_view_->GetIconBoundsInScreen().origin(), drag_view_->GetIconImage(),
-      drag_view_, kDragAndDropProxyScale);
+      drag_view_, kDragAndDropProxyScale,
+      is_new_style_launcher_enabled_ && drag_view_->item()->is_folder()
+          ? AppListConfig::instance().blur_radius()
+          : 0);
 
   SetViewHidden(drag_view_, true /* hide */, true /* no animation */);
 }
diff --git a/ash/app_list/views/suggestion_chip_view.cc b/ash/app_list/views/suggestion_chip_view.cc
index ba33770..618b1836 100644
--- a/ash/app_list/views/suggestion_chip_view.cc
+++ b/ash/app_list/views/suggestion_chip_view.cc
@@ -41,6 +41,7 @@
 constexpr SkColor kAppListRippleColor = SkColorSetA(gfx::kGoogleGrey100, 0x0F);
 constexpr SkColor kAppListFocusColor = SkColorSetA(gfx::kGoogleGrey100, 0x14);
 constexpr int kAppListMaxTextWidth = 192;
+constexpr int kBlurRadius = 5;
 
 // Shared style:
 constexpr int kIconMarginDip = 8;
@@ -65,6 +66,16 @@
       assistant_style_(params.assistant_style) {
   SetFocusBehavior(FocusBehavior::ALWAYS);
   SetInkDropMode(InkDropHostView::InkDropMode::ON);
+
+  if (!assistant_style_) {
+    // Set background blur for the chip and use mask layer to clip it into
+    // rounded rect.
+    SetPaintToLayer();
+    layer()->SetFillsBoundsOpaquely(false);
+    layer()->SetBackgroundBlur(kBlurRadius);
+    SetRoundedRectMaskLayer(kPreferredHeightDip / 2);
+  }
+
   InitLayout(params);
 }
 
@@ -166,6 +177,11 @@
   SchedulePaint();
 }
 
+void SuggestionChipView::OnBoundsChanged(const gfx::Rect& previous_bounds) {
+  if (chip_mask_)
+    chip_mask_->layer()->SetBounds(GetContentsBounds());
+}
+
 std::unique_ptr<views::InkDrop> SuggestionChipView::CreateInkDrop() {
   std::unique_ptr<views::InkDropImpl> ink_drop =
       Button::CreateDefaultInkDropImpl();
@@ -192,6 +208,13 @@
       GetInkDropCenterBasedOnLastEvent(), kAppListRippleColor, 1.0f);
 }
 
+std::unique_ptr<ui::Layer> SuggestionChipView::RecreateLayer() {
+  std::unique_ptr<ui::Layer> old_layer = views::View::RecreateLayer();
+  if (layer())
+    SetRoundedRectMaskLayer(kPreferredHeightDip / 2);
+  return old_layer;
+}
+
 void SuggestionChipView::SetIcon(const gfx::ImageSkia& icon) {
   icon_view_->SetImage(icon);
   icon_view_->SetVisible(true);
@@ -210,4 +233,13 @@
   return text_view_->text();
 }
 
+void SuggestionChipView::SetRoundedRectMaskLayer(int corner_radius) {
+  chip_mask_ = views::Painter::CreatePaintedLayer(
+      views::Painter::CreateSolidRoundRectPainter(SK_ColorBLACK,
+                                                  corner_radius));
+  chip_mask_->layer()->SetFillsBoundsOpaquely(false);
+  chip_mask_->layer()->SetBounds(GetLocalBounds());
+  layer()->SetMaskLayer(chip_mask_->layer());
+}
+
 }  // namespace app_list
diff --git a/ash/app_list/views/suggestion_chip_view.h b/ash/app_list/views/suggestion_chip_view.h
index 6ae83c30..7642e349 100644
--- a/ash/app_list/views/suggestion_chip_view.h
+++ b/ash/app_list/views/suggestion_chip_view.h
@@ -49,12 +49,16 @@
   void OnPaintBackground(gfx::Canvas* canvas) override;
   void OnFocus() override;
   void OnBlur() override;
+  void OnBoundsChanged(const gfx::Rect& previous_bounds) override;
 
   // views::InkDropHost:
   std::unique_ptr<views::InkDrop> CreateInkDrop() override;
   std::unique_ptr<views::InkDropMask> CreateInkDropMask() const override;
   std::unique_ptr<views::InkDropRipple> CreateInkDropRipple() const override;
 
+  // ui::LayerOwner:
+  std::unique_ptr<ui::Layer> RecreateLayer() override;
+
   void SetIcon(const gfx::ImageSkia& icon);
 
   void SetText(const base::string16& text);
@@ -63,6 +67,9 @@
  private:
   void InitLayout(const Params& params);
 
+  // Sets a rounded rect mask layer with |corner_radius| to clip the chip.
+  void SetRoundedRectMaskLayer(int corner_radius);
+
   views::ImageView* icon_view_;  // Owned by view hierarchy.
   views::Label* text_view_;      // Owned by view hierarchy.
 
@@ -71,6 +78,9 @@
   // True if this chip should use assistant style.
   bool assistant_style_;
 
+  // The owner of a mask layer used to clip the chip.
+  std::unique_ptr<ui::LayerOwner> chip_mask_;
+
   DISALLOW_COPY_AND_ASSIGN(SuggestionChipView);
 };
 
diff --git a/ash/ash_strings.grd b/ash/ash_strings.grd
index d85eee2..75c262f 100644
--- a/ash/ash_strings.grd
+++ b/ash/ash_strings.grd
@@ -1394,9 +1394,6 @@
       <message name="IDS_ASH_LOGIN_POD_PASSWORD_TAP_PLACEHOLDER" desc="Text to display as placeholder in the password field when the user can click/tap their profile picture to unlock.">
         Click your photo
       </message>
-      <message name="IDS_ASH_LOGIN_POD_PASSWORD_SIGNING_IN_PLACEHOLDER" desc="Text to display as placeholder in the password field during signing in the user. The translation should be as short as possible because of limited UI space.">
-        Signing in
-      </message>
       <message name="IDS_ASH_LOGIN_ERROR_AUTHENTICATING" desc="Couldn't sign in because password is invalid">
         Sorry, your password couldn't be verified. Please try again.
       </message>
diff --git a/ash/assistant/assistant_controller.cc b/ash/assistant/assistant_controller.cc
index d4f7ee0..e888bab 100644
--- a/ash/assistant/assistant_controller.cc
+++ b/ash/assistant/assistant_controller.cc
@@ -214,6 +214,7 @@
 
 void AssistantController::ShouldOpenUrlFromTab(
     const GURL& url,
+    WindowOpenDisposition disposition,
     ash::mojom::ManagedWebContentsOpenUrlDelegate::ShouldOpenUrlFromTabCallback
         callback) {
   // We always handle deep links ourselves.
@@ -223,10 +224,13 @@
     return;
   }
 
+  AssistantUiMode ui_mode = assistant_ui_controller_->model()->ui_mode();
+
   // When in main UI mode, WebContents should not navigate as they are hosting
-  // Assistant cards. Instead, we route navigation attempts to the browser.
-  if (assistant_ui_controller_->model()->ui_mode() ==
-      AssistantUiMode::kMainUi) {
+  // Assistant cards. Instead, we route navigation attempts to the browser. We
+  // also respect open |disposition| to launch in the browser if appropriate.
+  if (ui_mode == AssistantUiMode::kMainUi ||
+      disposition == WindowOpenDisposition::NEW_FOREGROUND_TAB) {
     std::move(callback).Run(/*should_open=*/false);
     OpenUrl(url);
     return;
diff --git a/ash/assistant/assistant_controller.h b/ash/assistant/assistant_controller.h
index 0742854..8ad03bc 100644
--- a/ash/assistant/assistant_controller.h
+++ b/ash/assistant/assistant_controller.h
@@ -112,6 +112,7 @@
   // mojom::ManagedWebContentsOpenUrlDelegate:
   void ShouldOpenUrlFromTab(
       const GURL& url,
+      WindowOpenDisposition disposition,
       mojom::ManagedWebContentsOpenUrlDelegate::ShouldOpenUrlFromTabCallback
           callback) override;
 
diff --git a/ash/display/window_tree_host_manager.cc b/ash/display/window_tree_host_manager.cc
index d99ca268..81304dd1 100644
--- a/ash/display/window_tree_host_manager.cc
+++ b/ash/display/window_tree_host_manager.cc
@@ -77,14 +77,6 @@
       CreateRootWindowTransformerForDisplay(host->window(), display));
   ash_host->SetRootWindowTransformer(std::move(transformer));
 
-  display::ManagedDisplayMode mode;
-  if (GetDisplayManager()->GetActiveModeForDisplayId(display.id(), &mode) &&
-      mode.refresh_rate() > 0.0f) {
-    host->compositor()->SetAuthoritativeVSyncInterval(
-        base::TimeDelta::FromMicroseconds(base::Time::kMicrosecondsPerSecond /
-                                          mode.refresh_rate()));
-  }
-
   // Just moving the display requires the full redraw.
   // chrome-os-partner:33558.
   host->compositor()->ScheduleFullRedraw();
diff --git a/ash/frame/caption_buttons/frame_caption_button.cc b/ash/frame/caption_buttons/frame_caption_button.cc
index 025e214d..b429e1c 100644
--- a/ash/frame/caption_buttons/frame_caption_button.cc
+++ b/ash/frame/caption_buttons/frame_caption_button.cc
@@ -37,6 +37,22 @@
 // The ratio applied to the button's alpha when the button is disabled.
 const float kDisabledButtonAlphaRatio = 0.5f;
 
+// Minimum theme light color contrast.
+const float kContrastLightItemThreshold = 3;
+// The amount to darken a light theme color by for use as foreground color.
+const float kThemedForegroundBlackFraction = 0.64;
+
+// This mimics |shouldUseLightForegroundOnBackground| in ColorUtils.java.
+bool UseLightColor(FrameCaptionButton::ColorMode color_mode,
+                   SkColor background_color) {
+  if (color_mode == FrameCaptionButton::ColorMode::kThemed) {
+    return color_utils::GetContrastRatio(SK_ColorWHITE, background_color) >=
+           kContrastLightItemThreshold;
+  }
+  DCHECK_EQ(color_mode, FrameCaptionButton::ColorMode::kDefault);
+  return color_utils::IsDark(background_color);
+}
+
 // Returns the amount by which the inkdrop ripple and mask should be insetted
 // from the button size in order to achieve a circular inkdrop with a size
 // equals to kInkDropHighlightSize.
@@ -83,12 +99,18 @@
 // static
 SkColor FrameCaptionButton::GetButtonColor(ColorMode color_mode,
                                            SkColor background_color) {
-  if (color_mode == ColorMode::kThemed)
-    return color_utils::GetThemedAssetColor(background_color);
+  bool use_light_color = UseLightColor(color_mode, background_color);
+
+  if (color_mode == ColorMode::kThemed) {
+    // This mimics |getThemedAssetColor| in ColorUtils.java.
+    return use_light_color
+               ? SK_ColorWHITE
+               : color_utils::AlphaBlend(SK_ColorBLACK, background_color,
+                                         255 * kThemedForegroundBlackFraction);
+  }
 
   DCHECK_EQ(color_mode, ColorMode::kDefault);
-  return color_utils::IsDark(background_color) ? gfx::kGoogleGrey200
-                                               : gfx::kGoogleGrey700;
+  return use_light_color ? gfx::kGoogleGrey200 : gfx::kGoogleGrey700;
 }
 
 // static
@@ -281,10 +303,9 @@
 }
 
 void FrameCaptionButton::UpdateInkDropBaseColor() {
-  set_ink_drop_base_color(
-      color_utils::IsDark(GetButtonColor(color_mode_, background_color_))
-          ? SK_ColorBLACK
-          : SK_ColorWHITE);
+  set_ink_drop_base_color(UseLightColor(color_mode_, background_color_)
+                              ? SK_ColorWHITE
+                              : SK_ColorBLACK);
 }
 
 }  // namespace ash
diff --git a/ash/login/ui/login_auth_user_view.cc b/ash/login/ui/login_auth_user_view.cc
index 44e2b4a..a6ac8a0 100644
--- a/ash/login/ui/login_auth_user_view.cc
+++ b/ash/login/ui/login_auth_user_view.cc
@@ -726,7 +726,8 @@
   // Pressing enter when the password field is empty and tap-to-unlock is
   // enabled should attempt unlock.
   if (HasAuthMethod(AUTH_TAP) && password.empty()) {
-    OnUserViewTap();
+    Shell::Get()->login_screen_controller()->AttemptUnlock(
+        current_user()->basic_user_info->account_id);
     return;
   }
 
@@ -754,42 +755,8 @@
   on_auth_.Run(auth_success.value());
 }
 
-void LoginAuthUserView::AnimateEllipses(const base::string16& placeholder,
-                                        int step) {
-  const int kEllipsesCount = 3;
-  const int kAnimationDelayMs = 750;
-  const base::char16 kPeriod = '.';
-
-  // There are four total steps, so increment just past kEllipsesCount.
-  if (step > kEllipsesCount) {
-    // Start the animation over.
-    step = 0;
-  }
-
-  base::string16 ellipses;
-  for (int i = 0; i < step; i++)
-    ellipses += kPeriod;
-
-  password_view_->SetPlaceholderText(placeholder + ellipses);
-
-  base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
-      FROM_HERE,
-      base::BindOnce(&LoginAuthUserView::AnimateEllipses,
-                     weak_factory_.GetWeakPtr(), placeholder, ++step),
-      base::TimeDelta::FromMilliseconds(kAnimationDelayMs));
-}
-
 void LoginAuthUserView::OnUserViewTap() {
   if (HasAuthMethod(AUTH_TAP)) {
-    password_view_->SetReadOnly(true);
-
-    base::string16 placeholder = l10n_util::GetStringUTF16(
-        IDS_ASH_LOGIN_POD_PASSWORD_SIGNING_IN_PLACEHOLDER);
-    password_view_->SetPlaceholderText(placeholder);
-
-    const int kInitialStep = 0;
-    AnimateEllipses(placeholder, kInitialStep);
-
     Shell::Get()->login_screen_controller()->AttemptUnlock(
         current_user()->basic_user_info->account_id);
   } else if (HasAuthMethod(AUTH_ONLINE_SIGN_IN)) {
diff --git a/ash/login/ui/login_auth_user_view.h b/ash/login/ui/login_auth_user_view.h
index 342b6288..ea96bf4 100644
--- a/ash/login/ui/login_auth_user_view.h
+++ b/ash/login/ui/login_auth_user_view.h
@@ -141,10 +141,6 @@
   // Called with the result of the request started in |OnAuthSubmit|.
   void OnAuthComplete(base::Optional<bool> auth_success);
 
-  // Animates the display of ellipses after the |placeholder| text. |step| is
-  // the current index of the animation sequence.
-  void AnimateEllipses(const base::string16& placeholder, int step);
-
   // Called when the user view has been tapped. This will run |on_auth_| if tap
   // to unlock is enabled, or run |OnOnlineSignInMessageTap| if the online
   // sign-in message is shown, otherwise it will run |on_tap_|.
diff --git a/ash/login/ui/login_auth_user_view_unittest.cc b/ash/login/ui/login_auth_user_view_unittest.cc
index 4101287..ce61283 100644
--- a/ash/login/ui/login_auth_user_view_unittest.cc
+++ b/ash/login/ui/login_auth_user_view_unittest.cc
@@ -9,12 +9,10 @@
 #include "ash/login/ui/login_test_base.h"
 #include "ash/login/ui/login_test_utils.h"
 #include "ash/login/ui/login_user_view.h"
-#include "ash/strings/grit/ash_strings.h"
 #include "base/bind_helpers.h"
 #include "base/run_loop.h"
 #include "base/strings/utf_string_conversions.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "ui/base/l10n/l10n_util.h"
 #include "ui/events/event_utils.h"
 #include "ui/events/test/event_generator.h"
 #include "ui/views/controls/textfield/textfield.h"
@@ -108,7 +106,6 @@
 
   LoginAuthUserView::TestApi test_auth_user_view(view_);
   LoginPasswordView* password_view(test_auth_user_view.password_view());
-  LoginPasswordView::TestApi test_password_view(password_view);
   LoginUserView* user_view(test_auth_user_view.user_view());
 
   SetUserCount(1);
@@ -122,13 +119,6 @@
 
   generator->PressKey(ui::KeyboardCode::VKEY_RETURN, 0);
   base::RunLoop().RunUntilIdle();
-
-  // Verify the "Signing in" placeholder text was set.
-  base::string16 placeholder =
-      test_password_view.textfield()->GetPlaceholderText();
-  base::string16 expected_placeholder = l10n_util::GetStringUTF16(
-      IDS_ASH_LOGIN_POD_PASSWORD_SIGNING_IN_PLACEHOLDER);
-  EXPECT_EQ(expected_placeholder, placeholder);
 }
 
 TEST_F(LoginAuthUserViewUnittest, OnlineSignInMessage) {
diff --git a/ash/public/cpp/app_list/app_list_config.cc b/ash/public/cpp/app_list/app_list_config.cc
index 1b6fb87d..753d636 100644
--- a/ash/public/cpp/app_list/app_list_config.cc
+++ b/ash/public/cpp/app_list/app_list_config.cc
@@ -57,7 +57,8 @@
       grid_tile_spacing_in_folder_(12),
       // TODO(manucornet): Share the value with ShelfConstants and use
       // 48 when the new shelf UI is turned off.
-      shelf_height_(56) {
+      shelf_height_(56),
+      blur_radius_(30) {
   if (features::IsNewStyleLauncherEnabled()) {
     grid_tile_width_ = 120;
     grid_tile_height_ = 112;
diff --git a/ash/public/cpp/app_list/app_list_config.h b/ash/public/cpp/app_list/app_list_config.h
index 3a1b010..39a04c1 100644
--- a/ash/public/cpp/app_list/app_list_config.h
+++ b/ash/public/cpp/app_list/app_list_config.h
@@ -90,6 +90,7 @@
     return grid_tile_spacing_in_folder_;
   }
   int shelf_height() const { return shelf_height_; }
+  int blur_radius() const { return blur_radius_; }
 
   gfx::Size grid_icon_size() const {
     return gfx::Size(grid_icon_dimension_, grid_icon_dimension_);
@@ -255,6 +256,9 @@
 
   // The height/width of the shelf from the bottom/side of the screen.
   int shelf_height_;
+
+  // The blur radius used in the app list.
+  int blur_radius_ = 30;
 };
 
 }  // namespace app_list
diff --git a/ash/public/interfaces/BUILD.gn b/ash/public/interfaces/BUILD.gn
index e4865bd2..9ddee092 100644
--- a/ash/public/interfaces/BUILD.gn
+++ b/ash/public/interfaces/BUILD.gn
@@ -77,6 +77,7 @@
     "//skia/public/interfaces",
     "//ui/accessibility:ax_enums_mojo",
     "//ui/base/ime/chromeos/public/interfaces",
+    "//ui/base/mojo",
     "//ui/display/mojo:interfaces",
     "//ui/events/mojo:interfaces",
     "//ui/gfx/geometry/mojo",
diff --git a/ash/public/interfaces/web_contents_manager.mojom b/ash/public/interfaces/web_contents_manager.mojom
index 02f6c1f..65037d9 100644
--- a/ash/public/interfaces/web_contents_manager.mojom
+++ b/ash/public/interfaces/web_contents_manager.mojom
@@ -6,6 +6,7 @@
 
 import "components/account_id/interfaces/account_id.mojom";
 import "mojo/public/mojom/base/unguessable_token.mojom";
+import "ui/base/mojo/window_open_disposition.mojom";
 import "ui/gfx/geometry/mojo/geometry.mojom";
 import "url/mojom/url.mojom";
 
@@ -58,7 +59,9 @@
 // Interface for a delegate to handle top level browser requests for a
 // managed WebContents.
 interface ManagedWebContentsOpenUrlDelegate {
-  // Invoked on a top level browser request to navigate to |url|. Return false
-  // to consume the navigation attempt or true to allow it to continue.
-  ShouldOpenUrlFromTab(url.mojom.Url url) => (bool should_open);
+  // Invoked on a top level browser request to navigate to |url| with the
+  // specified window open |disposition|. Return false to consume the
+  // navigation attempt or true to allow it to continue.
+  ShouldOpenUrlFromTab(url.mojom.Url url,
+    ui.mojom.WindowOpenDisposition disposition) => (bool should_open);
 };
diff --git a/ash/shelf/overflow_button.cc b/ash/shelf/overflow_button.cc
index 0e63618e..d614251 100644
--- a/ash/shelf/overflow_button.cc
+++ b/ash/shelf/overflow_button.cc
@@ -64,12 +64,10 @@
 }
 
 void OverflowButton::OnOverflowBubbleShown() {
-  AnimateInkDrop(views::InkDropState::ACTIVATED, nullptr);
   UpdateChevronImage();
 }
 
 void OverflowButton::OnOverflowBubbleHidden() {
-  AnimateInkDrop(views::InkDropState::DEACTIVATED, nullptr);
   UpdateChevronImage();
 }
 
diff --git a/ash/shelf/shelf_view.cc b/ash/shelf/shelf_view.cc
index f510ddeb..5915327 100644
--- a/ash/shelf/shelf_view.cc
+++ b/ash/shelf/shelf_view.cc
@@ -658,12 +658,15 @@
     const gfx::Point& origin_in_screen_coordinates,
     const gfx::ImageSkia& icon,
     views::View* replaced_view,
-    float scale_factor) {
+    float scale_factor,
+    int blur_radius) {
   drag_replaced_view_ = replaced_view;
   aura::Window* root_window =
       drag_replaced_view_->GetWidget()->GetNativeWindow()->GetRootWindow();
   drag_image_ = std::make_unique<DragImageView>(
       root_window, ui::DragDropTypes::DRAG_EVENT_SOURCE_MOUSE);
+  if (blur_radius > 0)
+    SetDragImageBlur(icon.size(), blur_radius);
   drag_image_->SetImage(icon);
   gfx::Size size = drag_image_->GetPreferredSize();
   size.set_width(size.width() * scale_factor);
@@ -2225,4 +2228,15 @@
       IsTabletModeEnabled() ? FocusBehavior::ALWAYS : FocusBehavior::NEVER);
 }
 
+void ShelfView::SetDragImageBlur(const gfx::Size& size, int blur_radius) {
+  drag_image_->SetPaintToLayer();
+  drag_image_->layer()->SetFillsBoundsOpaquely(false);
+  drag_image_mask_ = views::Painter::CreatePaintedLayer(
+      views::Painter::CreateSolidRoundRectPainter(SK_ColorBLACK,
+                                                  size.width() / 2.0f));
+  drag_image_mask_->layer()->SetBounds(gfx::Rect(size));
+  drag_image_->layer()->SetMaskLayer(drag_image_mask_->layer());
+  drag_image_->layer()->SetBackgroundBlur(blur_radius);
+}
+
 }  // namespace ash
diff --git a/ash/shelf/shelf_view.h b/ash/shelf/shelf_view.h
index 8611179..819605b 100644
--- a/ash/shelf/shelf_view.h
+++ b/ash/shelf/shelf_view.h
@@ -37,7 +37,7 @@
 class BoundsAnimator;
 class MenuRunner;
 class Separator;
-}
+}  // namespace views
 
 namespace ash {
 class AppListButton;
@@ -193,7 +193,8 @@
       const gfx::Point& origin_in_screen_coordinates,
       const gfx::ImageSkia& icon,
       views::View* replaced_view,
-      float scale_factor) override;
+      float scale_factor,
+      int blur_radius) override;
 
   void UpdateDragIconProxy(
       const gfx::Point& location_in_screen_coordinates) override;
@@ -468,6 +469,9 @@
   // Updates the back button opacity and focus behavior based on tablet mode.
   void UpdateBackButton();
 
+  // Set background blur to the dragged image. |size| is the image size.
+  void SetDragImageBlur(const gfx::Size& size, int blur_radius);
+
   // The model; owned by Launcher.
   ShelfModel* model_;
 
@@ -548,6 +552,9 @@
   // the item can be dragged outside the app grid.
   std::unique_ptr<ash::DragImageView> drag_image_;
 
+  // The owner of a mask layer used to clip the background blur.
+  std::unique_ptr<ui::LayerOwner> drag_image_mask_;
+
   // The cursor offset to the middle of the dragged item.
   gfx::Vector2d drag_image_offset_;
 
diff --git a/base/BUILD.gn b/base/BUILD.gn
index 9774251..33460a70 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -851,6 +851,7 @@
     "threading/post_task_and_reply_impl.h",
     "threading/scoped_blocking_call.cc",
     "threading/scoped_blocking_call.h",
+    "threading/sequence_bound.h",
     "threading/sequence_local_storage_map.cc",
     "threading/sequence_local_storage_map.h",
     "threading/sequence_local_storage_slot.cc",
@@ -2480,6 +2481,7 @@
     "threading/platform_thread_unittest.cc",
     "threading/post_task_and_reply_impl_unittest.cc",
     "threading/scoped_blocking_call_unittest.cc",
+    "threading/sequence_bound_unittest.cc",
     "threading/sequence_local_storage_map_unittest.cc",
     "threading/sequence_local_storage_slot_unittest.cc",
     "threading/sequenced_task_runner_handle_unittest.cc",
diff --git a/base/metrics/histogram.cc b/base/metrics/histogram.cc
index 9352fdd3c..8180a55 100644
--- a/base/metrics/histogram.cc
+++ b/base/metrics/histogram.cc
@@ -184,6 +184,30 @@
     DCHECK_EQ(minimum_, registered_ranges->range(1));
     DCHECK_EQ(maximum_, registered_ranges->range(bucket_count_ - 1));
 
+// Temporary check for https://crbug.com/836238
+#if defined(OS_WIN)  // Only Windows has a debugger that makes this useful.
+    if (maximum_ != registered_ranges->range(bucket_count_ - 1)) {
+      // Create local copies of the parameters to be sure they'll be available
+      // in the crash dump for the debugger to see.
+      Sample hash_32 = static_cast<Sample>(HashMetricName(name_));
+      debug::Alias(&hash_32);
+      DEBUG_ALIAS_FOR_CSTR(h_name, name_.c_str(), 100);
+      HistogramType h_type = histogram_type_;
+      Sample h_min = minimum_;
+      Sample h_max = maximum_;
+      uint32_t h_count = bucket_count_;
+      debug::Alias(&h_type);
+      debug::Alias(&h_min);
+      debug::Alias(&h_max);
+      debug::Alias(&h_count);
+      uint32_t ranges_min = registered_ranges->range(1);
+      uint32_t ranges_max = registered_ranges->range(bucket_count_ - 1);
+      debug::Alias(&ranges_min);
+      debug::Alias(&ranges_max);
+      CHECK(false) << name_;
+    }
+#endif
+
     // Try to create the histogram using a "persistent" allocator. As of
     // 2016-02-25, the availability of such is controlled by a base::Feature
     // that is off by default. If the allocator doesn't exist or if
diff --git a/base/metrics/persistent_memory_allocator.cc b/base/metrics/persistent_memory_allocator.cc
index 92052667..a8cd4a4c 100644
--- a/base/metrics/persistent_memory_allocator.cc
+++ b/base/metrics/persistent_memory_allocator.cc
@@ -14,6 +14,7 @@
 #include <sys/mman.h>
 #endif
 
+#include "base/debug/alias.h"
 #include "base/files/memory_mapped_file.h"
 #include "base/logging.h"
 #include "base/memory/shared_memory.h"
@@ -1064,6 +1065,32 @@
   return IsMemoryAcceptable(file.data(), file.length(), 0, read_only);
 }
 
+void FilePersistentMemoryAllocator::Cache() {
+  // Since this method is expected to load data from permanent storage
+  // into memory, blocking I/O may occur.
+  AssertBlockingAllowed();
+
+  // Calculate begin/end addresses so that the first byte of every page
+  // in that range can be read. Keep within the used space. The |volatile|
+  // keyword makes it so the compiler can't make assumptions about what is
+  // in a given memory location and thus possibly avoid the read.
+  const volatile char* mem_end = mem_base_ + used();
+  const volatile char* mem_begin = mem_base_;
+
+  // Iterate over the memory a page at a time, reading the first byte of
+  // every page. The values are added to a |total| so that the compiler
+  // can't omit the read.
+  int total = 0;
+  for (const volatile char* memory = mem_begin; memory < mem_end;
+       memory += vm_page_size_) {
+    total += *memory;
+  }
+
+  // Tell the compiler that |total| is used so that it can't optimize away
+  // the memory accesses above.
+  debug::Alias(&total);
+}
+
 void FilePersistentMemoryAllocator::FlushPartial(size_t length, bool sync) {
   if (sync)
     AssertBlockingAllowed();
diff --git a/base/metrics/persistent_memory_allocator.h b/base/metrics/persistent_memory_allocator.h
index 978a362..ef0ba20 100644
--- a/base/metrics/persistent_memory_allocator.h
+++ b/base/metrics/persistent_memory_allocator.h
@@ -627,6 +627,7 @@
   const MemoryType mem_type_;      // Type of memory allocation.
   const uint32_t mem_size_;        // Size of entire memory segment.
   const uint32_t mem_page_;        // Page size allocations shouldn't cross.
+  const size_t vm_page_size_;      // The page size used by the OS.
 
  private:
   struct SharedMetadata;
@@ -672,7 +673,6 @@
   // Record an error in the internal histogram.
   void RecordError(int error) const;
 
-  const size_t vm_page_size_;          // The page size used by the OS.
   const bool readonly_;                // Indicates access to read-only memory.
   mutable std::atomic<bool> corrupt_;  // Local version of "corrupted" flag.
 
@@ -759,6 +759,14 @@
   // the rest.
   static bool IsFileAcceptable(const MemoryMappedFile& file, bool read_only);
 
+  // Load all or a portion of the file into memory for fast access. This can
+  // be used to force the disk access to be done on a background thread and
+  // then have the data available to be read on the main thread with a greatly
+  // reduced risk of blocking due to I/O. The risk isn't eliminated completely
+  // because the system could always release the memory when under pressure
+  // but this can happen to any block of memory (i.e. swapped out).
+  void Cache();
+
  protected:
   // PersistentMemoryAllocator:
   void FlushPartial(size_t length, bool sync) override;
diff --git a/base/task/task_scheduler/service_thread.cc b/base/task/task_scheduler/service_thread.cc
index 5d630237..a4a38ff 100644
--- a/base/task/task_scheduler/service_thread.cc
+++ b/base/task/task_scheduler/service_thread.cc
@@ -91,11 +91,17 @@
   // every set of traits in case PostTaskWithTraits() itself is slow.
   // Bonus: this appraoch also includes the overhead of Bind() in the reported
   // latency).
+  // TODO(jessemckenna): pass |profiled_traits| directly to
+  // RecordHeartbeatLatencyAndTasksRunWhileQueuingHistograms() once compiler
+  // error on NaCl is fixed
+  TaskPriority task_priority = profiled_traits.priority();
+  bool may_block = profiled_traits.may_block();
   base::PostTaskWithTraits(
       FROM_HERE, profiled_traits,
-      BindOnce(&TaskTracker::RecordLatencyHistogram, Unretained(task_tracker_),
-               TaskTracker::LatencyHistogramType::HEARTBEAT_LATENCY,
-               profiled_traits, TimeTicks::Now()));
+      BindOnce(
+          &TaskTracker::RecordHeartbeatLatencyAndTasksRunWhileQueuingHistograms,
+          Unretained(task_tracker_), task_priority, may_block, TimeTicks::Now(),
+          task_tracker_->GetNumTasksRun()));
 }
 
 }  // namespace internal
diff --git a/base/task/task_scheduler/task_tracker.cc b/base/task/task_scheduler/task_tracker.cc
index 346fbe36..8aab0ce 100644
--- a/base/task/task_scheduler/task_tracker.cc
+++ b/base/task/task_scheduler/task_tracker.cc
@@ -4,6 +4,7 @@
 
 #include "base/task/task_scheduler/task_tracker.h"
 
+#include <atomic>
 #include <string>
 #include <vector>
 
@@ -98,6 +99,39 @@
       HistogramBase::kUmaTargetedHistogramFlag);
 }
 
+// Constructs a histogram to track task count which is logging to
+// "TaskScheduler.{histogram_name}.{histogram_label}.{task_type_suffix}".
+HistogramBase* GetCountHistogram(StringPiece histogram_name,
+                                 StringPiece histogram_label,
+                                 StringPiece task_type_suffix) {
+  DCHECK(!histogram_name.empty());
+  DCHECK(!histogram_label.empty());
+  DCHECK(!task_type_suffix.empty());
+  // Mimics the UMA_HISTOGRAM_CUSTOM_COUNTS macro.
+  const std::string histogram = JoinString(
+      {"TaskScheduler", histogram_name, histogram_label, task_type_suffix},
+      ".");
+  // 500 was chosen as the maximum number of tasks run while queuing because
+  // values this high would likely indicate an error, beyond which knowing the
+  // actual number of tasks is not informative.
+  return Histogram::FactoryGet(histogram, 1, 500, 50,
+                               HistogramBase::kUmaTargetedHistogramFlag);
+}
+
+// Returns a histogram stored in a 2D array indexed by task priority and
+// whether it may block.
+// TODO(jessemckenna): use the STATIC_HISTOGRAM_POINTER_GROUP macro from
+// histogram_macros.h instead.
+HistogramBase* GetHistogramForTaskTraits(
+    TaskTraits task_traits,
+    HistogramBase* const (*histograms)[2]) {
+  return histograms[static_cast<int>(task_traits.priority())]
+                   [task_traits.may_block() ||
+                            task_traits.with_base_sync_primitives()
+                        ? 1
+                        : 0];
+}
+
 // Upper bound for the
 // TaskScheduler.BlockShutdownTasksPostedDuringShutdown histogram.
 constexpr HistogramBase::Sample kMaxBlockShutdownTasksPostedDuringShutdown =
@@ -249,6 +283,7 @@
 TaskTracker::TaskTracker(StringPiece histogram_label)
     : TaskTracker(histogram_label, GetMaxNumScheduledBestEffortSequences()) {}
 
+// TODO(jessemckenna): Write a helper function to avoid code duplication below.
 TaskTracker::TaskTracker(StringPiece histogram_label,
                          int max_num_scheduled_best_effort_sequences)
     : state_(new State),
@@ -292,6 +327,25 @@
            GetLatencyHistogram("HeartbeatLatencyMicroseconds",
                                histogram_label,
                                "UserBlockingTaskPriority_MayBlock")}},
+      num_tasks_run_while_queuing_histograms_{
+          {GetCountHistogram("NumTasksRunWhileQueuing",
+                             histogram_label,
+                             "BackgroundTaskPriority"),
+           GetCountHistogram("NumTasksRunWhileQueuing",
+                             histogram_label,
+                             "BackgroundTaskPriority_MayBlock")},
+          {GetCountHistogram("NumTasksRunWhileQueuing",
+                             histogram_label,
+                             "UserVisibleTaskPriority"),
+           GetCountHistogram("NumTasksRunWhileQueuing",
+                             histogram_label,
+                             "UserVisibleTaskPriority_MayBlock")},
+          {GetCountHistogram("NumTasksRunWhileQueuing",
+                             histogram_label,
+                             "UserBlockingTaskPriority"),
+           GetCountHistogram("NumTasksRunWhileQueuing",
+                             histogram_label,
+                             "UserBlockingTaskPriority_MayBlock")}},
       tracked_ref_factory_(this) {
   // Confirm that all |task_latency_histograms_| have been initialized above.
   DCHECK(*(&task_latency_histograms_[static_cast<int>(TaskPriority::HIGHEST) +
@@ -435,8 +489,10 @@
   const bool is_delayed = !task->delayed_run_time.is_null();
 
   RunOrSkipTask(std::move(task.value()), sequence.get(), can_run_task);
-  if (can_run_task)
+  if (can_run_task) {
+    IncrementNumTasksRun();
     AfterRunTask(shutdown_behavior);
+  }
 
   if (!is_delayed)
     DecrementNumIncompleteUndelayedTasks();
@@ -485,11 +541,31 @@
       latency_histogram_type == LatencyHistogramType::TASK_LATENCY
           ? task_latency_histograms_
           : heartbeat_latency_histograms_;
-  histograms[static_cast<int>(task_traits.priority())]
-            [task_traits.may_block() || task_traits.with_base_sync_primitives()
-                 ? 1
-                 : 0]
-                ->AddTimeMicrosecondsGranularity(task_latency);
+  GetHistogramForTaskTraits(task_traits, histograms)
+      ->AddTimeMicrosecondsGranularity(task_latency);
+}
+
+void TaskTracker::RecordHeartbeatLatencyAndTasksRunWhileQueuingHistograms(
+    TaskPriority task_priority,
+    bool may_block,
+    TimeTicks posted_time,
+    int num_tasks_run_when_posted) const {
+  TaskTraits task_traits = {task_priority};
+  if (may_block)
+    task_traits = TaskTraits::Override(task_traits, {MayBlock()});
+  RecordLatencyHistogram(LatencyHistogramType::HEARTBEAT_LATENCY, task_traits,
+                         posted_time);
+  GetHistogramForTaskTraits(task_traits,
+                            num_tasks_run_while_queuing_histograms_)
+      ->Add(GetNumTasksRun() - num_tasks_run_when_posted);
+}
+
+int TaskTracker::GetNumTasksRun() const {
+  return num_tasks_run_.load(std::memory_order_relaxed);
+}
+
+void TaskTracker::IncrementNumTasksRun() {
+  num_tasks_run_.fetch_add(1, std::memory_order_relaxed);
 }
 
 void TaskTracker::RunOrSkipTask(Task task,
diff --git a/base/task/task_scheduler/task_tracker.h b/base/task/task_scheduler/task_tracker.h
index c9341a0..c06c5b0 100644
--- a/base/task/task_scheduler/task_tracker.h
+++ b/base/task/task_scheduler/task_tracker.h
@@ -5,6 +5,7 @@
 #ifndef BASE_TASK_TASK_SCHEDULER_TASK_TRACKER_H_
 #define BASE_TASK_TASK_SCHEDULER_TASK_TRACKER_H_
 
+#include <atomic>
 #include <functional>
 #include <limits>
 #include <memory>
@@ -183,11 +184,21 @@
   // cannot be called after this.
   void SetHasShutdownStartedForTesting();
 
-  // Records |Now() - posted_time| to the appropriate |latency_histogram_type|
-  // based on |task_traits|.
-  void RecordLatencyHistogram(LatencyHistogramType latency_histogram_type,
-                              TaskTraits task_traits,
-                              TimeTicks posted_time) const;
+  // Records two histograms
+  // 1. TaskScheduler.[label].HeartbeatLatencyMicroseconds.[suffix]:
+  //    Now() - posted_time
+  // 2. TaskScheduler.[label].NumTasksRunWhileQueuing.[suffix]:
+  //    GetNumTasksRun() - num_tasks_run_when_posted.
+  // [label] is the histogram label provided to the constructor.
+  // [suffix] is derived from |task_priority| and |may_block|.
+  void RecordHeartbeatLatencyAndTasksRunWhileQueuingHistograms(
+      TaskPriority task_priority,
+      bool may_block,
+      TimeTicks posted_time,
+      int num_tasks_run_when_posted) const;
+
+  // Returns the number of tasks run so far
+  int GetNumTasksRun() const;
 
   TrackedRef<TaskTracker> GetTrackedRef() {
     return tracked_ref_factory_.GetTrackedRef();
@@ -323,6 +334,14 @@
   // manner.
   void CallFlushCallbackForTesting();
 
+  // Records |Now() - posted_time| to the appropriate |latency_histogram_type|
+  // based on |task_traits|.
+  void RecordLatencyHistogram(LatencyHistogramType latency_histogram_type,
+                              TaskTraits task_traits,
+                              TimeTicks posted_time) const;
+
+  void IncrementNumTasksRun();
+
   debug::TaskAnnotator task_annotator_;
 
   // Number of tasks blocking shutdown and boolean indicating whether shutdown
@@ -358,8 +377,13 @@
   // completes.
   std::unique_ptr<WaitableEvent> shutdown_event_;
 
-  // TaskScheduler.TaskLatencyMicroseconds.* and
-  // TaskScheduler.HeartbeatLatencyMicroseconds.* histograms. The first index is
+  // Counter for number of tasks run so far, used to record tasks run while
+  // a task queued to histogram.
+  std::atomic_int num_tasks_run_{0};
+
+  // TaskScheduler.TaskLatencyMicroseconds.*,
+  // TaskScheduler.HeartbeatLatencyMicroseconds.*, and
+  // TaskScheduler.NumTasksRunWhileQueuing.* histograms. The first index is
   // a TaskPriority. The second index is 0 for non-blocking tasks, 1 for
   // blocking tasks. Intentionally leaked.
   // TODO(scheduler-dev): Consider using STATIC_HISTOGRAM_POINTER_GROUP for
@@ -368,6 +392,8 @@
       static_cast<int>(TaskPriority::HIGHEST) + 1;
   HistogramBase* const task_latency_histograms_[kNumTaskPriorities][2];
   HistogramBase* const heartbeat_latency_histograms_[kNumTaskPriorities][2];
+  HistogramBase* const
+      num_tasks_run_while_queuing_histograms_[kNumTaskPriorities][2];
 
   PreemptionState preemption_state_[kNumTaskPriorities];
 
diff --git a/base/threading/sequence_bound.h b/base/threading/sequence_bound.h
new file mode 100644
index 0000000..b4bd183
--- /dev/null
+++ b/base/threading/sequence_bound.h
@@ -0,0 +1,254 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_THREADING_SEQUENCE_BOUND_H_
+#define BASE_THREADING_SEQUENCE_BOUND_H_
+
+#include <new>
+#include <type_traits>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/compiler_specific.h"
+#include "base/location.h"
+#include "base/memory/aligned_memory.h"
+#include "base/memory/ptr_util.h"
+#include "base/sequenced_task_runner.h"
+
+namespace base {
+
+// SequenceBound facilitates owning objects that live on a specified sequence,
+// which is potentially different than the owner's sequence.  It encapsulates
+// the work of posting tasks to the specified sequence to construct T, call
+// methods on T, and destroy T.
+//
+// It does not provide explicit access to the underlying object directly, to
+// prevent accidentally using it from the wrong sequence.
+//
+// Like std::unique_ptr<T>, a SequenceBound<T> may be moved between owners,
+// and posted across threads.  It may also be up-casted (only), to permit
+// SequenceBound to be used with interfaces.
+//
+// Basic usage looks like this:
+//
+//   // Some class that lives on |main_task_runner|.
+//   class MyClass {
+//    public:
+//     explicit MyClass(const char* widget_title) {}
+//     virtual ~MyClass() { ... }
+//     virtual void DoSomething(int arg) { ... }
+//   };
+//
+//   // On any thread...
+//   scoped_refptr<SequencedTaskRunner> main_task_runner = ...;
+//   auto widget = SequenceBound<MyClass>(main_task_runner, "My Title");
+//   widget.Post(&MyObject::DoSomething, 1234);
+//
+// Note that |widget| is constructed asynchronously on |main_task_runner|,
+// but calling Post() immediately is safe, since the actual call is posted
+// to |main_task_runner| as well.
+//
+// |widget| will be deleted on |main_task_runner| asynchronously when it goes
+// out of scope, or when Reset() is called.
+//
+// Here is a more complicated example that shows injection and upcasting:
+//
+//   // Some unrelated class that uses a |MyClass| to do something.
+//   class SomeConsumer {
+//    public:
+//    // Note that ownership of |widget| is given to us!
+//    explicit SomeConsumer(SequenceBound<MyClass> widget)
+//        : widget_(std::move(widget)) { ... }
+//
+//    ~SomeConsumer() {
+//      // |widget_| will be destroyed on the associated task runner.
+//    }
+//
+//     SequenceBound<MyClass> widget_;
+//   };
+//
+//   // Implementation of MyClass.
+//   class MyDerivedClass : public MyClass { ... };
+//
+//   auto widget =
+//     SequenceBound<MyDerivedClass>(main_task_runner, ctor args);
+//   auto c = new SomeConsumer(std::move(widget));  // upcasts to MyClass
+
+namespace internal {
+
+// If we can't cast |Base*| into |Derived*|, then it's a virtual base if and
+// only if |Base| is actually a base class of |Derived|.  Otherwise (including
+// unrelated types), it isn't.  We default to Derived* so that the
+// specialization below will apply when the cast to |Derived*| is valid.
+template <typename Base, typename Derived, typename = Derived*>
+struct is_virtual_base_of : public std::is_base_of<Base, Derived> {};
+
+// If we can cast |Base*| into |Derived*|, then it's definitely not a virtual
+// base.  When this happens, we'll match the default third template argument.
+template <typename Base, typename Derived>
+struct is_virtual_base_of<Base,
+                          Derived,
+                          decltype(static_cast<Derived*>(
+                              static_cast<Base*>(nullptr)))> : std::false_type {
+};
+
+};  // namespace internal
+
+template <typename T>
+class SequenceBound {
+ public:
+  // Allow explicit null.
+  SequenceBound() = default;
+
+  // Construct a new instance of |T| that will be accessed only on
+  // |task_runner|.  One may post calls to it immediately upon return.
+  // This is marked as NO_SANITIZE because cfi doesn't like that we're casting
+  // uninitialized memory to a |T*|.  However, it's safe since (a) the cast is
+  // defined (see http://eel.is/c++draft/basic.life#6 for details), and (b) we
+  // don't use the resulting pointer in any way that requries it to be
+  // constructed, except by posting such a access to |impl_task_runner_| after
+  // posting construction there as well.
+  template <typename... Args>
+  SequenceBound(scoped_refptr<base::SequencedTaskRunner> task_runner,
+                Args&&... args) NO_SANITIZE("cfi-unrelated-cast")
+      : impl_task_runner_(std::move(task_runner)) {
+    // Allocate space for but do not construct an instance of |T|.
+    storage_ = AlignedAlloc(sizeof(T), alignof(T));
+    t_ = reinterpret_cast<T*>(storage_);
+
+    // Post construction to the impl thread.
+    impl_task_runner_->PostTask(
+        FROM_HERE,
+        base::BindOnce(&ConstructOwnerRecord<Args...>, base::Unretained(t_),
+                       std::forward<Args>(args)...));
+  }
+
+  ~SequenceBound() { Reset(); }
+
+  // Move construction from the same type can just take the pointer without
+  // adjusting anything.  This is required in addition to the move conversion
+  // constructor below.
+  SequenceBound(SequenceBound&& other) { MoveRecordFrom(other); }
+
+  // Move construction is supported from any type that's compatible with |T|.
+  // This case handles |From| != |T|, so we must adjust the pointer offset.
+  template <typename From>
+  SequenceBound(SequenceBound<From>&& other) {
+    MoveRecordFrom(other);
+  }
+
+  SequenceBound& operator=(SequenceBound&& other) {
+    // Clean up any object we currently own.
+    Reset();
+    MoveRecordFrom(other);
+    return *this;
+  }
+
+  template <typename From>
+  SequenceBound<T>& operator=(SequenceBound<From>&& other) {
+    // Clean up any object that we currently own.
+    Reset();
+    MoveRecordFrom(other);
+    return *this;
+  }
+
+  // Move everything from |other|, doing pointer adjustment as needed.
+  // This method is marked as NO_SANITIZE since (a) it might run before the
+  // posted ctor runs on |impl_task_runner_|, and (b) implicit conversions to
+  // non-virtual base classes are allowed before construction by the standard.
+  // See http://eel.is/c++draft/basic.life#6 for more information.
+  template <typename From>
+  void MoveRecordFrom(From&& other) NO_SANITIZE("cfi-unrelated-cast") {
+    // |other| might be is_null(), but that's okay.
+    impl_task_runner_ = std::move(other.impl_task_runner_);
+
+    // Note that static_cast<> isn't, in general, safe, since |other| might not
+    // be constructed yet.  Implicit conversion is supported, as long as it
+    // doesn't convert to a virtual base.  Of course, it allows only upcasts.
+    t_ = other.t_;
+
+    // The original storage is kept unmodified, so we can free it later.
+    storage_ = other.storage_;
+
+    other.storage_ = nullptr;
+    other.t_ = nullptr;
+  }
+
+  // Post a call to |method| to |impl_task_runner_|.
+  template <typename... Args>
+  void Post(const base::Location& from_here,
+            void (T::*method)(Args...),
+            Args&&... args) const {
+    impl_task_runner_->PostTask(
+        from_here, base::BindOnce(
+                       [](void (T::*method)(Args...), T* t, Args... args) {
+                         (t->*method)(std::forward<Args>(args)...);
+                       },
+                       std::move(method), base::Unretained(t_),
+                       std::forward<Args>(args)...));
+  }
+
+  // TODO(liberato): Add PostOrCall(), to support cases where synchronous calls
+  // are okay if it's the same task runner.
+
+  // TODO(liberato): Add PostAndReply()
+
+  // TODO(liberato): Allow creation of callbacks that bind to a weak pointer,
+  // and thread-hop to |impl_task_runner_| if needed.
+
+  // Post destruction of any object we own, and return to the null state.
+  void Reset() {
+    if (is_null())
+      return;
+
+    // Destruct the object on the impl thread.
+    impl_task_runner_->PostTask(
+        FROM_HERE, base::BindOnce(&DeleteOwnerRecord, base::Unretained(t_),
+                                  base::Unretained(storage_)));
+
+    impl_task_runner_ = nullptr;
+    t_ = nullptr;
+    storage_ = nullptr;
+  }
+
+  // Return whether we own anything.  Note that this does not guarantee that any
+  // previously owned object has been destroyed.  In particular, it will return
+  // true immediately after a call to Reset(), though the underlying object
+  // might still be pending destruction on the impl thread.
+  bool is_null() const { return !t_; }
+
+ private:
+  // Pointer to the object,  Pointer may be modified on the owning thread.
+  T* t_ = nullptr;
+
+  // Original allocated storage for the object.
+  void* storage_ = nullptr;
+
+  // The task runner on which all access to |t_| should happen.
+  scoped_refptr<base::SequencedTaskRunner> impl_task_runner_;
+
+  // For move conversion.
+  template <typename U>
+  friend class SequenceBound;
+
+  // Run on impl thread to construct |t|'s storage.
+  template <typename... Args>
+  static void ConstructOwnerRecord(T* t, Args&&... args) {
+    new (t) T(std::forward<Args>(args)...);
+  }
+
+  // Destruct the object associated with |t|, and delete |storage|.
+  static void DeleteOwnerRecord(T* t, void* storage) {
+    t->~T();
+    AlignedFree(storage);
+  }
+
+  // To preserve ownership semantics, we disallow copy construction / copy
+  // assignment.  Move construction / assignment is fine.
+  DISALLOW_COPY_AND_ASSIGN(SequenceBound);
+};
+
+}  // namespace base
+
+#endif  // BASE_THREADING_SEQUENCE_BOUND_H_
diff --git a/base/threading/sequence_bound_unittest.cc b/base/threading/sequence_bound_unittest.cc
new file mode 100644
index 0000000..9288b06a
--- /dev/null
+++ b/base/threading/sequence_bound_unittest.cc
@@ -0,0 +1,317 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/threading/sequence_bound.h"
+
+#include "base/run_loop.h"
+#include "base/test/scoped_task_environment.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+
+class SequenceBoundTest : public ::testing::Test {
+ public:
+  // Helpful values that our test classes use.
+  enum Value {
+    kInitialValue = 0,
+    kDifferentValue = 1,
+
+    // Values used by the Derived class.
+    kDerivedCtorValue = 111,
+    kDerivedDtorValue = 222,
+
+    // Values used by the Other class.
+    kOtherCtorValue = 333,
+    kOtherDtorValue = 444,
+  };
+
+  void SetUp() override { task_runner_ = base::ThreadTaskRunnerHandle::Get(); }
+
+  void TearDown() override { scoped_task_environment_.RunUntilIdle(); }
+
+  // Do-nothing base class, just so we can test assignment of derived classes.
+  // It introduces a virtual destructor, so that casting derived classes to
+  // Base should still use the appropriate (virtual) destructor.
+  class Base {
+   public:
+    virtual ~Base() {}
+  };
+
+  // Handy class to set an int ptr to different values, to verify that things
+  // are being run properly.
+  class Derived : public Base {
+   public:
+    Derived(Value* ptr) : ptr_(ptr) { *ptr_ = kDerivedCtorValue; }
+    ~Derived() override { *ptr_ = kDerivedDtorValue; }
+    void SetValue(Value value) { *ptr_ = value; }
+    Value* ptr_;
+  };
+
+  // Another base class, which sets ints to different values.
+  class Other {
+   public:
+    Other(Value* ptr) : ptr_(ptr) { *ptr = kOtherCtorValue; };
+    virtual ~Other() { *ptr_ = kOtherDtorValue; }
+    void SetValue(Value value) { *ptr_ = value; }
+    Value* ptr_;
+  };
+
+  class MultiplyDerived : public Other, public Derived {
+   public:
+    MultiplyDerived(Value* ptr1, Value* ptr2) : Other(ptr1), Derived(ptr2) {}
+  };
+
+  struct VirtuallyDerived : public virtual Base {};
+
+  base::test::ScopedTaskEnvironment scoped_task_environment_;
+  scoped_refptr<base::SequencedTaskRunner> task_runner_;
+  Value value = kInitialValue;
+};
+
+TEST_F(SequenceBoundTest, ConstructThenPostThenReset) {
+  auto derived = SequenceBound<Derived>(task_runner_, &value);
+  EXPECT_FALSE(derived.is_null());
+
+  // Nothing should happen until we run the message loop.
+  EXPECT_EQ(value, kInitialValue);
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(value, kDerivedCtorValue);
+
+  // Post now that the object has been constructed.
+  derived.Post(FROM_HERE, &Derived::SetValue, kDifferentValue);
+  EXPECT_EQ(value, kDerivedCtorValue);
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(value, kDifferentValue);
+
+  // Reset it, and make sure that destruction is posted.  The owner should
+  // report that it is null immediately.
+  derived.Reset();
+  EXPECT_TRUE(derived.is_null());
+  EXPECT_EQ(value, kDifferentValue);
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(value, kDerivedDtorValue);
+}
+
+TEST_F(SequenceBoundTest, PostBeforeConstruction) {
+  // Construct an object and post a message to it, before construction has been
+  // run on |task_runner_|.
+  auto derived = SequenceBound<Derived>(task_runner_, &value);
+  derived.Post(FROM_HERE, &Derived::SetValue, kDifferentValue);
+  EXPECT_EQ(value, kInitialValue);
+  // Both construction and SetValue should run.
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(value, kDifferentValue);
+}
+
+TEST_F(SequenceBoundTest, MoveConstructionFromSameClass) {
+  // Verify that we can move-construct with the same class.
+  auto derived_old = SequenceBound<Derived>(task_runner_, &value);
+  auto derived_new = std::move(derived_old);
+  EXPECT_TRUE(derived_old.is_null());
+  EXPECT_FALSE(derived_new.is_null());
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(value, kDerivedCtorValue);
+
+  // Verify that |derived_new| owns the object now, and that the virtual
+  // destructor is called.
+  derived_new.Reset();
+  EXPECT_EQ(value, kDerivedCtorValue);
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(value, kDerivedDtorValue);
+}
+
+TEST_F(SequenceBoundTest, MoveConstructionFromDerivedClass) {
+  // Verify that we can move-construct to a base class from a derived class.
+  auto derived = SequenceBound<Derived>(task_runner_, &value);
+  SequenceBound<Base> base(std::move(derived));
+  EXPECT_TRUE(derived.is_null());
+  EXPECT_FALSE(base.is_null());
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(value, kDerivedCtorValue);
+
+  // Verify that |base| owns the object now, and that destruction still destroys
+  // Derived properly.
+  base.Reset();
+  EXPECT_EQ(value, kDerivedCtorValue);
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(value, kDerivedDtorValue);
+}
+
+TEST_F(SequenceBoundTest, MultiplyDerivedDestructionWorksLeftSuper) {
+  // Verify that everything works when we're casting around in ways that might
+  // change the address.  We cast to the left side of MultiplyDerived and then
+  // reset the owner.  ASAN will catch free() errors.
+  Value value2 = kInitialValue;
+  auto mderived = SequenceBound<MultiplyDerived>(task_runner_, &value, &value2);
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(value, kOtherCtorValue);
+  EXPECT_EQ(value2, kDerivedCtorValue);
+  SequenceBound<Other> other = std::move(mderived);
+
+  other.Reset();
+  base::RunLoop().RunUntilIdle();
+
+  // Both destructors should have run.
+  EXPECT_EQ(value, kOtherDtorValue);
+  EXPECT_EQ(value2, kDerivedDtorValue);
+}
+
+TEST_F(SequenceBoundTest, MultiplyDerivedDestructionWorksRightSuper) {
+  // Verify that everything works when we're casting around in ways that might
+  // change the address.  We cast to the right side of MultiplyDerived and then
+  // reset the owner.  ASAN will catch free() errors.
+  Value value2 = kInitialValue;
+  auto mderived = SequenceBound<MultiplyDerived>(task_runner_, &value, &value2);
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(value, kOtherCtorValue);
+  EXPECT_EQ(value2, kDerivedCtorValue);
+  SequenceBound<Base> base = std::move(mderived);
+
+  base.Reset();
+  base::RunLoop().RunUntilIdle();
+
+  // Both destructors should have run.
+  EXPECT_EQ(value, kOtherDtorValue);
+  EXPECT_EQ(value2, kDerivedDtorValue);
+}
+
+TEST_F(SequenceBoundTest, MoveAssignmentFromSameClass) {
+  // Test move-assignment using the same classes.
+  auto derived_old = SequenceBound<Derived>(task_runner_, &value);
+  SequenceBound<Derived> derived_new;
+
+  derived_new = std::move(derived_old);
+  EXPECT_TRUE(derived_old.is_null());
+  EXPECT_FALSE(derived_new.is_null());
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(value, kDerivedCtorValue);
+
+  // Verify that |derived_new| owns the object now.  Also verifies that move
+  // assignment from the same class deletes the outgoing object.
+  derived_new = SequenceBound<Derived>();
+  EXPECT_EQ(value, kDerivedCtorValue);
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(value, kDerivedDtorValue);
+}
+
+TEST_F(SequenceBoundTest, MoveAssignmentFromDerivedClass) {
+  // Move-assignment from a derived class to a base class.
+  auto derived = SequenceBound<Derived>(task_runner_, &value);
+  SequenceBound<Base> base;
+
+  base = std::move(derived);
+  EXPECT_TRUE(derived.is_null());
+  EXPECT_FALSE(base.is_null());
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(value, kDerivedCtorValue);
+
+  // Verify that |base| owns the object now, and that destruction still destroys
+  // Derived properly.
+  base.Reset();
+  EXPECT_EQ(value, kDerivedCtorValue);
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(value, kDerivedDtorValue);
+}
+
+TEST_F(SequenceBoundTest, MoveAssignmentFromDerivedClassDestroysOldObject) {
+  // Verify that move-assignment from a derived class runs the dtor of the
+  // outgoing object.
+  auto derived = SequenceBound<Derived>(task_runner_, &value);
+
+  Value value1 = kInitialValue;
+  Value value2 = kInitialValue;
+  auto mderived =
+      SequenceBound<MultiplyDerived>(task_runner_, &value1, &value2);
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_EQ(value, kDerivedCtorValue);
+
+  // Assign |mderived|, and verify that the original object in |derived| is
+  // destroyed properly.
+  derived = std::move(mderived);
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(value, kDerivedDtorValue);
+
+  // Delete |derived|, since it has pointers to local vars.
+  derived.Reset();
+  base::RunLoop().RunUntilIdle();
+}
+
+TEST_F(SequenceBoundTest, MultiplyDerivedPostToLeftBaseClass) {
+  // Cast and call methods on the left base class.
+  Value value1 = kInitialValue;
+  Value value2 = kInitialValue;
+  auto mderived =
+      SequenceBound<MultiplyDerived>(task_runner_, &value1, &value2);
+
+  // Cast to Other, the left base.
+  SequenceBound<Other> other(std::move(mderived));
+  other.Post(FROM_HERE, &Other::SetValue, kDifferentValue);
+
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_EQ(value1, kDifferentValue);
+  EXPECT_EQ(value2, kDerivedCtorValue);
+
+  other.Reset();
+  base::RunLoop().RunUntilIdle();
+}
+
+TEST_F(SequenceBoundTest, MultiplyDerivedPostToRightBaseClass) {
+  // Cast and call methods on the right base class.
+  Value value1 = kInitialValue;
+  Value value2 = kInitialValue;
+  auto mderived =
+      SequenceBound<MultiplyDerived>(task_runner_, &value1, &value2);
+
+  SequenceBound<Derived> derived(std::move(mderived));
+  derived.Post(FROM_HERE, &Derived::SetValue, kDifferentValue);
+
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_EQ(value1, kOtherCtorValue);
+  EXPECT_EQ(value2, kDifferentValue);
+
+  derived.Reset();
+  base::RunLoop().RunUntilIdle();
+}
+
+TEST_F(SequenceBoundTest, MoveConstructionFromNullWorks) {
+  // Verify that this doesn't crash.
+  SequenceBound<Derived> derived1;
+  SequenceBound<Derived> derived2(std::move(derived1));
+}
+
+TEST_F(SequenceBoundTest, MoveAssignmentFromNullWorks) {
+  // Verify that this doesn't crash.
+  SequenceBound<Derived> derived1;
+  SequenceBound<Derived> derived2;
+  derived2 = std::move(derived1);
+}
+
+TEST_F(SequenceBoundTest, ResetOnNullObjectWorks) {
+  // Verify that this doesn't crash.
+  SequenceBound<Derived> derived;
+  derived.Reset();
+}
+
+TEST_F(SequenceBoundTest, IsVirtualBaseClassOf) {
+  // Check that is_virtual_base_of<> works properly.
+
+  // Neither |Base| nor |Derived| is a virtual base of the other.
+  static_assert(!internal::is_virtual_base_of<Base, Derived>::value,
+                "|Base| shouldn't be a virtual base of |Derived|");
+  static_assert(!internal::is_virtual_base_of<Derived, Base>::value,
+                "|Derived| shouldn't be a virtual base of |Base|");
+
+  // |Base| should be a virtual base class of |VirtuallyDerived|, but not the
+  // other way.
+  static_assert(internal::is_virtual_base_of<Base, VirtuallyDerived>::value,
+                "|Base| should be a virtual base of |VirtuallyDerived|");
+  static_assert(!internal::is_virtual_base_of<VirtuallyDerived, Base>::value,
+                "|VirtuallyDerived shouldn't be a virtual base of |Base|");
+}
+
+}  // namespace base
diff --git a/base/trace_event/trace_log.cc b/base/trace_event/trace_log.cc
index 98992fb..6e8c3fc 100644
--- a/base/trace_event/trace_log.cc
+++ b/base/trace_event/trace_log.cc
@@ -1488,7 +1488,6 @@
     TraceEventETWExport::AddCompleteEndEvent(name);
 #endif  // OS_WIN
 
-  std::string console_message;
   if (category_group_enabled_local & TraceCategory::ENABLED_FOR_RECORDING) {
     AddTraceEventOverrideCallback trace_event_override =
         reinterpret_cast<AddTraceEventOverrideCallback>(
@@ -1507,25 +1506,22 @@
           trace_event_internal::kNoId /* bind_id */, 0, nullptr, nullptr,
           nullptr, nullptr, TRACE_EVENT_FLAG_NONE);
       trace_event_override(new_trace_event);
+
+#if defined(OS_ANDROID)
+      new_trace_event.SendToATrace();
+#endif
+
       return;
     }
+  }
 
+  std::string console_message;
+  if (category_group_enabled_local & TraceCategory::ENABLED_FOR_RECORDING) {
     OptionalAutoLock lock(&lock_);
 
     TraceEvent* trace_event = GetEventByHandleInternal(handle, &lock);
     if (trace_event) {
       DCHECK(trace_event->phase() == TRACE_EVENT_PHASE_COMPLETE);
-      // TEMP(oysteine) to debug crbug.com/638744
-      if (trace_event->duration().ToInternalValue() != -1) {
-        DVLOG(1) << "TraceHandle: chunk_seq " << handle.chunk_seq
-                 << ", chunk_index " << handle.chunk_index << ", event_index "
-                 << handle.event_index;
-
-        std::string serialized_event;
-        trace_event->AppendAsJSON(&serialized_event, ArgumentFilterPredicate());
-        DVLOG(1) << "TraceEvent: " << serialized_event;
-        lock_.AssertAcquired();
-      }
 
       trace_event->UpdateDuration(now, thread_now);
 #if defined(OS_ANDROID)
diff --git a/build/config/fuchsia/testing_sandbox_policy b/build/config/fuchsia/testing_sandbox_policy
index 8dbc3eae..401a6d9 100644
--- a/build/config/fuchsia/testing_sandbox_policy
+++ b/build/config/fuchsia/testing_sandbox_policy
@@ -10,6 +10,7 @@
       "fuchsia.process.Launcher",
       "fuchsia.ui.policy.Presenter",
       "fuchsia.ui.scenic.Scenic",
-      "fuchsia.ui.viewsv1.ViewManager"
+      "fuchsia.ui.viewsv1.ViewManager",
+      "fuchsia.vulkan.loader.Loader"
   ]
 }
diff --git a/build/config/win/BUILD.gn b/build/config/win/BUILD.gn
index 09e2899..c5c44479 100644
--- a/build/config/win/BUILD.gn
+++ b/build/config/win/BUILD.gn
@@ -38,9 +38,8 @@
   #   "desktop" - Windows Desktop Applications
   target_winuwp_family = "app"
 
-  # Set this to use clang style diagnostics format. Available only on clang.
-  # Tune this on for the editor integrations like Emacs compilation mode that
-  # match clang style error messages.
+  # Set this to use clang-style diagnostics format instead of MSVC-style, which
+  # is useful in e.g. Emacs compilation mode.
   # E.g.:
   #  Without this, clang emits a diagnostic message like this:
   #    foo/bar.cc(12,34): error: something went wrong
@@ -103,7 +102,7 @@
       ]
     }
 
-    if (is_clang && use_clang_diagnostics_format) {
+    if (use_clang_diagnostics_format) {
       cflags += [
         "-Xclang",
         "-fdiagnostics-format",
diff --git a/cc/trees/layer_tree_host.cc b/cc/trees/layer_tree_host.cc
index b232488..716021c 100644
--- a/cc/trees/layer_tree_host.cc
+++ b/cc/trees/layer_tree_host.cc
@@ -869,13 +869,13 @@
   return did_paint_content;
 }
 
-void LayerTreeHost::ApplyViewportDeltas(ScrollAndScaleSet* info) {
+void LayerTreeHost::ApplyViewportDeltas(const ScrollAndScaleSet& info) {
   gfx::Vector2dF inner_viewport_scroll_delta;
-  if (info->inner_viewport_scroll.element_id)
-    inner_viewport_scroll_delta = info->inner_viewport_scroll.scroll_delta;
+  if (info.inner_viewport_scroll.element_id)
+    inner_viewport_scroll_delta = info.inner_viewport_scroll.scroll_delta;
 
-  if (inner_viewport_scroll_delta.IsZero() && info->page_scale_delta == 1.f &&
-      info->elastic_overscroll_delta.IsZero() && !info->top_controls_delta)
+  if (inner_viewport_scroll_delta.IsZero() && info.page_scale_delta == 1.f &&
+      info.elastic_overscroll_delta.IsZero() && !info.top_controls_delta)
     return;
 
   // Preemptively apply the scroll offset and scale delta here before sending
@@ -888,21 +888,21 @@
             inner_viewport_scroll_delta));
   }
 
-  ApplyPageScaleDeltaFromImplSide(info->page_scale_delta);
+  ApplyPageScaleDeltaFromImplSide(info.page_scale_delta);
   SetElasticOverscrollFromImplSide(elastic_overscroll_ +
-                                   info->elastic_overscroll_delta);
+                                   info.elastic_overscroll_delta);
   // TODO(ccameron): pass the elastic overscroll here so that input events
   // may be translated appropriately.
   client_->ApplyViewportDeltas(inner_viewport_scroll_delta, gfx::Vector2dF(),
-                               info->elastic_overscroll_delta,
-                               info->page_scale_delta,
-                               info->top_controls_delta);
+                               info.elastic_overscroll_delta,
+                               info.page_scale_delta, info.top_controls_delta);
   SetNeedsUpdateLayers();
 }
 
-void LayerTreeHost::RecordWheelAndTouchScrollingCount(ScrollAndScaleSet* info) {
-  bool has_scrolled_by_wheel = info->has_scrolled_by_wheel;
-  bool has_scrolled_by_touch = info->has_scrolled_by_touch;
+void LayerTreeHost::RecordWheelAndTouchScrollingCount(
+    const ScrollAndScaleSet& info) {
+  bool has_scrolled_by_wheel = info.has_scrolled_by_wheel;
+  bool has_scrolled_by_touch = info.has_scrolled_by_touch;
 
   if (has_scrolled_by_wheel || has_scrolled_by_touch) {
     client_->RecordWheelAndTouchScrollingCount(has_scrolled_by_wheel,
@@ -911,6 +911,7 @@
 }
 
 void LayerTreeHost::ApplyScrollAndScale(ScrollAndScaleSet* info) {
+  DCHECK(info);
   for (auto& swap_promise : info->swap_promises) {
     TRACE_EVENT_WITH_FLOW1("input,benchmark", "LatencyInfo.Flow",
                            TRACE_ID_DONT_MANGLE(swap_promise->TraceId()),
@@ -939,9 +940,9 @@
   // This needs to happen after scroll deltas have been sent to prevent top
   // controls from clamping the layout viewport both on the compositor and
   // on the main thread.
-  ApplyViewportDeltas(info);
+  ApplyViewportDeltas(*info);
 
-  RecordWheelAndTouchScrollingCount(info);
+  RecordWheelAndTouchScrollingCount(*info);
 }
 
 const base::WeakPtr<InputHandler>& LayerTreeHost::GetInputHandler()
diff --git a/cc/trees/layer_tree_host.h b/cc/trees/layer_tree_host.h
index dda302a2..3531699 100644
--- a/cc/trees/layer_tree_host.h
+++ b/cc/trees/layer_tree_host.h
@@ -646,8 +646,8 @@
   // free of slow-paths before toggling the flag.
   enum { kNumFramesToConsiderBeforeRemovingSlowPathFlag = 60 };
 
-  void ApplyViewportDeltas(ScrollAndScaleSet* info);
-  void RecordWheelAndTouchScrollingCount(ScrollAndScaleSet* info);
+  void ApplyViewportDeltas(const ScrollAndScaleSet& info);
+  void RecordWheelAndTouchScrollingCount(const ScrollAndScaleSet& info);
   void ApplyPageScaleDeltaFromImplSide(float page_scale_delta);
   void InitializeProxy(std::unique_ptr<Proxy> proxy);
 
diff --git a/chrome/android/java/res/values-v17/styles.xml b/chrome/android/java/res/values-v17/styles.xml
index 88483a68..fb428f0 100644
--- a/chrome/android/java/res/values-v17/styles.xml
+++ b/chrome/android/java/res/values-v17/styles.xml
@@ -54,10 +54,12 @@
     </style>
 
     <style name="TabbedModeThemeBase" parent="MainTheme">
-        <item name="android:windowBackground">@android:color/white</item>
+        <item name="android:statusBarColor" tools:targetApi="21">@color/modern_primary_color</item>
     </style>
 
-    <style name="TabbedModeTheme" parent="TabbedModeThemeBase" />
+    <style name="TabbedModeTheme" parent="TabbedModeThemeBase">
+        <item name="android:windowBackground">@android:color/white</item>
+    </style>
 
     <style name="FirstRunTheme" parent="DialogWhenLarge">
     </style>
diff --git a/chrome/android/java/res/values-v23/styles.xml b/chrome/android/java/res/values-v23/styles.xml
deleted file mode 100644
index f0d64e9d..0000000
--- a/chrome/android/java/res/values-v23/styles.xml
+++ /dev/null
@@ -1,11 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright 2018 The Chromium Authors. All rights reserved.
-     Use of this source code is governed by a BSD-style license that can be
-     found in the LICENSE file. -->
-
-<resources xmlns:tools="http://schemas.android.com/tools">
-    <style name="TabbedModeTheme" parent="TabbedModeThemeBase">
-        <item name="android:statusBarColor">@color/modern_primary_color</item>
-    </style>
-</resources>
-
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
index e1674645..d9fb45e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
@@ -558,7 +558,10 @@
     protected void onInitialLayoutInflationComplete() {
         mInflateInitialLayoutEndMs = SystemClock.elapsedRealtime();
         // Set the status bar color to white by default.
-        setStatusBarColor(ColorUtils.getDefaultThemeColor(getResources(), false), true);
+        setStatusBarColor(null,
+                DeviceFormFactor.isNonMultiDisplayContextOnTablet(this)
+                        ? Color.BLACK
+                        : ColorUtils.getDefaultThemeColor(getResources(), false));
 
         ViewGroup rootView = (ViewGroup) getWindow().getDecorView().getRootView();
         mCompositorViewHolder = (CompositorViewHolder) findViewById(R.id.compositor_view_holder);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingMediator.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingMediator.java
index 2796303..869a329 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingMediator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingMediator.java
@@ -4,15 +4,16 @@
 
 package org.chromium.chrome.browser.autofill.keyboard_accessory;
 
-import android.os.Build;
 import android.support.annotation.Nullable;
 import android.support.annotation.Px;
 import android.view.View;
 import android.view.ViewGroup;
 
+import org.chromium.base.Supplier;
 import org.chromium.base.VisibleForTesting;
 import org.chromium.chrome.browser.ChromeActivity;
 import org.chromium.chrome.browser.ChromeFeatureList;
+import org.chromium.chrome.browser.InsetObserverView;
 import org.chromium.chrome.browser.autofill.keyboard_accessory.KeyboardAccessoryData.Action;
 import org.chromium.chrome.browser.autofill.keyboard_accessory.KeyboardAccessoryData.Provider;
 import org.chromium.chrome.browser.compositor.CompositorViewHolder;
@@ -43,6 +44,7 @@
     private @Px int mPreviousControlHeight;
     private final WindowAndroid.KeyboardVisibilityListener mVisibilityListener =
             this::onKeyboardVisibilityChanged;
+    private Supplier<InsetObserverView> mInsetObserverViewSupplier;
 
     /**
      * Provides a cache for a given Provider which can repeat the last notification to all
@@ -146,6 +148,7 @@
         mKeyboardAccessory = keyboardAccessory;
         mAccessorySheet = accessorySheet;
         mActivity = (ChromeActivity) windowAndroid.getActivity().get();
+        setInsetObserverViewSupplier(mActivity::getInsetObserverView);
         LayoutManager manager = getLayoutManager();
         if (manager != null) manager.addSceneChangeObserver(mTabSwitcherObserver);
         windowAndroid.addKeyboardVisibilityListener(mVisibilityListener);
@@ -371,20 +374,12 @@
     }
 
     private @Px int calculateAccessorySheetHeight(View rootView) {
-        int accessorySheetSuggestionHeight = mActivity.getResources().getDimensionPixelSize(
-                org.chromium.chrome.R.dimen.keyboard_accessory_suggestion_height);
-        // Ensure that the minimum height is always sufficient to display a suggestion.
-
-        int calculcatedHeight =
-                mWindowAndroid.getKeyboardDelegate().calculateKeyboardHeight(mActivity, rootView);
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP
-                && Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
-            // Before Lollipop: The nav bars seem to be ignored while estimating the height.
-            // During Lollipop: The nav bars (esp. the accessory bar) are counted extra.
-            // After Lollipop: The height is already exact.
-            calculcatedHeight -= accessorySheetSuggestionHeight;
-        }
-        return Math.max(accessorySheetSuggestionHeight, calculcatedHeight);
+        InsetObserverView insetObserver = mInsetObserverViewSupplier.get();
+        if (insetObserver != null) return insetObserver.getSystemWindowInsetsBottom();
+        // Without known inset (which is keyboard + bottom soft keys), use the keyboard height.
+        return Math.max(mActivity.getResources().getDimensionPixelSize(
+                                org.chromium.chrome.R.dimen.keyboard_accessory_suggestion_height),
+                mWindowAndroid.getKeyboardDelegate().calculateKeyboardHeight(mActivity, rootView));
     }
 
     private @Px int calculateAccessoryBarHeight() {
@@ -431,6 +426,11 @@
         return state.mPasswordAccessorySheet;
     }
 
+    @VisibleForTesting
+    void setInsetObserverViewSupplier(Supplier<InsetObserverView> insetObserverViewSupplier) {
+        mInsetObserverViewSupplier = insetObserverViewSupplier;
+    }
+
     // TODO(fhorschig): Should be @VisibleForTesting.
     @Nullable
     KeyboardAccessoryCoordinator getKeyboardAccessory() {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerChrome.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerChrome.java
index 5eeab1f..66b62cb2 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerChrome.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerChrome.java
@@ -423,13 +423,13 @@
 
         @Override
         public void swipeFinished() {
-            if (mToolbarSwipeLayout == null) return;
+            if (mToolbarSwipeLayout == null || !mToolbarSwipeLayout.isActive()) return;
             mToolbarSwipeLayout.swipeFinished(time());
         }
 
         @Override
         public void swipeFlingOccurred(float x, float y, float tx, float ty, float vx, float vy) {
-            if (mToolbarSwipeLayout == null) return;
+            if (mToolbarSwipeLayout == null || !mToolbarSwipeLayout.isActive()) return;
             mToolbarSwipeLayout.swipeFlingOccurred(time(), x, y, tx, ty, vx, vy);
         }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contacts_picker/ContactDetails.java b/chrome/android/java/src/org/chromium/chrome/browser/contacts_picker/ContactDetails.java
index 6462768c..bc58f2f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contacts_picker/ContactDetails.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contacts_picker/ContactDetails.java
@@ -93,17 +93,21 @@
     public String getContactDetailsAsString() {
         int count = 0;
         StringBuilder builder = new StringBuilder();
-        for (String email : mEmails) {
-            if (count++ > 0) {
-                builder.append("\n");
+        if (mEmails != null) {
+            for (String email : mEmails) {
+                if (count++ > 0) {
+                    builder.append("\n");
+                }
+                builder.append(email);
             }
-            builder.append(email);
         }
-        for (String phoneNumber : mPhoneNumbers) {
-            if (count++ > 0) {
-                builder.append("\n");
+        if (mPhoneNumbers != null) {
+            for (String phoneNumber : mPhoneNumbers) {
+                if (count++ > 0) {
+                    builder.append("\n");
+                }
+                builder.append(phoneNumber);
             }
-            builder.append(phoneNumber);
         }
 
         return builder.toString();
@@ -121,15 +125,19 @@
         writer.name("emails");
 
         writer.beginArray();
-        for (String email : mEmails) {
-            writer.value(email);
+        if (mEmails != null) {
+            for (String email : mEmails) {
+                writer.value(email);
+            }
         }
         writer.endArray();
 
         writer.name("phoneNumbers");
         writer.beginArray();
-        for (String phoneNumber : mPhoneNumbers) {
-            writer.value(phoneNumber);
+        if (mPhoneNumbers != null) {
+            for (String phoneNumber : mPhoneNumbers) {
+                writer.value(phoneNumber);
+            }
         }
         writer.endArray();
 
@@ -148,7 +156,7 @@
 
     @Override
     public int hashCode() {
-        Object[] values = {mId, mDisplayName, mEmails};
+        Object[] values = {mId, mDisplayName};
         return Arrays.hashCode(values);
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contacts_picker/PickerAdapter.java b/chrome/android/java/src/org/chromium/chrome/browser/contacts_picker/PickerAdapter.java
index d8d788f..4e1a66ab 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contacts_picker/PickerAdapter.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contacts_picker/PickerAdapter.java
@@ -5,10 +5,8 @@
 package org.chromium.chrome.browser.contacts_picker;
 
 import android.content.ContentResolver;
-import android.content.ContentUris;
 import android.database.Cursor;
 import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
 import android.net.Uri;
 import android.provider.ContactsContract;
 import android.support.v7.widget.RecyclerView.Adapter;
@@ -18,10 +16,10 @@
 
 import org.chromium.chrome.R;
 
-import java.io.ByteArrayInputStream;
 import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.Set;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
 
 /**
  * A data adapter for the Contacts Picker.
@@ -33,8 +31,11 @@
     // The content resolver to query data from.
     private ContentResolver mContentResolver;
 
-    // A cursor containing the raw contacts data.
-    private Cursor mContactsCursor;
+    // The full list of all registered contacts on the device.
+    private ArrayList<ContactDetails> mContactDetails;
+
+    // A list of search result indices into the larger data set.
+    private ArrayList<Integer> mSearchResults;
 
     /**
      * Holds on to a {@link ContactView} that displays information about a contact.
@@ -62,8 +63,7 @@
     public PickerAdapter(PickerCategoryView categoryView, ContentResolver contentResolver) {
         mCategoryView = categoryView;
         mContentResolver = contentResolver;
-        mContactsCursor = contentResolver.query(ContactsContract.Contacts.CONTENT_URI, PROJECTION,
-                null, null, ContactsContract.Contacts.DISPLAY_NAME_PRIMARY + " ASC");
+        mContactDetails = getAllContacts();
     }
 
     /**
@@ -71,31 +71,61 @@
      * @param query The search term to use.
      */
     public void setSearchString(String query) {
-        String searchString = "%" + query + "%";
-        String[] selectionArgs = {searchString};
-        mContactsCursor.close();
-
-        mContactsCursor = mContentResolver.query(ContactsContract.Contacts.CONTENT_URI, PROJECTION,
-                ContactsContract.Contacts.DISPLAY_NAME_PRIMARY + " LIKE ?", selectionArgs,
-                ContactsContract.Contacts.DISPLAY_NAME_PRIMARY + " ASC");
+        if (query.equals("")) {
+            mSearchResults.clear();
+            mSearchResults = null;
+        } else {
+            mSearchResults = new ArrayList<Integer>();
+            Integer count = 0;
+            String query_lower = query.toLowerCase(Locale.getDefault());
+            for (ContactDetails contact : mContactDetails) {
+                if (contact.getDisplayName().toLowerCase(Locale.getDefault()).contains(query_lower)
+                        || contact.getContactDetailsAsString()
+                                   .toLowerCase(Locale.getDefault())
+                                   .contains(query_lower)) {
+                    mSearchResults.add(count);
+                }
+                count++;
+            }
+        }
         notifyDataSetChanged();
     }
 
     /**
-     * Fetches all known contacts and their emails.
-     * @return The contact list as a set.
+     * Fetches all known contacts.
+     * @return The contact list as an array.
      */
-    public Set<ContactDetails> getAllContacts() {
-        Set<ContactDetails> contacts = new HashSet<>();
-        if (!mContactsCursor.moveToFirst()) return contacts;
-        do {
-            String id = mContactsCursor.getString(
-                    mContactsCursor.getColumnIndex(ContactsContract.Contacts._ID));
-            String name = mContactsCursor.getString(
-                    mContactsCursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME_PRIMARY));
-            contacts.add(new ContactDetails(id, name, getEmails(), getPhoneNumbers(), getPhoto()));
-        } while (mContactsCursor.moveToNext());
+    public ArrayList<ContactDetails> getAllContacts() {
+        if (mContactDetails != null) return mContactDetails;
 
+        Map<String, ArrayList<String>> emailMap =
+                getDetails(ContactsContract.CommonDataKinds.Email.CONTENT_URI,
+                        ContactsContract.CommonDataKinds.Email.CONTACT_ID,
+                        ContactsContract.CommonDataKinds.Email.DATA,
+                        ContactsContract.CommonDataKinds.Email.CONTACT_ID + " ASC, "
+                                + ContactsContract.CommonDataKinds.Email.DATA + " ASC");
+
+        Map<String, ArrayList<String>> phoneMap =
+                getDetails(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
+                        ContactsContract.CommonDataKinds.Phone.CONTACT_ID,
+                        ContactsContract.CommonDataKinds.Email.DATA,
+                        ContactsContract.CommonDataKinds.Email.CONTACT_ID + " ASC, "
+                                + ContactsContract.CommonDataKinds.Phone.NUMBER + " ASC");
+
+        // A cursor containing the raw contacts data.
+        Cursor cursor = mContentResolver.query(ContactsContract.Contacts.CONTENT_URI, PROJECTION,
+                null, null, ContactsContract.Contacts.DISPLAY_NAME_PRIMARY + " ASC");
+
+        ArrayList<ContactDetails> contacts = new ArrayList<ContactDetails>(cursor.getCount());
+        if (!cursor.moveToFirst()) return contacts;
+        do {
+            String id = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts._ID));
+            String name = cursor.getString(
+                    cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME_PRIMARY));
+            contacts.add(new ContactDetails(id, name, emailMap.get(id), phoneMap.get(id), null));
+        } while (cursor.moveToNext());
+
+        cursor.close();
         return contacts;
     }
 
@@ -111,79 +141,64 @@
 
     @Override
     public void onBindViewHolder(ViewHolder holder, int position) {
-        String id = "";
-        String name = "";
-        if (mContactsCursor.moveToPosition(position)) {
-            id = mContactsCursor.getString(
-                    mContactsCursor.getColumnIndex(ContactsContract.Contacts._ID));
-            name = mContactsCursor.getString(
-                    mContactsCursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME_PRIMARY));
+        ContactDetails contact;
+        if (mSearchResults == null) {
+            contact = mContactDetails.get(position);
+        } else {
+            Integer index = mSearchResults.get(position);
+            contact = mContactDetails.get(index);
         }
-
-        ((ContactView) holder.itemView)
-                .initialize(
-                        new ContactDetails(id, name, getEmails(), getPhoneNumbers(), getPhoto()));
+        ((ContactView) holder.itemView).initialize(contact);
     }
 
-    private ArrayList<String> getEmails() {
-        // Look up all associated emails for this contact.
-        String id = mContactsCursor.getString(
-                mContactsCursor.getColumnIndex(ContactsContract.Contacts._ID));
-        Cursor emailCursor =
-                mContentResolver.query(ContactsContract.CommonDataKinds.Email.CONTENT_URI, null,
-                        ContactsContract.CommonDataKinds.Email.CONTACT_ID + " = " + id, null,
-                        ContactsContract.CommonDataKinds.Email.DATA + " ASC");
-        ArrayList<String> emails = new ArrayList<String>();
-        while (emailCursor.moveToNext()) {
-            emails.add(emailCursor.getString(
-                    emailCursor.getColumnIndex(ContactsContract.CommonDataKinds.Email.DATA)));
-        }
-        emailCursor.close();
-        return emails;
-    }
+    /**
+     * Fetches details for a contact.
+     * @param source The source URI to use for the lookup.
+     * @param idColumn The name of the id column.
+     * @param idColumn The name of the data column.
+     * @param sortOrder The sort order. Data must be sorted by CONTACT_ID but can be additionally
+     *                  sorted also.
+     * @return A map of ids to contact details (as ArrayList).
+     */
+    private Map<String, ArrayList<String>> getDetails(
+            Uri source, String idColumn, String dataColumn, String sortOrder) {
+        Map<String, ArrayList<String>> map = new HashMap<String, ArrayList<String>>();
 
-    private ArrayList<String> getPhoneNumbers() {
-        // Look up all associated phone numbers for this contact.
-        String id = mContactsCursor.getString(
-                mContactsCursor.getColumnIndex(ContactsContract.Contacts._ID));
-        Cursor phoneNumberCursor =
-                mContentResolver.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null,
-                        ContactsContract.CommonDataKinds.Phone.CONTACT_ID + " = " + id, null,
-                        ContactsContract.CommonDataKinds.Phone.NUMBER + " ASC");
-        ArrayList<String> phoneNumbers = new ArrayList<String>();
-        while (phoneNumberCursor.moveToNext()) {
-            phoneNumbers.add(phoneNumberCursor.getString(
-                    phoneNumberCursor.getColumnIndex(ContactsContract.CommonDataKinds.Email.DATA)));
+        Cursor cursor = mContentResolver.query(source, null, null, null, sortOrder);
+        ArrayList<String> list = new ArrayList<String>();
+        String key = "";
+        String value;
+        while (cursor.moveToNext()) {
+            String id = cursor.getString(cursor.getColumnIndex(idColumn));
+            value = cursor.getString(cursor.getColumnIndex(dataColumn));
+            if (key.isEmpty()) {
+                key = id;
+                list.add(value);
+            } else {
+                if (key.equals(id)) {
+                    list.add(value);
+                } else {
+                    map.put(key, list);
+                    list = new ArrayList<String>();
+                    list.add(value);
+                    key = id;
+                }
+            }
         }
-        phoneNumberCursor.close();
-        return phoneNumbers;
+        map.put(key, list);
+        cursor.close();
+
+        return map;
     }
 
     private Bitmap getPhoto() {
-        String id = mContactsCursor.getString(
-                mContactsCursor.getColumnIndex(ContactsContract.Contacts._ID));
-        Uri contactUri = ContentUris.withAppendedId(
-                ContactsContract.Contacts.CONTENT_URI, Long.parseLong(id));
-        Uri photoUri =
-                Uri.withAppendedPath(contactUri, ContactsContract.Contacts.Photo.CONTENT_DIRECTORY);
-        Cursor cursor = mContentResolver.query(
-                photoUri, new String[] {ContactsContract.Contacts.Photo.PHOTO}, null, null, null);
-        if (cursor == null) return null;
-        try {
-            if (cursor.moveToFirst()) {
-                byte[] data = cursor.getBlob(0);
-                if (data != null) {
-                    return BitmapFactory.decodeStream(new ByteArrayInputStream(data));
-                }
-            }
-        } finally {
-            cursor.close();
-        }
         return null;
     }
 
     @Override
     public int getItemCount() {
-        return mContactsCursor.getCount();
+        if (mSearchResults != null) return mSearchResults.size();
+
+        return mContactDetails.size();
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contacts_picker/PickerCategoryView.java b/chrome/android/java/src/org/chromium/chrome/browser/contacts_picker/PickerCategoryView.java
index 8513882..1acc872 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contacts_picker/PickerCategoryView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contacts_picker/PickerCategoryView.java
@@ -249,7 +249,8 @@
         } else if (id == R.id.action) {
             if (mSelectAllMode) {
                 mPreviousSelection = mSelectionDelegate.getSelectedItems();
-                mSelectionDelegate.setSelectedItems(mPickerAdapter.getAllContacts());
+                mSelectionDelegate.setSelectedItems(
+                        new HashSet<ContactDetails>(mPickerAdapter.getAllContacts()));
                 mActionButton.setImageResource(R.drawable.ic_undo);
                 mActionButton.setContentDescription(mLabelUndo);
             } else {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageLayout.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageLayout.java
index 46014803..a36ee7d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageLayout.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageLayout.java
@@ -134,7 +134,7 @@
      */
     public static boolean isSimplifiedNtpAblationEnabled() {
         return ChromeFeatureList.getFieldTrialParamByFeatureAsBoolean(
-                ChromeFeatureList.SIMPLIFIED_NTP, PARAM_SIMPLIFIED_NTP_ABLATION, false);
+                ChromeFeatureList.SIMPLIFIED_NTP, PARAM_SIMPLIFIED_NTP_ABLATION, true);
     }
 
     /**
diff --git a/chrome/android/java/strings/android_chrome_strings.grd b/chrome/android/java/strings/android_chrome_strings.grd
index 81dab35..54b7319 100644
--- a/chrome/android/java/strings/android_chrome_strings.grd
+++ b/chrome/android/java/strings/android_chrome_strings.grd
@@ -2612,7 +2612,7 @@
         Your passwords, history &amp; more on all devices
       </message>
       <message name="IDS_SIGNIN_PERSONALIZATION_DESCRIPTION" desc="Description of personalization features for the screen that asks users to sign-in and turn on Sync and personalization.">
-        Personalized Google services like Google Pay
+        More personal Google services, like better page suggestions
       </message>
       <message name="IDS_SIGNIN_PERSONALIZATION_DESCRIPTION_CHILD_ACCOUNT" desc="Description of personalization features for the screen that asks users to sign-in and turn on Sync and personalization. This version of the description is used with child accounts.">
         Personalized Google services
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingTestHelper.java b/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingTestHelper.java
index 2ccaadc..b696f89e 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingTestHelper.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingTestHelper.java
@@ -34,6 +34,8 @@
 import org.chromium.base.ThreadUtils;
 import org.chromium.base.test.util.UrlUtils;
 import org.chromium.chrome.R;
+import org.chromium.chrome.browser.ChromeTabbedActivity;
+import org.chromium.chrome.browser.InsetObserverView;
 import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
 import org.chromium.content_public.browser.ImeAdapter;
 import org.chromium.content_public.browser.WebContents;
@@ -57,7 +59,7 @@
     private TestInputMethodManagerWrapper mInputMethodManagerWrapper;
 
     private class FakeKeyboard extends KeyboardVisibilityDelegate {
-        static final int KEYBOARD_HEIGHT = 678;
+        static final int KEYBOARD_HEIGHT = 400;
         private boolean mIsShowing;
 
         @Override
@@ -88,6 +90,20 @@
         public int calculateKeyboardHeight(Context context, View rootView) {
             return mIsShowing ? KEYBOARD_HEIGHT : 0;
         }
+
+        /**
+         * Creates an inset observer view calculating the bottom inset based on the fake keyboard.
+         * @param context Context used to instantiate this view.
+         * @return a {@link InsetObserverView}
+         */
+        InsetObserverView createInsetObserver(Context context) {
+            return new InsetObserverView(context) {
+                @Override
+                public int getSystemWindowInsetsBottom() {
+                    return mIsShowing ? KEYBOARD_HEIGHT : 0;
+                }
+            };
+        }
     }
     private final FakeKeyboard mKeyboard = new FakeKeyboard();
 
@@ -107,7 +123,14 @@
                 + "</form></body></html>"));
         setRtlForTesting(isRtl);
         ThreadUtils.runOnUiThreadBlocking(() -> {
-            mWebContentsRef.set(mActivityTestRule.getActivity().getActivityTab().getWebContents());
+            ChromeTabbedActivity activity = mActivityTestRule.getActivity();
+            mWebContentsRef.set(activity.getActivityTab().getWebContents());
+            activity.getManualFillingController()
+                    .getMediatorForTesting()
+                    .setInsetObserverViewSupplier(() -> {
+                        return mKeyboard.createInsetObserver(
+                                activity.getInsetObserverView().getContext());
+                    });
             // The TestInputMethodManagerWrapper intercepts showSoftInput so that a keyboard is
             // never brought up.
             final ImeAdapter imeAdapter = ImeAdapter.fromWebContents(mWebContentsRef.get());
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/NewTabPageTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/NewTabPageTest.java
index f1d9af24..623e114 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/NewTabPageTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/NewTabPageTest.java
@@ -8,9 +8,6 @@
 import static android.support.test.espresso.action.ViewActions.click;
 import static android.support.test.espresso.matcher.ViewMatchers.withId;
 
-import android.app.Activity;
-import android.app.Instrumentation;
-import android.app.Instrumentation.ActivityMonitor;
 import android.content.ComponentCallbacks2;
 import android.graphics.Canvas;
 import android.support.test.InstrumentationRegistry;
@@ -41,14 +38,11 @@
 import org.chromium.base.test.util.DisableIf;
 import org.chromium.base.test.util.DisabledTest;
 import org.chromium.base.test.util.Feature;
-import org.chromium.base.test.util.Restriction;
 import org.chromium.base.test.util.RetryOnFailure;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ChromeFeatureList;
 import org.chromium.chrome.browser.ChromeSwitches;
 import org.chromium.chrome.browser.UrlConstants;
-import org.chromium.chrome.browser.bookmarks.BookmarkActivity;
-import org.chromium.chrome.browser.download.DownloadActivity;
 import org.chromium.chrome.browser.feed.FeedNewTabPage;
 import org.chromium.chrome.browser.ntp.cards.NewTabPageAdapter;
 import org.chromium.chrome.browser.ntp.cards.NewTabPageRecyclerView;
@@ -60,7 +54,6 @@
 import org.chromium.chrome.browser.omnibox.UrlBar;
 import org.chromium.chrome.browser.preferences.Pref;
 import org.chromium.chrome.browser.preferences.PrefServiceBridge;
-import org.chromium.chrome.browser.search_engines.TemplateUrlServiceTestUtils;
 import org.chromium.chrome.browser.suggestions.SiteSuggestion;
 import org.chromium.chrome.browser.suggestions.TileSectionType;
 import org.chromium.chrome.browser.suggestions.TileSource;
@@ -89,7 +82,6 @@
 import org.chromium.net.test.util.TestWebServer;
 import org.chromium.policy.test.annotations.Policies;
 import org.chromium.ui.base.PageTransition;
-import org.chromium.ui.test.util.UiRestriction;
 
 import java.io.IOException;
 import java.util.ArrayList;
@@ -230,66 +222,6 @@
 
     @Test
     @MediumTest
-    @Feature({"NewTabPage", "FeedNewTabPage"})
-    @EnableFeatures({ChromeFeatureList.SIMPLIFIED_NTP})
-    @Restriction(UiRestriction.RESTRICTION_TYPE_PHONE)
-    @ParameterAnnotations.UseMethodParameter(InterestFeedParams.class)
-    public void testSimplifiedNtp_BookmarksShortcuts(boolean interestFeedEnabled) {
-        ActivityMonitor activityMonitor = InstrumentationRegistry.getInstrumentation().addMonitor(
-                BookmarkActivity.class.getName(),
-                new Instrumentation.ActivityResult(Activity.RESULT_OK, null), true);
-
-        ThreadUtils.runOnUiThreadBlocking(() -> {
-            View button = mNtp.getView().findViewById(R.id.bookmarks_button);
-            button.performClick();
-        });
-
-        Assert.assertEquals(1, activityMonitor.getHits());
-    }
-
-    @Test
-    @MediumTest
-    @Feature({"NewTabPage", "FeedNewTabPage"})
-    @EnableFeatures({ChromeFeatureList.SIMPLIFIED_NTP})
-    @Restriction(UiRestriction.RESTRICTION_TYPE_PHONE)
-    @ParameterAnnotations.UseMethodParameter(InterestFeedParams.class)
-    public void testSimplifiedNtp_DownloadsShortcuts(boolean interestFeedEnabled) {
-        ActivityMonitor activityMonitor = InstrumentationRegistry.getInstrumentation().addMonitor(
-                DownloadActivity.class.getName(),
-                new Instrumentation.ActivityResult(Activity.RESULT_OK, null), true);
-
-        ThreadUtils.runOnUiThreadBlocking(() -> {
-            View button = mNtp.getView().findViewById(R.id.downloads_button);
-            button.performClick();
-        });
-
-        Assert.assertEquals(1, activityMonitor.getHits());
-    }
-
-    @Test
-    @MediumTest
-    @Feature({"NewTabPage", "FeedNewTabPage"})
-    @EnableFeatures({ChromeFeatureList.SIMPLIFIED_NTP})
-    @Restriction(UiRestriction.RESTRICTION_TYPE_PHONE)
-    @ParameterAnnotations.UseMethodParameter(InterestFeedParams.class)
-    public void testSimplifiedNtp_DefaultSearchEngineChange(boolean interestFeedEnabled)
-            throws Exception {
-        View logo = mNtp.getView().findViewById(R.id.search_provider_logo);
-        View shortcuts = mNtp.getView().findViewById(R.id.shortcuts);
-        Assert.assertEquals(View.VISIBLE, logo.getVisibility());
-        Assert.assertEquals(View.VISIBLE, shortcuts.getVisibility());
-
-        TemplateUrlServiceTestUtils.setSearchEngine("bing.com");
-        Assert.assertEquals(View.GONE, logo.getVisibility());
-        Assert.assertEquals(View.VISIBLE, shortcuts.getVisibility());
-
-        TemplateUrlServiceTestUtils.setSearchEngine("google.com");
-        Assert.assertEquals(View.VISIBLE, logo.getVisibility());
-        Assert.assertEquals(View.VISIBLE, shortcuts.getVisibility());
-    }
-
-    @Test
-    @MediumTest
     @Feature({"NewTabPage"})
     public void testThumbnailInvalidations() throws Throwable {
         mActivityTestRule.runOnUiThread(new Runnable() {
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebappSplashScreenTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebappSplashScreenTest.java
index 42c265f7..d0f23f1 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebappSplashScreenTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebappSplashScreenTest.java
@@ -27,6 +27,7 @@
 import org.chromium.base.ThreadUtils;
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.base.test.util.CommandLineFlags;
+import org.chromium.base.test.util.DisabledTest;
 import org.chromium.base.test.util.Feature;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ChromeSwitches;
@@ -78,6 +79,7 @@
     }
 
     @Test
+    @DisabledTest
     @SmallTest
     @Feature({"Webapps"})
     @TargetApi(Build.VERSION_CODES.LOLLIPOP)
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebappSplashScreenThemeColorTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebappSplashScreenThemeColorTest.java
index a38a998..749652b0 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebappSplashScreenThemeColorTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebappSplashScreenThemeColorTest.java
@@ -18,6 +18,7 @@
 import org.chromium.base.ThreadUtils;
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.base.test.util.CommandLineFlags;
+import org.chromium.base.test.util.DisabledTest;
 import org.chromium.base.test.util.Feature;
 import org.chromium.chrome.browser.ChromeSwitches;
 import org.chromium.chrome.browser.ShortcutHelper;
@@ -49,6 +50,7 @@
     }
 
     @Test
+    @DisabledTest
     @SmallTest
     @Feature({"Webapps"})
     @TargetApi(Build.VERSION_CODES.LOLLIPOP)
diff --git a/chrome/android/profiles/newest.txt b/chrome/android/profiles/newest.txt
index ef45bd6..0c6f55f 100644
--- a/chrome/android/profiles/newest.txt
+++ b/chrome/android/profiles/newest.txt
@@ -1 +1 @@
-chromeos-chrome-amd64-71.0.3554.0_rc-r1.afdo.bz2
\ No newline at end of file
+chromeos-chrome-amd64-71.0.3555.0_rc-r1.afdo.bz2
\ No newline at end of file
diff --git a/chrome/android/webapk/shell_apk/BUILD.gn b/chrome/android/webapk/shell_apk/BUILD.gn
index 70ad9d7..edfc48a 100644
--- a/chrome/android/webapk/shell_apk/BUILD.gn
+++ b/chrome/android/webapk/shell_apk/BUILD.gn
@@ -92,6 +92,9 @@
     native_lib_placeholders = [ "libfoo.so" ]
 
     if (!is_java_debug) {
+      if (defined(webapk_proguard_jar_path)) {
+        proguard_jar_path = webapk_proguard_jar_path
+      }
       proguard_enabled = true
       proguard_configs = [
         "//chrome/android/webapk/shell_apk/proguard.flags",
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index b461b84..b27902c2 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -6693,7 +6693,7 @@
         Google may use your browsing history to personalize Search, ads, and other Google services
       </message>
       <message name="IDS_SYNC_CONFIRMATION_DICE_PERSONALIZE_SERVICES_BODY" desc="Body of the personalize services section of the sync confirmation dialog in the tab modal signin flow">
-        Personalized Google services like Google Pay
+        More personal Google services, like better page suggestions
       </message>
       <message name="IDS_SYNC_CONFIRMATION_PERSONALIZE_SERVICES_BODY_CHILD_ACCOUNT" desc="Body of the personalize services section of the sync confirmation dialog in the tab modal signin flow for child accounts" formatter_data="android_java">
         Google may use your browsing history to personalize Search and other Google services
diff --git a/chrome/app/vector_icons/BUILD.gn b/chrome/app/vector_icons/BUILD.gn
index adc13b33..ce408882 100644
--- a/chrome/app/vector_icons/BUILD.gn
+++ b/chrome/app/vector_icons/BUILD.gn
@@ -66,7 +66,6 @@
     "navigate_home_touch.icon",
     "navigate_stop.icon",
     "navigate_stop_touch.icon",
-    "new_tab_button_incognito.icon",
     "nfc.icon",
     "overflow_chevron.icon",
     "page_info_content_paste.icon",
diff --git a/chrome/app/vector_icons/new_tab_button_incognito.icon b/chrome/app/vector_icons/new_tab_button_incognito.icon
deleted file mode 100644
index b487335f..0000000
--- a/chrome/app/vector_icons/new_tab_button_incognito.icon
+++ /dev/null
@@ -1,44 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-CANVAS_DIMENSIONS, 12,
-MOVE_TO, 4.51f, 0.03f,
-LINE_TO, 6, 1,
-LINE_TO, 7.48f, 0.03f,
-R_CUBIC_TO, 0.36f, -0.1f, 0.76f, 0.06f, 0.89f, 0.36f,
-LINE_TO, 10, 4.97f,
-H_LINE_TO, 2,
-LINE_TO, 3.62f, 0.39f,
-R_CUBIC_TO, 0.14f, -0.3f, 0.53f, -0.46f, 0.89f, -0.36f,
-CLOSE,
-MOVE_TO, 9, 12,
-R_CUBIC_TO, -1, 0, -2, -0.5f, -2, -1.5f,
-R_CUBIC_TO, -0.5f, -0.66f, -1.5f, -0.66f, -2, 0,
-R_CUBIC_TO, 0, 1, -1, 1.5f, -2, 1.5f,
-R_CUBIC_TO, -1.5f, 0, -2, -1, -2, -2.01f,
-R_CUBIC_TO, 0, -1, 0.5f, -2.01f, 2, -2.01f,
-R_CUBIC_TO, 1, 0, 2, 0.52f, 2, 1.52f,
-R_CUBIC_TO, 0.5f, -0.51f, 1.5f, -0.51f, 2, 0,
-R_CUBIC_TO, 0, -1, 1, -1.52f, 2, -1.52f,
-R_CUBIC_TO, 1.5f, 0, 2, 1.02f, 2, 2.02f,
-R_CUBIC_TO, 0, 1, -0.5f, 2, -2, 2,
-CLOSE,
-R_MOVE_TO, 3, -5.02f,
-H_LINE_TO, 0,
-V_LINE_TO, 5.97f,
-R_H_LINE_TO, 12,
-R_V_LINE_TO, 1,
-CLOSE,
-R_MOVE_TO, -9, 4.02f,
-R_CUBIC_TO, 0.55f, 0, 1, -0.45f, 1, -1,
-R_CUBIC_TO, 0, -0.56f, -0.45f, -1, -1, -1,
-R_CUBIC_TO, -0.55f, 0, -1, 0.45f, -1, 1,
-R_CUBIC_TO, 0, 0.56f, 0.45f, 1, 1, 1,
-CLOSE,
-R_MOVE_TO, 6, 0,
-R_CUBIC_TO, 0.55f, 0, 1, -0.45f, 1, -1,
-R_CUBIC_TO, 0, -0.56f, -0.45f, -1, -1, -1,
-R_CUBIC_TO, -0.55f, 0, -1, 0.45f, -1, 1,
-R_CUBIC_TO, 0, 0.56f, 0.45f, 1, 1, 1,
-CLOSE
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index de0628c..56e24d3 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -111,6 +111,8 @@
     "autofill/personal_data_manager_factory.h",
     "autofill/risk_util.cc",
     "autofill/risk_util.h",
+    "autofill/strike_database.cc",
+    "autofill/strike_database.h",
     "autofill/validation_rules_storage_factory.cc",
     "autofill/validation_rules_storage_factory.h",
     "background_fetch/background_fetch_delegate_factory.cc",
@@ -1695,6 +1697,7 @@
     "//chrome:strings",
     "//chrome/app/resources:platform_locale_settings",
     "//chrome/app/theme:theme_resources",
+    "//chrome/browser/autofill:strike_data",
     "//chrome/browser/devtools",
     "//chrome/browser/media:media_engagement_preload_proto",
     "//chrome/browser/media:mojo_bindings",
diff --git a/chrome/browser/android/chrome_feature_list.cc b/chrome/browser/android/chrome_feature_list.cc
index 5b4f27b..9f519e7 100644
--- a/chrome/browser/android/chrome_feature_list.cc
+++ b/chrome/browser/android/chrome_feature_list.cc
@@ -365,7 +365,7 @@
                                      base::FEATURE_ENABLED_BY_DEFAULT};
 
 const base::Feature kSimplifiedNTP{"SimplifiedNTP",
-                                   base::FEATURE_DISABLED_BY_DEFAULT};
+                                   base::FEATURE_ENABLED_BY_DEFAULT};
 
 const base::Feature kSoleIntegration{"SoleIntegration",
                                      base::FEATURE_ENABLED_BY_DEFAULT};
diff --git a/chrome/browser/apps/guest_view/web_view_browsertest.cc b/chrome/browser/apps/guest_view/web_view_browsertest.cc
index 3775a3f..d08979e 100644
--- a/chrome/browser/apps/guest_view/web_view_browsertest.cc
+++ b/chrome/browser/apps/guest_view/web_view_browsertest.cc
@@ -1081,14 +1081,8 @@
 }
 
 // This test exercises the webview spatial navigation API
-#if defined(OS_WIN)
-// Flaky on Windows. See https://crbug.com/884570.
-#define MAYBE_SpatialNavigationJavascriptAPI \
-  DISABLED_SpatialNavigationJavascriptAPI
-#else
-#define MAYBE_SpatialNavigationJavascriptAPI SpatialNavigationJavascriptAPI
-#endif
-IN_PROC_BROWSER_TEST_F(WebViewTest, MAYBE_SpatialNavigationJavascriptAPI) {
+// This test is disabled due to being flaky. http://crbug.com/884306
+IN_PROC_BROWSER_TEST_F(WebViewTest, DISABLED_SpatialNavigationJavascriptAPI) {
   base::CommandLine::ForCurrentProcess()->AppendSwitch(
       switches::kEnableSpatialNavigation);
 
diff --git a/chrome/browser/apps/platform_apps/app_browsertest_util.cc b/chrome/browser/apps/platform_apps/app_browsertest_util.cc
index 7d69cfb..e350ce5 100644
--- a/chrome/browser/apps/platform_apps/app_browsertest_util.cc
+++ b/chrome/browser/apps/platform_apps/app_browsertest_util.cc
@@ -49,6 +49,8 @@
   ChromeAppDelegate::DisableExternalOpenForTesting();
 }
 
+PlatformAppBrowserTest::~PlatformAppBrowserTest() {}
+
 void PlatformAppBrowserTest::SetUpCommandLine(base::CommandLine* command_line) {
   // Skips ExtensionApiTest::SetUpCommandLine.
   ExtensionBrowserTest::SetUpCommandLine(command_line);
@@ -58,25 +60,26 @@
   ProcessManager::SetEventPageSuspendingTimeForTesting(1000);
 }
 
-void PlatformAppBrowserTest::SetUpInProcessBrowserTestFixture() {
-  ExtensionApiTest::SetUpInProcessBrowserTestFixture();
+void PlatformAppBrowserTest::SetUpOnMainThread() {
+  ExtensionApiTest::SetUpOnMainThread();
 #if defined(OS_CHROMEOS)
   // Mock the Media Router in extension api tests. Several of the
   // PlatformAppBrowserTest suites call RunAllPendingInMessageLoop() when there
   // are mojo messages that will call back into Profile creation through the
   // media router.
-  ON_CALL(media_router_, RegisterMediaSinksObserver(testing::_))
+  media_router_ = std::make_unique<media_router::MockMediaRouter>();
+  ON_CALL(*media_router_, RegisterMediaSinksObserver(testing::_))
       .WillByDefault(testing::Return(true));
 
-  CastConfigClientMediaRouter::SetMediaRouterForTest(&media_router_);
+  CastConfigClientMediaRouter::SetMediaRouterForTest(media_router_.get());
 #endif
 }
 
-void PlatformAppBrowserTest::TearDownInProcessBrowserTestFixture() {
+void PlatformAppBrowserTest::TearDownOnMainThread() {
 #if defined(OS_CHROMEOS)
   CastConfigClientMediaRouter::SetMediaRouterForTest(nullptr);
 #endif
-  ExtensionApiTest::TearDownInProcessBrowserTestFixture();
+  ExtensionApiTest::TearDownOnMainThread();
 }
 
 // static
diff --git a/chrome/browser/apps/platform_apps/app_browsertest_util.h b/chrome/browser/apps/platform_apps/app_browsertest_util.h
index f4fafdc..b5fd973 100644
--- a/chrome/browser/apps/platform_apps/app_browsertest_util.h
+++ b/chrome/browser/apps/platform_apps/app_browsertest_util.h
@@ -7,6 +7,7 @@
 
 #include <stddef.h>
 
+#include <memory>
 #include <string>
 
 #include "base/macros.h"
@@ -35,10 +36,11 @@
 class PlatformAppBrowserTest : public ExtensionApiTest {
  public:
   PlatformAppBrowserTest();
+  ~PlatformAppBrowserTest() override;
 
   void SetUpCommandLine(base::CommandLine* command_line) override;
-  void SetUpInProcessBrowserTestFixture() override;
-  void TearDownInProcessBrowserTestFixture() override;
+  void SetUpOnMainThread() override;
+  void TearDownOnMainThread() override;
 
   // Gets the first app window that is found for a given browser.
   static AppWindow* GetFirstAppWindowForBrowser(Browser* browser);
@@ -129,7 +131,7 @@
 
  private:
 #if defined(OS_CHROMEOS)
-  media_router::MockMediaRouter media_router_;
+  std::unique_ptr<media_router::MockMediaRouter> media_router_;
 #endif
 
   DISALLOW_COPY_AND_ASSIGN(PlatformAppBrowserTest);
diff --git a/chrome/browser/autofill/BUILD.gn b/chrome/browser/autofill/BUILD.gn
new file mode 100644
index 0000000..d8592026
--- /dev/null
+++ b/chrome/browser/autofill/BUILD.gn
@@ -0,0 +1,11 @@
+# Copyright 2018 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//third_party/protobuf/proto_library.gni")
+
+proto_library("strike_data") {
+  sources = [
+    "strike_data.proto",
+  ]
+}
diff --git a/chrome/browser/autofill/strike_data.proto b/chrome/browser/autofill/strike_data.proto
new file mode 100644
index 0000000..8842f0df
--- /dev/null
+++ b/chrome/browser/autofill/strike_data.proto
@@ -0,0 +1,18 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+syntax = "proto2";
+
+package autofill;
+
+option optimize_for = LITE_RUNTIME;
+
+message StrikeData {
+  // Total number of strikes thus far.
+  optional int32 num_strikes = 1;
+
+  // Timestamp of when a strike was last added or removed
+  // for this entry. Can be used to expire strikes later.
+  optional int64 last_update_timestamp = 2;
+}
diff --git a/chrome/browser/autofill/strike_database.cc b/chrome/browser/autofill/strike_database.cc
new file mode 100644
index 0000000..2cf664c
--- /dev/null
+++ b/chrome/browser/autofill/strike_database.cc
@@ -0,0 +1,35 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/autofill/strike_database.h"
+
+#include "base/task/post_task.h"
+#include "chrome/browser/autofill/strike_data.pb.h"
+#include "components/leveldb_proto/proto_database_impl.h"
+
+namespace autofill {
+
+namespace {
+
+const char kDatabaseClientName[] = "StrikeService";
+
+}  // namespace
+
+StrikeDatabase::StrikeDatabase(const base::FilePath& database_dir)
+    : db_(std::make_unique<leveldb_proto::ProtoDatabaseImpl<StrikeData>>(
+          base::CreateSequencedTaskRunnerWithTraits(
+              {base::MayBlock(), base::TaskPriority::BEST_EFFORT,
+               base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN}))),
+      weak_ptr_factory_(this) {
+  db_->Init(kDatabaseClientName, database_dir,
+            leveldb_proto::CreateSimpleOptions(),
+            base::BindRepeating(&StrikeDatabase::OnDatabaseInit,
+                                weak_ptr_factory_.GetWeakPtr()));
+}
+
+StrikeDatabase::~StrikeDatabase() {}
+
+void StrikeDatabase::OnDatabaseInit(bool success) {}
+
+}  // namespace autofill
diff --git a/chrome/browser/autofill/strike_database.h b/chrome/browser/autofill/strike_database.h
new file mode 100644
index 0000000..96376bc
--- /dev/null
+++ b/chrome/browser/autofill/strike_database.h
@@ -0,0 +1,36 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_AUTOFILL_STRIKE_DATABASE_H_
+#define CHROME_BROWSER_AUTOFILL_STRIKE_DATABASE_H_
+
+#include <memory>
+
+#include "base/callback_forward.h"
+#include "base/memory/weak_ptr.h"
+#include "components/leveldb_proto/proto_database.h"
+
+namespace autofill {
+class StrikeData;
+
+// Manages data on whether different Autofill opportunities should be offered to
+// the user. Projects can earn strikes in a number of ways; for instance, if a
+// user ignores or declines a prompt, or if a user accepts a prompt but the task
+// fails.
+class StrikeDatabase {
+ public:
+  StrikeDatabase(const base::FilePath& database_dir);
+  ~StrikeDatabase();
+
+ private:
+  void OnDatabaseInit(bool success);
+
+  std::unique_ptr<leveldb_proto::ProtoDatabase<StrikeData>> db_;
+
+  base::WeakPtrFactory<StrikeDatabase> weak_ptr_factory_;
+};
+
+}  // namespace autofill
+
+#endif  // CHROME_BROWSER_AUTOFILL_STRIKE_DATABASE_H_
diff --git a/chrome/browser/browser_switcher/alternative_browser_driver_linux.cc b/chrome/browser/browser_switcher/alternative_browser_driver_linux.cc
index d38df6d..5e6dded 100644
--- a/chrome/browser/browser_switcher/alternative_browser_driver_linux.cc
+++ b/chrome/browser/browser_switcher/alternative_browser_driver_linux.cc
@@ -7,10 +7,15 @@
 #include "base/command_line.h"
 #include "base/files/file_path.h"
 #include "base/process/launch.h"
+#include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/browser_switcher/alternative_browser_launcher.h"
 #include "url/gurl.h"
 
+#include "third_party/re2/src/re2/re2.h"
+
+#include <stdlib.h>
+
 namespace browser_switcher {
 
 namespace {
@@ -34,6 +39,48 @@
     {kOperaVarName, kOperaExecutableName},
 };
 
+bool ExpandUrlVarName(std::string* arg, const GURL& url) {
+  size_t url_index = arg->find(kUrlVarName);
+  if (url_index == std::string::npos)
+    return false;
+  arg->replace(url_index, strlen(kUrlVarName), url.spec());
+  return true;
+}
+
+void ExpandTilde(std::string* arg) {
+  if (base::StartsWith(*arg, "~", base::CompareCase::SENSITIVE))
+    arg->replace(0, 1, getenv("HOME"));
+}
+
+void ExpandEnvironmentVariables(std::string* arg) {
+  static re2::LazyRE2 re = {
+      "\\$\\{([a-zA-Z_][a-zA-Z_0-9]*)\\}|\\$([a-zA-Z_][a-zA-Z_0-9]*)"};
+  std::string out;
+  re2::StringPiece submatch[3] = {0};
+  size_t start = 0;
+  bool matched = false;
+  while (re->Match(*arg, start, arg->size(), re2::RE2::Anchor::UNANCHORED,
+                   submatch, base::size(submatch))) {
+    out.append(*arg, start, submatch[0].data() - (arg->data() + start));
+    if (submatch[0] == kUrlVarName) {
+      // Don't treat '${url}' as an environment variable, leave it as is.
+      out.append(kUrlVarName);
+    } else {
+      std::string var_name =
+          (submatch[1].empty() ? submatch[2] : submatch[1]).as_string();
+      const char* var_value = getenv(var_name.c_str());
+      if (var_value != NULL)
+        out.append(var_value);
+    }
+    start = submatch[0].end() - arg->data();
+    matched = true;
+  }
+  if (!matched)
+    return;
+  out.append(arg->data() + start, arg->size() - start);
+  std::swap(out, *arg);
+}
+
 }  // namespace
 
 AlternativeBrowserDriver::~AlternativeBrowserDriver() {}
@@ -76,7 +123,10 @@
   const int max_num_args = browser_params_.size() + 2;
   std::vector<std::string> argv;
   argv.reserve(max_num_args);
-  argv.push_back(browser_path_);
+  std::string path = browser_path_;
+  ExpandTilde(&path);
+  ExpandEnvironmentVariables(&path);
+  argv.push_back(path);
   AppendCommandLineArguments(&argv, browser_params_, url);
 
   base::CommandLine cmd_line = base::CommandLine(argv);
@@ -98,15 +148,12 @@
   // TODO(crbug/882520): Do environment variable and tilde expansion.
   bool contains_url = false;
   for (const auto& arg : raw_args) {
-    size_t url_index = arg.find(kUrlVarName);
-    if (url_index != std::string::npos) {
-      std::string expanded_arg = arg;
-      expanded_arg.replace(url_index, strlen(kUrlVarName), url.spec());
-      argv->push_back(expanded_arg);
+    std::string expanded_arg = arg;
+    ExpandTilde(&expanded_arg);
+    ExpandEnvironmentVariables(&expanded_arg);
+    if (ExpandUrlVarName(&expanded_arg, url))
       contains_url = true;
-    } else {
-      argv->push_back(arg);
-    }
+    argv->push_back(expanded_arg);
   }
   if (!contains_url)
     argv->push_back(url.spec());
diff --git a/chrome/browser/browser_switcher/alternative_browser_driver_unittest.cc b/chrome/browser/browser_switcher/alternative_browser_driver_unittest.cc
index 6853f1b..1a28507 100644
--- a/chrome/browser/browser_switcher/alternative_browser_driver_unittest.cc
+++ b/chrome/browser/browser_switcher/alternative_browser_driver_unittest.cc
@@ -43,7 +43,7 @@
   AlternativeBrowserDriverImpl::AppendCommandLineArguments(
       &argv, UTF8VectorToNative({"-O", "-", "--"}),
       GURL("https://example.com/"));
-  ASSERT_EQ(5u, argv.size());
+  EXPECT_EQ(5u, argv.size());
   EXPECT_EQ(UTF8ToNative("wget"), argv[0]);
   EXPECT_EQ(UTF8ToNative("-O"), argv[1]);
   EXPECT_EQ(UTF8ToNative("-"), argv[2]);
@@ -51,14 +51,96 @@
   EXPECT_EQ(UTF8ToNative("https://example.com/"), argv[4]);
 }
 
-TEST_F(AlternativeBrowserDriverTest, AppendCommandLineArgumentsSubstitutesUrl) {
+TEST_F(AlternativeBrowserDriverTest, AppendCommandLineArgumentsExpandsUrl) {
   std::vector<StringType> argv = {UTF8ToNative("google-chrome")};
   AlternativeBrowserDriverImpl::AppendCommandLineArguments(
       &argv, UTF8VectorToNative({"--app=${url}#fragment"}),
       GURL("https://example.com/"));
-  ASSERT_EQ(2u, argv.size());
+  EXPECT_EQ(2u, argv.size());
   EXPECT_EQ(UTF8ToNative("google-chrome"), argv[0]);
   EXPECT_EQ(UTF8ToNative("--app=https://example.com/#fragment"), argv[1]);
 }
 
+#if defined(OS_WIN)
+TEST_F(AlternativeBrowserDriverTest, AppendCommandLineArgumentsExpandsEnvVars) {
+  std::vector<std::wstring> argv = {L"something.exe"};
+  _putenv("A=AAA");
+  _putenv("B=BBB");
+  _putenv("CC=CCC");
+  _putenv("D=DDD");
+  AlternativeBrowserDriverImpl::AppendCommandLineArguments(
+      &argv,
+      {L"%A%", L"%B%", L"before_%CC%_between_%D%_after", L"%NONEXISTENT%"},
+      GURL("https://example.com/"));
+  EXPECT_EQ(6u, argv.size());
+  EXPECT_EQ(L"something.exe", argv[0]);
+  EXPECT_EQ(L"AAA", argv[1]);
+  EXPECT_EQ(L"BBB", argv[2]);
+  EXPECT_EQ(L"before_CCC_between_DDD_after", argv[3]);
+  EXPECT_EQ(L"%NONEXISTENT%", argv[4]);
+  EXPECT_EQ(L"https://example.com/", argv[5]);
+}
+
+TEST_F(AlternativeBrowserDriverTest,
+       AppendCommandLineArgumentDoesntExpandUrlContent) {
+  std::vector<std::wstring> argv = {L"something.exe"};
+  _putenv("A=AAA");
+  AlternativeBrowserDriverImpl::AppendCommandLineArguments(
+      &argv, {}, GURL("https://evil.com/%A%"));
+  AlternativeBrowserDriverImpl::AppendCommandLineArguments(
+      &argv, {L"${url}"}, GURL("https://evil.com/%A%"));
+  EXPECT_EQ(3u, argv.size());
+  EXPECT_EQ(L"something.exe", argv[0]);
+  EXPECT_EQ(L"https://evil.com/%A%", argv[1]);
+  EXPECT_EQ(L"https://evil.com/%A%", argv[2]);
+}
+#endif  // defined(OS_WIN)
+
+#if defined(OS_POSIX)
+TEST_F(AlternativeBrowserDriverTest, AppendCommandLineArgumentsExpandsTilde) {
+  std::vector<std::string> argv = {"some_script"};
+  setenv("HOME", "/home/foobar", true);
+  AlternativeBrowserDriverImpl::AppendCommandLineArguments(
+      &argv, {"~/file.txt", "/tmp/backup.txt~"}, GURL("https://example.com/"));
+  EXPECT_EQ(4u, argv.size());
+  EXPECT_EQ("some_script", argv[0]);
+  EXPECT_EQ("/home/foobar/file.txt", argv[1]);
+  EXPECT_EQ("/tmp/backup.txt~", argv[2]);
+  EXPECT_EQ("https://example.com/", argv[3]);
+}
+
+TEST_F(AlternativeBrowserDriverTest, AppendCommandLineArgumentsExpandsEnvVars) {
+  std::vector<std::string> argv = {"some_script"};
+  setenv("A", "AAA", true);
+  setenv("B", "BBB", true);
+  setenv("CC", "CCC", true);
+  setenv("D", "DDD", true);
+  AlternativeBrowserDriverImpl::AppendCommandLineArguments(
+      &argv, {"$A", "${B}", "before_${CC}_between_${D}_after", "$NONEXISTENT"},
+      GURL("https://example.com/"));
+  EXPECT_EQ(6u, argv.size());
+  EXPECT_EQ("some_script", argv[0]);
+  EXPECT_EQ("AAA", argv[1]);
+  EXPECT_EQ("BBB", argv[2]);
+  EXPECT_EQ("before_CCC_between_DDD_after", argv[3]);
+  EXPECT_EQ("", argv[4]);
+  EXPECT_EQ("https://example.com/", argv[5]);
+}
+
+TEST_F(AlternativeBrowserDriverTest,
+       AppendCommandLineArgumentDoesntExpandUrlContent) {
+  std::vector<std::string> argv = {"something"};
+  setenv("A", "AAA", true);
+  setenv("B", "BBB", true);
+  AlternativeBrowserDriverImpl::AppendCommandLineArguments(
+      &argv, {}, GURL("https://evil.com/$A${B}"));
+  AlternativeBrowserDriverImpl::AppendCommandLineArguments(
+      &argv, {"${url}"}, GURL("https://evil.com/$A${B}"));
+  EXPECT_EQ(3u, argv.size());
+  EXPECT_EQ("something", argv[0]);
+  EXPECT_EQ("https://evil.com/$A$%7BB%7D", argv[1]);
+  EXPECT_EQ("https://evil.com/$A$%7BB%7D", argv[2]);
+}
+#endif  // defined(OS_POSIX)
+
 }  // namespace browser_switcher
diff --git a/chrome/browser/browser_switcher/alternative_browser_driver_win.cc b/chrome/browser/browser_switcher/alternative_browser_driver_win.cc
index aea295d..2f82dc8 100644
--- a/chrome/browser/browser_switcher/alternative_browser_driver_win.cc
+++ b/chrome/browser/browser_switcher/alternative_browser_driver_win.cc
@@ -91,6 +91,28 @@
   return location;
 }
 
+bool ExpandUrlVarName(std::wstring* arg, const std::wstring& url_spec) {
+  size_t url_index = arg->find(kUrlVarName);
+  if (url_index == std::wstring::npos)
+    return false;
+  arg->replace(url_index, wcslen(kUrlVarName), url_spec);
+  return true;
+}
+
+void ExpandEnvironmentVariables(std::wstring* arg) {
+  DWORD expanded_size = 0;
+  expanded_size = ::ExpandEnvironmentStrings(arg->c_str(), NULL, expanded_size);
+  if (expanded_size == 0)
+    return;
+
+  // The expected buffer length as defined in MSDN is chars + null + 1.
+  std::unique_ptr<wchar_t[]> out(new wchar_t[expanded_size + 2]);
+  expanded_size =
+      ::ExpandEnvironmentStrings(arg->c_str(), out.get(), expanded_size);
+  if (expanded_size != 0)
+    *arg = out.get();
+}
+
 }  // namespace
 
 AlternativeBrowserDriver::~AlternativeBrowserDriver() {}
@@ -200,7 +222,9 @@
   const int max_num_args = browser_params_.size() + 2;
   std::vector<std::wstring> argv;
   argv.reserve(max_num_args);
-  argv.push_back(browser_path_);
+  std::wstring path = browser_path_;
+  ExpandEnvironmentVariables(&path);
+  argv.push_back(path);
   AppendCommandLineArguments(&argv, browser_params_, url);
 
   base::CommandLine cmd_line = base::CommandLine(argv);
@@ -222,15 +246,11 @@
   std::vector<std::wstring> command_line;
   bool contains_url = false;
   for (const auto& arg : raw_args) {
-    size_t url_index = arg.find(kUrlVarName);
-    if (url_index != std::string::npos) {
-      std::wstring expanded_arg = arg;
-      expanded_arg.replace(url_index, wcslen(kUrlVarName), url_spec);
-      argv->push_back(expanded_arg);
+    std::wstring expanded_arg = arg;
+    ExpandEnvironmentVariables(&expanded_arg);
+    if (ExpandUrlVarName(&expanded_arg, url_spec))
       contains_url = true;
-    } else {
-      argv->push_back(arg);
-    }
+    argv->push_back(expanded_arg);
   }
   if (!contains_url)
     argv->push_back(url_spec);
diff --git a/chrome/browser/browser_switcher/browser_switcher_sitelist.cc b/chrome/browser/browser_switcher/browser_switcher_sitelist.cc
index 4edb8d1..3000c1c 100644
--- a/chrome/browser/browser_switcher/browser_switcher_sitelist.cc
+++ b/chrome/browser/browser_switcher/browser_switcher_sitelist.cc
@@ -10,6 +10,8 @@
 #include "components/prefs/pref_service.h"
 #include "url/gurl.h"
 
+#include "third_party/re2/src/re2/re2.h"
+
 namespace browser_switcher {
 
 namespace {
@@ -26,6 +28,14 @@
   return found != input.end();
 }
 
+// Checks if the omitted prefix for a non-fully specific prefix is one of the
+// expected parts that are allowed to be omitted (e.g. "https://").
+bool IsValidPrefix(base::StringPiece prefix) {
+  static re2::LazyRE2 re = {"(https?|file):(//)?"};
+  re2::StringPiece converted_prefix(prefix.data(), prefix.size());
+  return (prefix.empty() || re2::RE2::FullMatch(converted_prefix, *re));
+}
+
 // URL is passed as a (url_spec, url_host) to avoid heap-allocating a string for
 // the host every time this is called.
 bool UrlMatches(base::StringPiece url_spec,
@@ -37,8 +47,10 @@
   }
   if (pattern.find('/') != base::StringPiece::npos) {
     // Check prefix using the normalized URL, case sensitive.
-    return base::StartsWith(url_spec, GURL(pattern).spec(),
-                            base::CompareCase::SENSITIVE);
+    size_t pos = url_spec.find(pattern);
+    if (pos == std::string::npos)
+      return false;
+    return IsValidPrefix(base::StringPiece(url_spec.data(), pos));
   }
   // Compare hosts, case-insensitive.
   return StringContainsInsensitiveASCII(url_host, pattern);
diff --git a/chrome/browser/browser_switcher/browser_switcher_sitelist_unittest.cc b/chrome/browser/browser_switcher/browser_switcher_sitelist_unittest.cc
index 70e0aec8..3b4c5b6 100644
--- a/chrome/browser/browser_switcher/browser_switcher_sitelist_unittest.cc
+++ b/chrome/browser/browser_switcher/browser_switcher_sitelist_unittest.cc
@@ -95,13 +95,6 @@
   EXPECT_FALSE(sitelist()->ShouldSwitch(GURL("http://google.com/")));
 }
 
-TEST_F(BrowserSwitcherSitelistTest, ShouldRedirectPrefixNotLowerCase) {
-  // The scheme and host are case-insensitive, but the rest is case-sensitive.
-  Initialize({"HTTP://EXAMPLE.COM/SUBROUTE"}, {});
-  EXPECT_TRUE(sitelist()->ShouldSwitch(GURL("http://example.com/SUBROUTE")));
-  EXPECT_FALSE(sitelist()->ShouldSwitch(GURL("http://example.com/subroute")));
-}
-
 TEST_F(BrowserSwitcherSitelistTest, ShouldRedirectInvertedMatch) {
   // The most specific (i.e., longest string) rule should have priority.
   Initialize({"!subdomain.example.com", "example.com"}, {});
@@ -122,4 +115,23 @@
   EXPECT_FALSE(sitelist()->ShouldSwitch(GURL("http://example.com/")));
 }
 
+TEST_F(BrowserSwitcherSitelistTest, ShouldMatchAnySchema) {
+  // URLs formatted like these don't include a schema, so should match both HTTP
+  // and HTTPS.
+  Initialize({"//example.com", "reddit.com/r/funny"}, {});
+  EXPECT_TRUE(sitelist()->ShouldSwitch(GURL("http://example.com/something")));
+  EXPECT_TRUE(sitelist()->ShouldSwitch(GURL("https://example.com/something")));
+  EXPECT_TRUE(sitelist()->ShouldSwitch(GURL("file://example.com/foobar/")));
+  EXPECT_FALSE(sitelist()->ShouldSwitch(GURL("https://foo.example.com/")));
+  EXPECT_FALSE(sitelist()->ShouldSwitch(GURL("mailto://example.com")));
+  EXPECT_FALSE(sitelist()->ShouldSwitch(GURL("http://bad.com/example.com/")));
+  EXPECT_FALSE(sitelist()->ShouldSwitch(GURL("http://bad.com//example.com/")));
+  EXPECT_FALSE(
+      sitelist()->ShouldSwitch(GURL("http://bad.com/hackme.html?example.com")));
+  EXPECT_TRUE(sitelist()->ShouldSwitch(GURL("http://reddit.com/r/funny")));
+  EXPECT_TRUE(sitelist()->ShouldSwitch(GURL("https://reddit.com/r/funny")));
+  EXPECT_FALSE(sitelist()->ShouldSwitch(GURL("http://reddit.com/r/pics")));
+  EXPECT_FALSE(sitelist()->ShouldSwitch(GURL("https://reddit.com/r/pics")));
+}
+
 }  // namespace browser_switcher
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index a4d9973dc..58e8c1e 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -393,6 +393,10 @@
 #include "chrome/browser/browser_switcher/browser_switcher_navigation_throttle.h"
 #endif
 
+#if defined(OS_WIN) || (defined(OS_LINUX) && !defined(OS_CHROMEOS))
+#include "chrome/browser/browser_switcher/browser_switcher_navigation_throttle.h"
+#endif
+
 #if defined(OS_POSIX) && !defined(OS_MACOSX)
 #include "base/debug/leak_annotations.h"
 #include "components/crash/content/app/breakpad_linux.h"
diff --git a/chrome/browser/chromeos/arc/print/arc_print_service.cc b/chrome/browser/chromeos/arc/print/arc_print_service.cc
index adfce80b..41801ae 100644
--- a/chrome/browser/chromeos/arc/print/arc_print_service.cc
+++ b/chrome/browser/chromeos/arc/print/arc_print_service.cc
@@ -424,7 +424,7 @@
       case printing::JobEventDetails::DOC_DONE:
         DCHECK(event_details.document());
         service_->JobIdGenerated(
-            this, chromeos::CupsPrintJob::GetUniqueId(
+            this, chromeos::CupsPrintJob::CreateUniqueId(
                       base::UTF16ToUTF8(
                           event_details.document()->settings().device_name()),
                       event_details.job_id()));
diff --git a/chrome/browser/chromeos/extensions/file_manager/private_api_drive.cc b/chrome/browser/chromeos/extensions/file_manager/private_api_drive.cc
index e89e5ea7..5d39719 100644
--- a/chrome/browser/chromeos/extensions/file_manager/private_api_drive.cc
+++ b/chrome/browser/chromeos/extensions/file_manager/private_api_drive.cc
@@ -571,15 +571,13 @@
                               base::File::Error error)>;
 
   // Creates an instance and starts the process.
-  static void Start(base::FilePath local_path,
-                    bool want_thumbnail,
+  static void Start(const storage::FileSystemURL& file_system_url,
                     Profile* const profile,
                     ResultCallback callback) {
     DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
     SingleEntryPropertiesGetterForDriveFs* instance =
-        new SingleEntryPropertiesGetterForDriveFs(std::move(local_path),
-                                                  want_thumbnail, profile,
+        new SingleEntryPropertiesGetterForDriveFs(file_system_url, profile,
                                                   std::move(callback));
     instance->StartProcess();
 
@@ -587,13 +585,12 @@
   }
 
  private:
-  SingleEntryPropertiesGetterForDriveFs(base::FilePath local_path,
-                                        bool want_thumbnail,
-                                        Profile* const profile,
-                                        ResultCallback callback)
+  SingleEntryPropertiesGetterForDriveFs(
+      const storage::FileSystemURL& file_system_url,
+      Profile* const profile,
+      ResultCallback callback)
       : callback_(std::move(callback)),
-        local_path_(std::move(local_path)),
-        want_thumbnail_(want_thumbnail),
+        file_system_url_(file_system_url),
         running_profile_(profile),
         properties_(std::make_unique<EntryProperties>()),
         weak_ptr_factory_(this) {
@@ -611,7 +608,8 @@
       return;
     }
     base::FilePath path;
-    if (!integration_service->GetRelativeDrivePath(local_path_, &path)) {
+    if (!integration_service->GetRelativeDrivePath(file_system_url_.path(),
+                                                   &path)) {
       CompleteGetEntryProperties(drive::FILE_ERROR_INVALID_OPERATION);
       return;
     }
@@ -623,7 +621,7 @@
     }
 
     drivefs_interface->GetMetadata(
-        path, want_thumbnail_,
+        path, /* want_thumbnail = */ false,
         mojo::WrapCallbackWithDefaultInvokeIfNotRun(
             base::BindOnce(
                 &SingleEntryPropertiesGetterForDriveFs::OnGetFileInfo,
@@ -704,34 +702,8 @@
     properties_->can_share =
         std::make_unique<bool>(metadata->capabilities->can_share);
 
-    if (metadata->thumbnail) {
-      base::PostTaskAndReplyWithResult(
-          FROM_HERE,
-          base::BindOnce(&SingleEntryPropertiesGetterForDriveFs::
-                             MakeThumbnailDataUrlOnSequence,
-                         std::move(*metadata->thumbnail)),
-          base::BindOnce(
-              &SingleEntryPropertiesGetterForDriveFs::SetThumbnailAndComplete,
-              weak_ptr_factory_.GetWeakPtr()));
-      return;
-    }
-
-    CompleteGetEntryProperties(drive::FILE_ERROR_OK);
-  }
-
-  static std::string MakeThumbnailDataUrlOnSequence(
-      const std::vector<uint8_t>& png_data) {
-    std::string encoded;
-    base::Base64Encode(
-        base::StringPiece(reinterpret_cast<const char*>(png_data.data()),
-                          png_data.size()),
-        &encoded);
-    return base::StrCat({"data:image/png;base64,", encoded});
-  }
-
-  void SetThumbnailAndComplete(std::string thumbnail_data_url) {
-    properties_->thumbnail_url =
-        std::make_unique<std::string>(std::move(thumbnail_data_url));
+    properties_->thumbnail_url = std::make_unique<std::string>(
+        base::StrCat({"drivefs:", file_system_url_.ToGURL().spec()}));
 
     CompleteGetEntryProperties(drive::FILE_ERROR_OK);
   }
@@ -747,8 +719,7 @@
 
   // Given parameters.
   ResultCallback callback_;
-  const base::FilePath local_path_;
-  const bool want_thumbnail_;
+  const storage::FileSystemURL file_system_url_;
   Profile* const running_profile_;
 
   // Values used in the process.
@@ -759,6 +730,16 @@
   DISALLOW_COPY_AND_ASSIGN(SingleEntryPropertiesGetterForDriveFs);
 };
 
+std::string MakeThumbnailDataUrlOnSequence(
+    const std::vector<uint8_t>& png_data) {
+  std::string encoded;
+  base::Base64Encode(
+      base::StringPiece(reinterpret_cast<const char*>(png_data.data()),
+                        png_data.size()),
+      &encoded);
+  return base::StrCat({"data:image/png;base64,", encoded});
+}
+
 }  // namespace
 
 FileManagerPrivateInternalGetEntryPropertiesFunction::
@@ -804,10 +785,7 @@
         break;
       case storage::kFileSystemTypeDriveFs:
         SingleEntryPropertiesGetterForDriveFs::Start(
-            file_system_url.path(),
-            names_as_set.count(
-                api::file_manager_private::ENTRY_PROPERTY_NAME_THUMBNAILURL),
-            GetProfile(),
+            file_system_url, GetProfile(),
             base::BindOnce(
                 &FileManagerPrivateInternalGetEntryPropertiesFunction::
                     CompleteGetEntryProperties,
@@ -1468,4 +1446,67 @@
   OnGotDownloadUrl(metadata ? GURL(metadata->download_url) : GURL());
 }
 
+FileManagerPrivateInternalGetThumbnailFunction::
+    FileManagerPrivateInternalGetThumbnailFunction() = default;
+
+FileManagerPrivateInternalGetThumbnailFunction::
+    ~FileManagerPrivateInternalGetThumbnailFunction() = default;
+
+// ChromeAsyncExtensionFunction overrides.
+bool FileManagerPrivateInternalGetThumbnailFunction::RunAsync() {
+  using extensions::api::file_manager_private_internal::GetThumbnail::Params;
+  const std::unique_ptr<Params> params(Params::Create(*args_));
+  EXTENSION_FUNCTION_VALIDATE(params);
+
+  scoped_refptr<storage::FileSystemContext> file_system_context =
+      file_manager::util::GetFileSystemContextForRenderFrameHost(
+          GetProfile(), render_frame_host());
+  const GURL url = GURL(params->url);
+  const storage::FileSystemURL file_system_url =
+      file_system_context->CrackURL(url);
+
+  if (file_system_url.type() != storage::kFileSystemTypeDriveFs) {
+    return false;
+  }
+  drive::DriveIntegrationService* integration_service =
+      drive::DriveIntegrationServiceFactory::FindForProfile(GetProfile());
+  base::FilePath path;
+  if (!integration_service || !integration_service->GetRelativeDrivePath(
+                                  file_system_url.path(), &path)) {
+    return false;
+  }
+  auto* drivefs_interface = integration_service->GetDriveFsInterface();
+  if (!drivefs_interface) {
+    return false;
+  }
+  drivefs_interface->GetThumbnail(
+      path, params->crop_to_square,
+      mojo::WrapCallbackWithDefaultInvokeIfNotRun(
+          base::BindOnce(
+              &FileManagerPrivateInternalGetThumbnailFunction::GotThumbnail,
+              this),
+          base::Optional<std::vector<uint8_t>>()));
+  return true;
+}
+
+void FileManagerPrivateInternalGetThumbnailFunction::GotThumbnail(
+    const base::Optional<std::vector<uint8_t>>& data) {
+  if (!data) {
+    SetResult(std::make_unique<base::Value>(""));
+    SendResponse(true);
+    return;
+  }
+  base::PostTaskAndReplyWithResult(
+      FROM_HERE, base::BindOnce(&MakeThumbnailDataUrlOnSequence, *data),
+      base::BindOnce(
+          &FileManagerPrivateInternalGetThumbnailFunction::SendEncodedThumbnail,
+          this));
+}
+
+void FileManagerPrivateInternalGetThumbnailFunction::SendEncodedThumbnail(
+    std::string thumbnail_data_url) {
+  SetResult(std::make_unique<base::Value>(std::move(thumbnail_data_url)));
+  SendResponse(true);
+}
+
 }  // namespace extensions
diff --git a/chrome/browser/chromeos/extensions/file_manager/private_api_drive.h b/chrome/browser/chromeos/extensions/file_manager/private_api_drive.h
index 5cb6682..accabca 100644
--- a/chrome/browser/chromeos/extensions/file_manager/private_api_drive.h
+++ b/chrome/browser/chromeos/extensions/file_manager/private_api_drive.h
@@ -263,6 +263,26 @@
   std::unique_ptr<google_apis::AuthService> auth_service_;
 };
 
+class FileManagerPrivateInternalGetThumbnailFunction
+    : public LoggedAsyncExtensionFunction {
+ public:
+  DECLARE_EXTENSION_FUNCTION("fileManagerPrivateInternal.getThumbnail",
+                             FILEMANAGERPRIVATEINTERNAL_GETTHUMBNAIL)
+
+  FileManagerPrivateInternalGetThumbnailFunction();
+
+ protected:
+  ~FileManagerPrivateInternalGetThumbnailFunction() override;
+
+  // ChromeAsyncExtensionFunction overrides.
+  bool RunAsync() override;
+
+ private:
+  void GotThumbnail(const base::Optional<std::vector<uint8_t>>& data);
+
+  void SendEncodedThumbnail(std::string thumbnail_data_url);
+};
+
 }  // namespace extensions
 
 #endif  // CHROME_BROWSER_CHROMEOS_EXTENSIONS_FILE_MANAGER_PRIVATE_API_DRIVE_H_
diff --git a/chrome/browser/chromeos/file_manager/external_filesystem_apitest.cc b/chrome/browser/chromeos/file_manager/external_filesystem_apitest.cc
index d0c28042..41045f4 100644
--- a/chrome/browser/chromeos/file_manager/external_filesystem_apitest.cc
+++ b/chrome/browser/chromeos/file_manager/external_filesystem_apitest.cc
@@ -294,9 +294,10 @@
     // Mock the Media Router in extension api tests. Dispatches to the message
     // loop now try to handle mojo messages that will call back into Profile
     // creation through the media router, which then confuse the drive code.
-    ON_CALL(media_router_, RegisterMediaSinksObserver(testing::_))
+    media_router_ = std::make_unique<media_router::MockMediaRouter>();
+    ON_CALL(*media_router_, RegisterMediaSinksObserver(testing::_))
         .WillByDefault(testing::Return(true));
-    CastConfigClientMediaRouter::SetMediaRouterForTest(&media_router_);
+    CastConfigClientMediaRouter::SetMediaRouterForTest(media_router_.get());
 
     extensions::ExtensionApiTest::SetUpOnMainThread();
   }
@@ -370,7 +371,7 @@
   virtual void AddTestMountPoint() = 0;
 
  private:
-  media_router::MockMediaRouter media_router_;
+  std::unique_ptr<media_router::MockMediaRouter> media_router_;
 
   DISALLOW_COPY_AND_ASSIGN(FileSystemExtensionApiTestBase);
 };
diff --git a/chrome/browser/chromeos/policy/remote_commands/crd_host_delegate.cc b/chrome/browser/chromeos/policy/remote_commands/crd_host_delegate.cc
index 99fb7f8..c81f28a3 100644
--- a/chrome/browser/chromeos/policy/remote_commands/crd_host_delegate.cc
+++ b/chrome/browser/chromeos/policy/remote_commands/crd_host_delegate.cc
@@ -7,6 +7,7 @@
 #include "base/json/json_reader.h"
 #include "base/json/json_writer.h"
 #include "chrome/browser/browser_process.h"
+#include "chrome/browser/chromeos/app_mode/arc/arc_kiosk_app_manager.h"
 #include "chrome/browser/chromeos/app_mode/kiosk_app_manager.h"
 #include "chrome/browser/chromeos/profiles/profile_helper.h"
 #include "chrome/browser/chromeos/settings/device_oauth2_token_service.h"
@@ -148,16 +149,26 @@
 bool CRDHostDelegate::IsRunningKiosk() const {
   auto* user_manager = user_manager::UserManager::Get();
   // TODO(antrim): find out if Arc Kiosk is also eligible.
-  if (!user_manager->IsLoggedInAsKioskApp())
+  if (!user_manager->IsLoggedInAsKioskApp() &&
+      !user_manager->IsLoggedInAsArcKioskApp()) {
     return false;
+  }
   if (!GetKioskProfile())
     return false;
-  chromeos::KioskAppManager* manager = chromeos::KioskAppManager::Get();
-  if (manager->GetAutoLaunchApp().empty())
-    return false;
-  chromeos::KioskAppManager::App app;
-  CHECK(manager->GetApp(manager->GetAutoLaunchApp(), &app));
-  return app.was_auto_launched_with_zero_delay;
+
+  if (user_manager->IsLoggedInAsKioskApp()) {
+    chromeos::KioskAppManager* manager = chromeos::KioskAppManager::Get();
+    if (manager->GetAutoLaunchApp().empty())
+      return false;
+    chromeos::KioskAppManager::App app;
+    CHECK(manager->GetApp(manager->GetAutoLaunchApp(), &app));
+    return app.was_auto_launched_with_zero_delay;
+  } else {  // ARC Kiosk
+    chromeos::ArcKioskAppManager* manager = chromeos::ArcKioskAppManager::Get();
+    if (!manager->GetAutoLaunchAccountId().is_valid())
+      return false;
+    return manager->current_app_was_auto_launched_with_zero_delay();
+  }
 }
 
 base::TimeDelta CRDHostDelegate::GetIdlenessPeriod() const {
diff --git a/chrome/browser/chromeos/printing/cups_print_job.cc b/chrome/browser/chromeos/printing/cups_print_job.cc
index f9310a0..f021f62 100644
--- a/chrome/browser/chromeos/printing/cups_print_job.cc
+++ b/chrome/browser/chromeos/printing/cups_print_job.cc
@@ -21,7 +21,7 @@
 CupsPrintJob::~CupsPrintJob() {}
 
 std::string CupsPrintJob::GetUniqueId() const {
-  return GetUniqueId(printer_.id(), job_id_);
+  return CreateUniqueId(printer_.id(), job_id_);
 }
 
 base::WeakPtr<CupsPrintJob> CupsPrintJob::GetWeakPtr() {
@@ -29,18 +29,18 @@
 }
 
 // static
-std::string CupsPrintJob::GetUniqueId(const std::string& printer_id,
-                                      int job_id) {
+std::string CupsPrintJob::CreateUniqueId(const std::string& printer_id,
+                                         int job_id) {
   return base::StringPrintf("%s%d", printer_id.c_str(), job_id);
 }
 
-bool CupsPrintJob::IsJobFinished() {
+bool CupsPrintJob::IsJobFinished() const {
   return state_ == CupsPrintJob::State::STATE_CANCELLED ||
          state_ == CupsPrintJob::State::STATE_ERROR ||
          state_ == CupsPrintJob::State::STATE_DOCUMENT_DONE;
 }
 
-bool CupsPrintJob::PipelineDead() {
+bool CupsPrintJob::PipelineDead() const {
   return error_code_ == CupsPrintJob::ErrorCode::FILTER_FAILED;
 }
 
diff --git a/chrome/browser/chromeos/printing/cups_print_job.h b/chrome/browser/chromeos/printing/cups_print_job.h
index 33544f0e..0c6a27b 100644
--- a/chrome/browser/chromeos/printing/cups_print_job.h
+++ b/chrome/browser/chromeos/printing/cups_print_job.h
@@ -43,7 +43,7 @@
   ~CupsPrintJob();
 
   // Create a unique id for a print job using the |printer_id| and |job_id|.
-  static std::string GetUniqueId(const std::string& printer_id, int job_id);
+  static std::string CreateUniqueId(const std::string& printer_id, int job_id);
 
   // Returns a unique id for the print job.
   std::string GetUniqueId() const;
@@ -68,17 +68,17 @@
   void set_error_code(ErrorCode error_code) { error_code_ = error_code; }
 
   // Returns true if |state_| represents a terminal state.
-  bool IsJobFinished();
+  bool IsJobFinished() const;
 
   // Returns true if cups pipeline failed.
-  bool PipelineDead();
+  bool PipelineDead() const;
 
  private:
-  Printer printer_;
-  int job_id_;
+  const Printer printer_;
+  const int job_id_;
 
   std::string document_title_;
-  int total_page_number_ = 0;
+  const int total_page_number_;
   int printed_page_number_ = 0;
 
   State state_ = State::STATE_NONE;
diff --git a/chrome/browser/chromeos/printing/cups_print_job_manager_impl.cc b/chrome/browser/chromeos/printing/cups_print_job_manager_impl.cc
index 4b28a9b..69f43b8 100644
--- a/chrome/browser/chromeos/printing/cups_print_job_manager_impl.cc
+++ b/chrome/browser/chromeos/printing/cups_print_job_manager_impl.cc
@@ -433,7 +433,7 @@
     std::vector<std::string> active_jobs;
     for (const auto& queue : result->queues) {
       for (auto& job : queue.jobs) {
-        std::string key = CupsPrintJob::GetUniqueId(job.printer_id, job.id);
+        std::string key = CupsPrintJob::CreateUniqueId(job.printer_id, job.id);
         const auto& entry = jobs_.find(key);
         if (entry == jobs_.end())
           continue;
diff --git a/chrome/browser/defaults.cc b/chrome/browser/defaults.cc
index 2aec41ba..be70bcf 100644
--- a/chrome/browser/defaults.cc
+++ b/chrome/browser/defaults.cc
@@ -36,7 +36,6 @@
 #endif
 
 const bool kDownloadPageHasShowInFolder = true;
-const bool kSizeTabButtonToTopOfTabStrip = false;
 
 #if defined(OS_CHROMEOS) || defined(OS_ANDROID)
 const bool kSyncAutoStarts = true;
diff --git a/chrome/browser/defaults.h b/chrome/browser/defaults.h
index 1ea1f90e..d3eff41 100644
--- a/chrome/browser/defaults.h
+++ b/chrome/browser/defaults.h
@@ -36,9 +36,6 @@
 // Does the download page have the show in folder option?
 extern const bool kDownloadPageHasShowInFolder;
 
-// Should the tab strip be sized to the top of the tab strip?
-extern const bool kSizeTabButtonToTopOfTabStrip;
-
 // If true, we want to automatically start sync signin whenever we have
 // credentials (user doesn't need to go through the startup flow). This is
 // typically enabled on platforms (like ChromeOS) that have their own
diff --git a/chrome/browser/extensions/api/instance_id/instance_id_apitest.cc b/chrome/browser/extensions/api/instance_id/instance_id_apitest.cc
index d62c5a9..f154404 100644
--- a/chrome/browser/extensions/api/instance_id/instance_id_apitest.cc
+++ b/chrome/browser/extensions/api/instance_id/instance_id_apitest.cc
@@ -28,7 +28,8 @@
   InstanceIDApiTest();
 
  protected:
-  void SetUpOnMainThread() override;
+  void SetUp() override;
+  void TearDown() override;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(InstanceIDApiTest);
@@ -37,11 +38,14 @@
 InstanceIDApiTest::InstanceIDApiTest() {
 }
 
-void InstanceIDApiTest::SetUpOnMainThread() {
-  gcm::GCMProfileServiceFactory::GetInstance()->SetTestingFactory(
-      browser()->profile(), &gcm::FakeGCMProfileService::Build);
+void InstanceIDApiTest::SetUp() {
+  gcm::GCMProfileServiceFactory::SetGlobalTestingFactory(
+      &gcm::FakeGCMProfileService::Build);
+  ExtensionApiTest::SetUp();
+}
 
-  ExtensionApiTest::SetUpOnMainThread();
+void InstanceIDApiTest::TearDown() {
+  gcm::GCMProfileServiceFactory::SetGlobalTestingFactory(nullptr);
 }
 
 IN_PROC_BROWSER_TEST_F(InstanceIDApiTest, GetID) {
diff --git a/chrome/browser/extensions/service_worker_apitest.cc b/chrome/browser/extensions/service_worker_apitest.cc
index e080b8a5..17bade97 100644
--- a/chrome/browser/extensions/service_worker_apitest.cc
+++ b/chrome/browser/extensions/service_worker_apitest.cc
@@ -457,15 +457,20 @@
         ::switches::kEnableExperimentalWebPlatformFeatures);
     ServiceWorkerTest::SetUpCommandLine(command_line);
   }
+
+  void SetUp() override {
+    gcm::GCMProfileServiceFactory::SetGlobalTestingFactory(
+        &gcm::FakeGCMProfileService::Build);
+    ServiceWorkerTest::SetUp();
+  }
+
   void SetUpOnMainThread() override {
     NotificationDisplayServiceFactory::GetInstance()->SetTestingFactory(
         profile(), &StubNotificationDisplayService::FactoryForTests);
 
     gcm::FakeGCMProfileService* gcm_service =
         static_cast<gcm::FakeGCMProfileService*>(
-            gcm::GCMProfileServiceFactory::GetInstance()
-                ->SetTestingFactoryAndUse(profile(),
-                                          &gcm::FakeGCMProfileService::Build));
+            gcm::GCMProfileServiceFactory::GetForProfile(profile()));
     gcm_driver_ = static_cast<instance_id::FakeGCMDriverForInstanceID*>(
         gcm_service->driver());
     push_service_ = PushMessagingServiceFactory::GetForProfile(profile());
@@ -473,6 +478,11 @@
     ServiceWorkerTest::SetUpOnMainThread();
   }
 
+  void TearDown() override {
+    gcm::GCMProfileServiceFactory::SetGlobalTestingFactory(nullptr);
+    ServiceWorkerTest::TearDown();
+  }
+
   instance_id::FakeGCMDriverForInstanceID* gcm_driver() const {
     return gcm_driver_;
   }
diff --git a/chrome/browser/gcm/gcm_profile_service_factory.cc b/chrome/browser/gcm/gcm_profile_service_factory.cc
index 383e1d27..6b22c585 100644
--- a/chrome/browser/gcm/gcm_profile_service_factory.cc
+++ b/chrome/browser/gcm/gcm_profile_service_factory.cc
@@ -67,6 +67,9 @@
 
 }  // namespace
 
+BrowserContextKeyedServiceFactory::TestingFactoryFunction
+    GCMProfileServiceFactory::testing_factory_ = nullptr;
+
 // static
 GCMProfileService* GCMProfileServiceFactory::GetForProfile(
     content::BrowserContext* profile) {
@@ -83,6 +86,12 @@
   return base::Singleton<GCMProfileServiceFactory>::get();
 }
 
+// static
+void GCMProfileServiceFactory::SetGlobalTestingFactory(
+    BrowserContextKeyedServiceFactory::TestingFactoryFunction factory) {
+  testing_factory_ = factory;
+}
+
 GCMProfileServiceFactory::GCMProfileServiceFactory()
     : BrowserContextKeyedServiceFactory(
         "GCMProfileService",
@@ -101,6 +110,9 @@
   Profile* profile = Profile::FromBrowserContext(context);
   DCHECK(!profile->IsOffTheRecord());
 
+  if (testing_factory_)
+    return testing_factory_(context).release();
+
   scoped_refptr<base::SequencedTaskRunner> blocking_task_runner(
       base::CreateSequencedTaskRunnerWithTraits(
           {base::MayBlock(), base::TaskPriority::BEST_EFFORT,
diff --git a/chrome/browser/gcm/gcm_profile_service_factory.h b/chrome/browser/gcm/gcm_profile_service_factory.h
index d94b0539..0ff6a67 100644
--- a/chrome/browser/gcm/gcm_profile_service_factory.h
+++ b/chrome/browser/gcm/gcm_profile_service_factory.h
@@ -21,6 +21,7 @@
  public:
   static GCMProfileService* GetForProfile(content::BrowserContext* profile);
   static GCMProfileServiceFactory* GetInstance();
+  static void SetGlobalTestingFactory(TestingFactoryFunction factory);
 
  private:
   friend struct base::DefaultSingletonTraits<GCMProfileServiceFactory>;
@@ -34,6 +35,8 @@
   content::BrowserContext* GetBrowserContextToUse(
       content::BrowserContext* context) const override;
 
+  static TestingFactoryFunction testing_factory_;
+
   DISALLOW_COPY_AND_ASSIGN(GCMProfileServiceFactory);
 };
 
diff --git a/chrome/browser/policy/browser_dm_token_storage_linux_unittest.cc b/chrome/browser/policy/browser_dm_token_storage_linux_unittest.cc
index 0ed58760..b07d704e 100644
--- a/chrome/browser/policy/browser_dm_token_storage_linux_unittest.cc
+++ b/chrome/browser/policy/browser_dm_token_storage_linux_unittest.cc
@@ -217,7 +217,7 @@
   ASSERT_TRUE(base::DirectoryExists(token_dir_path_));
 
   base::FilePath tmp_file;
-  CreateTemporaryFileInDir(token_dir_path_, &tmp_file);
+  ASSERT_TRUE(base::CreateTemporaryFileInDir(token_dir_path_, &tmp_file));
 
   storage_.DeletePolicyDirectory();
 
@@ -268,7 +268,7 @@
   ASSERT_TRUE(base::DirectoryExists(token_dir_path_));
 
   base::FilePath tmp_file;
-  CreateTemporaryFileInDir(policy_dir_path_, &tmp_file);
+  ASSERT_TRUE(base::CreateTemporaryFileInDir(policy_dir_path_, &tmp_file));
 
   storage_.DeletePolicyDirectory();
 
diff --git a/chrome/browser/policy/browser_dm_token_storage_mac.h b/chrome/browser/policy/browser_dm_token_storage_mac.h
index ba27e08e..c0ba3c41 100644
--- a/chrome/browser/policy/browser_dm_token_storage_mac.h
+++ b/chrome/browser/policy/browser_dm_token_storage_mac.h
@@ -32,6 +32,7 @@
   std::string InitEnrollmentToken() override;
   std::string InitDMToken() override;
   void SaveDMToken(const std::string& token) override;
+  void DeletePolicyDirectory() override;
 
   // This should always be the last member of the class.
   base::WeakPtrFactory<BrowserDMTokenStorageMac> weak_factory_;
@@ -42,7 +43,13 @@
   FRIEND_TEST_ALL_PREFIXES(BrowserDMTokenStorageMacTest,
                            InitDMTokenWithoutDirectory);
   FRIEND_TEST_ALL_PREFIXES(BrowserDMTokenStorageMacTest, SaveDMToken);
-
+  FRIEND_TEST_ALL_PREFIXES(BrowserDMTokenStorageMacCleanupTest, Success);
+  FRIEND_TEST_ALL_PREFIXES(BrowserDMTokenStorageMacCleanupTest,
+                           TokenDirNotEmpty);
+  FRIEND_TEST_ALL_PREFIXES(BrowserDMTokenStorageMacCleanupTest,
+                           TokenDirNotExist);
+  FRIEND_TEST_ALL_PREFIXES(BrowserDMTokenStorageMacCleanupTest,
+                           TokenDirIsNotDir);
   DISALLOW_COPY_AND_ASSIGN(BrowserDMTokenStorageMac);
 };
 
diff --git a/chrome/browser/policy/browser_dm_token_storage_mac.mm b/chrome/browser/policy/browser_dm_token_storage_mac.mm
index e21673a..d7f8e37 100644
--- a/chrome/browser/policy/browser_dm_token_storage_mac.mm
+++ b/chrome/browser/policy/browser_dm_token_storage_mac.mm
@@ -190,4 +190,17 @@
                      weak_factory_.GetWeakPtr()));
 }
 
+void BrowserDMTokenStorageMac::DeletePolicyDirectory() {
+  base::FilePath token_file_path;
+  std::string dummy_id = "id";
+  if (!GetDmTokenFilePath(&token_file_path, dummy_id, /* create_dir = */ false))
+    return;
+
+  base::FilePath token_dir_path = token_file_path.DirName();
+  if (base::DirectoryExists(token_dir_path) &&
+      base::IsDirectoryEmpty(token_dir_path)) {
+    base::DeleteFile(token_dir_path, /* recursive = */ false);
+  }
+}
+
 }  // namespace policy
diff --git a/chrome/browser/policy/browser_dm_token_storage_mac_unittest.cc b/chrome/browser/policy/browser_dm_token_storage_mac_unittest.cc
index e2cb21b..a9e5faa5 100644
--- a/chrome/browser/policy/browser_dm_token_storage_mac_unittest.cc
+++ b/chrome/browser/policy/browser_dm_token_storage_mac_unittest.cc
@@ -29,21 +29,19 @@
 namespace {
 
 const char kDmTokenBaseDir[] =
-    FILE_PATH_LITERAL("Google/Chrome Cloud Enrollment/");
+    FILE_PATH_LITERAL("Google/Chrome Cloud Enrollment");
 
 constexpr char kDMToken[] = "fake-dm-token";
 
 }  // namespace
 
-class MockBrowserDMTokenStorageMac : public BrowserDMTokenStorageMac {};
-
 class BrowserDMTokenStorageMacTest : public testing::Test {
  private:
   content::TestBrowserThreadBundle thread_bundle_;
 };
 
 TEST_F(BrowserDMTokenStorageMacTest, InitClientId) {
-  MockBrowserDMTokenStorageMac storage;
+  BrowserDMTokenStorageMac storage;
   EXPECT_FALSE(storage.InitClientId().empty());
 }
 
@@ -76,14 +74,14 @@
 
 TEST_F(BrowserDMTokenStorageMacTest, SaveDMToken) {
   std::unique_ptr<base::ScopedPathOverride> path_override;
-  base::ScopedTempDir fake_user_data_dir;
+  base::ScopedTempDir fake_app_data_dir;
 
-  ASSERT_TRUE(fake_user_data_dir.CreateUniqueTempDir());
+  ASSERT_TRUE(fake_app_data_dir.CreateUniqueTempDir());
   path_override.reset(new base::ScopedPathOverride(
-      base::DIR_APP_DATA, fake_user_data_dir.GetPath()));
+      base::DIR_APP_DATA, fake_app_data_dir.GetPath()));
 
   TestStoreDMTokenDelegate delegate;
-  MockBrowserDMTokenStorageMac storage;
+  BrowserDMTokenStorageMac storage;
   storage.StoreDMToken(
       kDMToken, base::BindOnce(&TestStoreDMTokenDelegate::OnDMTokenStored,
                                base::Unretained(&delegate)));
@@ -124,4 +122,72 @@
   EXPECT_FALSE(base::PathExists(dm_token_dir_path));
 }
 
+class BrowserDMTokenStorageMacCleanupTest : public testing::Test {
+ protected:
+  BrowserDMTokenStorageMacCleanupTest() = default;
+  ~BrowserDMTokenStorageMacCleanupTest() override = default;
+
+  void SetUp() override {
+    ASSERT_TRUE(fake_app_data_dir_.CreateUniqueTempDir());
+    path_override_.reset(new base::ScopedPathOverride(
+        base::DIR_APP_DATA, fake_app_data_dir_.GetPath()));
+
+    token_dir_path_ = fake_app_data_dir_.GetPath().Append(kDmTokenBaseDir);
+  }
+
+  base::ScopedTempDir fake_app_data_dir_;
+  base::FilePath token_dir_path_;
+  BrowserDMTokenStorageMac storage_;
+  std::unique_ptr<base::ScopedPathOverride> path_override_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(BrowserDMTokenStorageMacCleanupTest);
+};
+
+TEST_F(BrowserDMTokenStorageMacCleanupTest, Success) {
+  ASSERT_TRUE(base::CreateDirectory(token_dir_path_));
+
+  ASSERT_TRUE(base::DirectoryExists(token_dir_path_));
+
+  storage_.DeletePolicyDirectory();
+
+  EXPECT_FALSE(base::PathExists(token_dir_path_));
+  EXPECT_TRUE(base::DirectoryExists(token_dir_path_.DirName()));
+}
+
+TEST_F(BrowserDMTokenStorageMacCleanupTest, TokenDirNotEmpty) {
+  ASSERT_TRUE(base::CreateDirectory(token_dir_path_));
+
+  ASSERT_TRUE(base::DirectoryExists(token_dir_path_));
+
+  base::FilePath tmp_file;
+  ASSERT_TRUE(base::CreateTemporaryFileInDir(token_dir_path_, &tmp_file));
+
+  storage_.DeletePolicyDirectory();
+
+  EXPECT_TRUE(base::DirectoryExists(token_dir_path_));
+}
+
+TEST_F(BrowserDMTokenStorageMacCleanupTest, TokenDirNotExist) {
+  ASSERT_FALSE(base::DirectoryExists(token_dir_path_));
+
+  storage_.DeletePolicyDirectory();
+
+  EXPECT_FALSE(base::PathExists(token_dir_path_));
+  EXPECT_FALSE(base::PathExists(token_dir_path_.DirName()));
+}
+
+TEST_F(BrowserDMTokenStorageMacCleanupTest, TokenDirIsNotDir) {
+  ASSERT_TRUE(base::CreateDirectory(token_dir_path_.DirName()));
+  ASSERT_TRUE(base::DirectoryExists(token_dir_path_.DirName()));
+
+  ASSERT_TRUE(base::CloseFile(base::OpenFile(token_dir_path_, "w")));
+
+  ASSERT_TRUE(base::PathExists(token_dir_path_));
+
+  storage_.DeletePolicyDirectory();
+
+  EXPECT_TRUE(base::PathExists(token_dir_path_));
+}
+
 }  // namespace policy
diff --git a/chrome/browser/profiles/profile_io_data.cc b/chrome/browser/profiles/profile_io_data.cc
index 55a9e2a7a..3af2f77 100644
--- a/chrome/browser/profiles/profile_io_data.cc
+++ b/chrome/browser/profiles/profile_io_data.cc
@@ -18,7 +18,6 @@
 #include "base/files/file_util.h"
 #include "base/logging.h"
 #include "base/macros.h"
-#include "base/path_service.h"
 #include "base/single_thread_task_runner.h"
 #include "base/stl_util.h"
 #include "base/strings/string_number_conversions.h"
@@ -47,7 +46,6 @@
 #include "chrome/common/buildflags.h"
 #include "chrome/common/chrome_constants.h"
 #include "chrome/common/chrome_features.h"
-#include "chrome/common/chrome_paths.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/common/url_constants.h"
@@ -210,78 +208,6 @@
   }
 };
 
-#if BUILDFLAG(DEBUG_DEVTOOLS)
-bool IsSupportedDevToolsURL(const GURL& url, base::FilePath* path) {
-  std::string bundled_path_prefix(chrome::kChromeUIDevToolsBundledPath);
-  bundled_path_prefix = "/" + bundled_path_prefix + "/";
-
-  if (!url.SchemeIs(content::kChromeDevToolsScheme) ||
-      url.host_piece() != chrome::kChromeUIDevToolsHost ||
-      !base::StartsWith(url.path_piece(), bundled_path_prefix,
-                        base::CompareCase::INSENSITIVE_ASCII)) {
-    return false;
-  }
-
-  if (!url.is_valid()) {
-    NOTREACHED();
-    return false;
-  }
-
-  // Remove Query and Ref from URL.
-  GURL stripped_url;
-  GURL::Replacements replacements;
-  replacements.ClearQuery();
-  replacements.ClearRef();
-  stripped_url = url.ReplaceComponents(replacements);
-
-  std::string relative_path;
-  const std::string& spec = stripped_url.possibly_invalid_spec();
-  const url::Parsed& parsed = stripped_url.parsed_for_possibly_invalid_spec();
-  int offset = parsed.CountCharactersBefore(url::Parsed::PATH, false);
-  if (offset < static_cast<int>(spec.size()))
-    relative_path.assign(spec.substr(offset + bundled_path_prefix.length()));
-
-  // Check that |relative_path| is not an absolute path (otherwise
-  // AppendASCII() will DCHECK).  The awkward use of StringType is because on
-  // some systems FilePath expects a std::string, but on others a std::wstring.
-  base::FilePath p(
-      base::FilePath::StringType(relative_path.begin(), relative_path.end()));
-  if (p.IsAbsolute())
-    return false;
-
-  base::FilePath inspector_debug_dir;
-  if (!base::PathService::Get(chrome::DIR_INSPECTOR_DEBUG,
-                              &inspector_debug_dir))
-    return false;
-
-  DCHECK(!inspector_debug_dir.empty());
-
-  *path = inspector_debug_dir.AppendASCII(relative_path);
-  return true;
-}
-
-class DebugDevToolsInterceptor : public net::URLRequestInterceptor {
- public:
-  // net::URLRequestInterceptor implementation.
-  net::URLRequestJob* MaybeInterceptRequest(
-      net::URLRequest* request,
-      net::NetworkDelegate* network_delegate) const override {
-    base::FilePath path;
-    if (IsSupportedDevToolsURL(request->url(), &path)) {
-      net::URLRequestFileJob* job = new net::URLRequestFileJob(
-          request, network_delegate, path,
-          base::CreateTaskRunnerWithTraits(
-              {base::MayBlock(), base::TaskPriority::USER_VISIBLE,
-               base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN}));
-      job->ShouldServeMimeTypeAsContentTypeHeader();
-      return job;
-    }
-
-    return NULL;
-  }
-};
-#endif  // BUILDFLAG(DEBUG_DEVTOOLS)
-
 #if defined(OS_CHROMEOS)
 // The following four functions are responsible for initializing NSS for each
 // profile on ChromeOS, which has a separate NSS database and TPM slot
@@ -1257,10 +1183,6 @@
       url::kFtpScheme, net::FtpProtocolHandler::Create(host_resolver));
 #endif  // !BUILDFLAG(DISABLE_FTP_SUPPORT)
 
-#if BUILDFLAG(DEBUG_DEVTOOLS)
-  request_interceptors.push_back(std::make_unique<DebugDevToolsInterceptor>());
-#endif
-
   // Set up interceptors in the reverse order.
   std::unique_ptr<net::URLRequestJobFactory> top_job_factory =
       std::move(job_factory);
@@ -1314,10 +1236,6 @@
       url::kAboutScheme,
       std::make_unique<about_handler::AboutProtocolHandler>());
 
-#if BUILDFLAG(DEBUG_DEVTOOLS)
-  request_interceptors.push_back(std::make_unique<DebugDevToolsInterceptor>());
-#endif
-
   builder->SetInterceptors(std::move(request_interceptors));
 
   if (protocol_handler_interceptor) {
diff --git a/chrome/browser/push_messaging/push_messaging_browsertest.cc b/chrome/browser/push_messaging/push_messaging_browsertest.cc
index d3aae92..b73e784cd 100644
--- a/chrome/browser/push_messaging/push_messaging_browsertest.cc
+++ b/chrome/browser/push_messaging/push_messaging_browsertest.cc
@@ -137,6 +137,9 @@
     https_server_->ServeFilesFromSourceDirectory("chrome/test/data");
     ASSERT_TRUE(https_server_->Start());
 
+    gcm::GCMProfileServiceFactory::SetGlobalTestingFactory(
+        &gcm::FakeGCMProfileService::Build);
+
     SiteEngagementScore::SetParamValuesForTesting();
     InProcessBrowserTest::SetUp();
   }
@@ -148,11 +151,13 @@
 
   // InProcessBrowserTest:
   void SetUpOnMainThread() override {
-    gcm_service_ = static_cast<gcm::FakeGCMProfileService*>(
-        gcm::GCMProfileServiceFactory::GetInstance()->SetTestingFactoryAndUse(
-            GetBrowser()->profile(), &gcm::FakeGCMProfileService::Build));
-    gcm_driver_ = static_cast<instance_id::FakeGCMDriverForInstanceID*>(
-        gcm_service_->driver());
+    KeyedService* keyed_service =
+        gcm::GCMProfileServiceFactory::GetForProfile(GetBrowser()->profile());
+    if (keyed_service) {
+      gcm_service_ = static_cast<gcm::FakeGCMProfileService*>(keyed_service);
+      gcm_driver_ = static_cast<instance_id::FakeGCMDriverForInstanceID*>(
+          gcm_service_->driver());
+    }
 
     notification_tester_ = std::make_unique<NotificationDisplayServiceTester>(
         GetBrowser()->profile());
@@ -163,6 +168,11 @@
     LoadTestPage();
   }
 
+  void TearDown() override {
+    gcm::GCMProfileServiceFactory::SetGlobalTestingFactory(nullptr);
+    InProcessBrowserTest::TearDown();
+  }
+
   void TearDownOnMainThread() override {
     notification_tester_.reset();
     InProcessBrowserTest::TearDownOnMainThread();
diff --git a/chrome/browser/resources/chromeos/chromevox/cvox2/background/background_test.extjs b/chrome/browser/resources/chromeos/chromevox/cvox2/background/background_test.extjs
index 6e6b75c..2171d17 100644
--- a/chrome/browser/resources/chromeos/chromevox/cvox2/background/background_test.extjs
+++ b/chrome/browser/resources/chromeos/chromevox/cvox2/background/background_test.extjs
@@ -1361,7 +1361,8 @@
   });
 });
 
-TEST_F('BackgroundTest', 'TextSelectionAndLiveRegion', function() {
+// Failing consistently, please see https://crbug.com/857382
+TEST_F('BackgroundTest', 'DISABLED_TextSelectionAndLiveRegion', function() {
   DesktopAutomationHandler.announceActions = true;
   var mockFeedback = this.createMockFeedback();
   this.runWithLoadedTree(function(root) {/*!
diff --git a/chrome/browser/resources/md_extensions/runtime_host_permissions.html b/chrome/browser/resources/md_extensions/runtime_host_permissions.html
index 358b8d5..d2bb9e6 100644
--- a/chrome/browser/resources/md_extensions/runtime_host_permissions.html
+++ b/chrome/browser/resources/md_extensions/runtime_host_permissions.html
@@ -105,7 +105,9 @@
       <extensions-runtime-hosts-dialog
           delegate="[[delegate]]" item-id="[[itemId]]"
           current-site="[[hostDialogModel_]]"
-          on-close="onHostDialogClose_">
+          update-host-access="[[dialogShouldUpdateHostAccess_(oldHostAccess_)]]"
+          on-close="onHostDialogClose_"
+          on-cancel="onHostDialogCancel_">
       </extensions-runtime-hosts-dialog>
     </template>
   </template>
diff --git a/chrome/browser/resources/md_extensions/runtime_host_permissions.js b/chrome/browser/resources/md_extensions/runtime_host_permissions.js
index c5b1d0ec..0463c047 100644
--- a/chrome/browser/resources/md_extensions/runtime_host_permissions.js
+++ b/chrome/browser/resources/md_extensions/runtime_host_permissions.js
@@ -71,6 +71,17 @@
       },
 
       /**
+       * The old host access setting; used when we don't immediately commit the
+       * change to host access so that we can reset it if the user cancels.
+       * @type {?string}
+       * @private
+       */
+      oldHostAccess_: {
+        type: String,
+        value: null,
+      },
+
+      /**
        * Proxying the enum to be used easily by the html template.
        * @private
        */
@@ -88,12 +99,23 @@
       const select = /** @type {!HTMLSelectElement} */ (event.target);
       const access =
           /** @type {chrome.developerPrivate.HostAccess} */ (select.value);
-      this.delegate.setItemHostAccess(this.itemId, access);
-      // Force the UI to update (in order to potentially hide or show the
-      // specific runtime hosts).
-      // TODO(devlin): Perhaps this should be handled by the backend updating
-      // and sending an onItemStateChanged event?
-      this.set('permissions.hostAccess', access);
+
+      if (access == chrome.developerPrivate.HostAccess.ON_SPECIFIC_SITES &&
+          (!this.permissions.runtimeHostPermissions ||
+           this.permissions.runtimeHostPermissions.length == 0)) {
+        // If the user is transitioning to the "on specific sites" option, show
+        // the "add host" dialog. This serves two purposes:
+        // - The user is prompted to add a host immediately, since otherwise
+        //   "on specific sites" is meaningless, and
+        // - The way the C++ code differentiates between "on click" and "on
+        //   specific sites" is by checking if there are any specific sites.
+        //   This ensures there will be at least one, so that the host access
+        //   is properly calculated.
+        this.oldHostAccess_ = assert(this.permissions.hostAccess);
+        this.doShowHostDialog_(select, null);
+      } else {
+        this.delegate.setItemHostAccess(this.itemId, access);
+      }
     },
 
     /**
@@ -137,6 +159,25 @@
       this.hostDialogAnchorElement_ = null;
     },
 
+    /** @private */
+    onHostDialogCancel_: function() {
+      // The user canceled the dialog. Set host-access back to the old value,
+      // if the dialog was shown when just transitioning to a new state.
+      if (this.oldHostAccess_) {
+        assert(this.permissions.hostAccess == this.oldHostAccess_);
+        this.$['host-access'].value = this.oldHostAccess_;
+        this.oldHostAccess_ = null;
+      }
+    },
+
+    /**
+     * @return {boolean}
+     * @private
+     */
+    dialogShouldUpdateHostAccess_: function() {
+      return !!this.oldHostAccess_;
+    },
+
     /**
      * @param {!{
      *   model: !{item: string},
diff --git a/chrome/browser/resources/md_extensions/runtime_hosts_dialog.js b/chrome/browser/resources/md_extensions/runtime_hosts_dialog.js
index ae100a0..32e0551 100644
--- a/chrome/browser/resources/md_extensions/runtime_hosts_dialog.js
+++ b/chrome/browser/resources/md_extensions/runtime_hosts_dialog.js
@@ -59,6 +59,15 @@
       },
 
       /**
+       * Whether the dialog should update the host access to be "on specific
+       * sites" before adding a new host permission.
+       */
+      updateHostAccess: {
+        type: Boolean,
+        value: false,
+      },
+
+      /**
        * The site to add an exception for.
        * @private
        */
@@ -83,6 +92,11 @@
       this.$.dialog.showModal();
     },
 
+    /** @return {boolean} */
+    isOpen: function() {
+      return this.$.dialog.open;
+    },
+
     /**
      * Validates that the pattern entered is valid.
      * @private
@@ -138,25 +152,53 @@
      * @private
      */
     onSubmitTap_: function() {
-      if (this.currentSite !== null) {
-        // No change in values, so no need to update the delegate.
-        if (this.currentSite == this.site_) {
-          this.$.dialog.close();
-          return;
-        }
+      if (this.currentSite !== null)
+        this.handleEdit_();
+      else
+        this.handleAdd_();
+    },
 
-        // Changing the entry is done through a remove followed by an add.
-        this.delegate.removeRuntimeHostPermission(this.itemId, this.currentSite)
-            .then(() => {
-              this.addPermission_();
-            });
-        return;
+    /**
+     * Handles adding a new site entry.
+     * @private
+     */
+    handleAdd_: function() {
+      assert(!this.currentSite);
+
+      if (this.updateHostAccess) {
+        this.delegate.setItemHostAccess(
+            this.itemId, chrome.developerPrivate.HostAccess.ON_SPECIFIC_SITES);
       }
 
       this.addPermission_();
     },
 
     /**
+     * Handles editing an existing site entry.
+     * @private
+     */
+    handleEdit_: function() {
+      assert(this.currentSite);
+      assert(
+          !this.updateHostAccess,
+          'Editing host permissions should only be possible if the host ' +
+              'access is already set to specific sites.');
+
+      if (this.currentSite == this.site_) {
+        // No change in values, so no need to update anything.
+        this.$.dialog.close();
+        return;
+      }
+
+      // Editing an existing entry is done by removing the current site entry,
+      // and then adding the new one.
+      this.delegate.removeRuntimeHostPermission(this.itemId, this.currentSite)
+          .then(() => {
+            this.addPermission_();
+          });
+    },
+
+    /**
      * Adds the runtime host permission through the delegate. If successful,
      * closes the dialog; otherwise displays the invalid input message.
      * @private
diff --git a/chrome/browser/resources/print_preview/new/app.html b/chrome/browser/resources/print_preview/new/app.html
index 964e5a1..f8cce708 100644
--- a/chrome/browser/resources/print_preview/new/app.html
+++ b/chrome/browser/resources/print_preview/new/app.html
@@ -72,21 +72,6 @@
         background-color: var(--google-grey-200);
         flex: 1;
       }
-
-      #cr-container-shadow {
-        background-color: rgb(232, 234, 237);
-        height: 1px;
-        margin-bottom: -1px;
-        opacity: 0.3;
-      }
-
-      #cr-container-shadow.has-shadow {
-        background-color: white;
-        box-shadow: inset 0 1px 2px 0 rgba(60, 64, 67, .3),
-                    inset 0 1px 3px -1px rgba(60, 64, 67, .15);
-        height: 3px;
-        margin-bottom: -3px;
-      }
     </style>
     <print-preview-state id="state" state="{{state}}"></print-preview-state>
     <print-preview-model id="model" settings="{{settings}}"
diff --git a/chrome/browser/resources/print_preview/new/header.html b/chrome/browser/resources/print_preview/new/header.html
index f6dedd3..74c5dd7b 100644
--- a/chrome/browser/resources/print_preview/new/header.html
+++ b/chrome/browser/resources/print_preview/new/header.html
@@ -16,6 +16,7 @@
     <style include="print-preview-shared paper-button-style">
       :host {
         background-color: white;
+        border-bottom: var(--print-preview-settings-border);
         display: block;
         padding-bottom: 8px;
         padding-inline-end: 16px;
diff --git a/chrome/browser/resources/print_preview/new/link_container.html b/chrome/browser/resources/print_preview/new/link_container.html
index cf52733..9a49030 100644
--- a/chrome/browser/resources/print_preview/new/link_container.html
+++ b/chrome/browser/resources/print_preview/new/link_container.html
@@ -29,7 +29,6 @@
 
       #systemDialogLink {
         border-top: var(--print-preview-settings-border);
-        margin-top: -15px;
       }
 
       .label {
diff --git a/chrome/browser/resources/print_preview/new/more_settings.html b/chrome/browser/resources/print_preview/new/more_settings.html
index fd726639..ff7f682 100644
--- a/chrome/browser/resources/print_preview/new/more_settings.html
+++ b/chrome/browser/resources/print_preview/new/more_settings.html
@@ -25,7 +25,7 @@
         border: none;
         border-top: var(--print-preview-settings-border);
         font: inherit;
-        margin: 0 auto 16px;
+        margin: 0 auto;
         min-height: 48px;
         padding: 0 18px;
         width: 100%;
diff --git a/chrome/browser/resources/print_preview/new/other_options_settings.html b/chrome/browser/resources/print_preview/new/other_options_settings.html
index 66748a1..9584980db 100644
--- a/chrome/browser/resources/print_preview/new/other_options_settings.html
+++ b/chrome/browser/resources/print_preview/new/other_options_settings.html
@@ -2,6 +2,7 @@
 
 <link rel="import" href="chrome://resources/cr_elements/hidden_style_css.html">
 <link rel="import" href="chrome://resources/cr_elements/cr_checkbox/cr_checkbox.html">
+<link rel="import" href="chrome://resources/html/i18n_behavior.html">
 <link rel="import" href="print_preview_shared_css.html">
 <link rel="import" href="settings_behavior.html">
 <link rel="import" href="settings_section.html">
@@ -9,47 +10,27 @@
 <dom-module id="print-preview-other-options-settings">
   <template>
     <style include="print-preview-shared cr-hidden-style">
-      #controls {
-        height: 100%;
+      print-preview-settings-section {
+        margin-bottom: 0;
       }
     </style>
-    <print-preview-settings-section class="multirow-controls">
-      <span slot="title" id="options-label">$i18n{optionsLabel}</span>
-      <div slot="controls" class="checkbox" id="controls">
-        <cr-checkbox id="headerFooter"
-            hidden$="[[!settings.headerFooter.available]]"
-            disabled$="[[headerFooterCheckboxDisabled_]]"
-            on-change="onHeaderFooterChange_"
-            checked$="[[settings.headerFooter.value]]" aria-live="polite">
-          $i18n{optionHeaderFooter}
-        </cr-checkbox>
-        <cr-checkbox id="duplex" on-change="onDuplexChange_"
-            hidden$="[[!settings.duplex.available]]" disabled$="[[disabled]]"
-            checked$="[[settings.duplex.value]]" aria-live="polite">
-          $i18n{optionTwoSided}
-        </cr-checkbox>
-        <cr-checkbox id="cssBackground"
-            hidden$="[[!settings.cssBackground.available]]"
-            on-change="onCssBackgroundChange_"
-            disabled$="[[disabled]]"
-            checked$="[[settings.cssBackground.value]]" aria-live="polite">
-          $i18n{optionBackgroundColorsAndImages}
-        </cr-checkbox>
-        <cr-checkbox id="rasterize"
-            hidden$="[[!settings.rasterize.available]]"
-            disabled$="[[disabled]]" on-change="onRasterizeChange_"
-            checked$="[[settings.rasterize.value]]" aria-live="polite">
-          $i18n{optionRasterize}
-        </cr-checkbox>
-        <cr-checkbox id="selectionOnly"
-            hidden$="[[!settings.selectionOnly.available]]"
-            disabled$="[[disabled]]"
-            on-change="onSelectionOnlyChange_"
-            checked$="[[settings.selectionOnly.value]]" aria-live="polite">
-          $i18n{optionSelectionOnly}
-        </cr-checkbox>
-      </div>
-    </print-preview-settings-section>
+    <template is="dom-repeat" items="[[options_]]">
+      <print-preview-settings-section managed="[[item.managed]]"
+          hidden$="[[!item.available]]" class="checkbox">
+        <div slot="title">
+          <span hidden$="[[isTitleHidden_(index, titleIndex_)]]">
+            $i18n{optionsLabel}
+          </span>
+        </div>
+        <div slot="controls" class="checkbox">
+          <cr-checkbox slot="controls" id$="[[item.name]]"
+              disabled$="[[getDisabled_(item.managed, disabled)]]"
+              on-change="onChange_" checked$="[[item.value]]">
+            <span>[[i18n(item.label)]]</span>
+          </cr-checkbox>
+        </div>
+      </print-preview-settings-section>
+    </template>
   </template>
   <script src="other_options_settings.js"></script>
 </dom-module>
diff --git a/chrome/browser/resources/print_preview/new/other_options_settings.js b/chrome/browser/resources/print_preview/new/other_options_settings.js
index 4b08539..3881149 100644
--- a/chrome/browser/resources/print_preview/new/other_options_settings.js
+++ b/chrome/browser/resources/print_preview/new/other_options_settings.js
@@ -5,25 +5,47 @@
 Polymer({
   is: 'print-preview-other-options-settings',
 
-  behaviors: [SettingsBehavior],
+  behaviors: [SettingsBehavior, I18nBehavior],
 
   properties: {
     disabled: Boolean,
 
-    /** @private */
-    headerFooterCheckboxDisabled_: {
-      type: Boolean,
-      computed: 'computeHeaderFooterCheckboxDisabled_(disabled, ' +
-          'settings.headerFooter.setByPolicy)'
+    /**
+     * @private {!Array<!{name: string,
+     *                    label: string,
+     *                    value: (boolean | undefined),
+     *                    managed: (boolean | undefined),
+     *                    available: (boolean | undefined)}>}
+     */
+    options_: {
+      type: Array,
+      value: function() {
+        return [
+          {name: 'headerFooter', label: 'optionHeaderFooter'},
+          {name: 'duplex', label: 'optionTwoSided'},
+          {name: 'cssBackground', label: 'optionBackgroundColorsAndImages'},
+          {name: 'rasterize', label: 'optionRasterize'},
+          {name: 'selectionOnly', label: 'optionSelectionOnly'},
+        ];
+      },
+    },
+
+    /**
+     * The index of the checkbox that should display the "Options" title.
+     * @private {number}
+     */
+    titleIndex_: {
+      type: Number,
+      value: 0,
     },
   },
 
   observers: [
-    'onHeaderFooterSettingChange_(settings.headerFooter.value)',
-    'onDuplexSettingChange_(settings.duplex.value)',
-    'onCssBackgroundSettingChange_(settings.cssBackground.value)',
-    'onRasterizeSettingChange_(settings.rasterize.value)',
-    'onSelectionOnlySettingChange_(settings.selectionOnly.value)',
+    'onHeaderFooterSettingChange_(settings.headerFooter.*)',
+    'onDuplexSettingChange_(settings.duplex.*)',
+    'onCssBackgroundSettingChange_(settings.cssBackground.*)',
+    'onRasterizeSettingChange_(settings.rasterize.*)',
+    'onSelectionOnlySettingChange_(settings.selectionOnly.*)',
   ],
 
   /** @private {!Map<string, ?number>} */
@@ -54,79 +76,67 @@
   },
 
   /**
-   * @param {boolean} globallyDisabled Value of the |disabled| property.
-   * @param {boolean} setByPolicy Value of |settings.headerFooter.setByPolicy|.
-   * @return {boolean} New value for |headerFooterCheckboxDisabled_|.
+   * @param {number} index The index of the option to update.
    * @private
    */
-  computeHeaderFooterCheckboxDisabled_(globallyDisabled, setByPolicy) {
-    return globallyDisabled || setByPolicy;
+  updateOptionFromSetting_: function(index) {
+    const setting = this.getSetting(this.options_[index].name);
+    this.set(`options_.${index}.available`, setting.available);
+    this.set(`options_.${index}.value`, setting.value);
+    this.set(`options_.${index}.managed`, setting.setByPolicy);
+    this.titleIndex_ = this.options_.findIndex(option => !!option.available);
   },
 
   /**
-   * @param {boolean} value The new value of the header footer setting.
+   * @param {boolean} managed Whether the setting is managed by policy.
+   * @param {boolean} disabled value of this.disabled
+   * @return {boolean} Whether the checkbox should be disabled.
    * @private
    */
-  onHeaderFooterSettingChange_: function(value) {
-    this.$.headerFooter.checked = value;
+  getDisabled_: function(managed, disabled) {
+    return managed || disabled;
+  },
+
+  /** @private */
+  onHeaderFooterSettingChange_: function() {
+    this.updateOptionFromSetting_(0);
+  },
+
+  /** @private */
+  onDuplexSettingChange_: function() {
+    this.updateOptionFromSetting_(1);
+  },
+
+  /** @private */
+  onCssBackgroundSettingChange_: function() {
+    this.updateOptionFromSetting_(2);
+  },
+
+  /** @private */
+  onRasterizeSettingChange_: function() {
+    this.updateOptionFromSetting_(3);
+  },
+
+  /** @private */
+  onSelectionOnlySettingChange_: function() {
+    this.updateOptionFromSetting_(4);
   },
 
   /**
-   * @param {boolean} value The new value of the duplex setting.
+   * @param {!Event} e Contains the checkbox item that was checked.
    * @private
    */
-  onDuplexSettingChange_: function(value) {
-    this.$.duplex.checked = value;
+  onChange_: function(e) {
+    const name = e.model.item.name;
+    this.updateSettingWithTimeout_(name, this.$$(`#${name}`).checked);
   },
 
   /**
-   * @param {boolean} value The new value of the css background setting.
+   * @param {number} index The index of the settings section.
+   * @return {boolean} Whether the title should be displayed on the section.
    * @private
    */
-  onCssBackgroundSettingChange_: function(value) {
-    this.$.cssBackground.checked = value;
-  },
-
-  /**
-   * @param {boolean} value The new value of the rasterize setting.
-   * @private
-   */
-  onRasterizeSettingChange_: function(value) {
-    this.$.rasterize.checked = value;
-  },
-
-  /**
-   * @param {boolean} value The new value of the selection only setting.
-   * @private
-   */
-  onSelectionOnlySettingChange_: function(value) {
-    this.$.selectionOnly.checked = value;
-  },
-
-  /** @private */
-  onHeaderFooterChange_: function() {
-    this.updateSettingWithTimeout_('headerFooter', this.$.headerFooter.checked);
-  },
-
-  /** @private */
-  onDuplexChange_: function() {
-    this.updateSettingWithTimeout_('duplex', this.$.duplex.checked);
-  },
-
-  /** @private */
-  onCssBackgroundChange_: function() {
-    this.updateSettingWithTimeout_(
-        'cssBackground', this.$.cssBackground.checked);
-  },
-
-  /** @private */
-  onRasterizeChange_: function() {
-    this.updateSettingWithTimeout_('rasterize', this.$.rasterize.checked);
-  },
-
-  /** @private */
-  onSelectionOnlyChange_: function() {
-    this.updateSettingWithTimeout_(
-        'selectionOnly', this.$.selectionOnly.checked);
+  isTitleHidden_: function(index) {
+    return index !== this.titleIndex_;
   },
 });
diff --git a/chrome/browser/search/instant_service.cc b/chrome/browser/search/instant_service.cc
index d005d59e..4b054d8 100644
--- a/chrome/browser/search/instant_service.cc
+++ b/chrome/browser/search/instant_service.cc
@@ -420,14 +420,24 @@
                              const content::NotificationSource& source,
                              const content::NotificationDetails& details) {
   switch (type) {
-    case content::NOTIFICATION_RENDERER_PROCESS_CREATED:
-      SendNewTabPageURLToRenderer(
-          content::Source<content::RenderProcessHost>(source).ptr());
+    case content::NOTIFICATION_RENDERER_PROCESS_CREATED: {
+      content::RenderProcessHost* rph =
+          content::Source<content::RenderProcessHost>(source).ptr();
+      Profile* renderer_profile =
+          static_cast<Profile*>(rph->GetBrowserContext());
+      if (profile_ == renderer_profile)
+        SendNewTabPageURLToRenderer(rph);
       break;
-    case content::NOTIFICATION_RENDERER_PROCESS_TERMINATED:
-      OnRendererProcessTerminated(
-          content::Source<content::RenderProcessHost>(source)->GetID());
+    }
+    case content::NOTIFICATION_RENDERER_PROCESS_TERMINATED: {
+      content::RenderProcessHost* rph =
+          content::Source<content::RenderProcessHost>(source).ptr();
+      Profile* renderer_profile =
+          static_cast<Profile*>(rph->GetBrowserContext());
+      if (profile_ == renderer_profile)
+        OnRendererProcessTerminated(rph->GetID());
       break;
+    }
     case chrome::NOTIFICATION_BROWSER_THEME_CHANGED:
       BuildThemeInfo();
       NotifyAboutThemeInfo();
diff --git a/chrome/browser/signin/chrome_signin_client_unittest.cc b/chrome/browser/signin/chrome_signin_client_unittest.cc
index 8c1af10e..6dfd70a5 100644
--- a/chrome/browser/signin/chrome_signin_client_unittest.cc
+++ b/chrome/browser/signin/chrome_signin_client_unittest.cc
@@ -60,9 +60,7 @@
 
 class ChromeSigninClientTest : public testing::Test {
  public:
-  ChromeSigninClientTest() {}
-
-  void SetUp() override {
+  ChromeSigninClientTest() {
     // Create a signed-in profile.
     TestingProfile::Builder builder;
     profile_ = builder.Build();
diff --git a/chrome/browser/ui/app_list/search/search_result_ranker/app_search_result_ranker_unittest.cc b/chrome/browser/ui/app_list/search/search_result_ranker/app_search_result_ranker_unittest.cc
index 9d963895..de22dfd 100644
--- a/chrome/browser/ui/app_list/search/search_result_ranker/app_search_result_ranker_unittest.cc
+++ b/chrome/browser/ui/app_list/search/search_result_ranker/app_search_result_ranker_unittest.cc
@@ -172,7 +172,8 @@
   EXPECT_TRUE(ranker.Rank().empty());
 }
 
-TEST_F(AppSearchResultRankerSerializationTest, SaveToDiskSucceed) {
+// Test is flaky. See https://crbug.com/884140
+TEST_F(AppSearchResultRankerSerializationTest, DISABLED_SaveToDiskSucceed) {
   // Construct ranker.
   AppSearchResultRanker ranker(temp_dir_.GetPath(), kNotAnEphemeralUser);
   // Wait for the loading to finish.
diff --git a/chrome/browser/ui/ash/assistant/web_contents_manager.cc b/chrome/browser/ui/ash/assistant/web_contents_manager.cc
index 0be7c5a..4ed81ca 100644
--- a/chrome/browser/ui/ash/assistant/web_contents_manager.cc
+++ b/chrome/browser/ui/ash/assistant/web_contents_manager.cc
@@ -26,6 +26,11 @@
 
 // ManagedWebContents ----------------------------------------------------------
 
+// This class wraps a WebContents and associated view. ManagedWebContents are
+// owned/managed by WebContentsManager on behalf of embedders in other code
+// areas/processes that cannot depend directly on chrome/browser. Instances are
+// created/destroyed via calls to WebContentsManager::ManageWebContents and
+// WebContentsManager::ReleaseWebContents respectively.
 class ManagedWebContents : public content::WebContentsDelegate,
                            public content::WebContentsObserver {
  public:
@@ -66,22 +71,30 @@
     web_view_->SetPreferredSize(new_size);
   }
 
+  bool ShouldCreateWebContents(
+      content::WebContents* web_contents,
+      content::RenderFrameHost* opener,
+      content::SiteInstance* source_site_instance,
+      int32_t route_id,
+      int32_t main_frame_route_id,
+      int32_t main_frame_widget_route_id,
+      content::mojom::WindowContainerType window_container_type,
+      const GURL& opener_url,
+      const std::string& frame_name,
+      const GURL& target_url,
+      const std::string& partition_id,
+      content::SessionStorageNamespace* session_storage_namespace) override {
+    // This is fired when trying to open links in a new tab, e.g.:
+    // <a href="https://www.google.com/" target="_blank">Link</a>
+    HandleNavigationAttempt(target_url,
+                            WindowOpenDisposition::NEW_FOREGROUND_TAB);
+    return false;
+  }
+
   content::WebContents* OpenURLFromTab(
       content::WebContents* source,
       const content::OpenURLParams& params) override {
-    if (!open_url_delegate_)
-      return content::WebContentsDelegate::OpenURLFromTab(source, params);
-
-    open_url_delegate_->ShouldOpenUrlFromTab(
-        params.url,
-        base::BindOnce(
-            [](base::WeakPtr<ManagedWebContents> managed_web_contents,
-               const GURL& url, bool should_open) {
-              if (should_open && managed_web_contents)
-                managed_web_contents->NavigateToUrl(url);
-            },
-            weak_factory_.GetWeakPtr(), params.url));
-
+    HandleNavigationAttempt(params.url, params.disposition);
     return nullptr;
   }
 
@@ -174,6 +187,22 @@
     }
   }
 
+  void HandleNavigationAttempt(const GURL& url,
+                               WindowOpenDisposition disposition) {
+    if (!open_url_delegate_)
+      return;
+
+    open_url_delegate_->ShouldOpenUrlFromTab(
+        url, disposition,
+        base::BindOnce(
+            [](const base::WeakPtr<ManagedWebContents>& managed_web_contents,
+               const GURL& url, bool should_open) {
+              if (should_open && managed_web_contents)
+                managed_web_contents->NavigateToUrl(url);
+            },
+            weak_factory_.GetWeakPtr(), url));
+  }
+
   ash::mojom::WebContentsManager::ManageWebContentsCallback callback_;
 
   std::unique_ptr<content::WebContents> web_contents_;
diff --git a/chrome/browser/ui/ash/system_tray_tray_cast_browsertest_media_router_chromeos.cc b/chrome/browser/ui/ash/system_tray_tray_cast_browsertest_media_router_chromeos.cc
index eb1a764..1def23f 100644
--- a/chrome/browser/ui/ash/system_tray_tray_cast_browsertest_media_router_chromeos.cc
+++ b/chrome/browser/ui/ash/system_tray_tray_cast_browsertest_media_router_chromeos.cc
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include <memory>
 #include <vector>
 
 #include "ash/public/cpp/ash_features.h"
@@ -110,6 +111,18 @@
 
  private:
   // InProcessBrowserTest:
+  void PreRunTestOnMainThread() override {
+    media_router_ = std::make_unique<media_router::MockMediaRouter>();
+    ON_CALL(*media_router_, RegisterMediaSinksObserver(_))
+        .WillByDefault(Invoke(
+            this, &SystemTrayTrayCastMediaRouterChromeOSTest::CaptureSink));
+    ON_CALL(*media_router_, RegisterMediaRoutesObserver(_))
+        .WillByDefault(Invoke(
+            this, &SystemTrayTrayCastMediaRouterChromeOSTest::CaptureRoutes));
+    CastConfigClientMediaRouter::SetMediaRouterForTest(media_router_.get());
+    InProcessBrowserTest::PreRunTestOnMainThread();
+  }
+
   void SetUpOnMainThread() override {
     InProcessBrowserTest::SetUpOnMainThread();
     // Connect to the ash test interface.
@@ -123,6 +136,11 @@
                         &ash_message_center_controller_);
   }
 
+  void PostRunTestOnMainThread() override {
+    InProcessBrowserTest::PostRunTestOnMainThread();
+    CastConfigClientMediaRouter::SetMediaRouterForTest(nullptr);
+  }
+
   bool CaptureSink(media_router::MediaSinksObserver* media_sinks_observer) {
     media_sinks_observer_ = media_sinks_observer;
     return true;
@@ -132,21 +150,7 @@
     media_routes_observer_ = media_routes_observer;
   }
 
-  void SetUpInProcessBrowserTestFixture() override {
-    ON_CALL(media_router_, RegisterMediaSinksObserver(_))
-        .WillByDefault(Invoke(
-            this, &SystemTrayTrayCastMediaRouterChromeOSTest::CaptureSink));
-    ON_CALL(media_router_, RegisterMediaRoutesObserver(_))
-        .WillByDefault(Invoke(
-            this, &SystemTrayTrayCastMediaRouterChromeOSTest::CaptureRoutes));
-    CastConfigClientMediaRouter::SetMediaRouterForTest(&media_router_);
-  }
-
-  void TearDownInProcessBrowserTestFixture() override {
-    CastConfigClientMediaRouter::SetMediaRouterForTest(nullptr);
-  }
-
-  media_router::MockMediaRouter media_router_;
+  std::unique_ptr<media_router::MockMediaRouter> media_router_;
   media_router::MediaSinksObserver* media_sinks_observer_ = nullptr;
   media_router::MediaRoutesObserver* media_routes_observer_ = nullptr;
   ash::mojom::SystemTrayTestApiPtr tray_test_api_;
diff --git a/chrome/browser/ui/cocoa/browser_dialogs_views_mac.cc b/chrome/browser/ui/cocoa/browser_dialogs_views_mac.cc
index b4a0c10..2f319cff 100644
--- a/chrome/browser/ui/cocoa/browser_dialogs_views_mac.cc
+++ b/chrome/browser/ui/cocoa/browser_dialogs_views_mac.cc
@@ -79,7 +79,7 @@
       new BookmarkBubbleSignInDelegate(browser));
 
   BookmarkBubbleView::ShowBubble(
-      nullptr, gfx::Rect(anchor_point, gfx::Size()), parent, observer,
+      nullptr, nullptr, gfx::Rect(anchor_point, gfx::Size()), parent, observer,
       std::move(delegate), browser->profile(), virtual_url, already_bookmarked);
 
   views::BubbleDialogDelegateView* bubble =
diff --git a/chrome/browser/ui/cocoa/translate/translate_bubble_bridge_views.mm b/chrome/browser/ui/cocoa/translate/translate_bubble_bridge_views.mm
index 6bd5681..bd25d9fc 100644
--- a/chrome/browser/ui/cocoa/translate/translate_bubble_bridge_views.mm
+++ b/chrome/browser/ui/cocoa/translate/translate_bubble_bridge_views.mm
@@ -24,8 +24,8 @@
   TranslateBubbleView::DisplayReason reason =
       is_user_gesture ? TranslateBubbleView::USER_GESTURE
                       : TranslateBubbleView::AUTOMATIC;
-  TranslateBubbleView::ShowBubble(nullptr, anchor_point, web_contents, step,
-                                  error_type, reason);
+  TranslateBubbleView::ShowBubble(nullptr, nullptr, anchor_point, web_contents,
+                                  step, error_type, reason);
   if (TranslateBubbleView::GetCurrentBubble()) {
     KeepBubbleAnchored(TranslateBubbleView::GetCurrentBubble(),
                        location_bar->translate_decoration());
diff --git a/chrome/browser/ui/views/bookmarks/bookmark_bubble_view.cc b/chrome/browser/ui/views/bookmarks/bookmark_bubble_view.cc
index f5c4fa3..e99debd 100644
--- a/chrome/browser/ui/views/bookmarks/bookmark_bubble_view.cc
+++ b/chrome/browser/ui/views/bookmarks/bookmark_bubble_view.cc
@@ -54,6 +54,7 @@
 // static
 views::Widget* BookmarkBubbleView::ShowBubble(
     views::View* anchor_view,
+    views::Button* highlighted_button,
     const gfx::Rect& anchor_rect,
     gfx::NativeView parent_window,
     bookmarks::BookmarkBubbleObserver* observer,
@@ -75,6 +76,8 @@
     bookmark_bubble_->SetAnchorRect(anchor_rect);
     bookmark_bubble_->set_parent_window(parent_window);
   }
+  if (highlighted_button)
+    bookmark_bubble_->SetHighlightedButton(highlighted_button);
   views::Widget* bubble_widget =
       views::BubbleDialogDelegateView::CreateBubble(bookmark_bubble_);
   bubble_widget->Show();
diff --git a/chrome/browser/ui/views/bookmarks/bookmark_bubble_view.h b/chrome/browser/ui/views/bookmarks/bookmark_bubble_view.h
index c7b2ecb4..00b781f 100644
--- a/chrome/browser/ui/views/bookmarks/bookmark_bubble_view.h
+++ b/chrome/browser/ui/views/bookmarks/bookmark_bubble_view.h
@@ -52,6 +52,7 @@
   // bubble already exists.
   static views::Widget* ShowBubble(
       views::View* anchor_view,
+      views::Button* highlighted_button,
       const gfx::Rect& anchor_rect,
       gfx::NativeView parent_window,
       bookmarks::BookmarkBubbleObserver* observer,
diff --git a/chrome/browser/ui/views/bubble_anchor_util_views.cc b/chrome/browser/ui/views/bubble_anchor_util_views.cc
index 14398ef..a2eb0082 100644
--- a/chrome/browser/ui/views/bubble_anchor_util_views.cc
+++ b/chrome/browser/ui/views/bubble_anchor_util_views.cc
@@ -8,6 +8,7 @@
 #include "chrome/browser/ui/views/frame/app_menu_button.h"
 #include "chrome/browser/ui/views/frame/browser_view.h"
 #include "chrome/browser/ui/views/location_bar/location_bar_view.h"
+#include "chrome/browser/ui/views/location_bar/location_icon_view.h"
 #include "chrome/browser/ui/views/toolbar/toolbar_view.h"
 #include "chrome/browser/ui/views_mode_controller.h"
 
@@ -25,14 +26,15 @@
   BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser);
 
   if (anchor == kLocationBar && browser_view->GetLocationBarView()->IsDrawn())
-    return {browser_view->GetLocationBarView()->GetSecurityBubbleAnchorView(),
+    return {browser_view->GetLocationBarView(),
+            browser_view->GetLocationBarView()->location_icon_view(),
             views::BubbleBorder::TOP_LEFT};
   // Fall back to menu button if no location bar present.
 
-  views::View* app_menu_button =
+  views::Button* app_menu_button =
       browser_view->toolbar_button_provider()->GetAppMenuButton();
   if (app_menu_button && app_menu_button->IsDrawn())
-    return {app_menu_button, views::BubbleBorder::TOP_RIGHT};
+    return {app_menu_button, app_menu_button, views::BubbleBorder::TOP_RIGHT};
   return {};
 }
 
diff --git a/chrome/browser/ui/views/bubble_anchor_util_views.h b/chrome/browser/ui/views/bubble_anchor_util_views.h
index f8671630..06afd4b 100644
--- a/chrome/browser/ui/views/bubble_anchor_util_views.h
+++ b/chrome/browser/ui/views/bubble_anchor_util_views.h
@@ -9,6 +9,7 @@
 #include "ui/views/bubble/bubble_border.h"
 
 namespace views {
+class Button;
 class View;
 }
 
@@ -18,11 +19,13 @@
 
 struct AnchorConfiguration {
   views::View* anchor_view = nullptr;
+  views::Button* highlighted_button = nullptr;
   views::BubbleBorder::Arrow bubble_arrow = views::BubbleBorder::TOP_LEFT;
 };
 
 // Returns:
-// - The PageInfo |anchor| View for |browser|, or null if it should not be
+// - The PageInfo |anchor| View.
+// - The view the be highlighted for |browser|, or null if it should not be
 //   used.
 // - The arrow position for the PageInfo bubble.
 AnchorConfiguration GetPageInfoAnchorConfiguration(Browser* browser,
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view.cc b/chrome/browser/ui/views/frame/browser_non_client_frame_view.cc
index ade2a2d..132ee16 100644
--- a/chrome/browser/ui/views/frame/browser_non_client_frame_view.cc
+++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view.cc
@@ -95,12 +95,16 @@
 }
 
 void BrowserNonClientFrameView::UpdateFullscreenTopUI(
-    bool is_exiting_fullscreen) {}
+    bool needs_check_tab_fullscreen) {}
 
 bool BrowserNonClientFrameView::ShouldHideTopUIForFullscreen() const {
   return frame_->IsFullscreen();
 }
 
+bool BrowserNonClientFrameView::IsFrameCondensed() const {
+  return frame_ && (frame_->IsMaximized() || frame_->IsFullscreen());
+}
+
 bool BrowserNonClientFrameView::HasClientEdge() const {
   return !MD::IsRefreshUi();
 }
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view.h b/chrome/browser/ui/views/frame/browser_non_client_frame_view.h
index 4a73c4b..50baf798 100644
--- a/chrome/browser/ui/views/frame/browser_non_client_frame_view.h
+++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view.h
@@ -82,11 +82,16 @@
 
   // Updates the top UI state to be hidden or shown in fullscreen according to
   // the preference's state. Currently only used on Mac.
-  virtual void UpdateFullscreenTopUI(bool is_exiting_fullscreen);
+  virtual void UpdateFullscreenTopUI(bool needs_check_tab_fullscreen);
 
   // Returns whether the top UI should hide.
   virtual bool ShouldHideTopUIForFullscreen() const;
 
+  // Determines whether the top frame is condensed vertically, as when the
+  // window is maximized. If true, the top frame is just the height of a tab,
+  // rather than having extra vertical space above the tabs.
+  virtual bool IsFrameCondensed() const;
+
   // Returns whether the content is painted with a client edge or not.
   virtual bool HasClientEdge() const;
 
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view_mac.h b/chrome/browser/ui/views/frame/browser_non_client_frame_view_mac.h
index 90b34cda..7ffde12 100644
--- a/chrome/browser/ui/views/frame/browser_non_client_frame_view_mac.h
+++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view_mac.h
@@ -26,7 +26,7 @@
   gfx::Rect GetBoundsForTabStrip(views::View* tabstrip) const override;
   int GetTopInset(bool restored) const override;
   int GetThemeBackgroundXInset() const override;
-  void UpdateFullscreenTopUI(bool is_exiting_fullscreen) override;
+  void UpdateFullscreenTopUI(bool needs_check_tab_fullscreen) override;
   bool ShouldHideTopUIForFullscreen() const override;
   void UpdateThrobber(bool running) override;
   int GetTabStripLeftInset() const override;
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view_mac.mm b/chrome/browser/ui/views/frame/browser_non_client_frame_view_mac.mm
index 1c34821..a05d7c0 100644
--- a/chrome/browser/ui/views/frame/browser_non_client_frame_view_mac.mm
+++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view_mac.mm
@@ -47,7 +47,7 @@
   pref_registrar_.Add(
       prefs::kShowFullscreenToolbar,
       base::BindRepeating(&BrowserNonClientFrameViewMac::UpdateFullscreenTopUI,
-                          base::Unretained(this), false));
+                          base::Unretained(this), true));
 }
 
 BrowserNonClientFrameViewMac::~BrowserNonClientFrameViewMac() {
@@ -65,7 +65,7 @@
     // Exiting tab fullscreen requires updating Top UI.
     // Called from here so we can capture exiting tab fullscreen both by
     // pressing 'ESC' key and by clicking green traffic light button.
-    UpdateFullscreenTopUI(true);
+    UpdateFullscreenTopUI(false);
     [fullscreen_toolbar_controller_ exitFullscreenMode];
   }
   browser_view()->Layout();
@@ -141,7 +141,7 @@
 }
 
 void BrowserNonClientFrameViewMac::UpdateFullscreenTopUI(
-    bool is_exiting_fullscreen) {
+    bool needs_check_tab_fullscreen) {
   FullscreenToolbarStyle old_style =
       [fullscreen_toolbar_controller_ toolbarStyle];
 
@@ -151,7 +151,7 @@
       browser_view()->GetExclusiveAccessManager()->fullscreen_controller();
   if ((controller->IsWindowFullscreenForTabOrPending() ||
        controller->IsExtensionFullscreenOrPending()) &&
-      !is_exiting_fullscreen) {
+      needs_check_tab_fullscreen) {
     new_style = FullscreenToolbarStyle::TOOLBAR_NONE;
   } else {
     new_style =
@@ -159,7 +159,8 @@
   }
   [fullscreen_toolbar_controller_ setToolbarStyle:new_style];
 
-  if (is_exiting_fullscreen || old_style == new_style)
+  if (![fullscreen_toolbar_controller_ isInFullscreen] ||
+      old_style == new_style)
     return;
 
   // Notify browser that top ui state has been changed so that we can update
diff --git a/chrome/browser/ui/views/frame/browser_view.cc b/chrome/browser/ui/views/frame/browser_view.cc
index 05b0900..de60ef9 100644
--- a/chrome/browser/ui/views/frame/browser_view.cc
+++ b/chrome/browser/ui/views/frame/browser_view.cc
@@ -1349,11 +1349,11 @@
 
   DCHECK(bubble);
 
-  views::Widget* bubble_widget =
-      views::BubbleDialogDelegateView::CreateBubble(bubble);
-
   if (card_view)
-    card_view->OnBubbleWidgetCreated(bubble_widget);
+    bubble->SetHighlightedButton(card_view);
+
+  views::BubbleDialogDelegateView::CreateBubble(bubble);
+
   bubble->Show(user_gesture ? autofill::SaveCardBubbleViews::USER_GESTURE
                             : autofill::SaveCardBubbleViews::AUTOMATIC);
   return bubble;
@@ -1366,14 +1366,13 @@
   LocationBarView* location_bar = GetLocationBarView();
   PageActionIconView* card_view =
       location_bar->local_card_migration_icon_view();
-
   autofill::LocalCardMigrationBubbleViews* bubble =
       new autofill::LocalCardMigrationBubbleViews(location_bar, gfx::Point(),
                                                   web_contents, controller);
-  views::Widget* bubble_widget =
-      views::BubbleDialogDelegateView::CreateBubble(bubble);
   if (card_view)
-    card_view->OnBubbleWidgetCreated(bubble_widget);
+    bubble->SetHighlightedButton(card_view);
+
+  views::BubbleDialogDelegateView::CreateBubble(bubble);
   bubble->Show(user_gesture
                    ? autofill::LocalCardMigrationBubbleViews::USER_GESTURE
                    : autofill::LocalCardMigrationBubbleViews::AUTOMATIC);
@@ -2975,7 +2974,7 @@
 
 void BrowserView::UpdateUIForTabFullscreen(TabFullscreenState state) {
   frame()->GetFrameView()->UpdateFullscreenTopUI(
-      state == ExclusiveAccessContext::STATE_EXIT_TAB_FULLSCREEN);
+      state == ExclusiveAccessContext::STATE_ENTER_TAB_FULLSCREEN);
 }
 
 WebContents* BrowserView::GetActiveWebContents() {
diff --git a/chrome/browser/ui/views/frame/browser_view_interactive_uitest.cc b/chrome/browser/ui/views/frame/browser_view_interactive_uitest.cc
index 4198d59..d1300a4 100644
--- a/chrome/browser/ui/views/frame/browser_view_interactive_uitest.cc
+++ b/chrome/browser/ui/views/frame/browser_view_interactive_uitest.cc
@@ -9,6 +9,7 @@
 #include "chrome/browser/ui/browser_commands.h"
 #include "chrome/browser/ui/exclusive_access/fullscreen_controller.h"
 #include "chrome/browser/ui/views/location_bar/location_bar_view.h"
+#include "chrome/browser/ui/views/tabs/tab_strip.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chrome/test/views/scoped_macviews_browser_mode.h"
@@ -75,6 +76,7 @@
   chrome::ToggleFullscreenMode(browser());
   EXPECT_TRUE(browser_view->IsFullscreen());
 
+  bool top_view_in_browser_fullscreen = false;
 #if defined(OS_MACOSX)
   // The top view should show up by default.
   EXPECT_TRUE(browser_view->IsTabStripVisible());
@@ -93,15 +95,14 @@
   // Test toggling toolbar while being in fullscreen mode.
   chrome::ToggleFullscreenToolbar(browser());
   EXPECT_TRUE(browser_view->IsFullscreen());
-  EXPECT_TRUE(browser_view->IsTabStripVisible());
+  top_view_in_browser_fullscreen = true;
 #else
   // In immersive fullscreen mode, the top view should show up; otherwise, it
   // always hides.
   if (browser_view->immersive_mode_controller()->IsEnabled())
-    EXPECT_TRUE(browser_view->IsTabStripVisible());
-  else
-    EXPECT_FALSE(browser_view->IsTabStripVisible());
+    top_view_in_browser_fullscreen = true;
 #endif
+  EXPECT_EQ(top_view_in_browser_fullscreen, browser_view->IsTabStripVisible());
 
   // Enter into tab fullscreen mode from browser fullscreen mode.
   FullscreenController* controller =
@@ -115,7 +116,19 @@
   else
     EXPECT_FALSE(browser_view->IsTabStripVisible());
 
-  // Return back to regular mode.
+  // Return back to browser fullscreen mode.
+  content::NativeWebKeyboardEvent event(
+      blink::WebInputEvent::kKeyDown, blink::WebInputEvent::kNoModifiers,
+      blink::WebInputEvent::GetStaticTimeStampForTests());
+  event.windows_key_code = ui::VKEY_ESCAPE;
+  browser()->exclusive_access_manager()->HandleUserKeyEvent(event);
+  EXPECT_TRUE(browser_view->IsFullscreen());
+  EXPECT_EQ(top_view_in_browser_fullscreen, browser_view->IsTabStripVisible());
+  // This makes sure that the layout was updated accordingly.
+  EXPECT_EQ(top_view_in_browser_fullscreen,
+            browser_view->tabstrip()->visible());
+
+  // Return to regular mode.
   chrome::ToggleFullscreenMode(browser());
   EXPECT_FALSE(browser_view->IsFullscreen());
   EXPECT_TRUE(browser_view->IsTabStripVisible());
diff --git a/chrome/browser/ui/views/frame/desktop_linux_browser_frame_view_layout_unittest.cc b/chrome/browser/ui/views/frame/desktop_linux_browser_frame_view_layout_unittest.cc
index 4e87ebf..0c5a6d83d 100644
--- a/chrome/browser/ui/views/frame/desktop_linux_browser_frame_view_layout_unittest.cc
+++ b/chrome/browser/ui/views/frame/desktop_linux_browser_frame_view_layout_unittest.cc
@@ -55,7 +55,6 @@
   }
   bool IsMaximized() const override { return false; }
   bool IsMinimized() const override { return false; }
-  bool IsFullscreen() const override { return false; }
   bool IsTabStripVisible() const override { return true; }
   int GetTabStripHeight() const override {
     return GetLayoutConstant(TAB_HEIGHT);
@@ -69,6 +68,7 @@
   }
   int GetTopAreaHeight() const override { return 0; }
   bool UseCustomFrame() const override { return true; }
+  bool IsFrameCondensed() const override { return false; }
   bool EverHasVisibleBackgroundTabShapes() const override { return false; }
 
  private:
diff --git a/chrome/browser/ui/views/frame/hosted_app_button_container.h b/chrome/browser/ui/views/frame/hosted_app_button_container.h
index f843ac45..768cc08 100644
--- a/chrome/browser/ui/views/frame/hosted_app_button_container.h
+++ b/chrome/browser/ui/views/frame/hosted_app_button_container.h
@@ -77,8 +77,6 @@
                         int y,
                         int available_height);
 
-  SkColor active_color_for_testing() const { return active_color_; }
-
  private:
   friend class HostedAppNonClientFrameViewAshTest;
   friend class HostedAppGlassBrowserFrameViewTest;
diff --git a/chrome/browser/ui/views/frame/opaque_browser_frame_view.cc b/chrome/browser/ui/views/frame/opaque_browser_frame_view.cc
index 22fda3fb..6cb008e 100644
--- a/chrome/browser/ui/views/frame/opaque_browser_frame_view.cc
+++ b/chrome/browser/ui/views/frame/opaque_browser_frame_view.cc
@@ -111,8 +111,6 @@
 ///////////////////////////////////////////////////////////////////////////////
 // OpaqueBrowserFrameView, public:
 
-constexpr char OpaqueBrowserFrameView::kClassName[];
-
 OpaqueBrowserFrameView::OpaqueBrowserFrameView(
     BrowserFrame* frame,
     BrowserView* browser_view,
@@ -170,8 +168,7 @@
 
   window_title_ = new views::Label(browser_view->GetWindowTitle());
   window_title_->SetVisible(browser_view->ShouldShowWindowTitle());
-  // Readability is ensured by |GetReadableFrameForegroundColor()|.
-  window_title_->SetAutoColorReadabilityEnabled(false);
+  window_title_->SetEnabledColor(kTitleBarFeatureColor);
   window_title_->SetSubpixelRenderingEnabled(false);
   window_title_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
   window_title_->set_id(VIEW_ID_WINDOW_TITLE);
@@ -180,8 +177,11 @@
   if (extensions::HostedAppBrowserController::IsForExperimentalHostedAppBrowser(
           browser_view->browser())) {
     hosted_app_button_container_ = new HostedAppButtonContainer(
-        frame, browser_view, GetReadableFrameForegroundColor(kActive),
-        GetReadableFrameForegroundColor(kInactive));
+        frame, browser_view,
+        color_utils::GetReadableColor(kTitleBarFeatureColor,
+                                      GetFrameColor(kActive)),
+        color_utils::GetReadableColor(kTitleBarFeatureColor,
+                                      GetFrameColor(kInactive)));
     hosted_app_button_container_->set_id(VIEW_ID_HOSTED_APP_BUTTON_CONTAINER);
     AddChildView(hosted_app_button_container_);
   }
@@ -290,7 +290,7 @@
     gfx::Rect sysmenu_rect(IconBounds());
     // In maximized mode we extend the rect to the screen corner to take
     // advantage of Fitts' Law.
-    if (layout_->IsTitleBarCondensed())
+    if (IsFrameCondensed())
       sysmenu_rect.SetRect(0, 0, sysmenu_rect.right(), sysmenu_rect.bottom());
     sysmenu_rect = GetMirroredRect(sysmenu_rect);
     if (sysmenu_rect.Contains(point))
@@ -343,7 +343,7 @@
                                            gfx::Path* window_mask) {
   DCHECK(window_mask);
 
-  if (layout_->IsTitleBarCondensed() || frame()->IsFullscreen())
+  if (IsFrameCondensed())
     return;
 
   views::GetDefaultWindowMask(
@@ -383,10 +383,6 @@
 ///////////////////////////////////////////////////////////////////////////////
 // OpaqueBrowserFrameView, views::View overrides:
 
-const char* OpaqueBrowserFrameView::GetClassName() const {
-  return kClassName;
-}
-
 void OpaqueBrowserFrameView::ChildPreferredSizeChanged(views::View* child) {
   BrowserNonClientFrameView::ChildPreferredSizeChanged(child);
   if (browser_view()->initialized() && child == hosted_app_button_container_)
@@ -508,10 +504,6 @@
   return frame()->IsMinimized();
 }
 
-bool OpaqueBrowserFrameView::IsFullscreen() const {
-  return frame()->IsFullscreen();
-}
-
 bool OpaqueBrowserFrameView::IsTabStripVisible() const {
   return browser_view()->IsTabStripVisible();
 }
@@ -550,6 +542,11 @@
   return frame()->UseCustomFrame();
 }
 
+bool OpaqueBrowserFrameView::IsFrameCondensed() const {
+  return BrowserNonClientFrameView::IsFrameCondensed() ||
+         !ShouldShowCaptionButtons();
+}
+
 bool OpaqueBrowserFrameView::EverHasVisibleBackgroundTabShapes() const {
   return BrowserNonClientFrameView::EverHasVisibleBackgroundTabShapes();
 }
@@ -564,7 +561,7 @@
     return;  // Nothing is visible, so don't bother to paint.
 
   SkColor frame_color = GetFrameColor();
-  window_title_->SetEnabledColor(GetReadableFrameForegroundColor(kUseCurrent));
+  window_title_->SetBackgroundColor(frame_color);
   frame_background_->set_frame_color(frame_color);
   frame_background_->set_use_custom_frame(frame()->UseCustomFrame());
   frame_background_->set_is_active(ShouldPaintAsActive());
@@ -578,7 +575,7 @@
   frame_background_->set_theme_overlay_image(GetFrameOverlayImage());
   frame_background_->set_top_area_height(GetTopAreaHeight());
 
-  if (layout_->IsTitleBarCondensed())
+  if (IsFrameCondensed())
     PaintMaximizedFrameBorder(canvas);
   else
     PaintRestoredFrameBorder(canvas);
@@ -735,19 +732,6 @@
       IsMaximized());
 }
 
-SkColor OpaqueBrowserFrameView::GetReadableFrameForegroundColor(
-    ActiveState active_state) const {
-  if (extensions::HostedAppBrowserController::IsForExperimentalHostedAppBrowser(
-          browser_view()->browser())) {
-    base::Optional<SkColor> theme_color =
-        browser_view()->browser()->hosted_app_controller()->GetThemeColor();
-    if (theme_color)
-      return color_utils::GetThemedAssetColor(*theme_color);
-  }
-  return color_utils::GetReadableColor(kTitleBarFeatureColor,
-                                       GetFrameColor(active_state));
-}
-
 void OpaqueBrowserFrameView::PaintRestoredFrameBorder(
     gfx::Canvas* canvas) const {
   const ui::ThemeProvider* tp = GetThemeProvider();
@@ -794,7 +778,7 @@
   }
 
   // In maximized mode, the only edge to draw is the top one, so we're done.
-  if (layout_->IsTitleBarCondensed())
+  if (IsFrameCondensed())
     return;
 
   const ui::ThemeProvider* tp = GetThemeProvider();
diff --git a/chrome/browser/ui/views/frame/opaque_browser_frame_view.h b/chrome/browser/ui/views/frame/opaque_browser_frame_view.h
index 1c504f7..b339a011 100644
--- a/chrome/browser/ui/views/frame/opaque_browser_frame_view.h
+++ b/chrome/browser/ui/views/frame/opaque_browser_frame_view.h
@@ -41,8 +41,6 @@
                                public TabIconViewModel,
                                public OpaqueBrowserFrameViewLayoutDelegate {
  public:
-  static constexpr char kClassName[] = "OpaqueBrowserFrameView";
-
   // Constructs a non-client view for an BrowserFrame.
   OpaqueBrowserFrameView(BrowserFrame* frame,
                          BrowserView* browser_view,
@@ -74,7 +72,6 @@
   bool HasClientEdge() const override;
 
   // views::View:
-  const char* GetClassName() const override;
   void ChildPreferredSizeChanged(views::View* child) override;
   void GetAccessibleNodeData(ui::AXNodeData* node_data) override;
   void OnNativeThemeChanged(const ui::NativeTheme* native_theme) override;
@@ -102,7 +99,6 @@
   gfx::ImageSkia GetIncognitoAvatarIcon() const override;
   bool IsMaximized() const override;
   bool IsMinimized() const override;
-  bool IsFullscreen() const override;
   bool IsTabStripVisible() const override;
   int GetTabStripHeight() const override;
   bool IsToolbarVisible() const override;
@@ -110,12 +106,9 @@
   gfx::Size GetNewTabButtonPreferredSize() const override;
   int GetTopAreaHeight() const override;
   bool UseCustomFrame() const override;
+  bool IsFrameCondensed() const override;
   bool EverHasVisibleBackgroundTabShapes() const override;
 
-  HostedAppButtonContainer* hosted_app_button_container_for_testing() {
-    return hosted_app_button_container_;
-  }
-
  protected:
   views::ImageButton* minimize_button() const { return minimize_button_; }
   views::ImageButton* maximize_button() const { return maximize_button_; }
@@ -188,10 +181,6 @@
   // Returns true if the view should draw its own custom title bar.
   bool ShouldShowWindowTitleBar() const;
 
-  // Returns the color to use for text and other title bar elements given the
-  // frame background color for |active_state|.
-  SkColor GetReadableFrameForegroundColor(ActiveState active_state) const;
-
   // Paint various sub-components of this view.  The *FrameBorder() functions
   // also paint the background of the titlebar area, since the top frame border
   // and titlebar background are a contiguous component.
diff --git a/chrome/browser/ui/views/frame/opaque_browser_frame_view_browsertest.cc b/chrome/browser/ui/views/frame/opaque_browser_frame_view_browsertest.cc
deleted file mode 100644
index ddab231..0000000
--- a/chrome/browser/ui/views/frame/opaque_browser_frame_view_browsertest.cc
+++ /dev/null
@@ -1,98 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/ui/views/frame/opaque_browser_frame_view.h"
-
-#include "base/test/scoped_feature_list.h"
-#include "build/build_config.h"
-#include "chrome/browser/extensions/browsertest_util.h"
-#include "chrome/browser/ui/browser.h"
-#include "chrome/browser/ui/views/frame/browser_view.h"
-#include "chrome/browser/ui/views/frame/hosted_app_button_container.h"
-#include "chrome/common/chrome_features.h"
-#include "chrome/common/web_application_info.h"
-#include "chrome/test/base/in_process_browser_test.h"
-
-// Tests hosted app windows that use the OpaqueBrowserFrameView implementation
-// for their non client frames.
-class HostedAppOpaqueBrowserFrameViewTest : public InProcessBrowserTest {
- public:
-  HostedAppOpaqueBrowserFrameViewTest() = default;
-  ~HostedAppOpaqueBrowserFrameViewTest() override = default;
-
-  static GURL GetAppURL() { return GURL("https://test.org"); }
-
-  bool InstallAndLaunchHostedApp(
-      base::Optional<SkColor> theme_color = base::nullopt) {
-    WebApplicationInfo web_app_info;
-    web_app_info.app_url = GetAppURL();
-    web_app_info.scope = GetAppURL().GetWithoutFilename();
-    web_app_info.theme_color = theme_color;
-
-    const extensions::Extension* app =
-        extensions::browsertest_util::InstallBookmarkApp(browser()->profile(),
-                                                         web_app_info);
-    Browser* app_browser = extensions::browsertest_util::LaunchAppBrowser(
-        browser()->profile(), app);
-
-    views::NonClientFrameView* frame_view =
-        BrowserView::GetBrowserViewForBrowser(app_browser)
-            ->GetWidget()
-            ->non_client_view()
-            ->frame_view();
-
-    // Not all platform configurations use OpaqueBrowserFrameView for their
-    // browser windows, see |CreateBrowserNonClientFrameView()|.
-    bool is_opaque_browser_frame_view =
-        frame_view->GetClassName() == OpaqueBrowserFrameView::kClassName;
-#if defined(OS_LINUX) && !defined(OS_CHROMEOS)
-    DCHECK(is_opaque_browser_frame_view);
-#else
-    if (!is_opaque_browser_frame_view)
-      return false;
-#endif
-
-    hosted_app_button_container_ =
-        static_cast<OpaqueBrowserFrameView*>(frame_view)
-            ->hosted_app_button_container_for_testing();
-    DCHECK(hosted_app_button_container_);
-    DCHECK(hosted_app_button_container_->visible());
-    return true;
-  }
-
-  HostedAppButtonContainer* hosted_app_button_container_ = nullptr;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(HostedAppOpaqueBrowserFrameViewTest);
-};
-
-IN_PROC_BROWSER_TEST_F(HostedAppOpaqueBrowserFrameViewTest, NoThemeColor) {
-  if (!InstallAndLaunchHostedApp())
-    return;
-  EXPECT_EQ(hosted_app_button_container_->active_color_for_testing(),
-            SK_ColorBLACK);
-}
-
-IN_PROC_BROWSER_TEST_F(HostedAppOpaqueBrowserFrameViewTest, LightThemeColor) {
-  if (!InstallAndLaunchHostedApp(SK_ColorYELLOW))
-    return;
-  SkColor dark_yellow = SkColorSetRGB(92, 92, 0);
-  EXPECT_EQ(hosted_app_button_container_->active_color_for_testing(),
-            dark_yellow);
-}
-
-IN_PROC_BROWSER_TEST_F(HostedAppOpaqueBrowserFrameViewTest, DarkThemeColor) {
-  if (!InstallAndLaunchHostedApp(SK_ColorBLUE))
-    return;
-  EXPECT_EQ(hosted_app_button_container_->active_color_for_testing(),
-            SK_ColorWHITE);
-}
-
-IN_PROC_BROWSER_TEST_F(HostedAppOpaqueBrowserFrameViewTest, MediumThemeColor) {
-  // Use the theme color for Gmail.
-  if (!InstallAndLaunchHostedApp(SkColorSetRGB(0xd6, 0x49, 0x3b)))
-    return;
-  EXPECT_EQ(hosted_app_button_container_->active_color_for_testing(),
-            SK_ColorWHITE);
-}
diff --git a/chrome/browser/ui/views/frame/opaque_browser_frame_view_layout.cc b/chrome/browser/ui/views/frame/opaque_browser_frame_view_layout.cc
index 08ce26a..5d2eb79 100644
--- a/chrome/browser/ui/views/frame/opaque_browser_frame_view_layout.cc
+++ b/chrome/browser/ui/views/frame/opaque_browser_frame_view_layout.cc
@@ -144,14 +144,12 @@
 }
 
 int OpaqueBrowserFrameViewLayout::FrameBorderThickness(bool restored) const {
-  return (!restored && (IsTitleBarCondensed() || delegate_->IsFullscreen())) ?
-      0 : kFrameBorderThickness;
+  return !restored && delegate_->IsFrameCondensed() ? 0 : kFrameBorderThickness;
 }
 
 int OpaqueBrowserFrameViewLayout::FrameTopBorderThickness(bool restored) const {
   int thickness = FrameBorderThickness(restored);
-  if (MD::IsRefreshUi() &&
-      (restored || (!IsTitleBarCondensed() && !delegate_->IsFullscreen())))
+  if (MD::IsRefreshUi() && (restored || !delegate_->IsFrameCondensed()))
     thickness += kRefreshNonClientExtraTopThickness;
   return thickness;
 }
@@ -163,8 +161,7 @@
 int OpaqueBrowserFrameViewLayout::NonClientBorderThickness() const {
   const int frame = FrameBorderThickness(false);
   // When we fill the screen, we don't show a client edge.
-  return (!HasClientEdge() || IsTitleBarCondensed() ||
-          delegate_->IsFullscreen())
+  return !HasClientEdge() || delegate_->IsFrameCondensed()
              ? frame
              : (frame + views::NonClientFrameView::kClientEdgeThickness);
 }
@@ -194,7 +191,7 @@
 
 int OpaqueBrowserFrameViewLayout::GetTabStripInsetsTop(bool restored) const {
   const int top = NonClientTopHeight(restored);
-  return (!restored && (IsTitleBarCondensed() || delegate_->IsFullscreen()))
+  return !restored || !delegate_->IsFrameCondensed()
              ? top
              : (top + GetNonClientRestoredExtraThickness());
 }
@@ -202,17 +199,18 @@
 int OpaqueBrowserFrameViewLayout::TitlebarTopThickness(bool restored) const {
   if (!delegate_->UseCustomFrame())
     return 0;
-  return (restored || !IsTitleBarCondensed()) ?
-      kTitlebarTopEdgeThickness : FrameBorderThickness(false);
+  return restored || !delegate_->IsFrameCondensed()
+             ? kTitlebarTopEdgeThickness
+             : FrameBorderThickness(false);
 }
 
 int OpaqueBrowserFrameViewLayout::DefaultCaptionButtonY(bool restored) const {
   // Maximized buttons start at window top, since the window has no border. This
   // offset is for the image (the actual clickable bounds extend all the way to
   // the top to take Fitts' Law into account).
-  const int frame = (!restored && IsTitleBarCondensed()) ?
-      FrameBorderThickness(false) :
-      views::NonClientFrameView::kFrameShadowThickness;
+  const int frame = !restored && delegate_->IsFrameCondensed()
+                        ? FrameBorderThickness(false)
+                        : views::NonClientFrameView::kFrameShadowThickness;
   return frame + extra_caption_y_;
 }
 
@@ -266,9 +264,9 @@
     if (is_leading_button) {
       // If we're the first button and maximized, add width to the right
       // hand side of the screen.
-      return (IsTitleBarCondensed() && is_leading_button)
-                 ? (kFrameBorderThickness -
-                    views::NonClientFrameView::kFrameShadowThickness)
+      return delegate_->IsFrameCondensed() && is_leading_button
+                 ? kFrameBorderThickness -
+                       views::NonClientFrameView::kFrameShadowThickness
                  : 0;
     }
     if (forced_window_caption_spacing_ >= 0)
@@ -278,13 +276,6 @@
   return 0;
 }
 
-bool OpaqueBrowserFrameViewLayout::IsTitleBarCondensed() const {
-  // If there are no caption buttons, there is no need to have an uncondensed
-  // title bar. If the window is maximized, the title bar is condensed
-  // regardless of whether there are caption buttons.
-  return !delegate_->ShouldShowCaptionButtons() || delegate_->IsMaximized();
-}
-
 int OpaqueBrowserFrameViewLayout::GetNonClientRestoredExtraThickness() const {
   // Besides the frame border, there's empty space atop the window in restored
   // mode, to use to drag the window around.
@@ -315,14 +306,14 @@
     button_width_with_offset += kCaptionSpacing;
 
   const int button_x = available_space_trailing_x_ - button_width_with_offset;
-  const int button_y = DefaultCaptionButtonY(!IsTitleBarCondensed());
+  const int button_y = DefaultCaptionButtonY(!delegate_->IsFrameCondensed());
 
   minimum_size_for_buttons_ += button_width_with_offset;
   available_space_trailing_x_ -= button_width_with_offset;
 
   // In non-maximized mode, allow the new tab button to completely slide under
   // the avatar button.
-  if (!IsTitleBarCondensed()) {
+  if (!delegate_->IsFrameCondensed()) {
     available_space_trailing_x_ +=
         delegate_->GetNewTabButtonPreferredSize().width() + kCaptionSpacing;
   }
@@ -353,8 +344,9 @@
   if (MD::IsRefreshUi())
     return 0;
 
-  return (has_trailing_buttons_ && IsTitleBarCondensed()) ?
-      kNewTabCaptionCondensedSpacing : kCaptionSpacing;
+  return has_trailing_buttons_ && delegate_->IsFrameCondensed()
+             ? kNewTabCaptionCondensedSpacing
+             : kCaptionSpacing;
 }
 
 void OpaqueBrowserFrameViewLayout::LayoutWindowControls(views::View* host) {
@@ -554,7 +546,7 @@
   // side of the caption buttons.  In maximized mode we extend buttons to the
   // screen top and the rightmost button to the screen right (or leftmost button
   // to the screen left, for left-aligned buttons) to obey Fitts' Law.
-  bool title_bar_condensed = IsTitleBarCondensed();
+  bool is_frame_condensed = delegate_->IsFrameCondensed();
 
   // When we are the first button on the leading side and are the close
   // button, we must flip ourselves, because the close button assets have
@@ -571,9 +563,9 @@
       available_space_leading_x_ += button_start_spacing;
       minimum_size_for_buttons_ += button_start_spacing;
 
-      bool top_spacing_clickable = title_bar_condensed;
+      bool top_spacing_clickable = is_frame_condensed;
       bool start_spacing_clickable =
-          title_bar_condensed && !has_leading_buttons_;
+          is_frame_condensed && !has_leading_buttons_;
       button->SetBounds(
           available_space_leading_x_ - (start_spacing_clickable
                                             ? button_start_spacing + extra_width
@@ -598,9 +590,9 @@
       available_space_trailing_x_ -= button_start_spacing;
       minimum_size_for_buttons_ += button_start_spacing;
 
-      bool top_spacing_clickable = title_bar_condensed;
+      bool top_spacing_clickable = is_frame_condensed;
       bool start_spacing_clickable =
-          title_bar_condensed && !has_trailing_buttons_;
+          is_frame_condensed && !has_trailing_buttons_;
       button->SetBounds(
           available_space_trailing_x_ - button_size.width(),
           top_spacing_clickable ? 0 : caption_y,
diff --git a/chrome/browser/ui/views/frame/opaque_browser_frame_view_layout.h b/chrome/browser/ui/views/frame/opaque_browser_frame_view_layout.h
index eb8e3bd..c831e59 100644
--- a/chrome/browser/ui/views/frame/opaque_browser_frame_view_layout.h
+++ b/chrome/browser/ui/views/frame/opaque_browser_frame_view_layout.h
@@ -134,12 +134,6 @@
 
   const gfx::Rect& client_view_bounds() const { return client_view_bounds_; }
 
-  // Determines whether the title bar is condensed vertically, as when the
-  // window is maximized. If true, the title bar is just the height of a tab,
-  // rather than having extra vertical space above the tabs. This also removes
-  // the thick frame border and rounded corners.
-  bool IsTitleBarCondensed() const;
-
   // Returns the extra thickness of the area above the tabs. The value returned
   // is dependent on whether in material refresh mode or not.
   int GetNonClientRestoredExtraThickness() const;
@@ -205,7 +199,7 @@
   // Internal implementation of ViewAdded() and ViewRemoved().
   void SetView(int id, views::View* view);
 
-  // Overriden from views::LayoutManager:
+  // views::LayoutManager:
   void Layout(views::View* host) override;
   gfx::Size GetPreferredSize(const views::View* host) const override;
   void ViewAdded(views::View* host, views::View* view) override;
diff --git a/chrome/browser/ui/views/frame/opaque_browser_frame_view_layout_delegate.h b/chrome/browser/ui/views/frame/opaque_browser_frame_view_layout_delegate.h
index 6f206a0..1f8bb467 100644
--- a/chrome/browser/ui/views/frame/opaque_browser_frame_view_layout_delegate.h
+++ b/chrome/browser/ui/views/frame/opaque_browser_frame_view_layout_delegate.h
@@ -42,7 +42,6 @@
   // Controls window state.
   virtual bool IsMaximized() const = 0;
   virtual bool IsMinimized() const = 0;
-  virtual bool IsFullscreen() const = 0;
 
   virtual bool IsTabStripVisible() const = 0;
   virtual int GetTabStripHeight() const = 0;
@@ -61,6 +60,12 @@
   // Returns true if the window frame is rendered by Chrome.
   virtual bool UseCustomFrame() const = 0;
 
+  // Determines whether the top frame is condensed vertically, as when the
+  // window is maximized. If true, the top frame is just the height of a tab,
+  // rather than having extra vertical space above the tabs. This also removes
+  // the thick frame border and rounded corners.
+  virtual bool IsFrameCondensed() const = 0;
+
   // Returns whether the shapes of background tabs are visible against the frame
   // for either active or inactive windows.
   virtual bool EverHasVisibleBackgroundTabShapes() const = 0;
diff --git a/chrome/browser/ui/views/frame/opaque_browser_frame_view_layout_unittest.cc b/chrome/browser/ui/views/frame/opaque_browser_frame_view_layout_unittest.cc
index 4497d19..ae3a293 100644
--- a/chrome/browser/ui/views/frame/opaque_browser_frame_view_layout_unittest.cc
+++ b/chrome/browser/ui/views/frame/opaque_browser_frame_view_layout_unittest.cc
@@ -70,7 +70,6 @@
   }
   bool IsMaximized() const override { return maximized_; }
   bool IsMinimized() const override { return false; }
-  bool IsFullscreen() const override { return false; }
   bool IsTabStripVisible() const override { return window_title_.empty(); }
   int GetTabStripHeight() const override {
     return IsTabStripVisible() ? GetLayoutConstant(TAB_HEIGHT) : 0;
@@ -84,6 +83,9 @@
   }
   int GetTopAreaHeight() const override { return 0; }
   bool UseCustomFrame() const override { return true; }
+  bool IsFrameCondensed() const override {
+    return !show_caption_buttons_ || maximized_;
+  }
   bool EverHasVisibleBackgroundTabShapes() const override { return false; }
 
  private:
diff --git a/chrome/browser/ui/views/location_bar/content_setting_image_view.cc b/chrome/browser/ui/views/location_bar/content_setting_image_view.cc
index bdb1093d..fedefea 100644
--- a/chrome/browser/ui/views/location_bar/content_setting_image_view.cc
+++ b/chrome/browser/ui/views/location_bar/content_setting_image_view.cc
@@ -66,7 +66,6 @@
   // the user.  If this becomes a problem, we could design some sort of queueing
   // mechanism to show one after the other, but it doesn't seem important now.
   int string_id = content_setting_image_model_->explanatory_string_id();
-  AnimateInkDrop(views::InkDropState::HIDDEN, nullptr);
   if (string_id)
     AnimateIn(string_id);
 
@@ -96,6 +95,21 @@
   return !tooltip->empty();
 }
 
+bool ContentSettingImageView::OnMousePressed(const ui::MouseEvent& event) {
+  // Pause animation so that the icon does not shrink and deselect while the
+  // user is attempting to press it.
+  PauseAnimation();
+  return IconLabelBubbleView::OnMousePressed(event);
+}
+
+bool ContentSettingImageView::OnKeyPressed(const ui::KeyEvent& event) {
+  // Pause animation so that the icon does not shrink and deselect while the
+  // user is attempting to press it using key commands.
+  if (GetKeyClickActionForEvent(event) == KeyClickAction::CLICK_ON_KEY_RELEASE)
+    PauseAnimation();
+  return Button::OnKeyPressed(event);
+}
+
 void ContentSettingImageView::OnNativeThemeChanged(
     const ui::NativeTheme* native_theme) {
   UpdateImage();
@@ -122,18 +136,10 @@
             delegate_->GetContentSettingBubbleModelDelegate(), web_contents,
             Profile::FromBrowserContext(web_contents->GetBrowserContext())),
         web_contents, anchor, views::BubbleBorder::TOP_RIGHT);
+    bubble_view_->SetHighlightedButton(this);
     views::Widget* bubble_widget =
         views::BubbleDialogDelegateView::CreateBubble(bubble_view_);
     bubble_widget->AddObserver(this);
-    // This is triggered by an input event. If the user clicks the icon while
-    // it's not animating, the icon will be placed in an active state, so the
-    // bubble doesn't need an arrow. If the user clicks during an animation,
-    // the animation simply pauses and no other visible state change occurs, so
-    // show the arrow in this case.
-    if (!is_animation_paused()) {
-      AnimateInkDrop(views::InkDropState::ACTIVATED,
-                     ui::LocatedEvent::FromIfValid(&event));
-    }
     bubble_widget->Show();
     delegate_->OnContentSettingImageBubbleShown(
         content_setting_image_model_->image_type());
@@ -164,13 +170,6 @@
   UnpauseAnimation();
 }
 
-void ContentSettingImageView::OnWidgetVisibilityChanged(views::Widget* widget,
-                                                        bool visible) {
-  // |widget| is a bubble that has just got shown / hidden.
-  if (!visible)
-    AnimateInkDrop(views::InkDropState::DEACTIVATED, nullptr /* event */);
-}
-
 void ContentSettingImageView::UpdateImage() {
   SetImage(content_setting_image_model_
                ->GetIcon(icon_color_ ? icon_color_.value()
diff --git a/chrome/browser/ui/views/location_bar/content_setting_image_view.h b/chrome/browser/ui/views/location_bar/content_setting_image_view.h
index c94cbce4..17b6ce95 100644
--- a/chrome/browser/ui/views/location_bar/content_setting_image_view.h
+++ b/chrome/browser/ui/views/location_bar/content_setting_image_view.h
@@ -33,7 +33,8 @@
 // The ContentSettingImageView displays an icon and optional text label for
 // various content settings affordances in the location bar (i.e. plugin
 // blocking, geolocation).
-class ContentSettingImageView : public IconLabelBubbleView {
+class ContentSettingImageView : public IconLabelBubbleView,
+                                public views::WidgetObserver {
  public:
   class Delegate {
    public:
@@ -71,6 +72,8 @@
   void OnBoundsChanged(const gfx::Rect& previous_bounds) override;
   bool GetTooltipText(const gfx::Point& p,
                       base::string16* tooltip) const override;
+  bool OnMousePressed(const ui::MouseEvent& event) override;
+  bool OnKeyPressed(const ui::KeyEvent& event) override;
   void OnNativeThemeChanged(const ui::NativeTheme* native_theme) override;
   SkColor GetInkDropBaseColor() const override;
   SkColor GetTextColor() const override;
@@ -83,7 +86,6 @@
  private:
   // views::WidgetObserver:
   void OnWidgetDestroying(views::Widget* widget) override;
-  void OnWidgetVisibilityChanged(views::Widget* widget, bool visible) override;
 
   // Updates the image and tooltip to match the current model state.
   void UpdateImage();
diff --git a/chrome/browser/ui/views/location_bar/icon_label_bubble_view.cc b/chrome/browser/ui/views/location_bar/icon_label_bubble_view.cc
index a4d1a558..c49a812 100644
--- a/chrome/browser/ui/views/location_bar/icon_label_bubble_view.cc
+++ b/chrome/browser/ui/views/location_bar/icon_label_bubble_view.cc
@@ -183,10 +183,6 @@
   image_->SetImage(image_skia);
 }
 
-void IconLabelBubbleView::OnBubbleCreated(views::Widget* bubble_widget) {
-  bubble_widget->AddObserver(this);
-}
-
 bool IconLabelBubbleView::ShouldShowSeparator() const {
   return ShouldShowLabel();
 }
@@ -300,14 +296,6 @@
   }
 }
 
-void IconLabelBubbleView::OnBoundsChanged(const gfx::Rect& previous_bounds) {
-  if (!IsMouseHovered()) {
-    GetInkDrop()->AnimateToState(views::InkDropState::HIDDEN);
-    GetInkDrop()->SetHovered(false);
-  }
-  Button::OnBoundsChanged(previous_bounds);
-}
-
 void IconLabelBubbleView::OnNativeThemeChanged(
     const ui::NativeTheme* native_theme) {
   label_->SetEnabledColor(GetTextColor());
@@ -431,17 +419,6 @@
   AnimationEnded(animation);
 }
 
-void IconLabelBubbleView::OnWidgetDestroying(views::Widget* widget) {
-  widget->RemoveObserver(this);
-}
-
-void IconLabelBubbleView::OnWidgetVisibilityChanged(views::Widget* widget,
-                                                    bool visible) {
-  // |widget| is a bubble that has just got shown / hidden.
-  if (!visible)
-    AnimateInkDrop(views::InkDropState::HIDDEN, nullptr /* event */);
-}
-
 SkColor IconLabelBubbleView::GetParentBackgroundColor() const {
   return GetNativeTheme()->GetSystemColor(
       ui::NativeTheme::kColorId_TextfieldDefaultBackground);
@@ -521,13 +498,7 @@
 }
 
 bool IconLabelBubbleView::OnActivate(const ui::Event& event) {
-  if (ShowBubble(event)) {
-    AnimateInkDrop(views::InkDropState::ACTIVATED, nullptr);
-    return true;
-  }
-
-  AnimateInkDrop(views::InkDropState::HIDDEN, nullptr);
-  return false;
+  return ShowBubble(event);
 }
 
 const char* IconLabelBubbleView::GetClassName() const {
diff --git a/chrome/browser/ui/views/location_bar/icon_label_bubble_view.h b/chrome/browser/ui/views/location_bar/icon_label_bubble_view.h
index 79de08a..7fb9872 100644
--- a/chrome/browser/ui/views/location_bar/icon_label_bubble_view.h
+++ b/chrome/browser/ui/views/location_bar/icon_label_bubble_view.h
@@ -33,8 +33,7 @@
 // base for the classes that handle the location icon (including the EV bubble),
 // tab-to-search UI, and content settings.
 class IconLabelBubbleView : public views::InkDropObserver,
-                            public views::Button,
-                            public views::WidgetObserver {
+                            public views::Button {
  public:
   static constexpr int kTrailingPaddingPreMd = 2;
 
@@ -85,8 +84,6 @@
     next_element_interior_padding_ = padding;
   }
 
-  void OnBubbleCreated(views::Widget* bubble_widget);
-
  protected:
   static constexpr int kOpenTimeMS = 150;
 
@@ -130,7 +127,6 @@
   void Layout() override;
   bool OnMousePressed(const ui::MouseEvent& event) override;
   void GetAccessibleNodeData(ui::AXNodeData* node_data) override;
-  void OnBoundsChanged(const gfx::Rect& previous_bounds) override;
   void OnNativeThemeChanged(const ui::NativeTheme* native_theme) override;
   void AddInkDropLayer(ui::Layer* ink_drop_layer) override;
   void RemoveInkDropLayer(ui::Layer* ink_drop_layer) override;
@@ -151,10 +147,6 @@
   void AnimationProgressed(const gfx::Animation* animation) override;
   void AnimationCanceled(const gfx::Animation* animation) override;
 
-  // views::WidgetObserver:
-  void OnWidgetDestroying(views::Widget* widget) override;
-  void OnWidgetVisibilityChanged(views::Widget* widget, bool visible) override;
-
   const gfx::FontList& font_list() const { return label_->font_list(); }
 
   SkColor GetParentBackgroundColor() const;
diff --git a/chrome/browser/ui/views/location_bar/icon_label_bubble_view_unittest.cc b/chrome/browser/ui/views/location_bar/icon_label_bubble_view_unittest.cc
index 57c3a0c..2f055f6 100644
--- a/chrome/browser/ui/views/location_bar/icon_label_bubble_view_unittest.cc
+++ b/chrome/browser/ui/views/location_bar/icon_label_bubble_view_unittest.cc
@@ -75,7 +75,7 @@
   }
 
   void HideBubble() {
-    OnWidgetVisibilityChanged(nullptr, false);
+    AnimateInkDrop(views::InkDropState::HIDDEN, nullptr /* event */);
     is_bubble_showing_ = false;
   }
 
@@ -112,6 +112,7 @@
   bool IsShrinking() const override { return state() == SHRINKING; }
 
   bool ShowBubble(const ui::Event& event) override {
+    AnimateInkDrop(views::InkDropState::ACTIVATED, nullptr /* event */);
     is_bubble_showing_ = true;
     return true;
   }
diff --git a/chrome/browser/ui/views/location_bar/location_bar_view.cc b/chrome/browser/ui/views/location_bar/location_bar_view.cc
index 171675a..7bd2060 100644
--- a/chrome/browser/ui/views/location_bar/location_bar_view.cc
+++ b/chrome/browser/ui/views/location_bar/location_bar_view.cc
@@ -362,10 +362,6 @@
   omnibox_view_->SelectAll(true);
 }
 
-views::View* LocationBarView::GetSecurityBubbleAnchorView() {
-  return this;
-}
-
 bool LocationBarView::ShowPageInfoDialog(WebContents* contents) {
   DCHECK(contents);
   content::NavigationEntry* entry = contents->GetController().GetVisibleEntry();
@@ -381,10 +377,9 @@
   DCHECK(GetWidget());
   views::BubbleDialogDelegateView* bubble =
       PageInfoBubbleView::CreatePageInfoBubble(
-          GetSecurityBubbleAnchorView(), gfx::Rect(),
-          GetWidget()->GetNativeWindow(), profile(), contents,
-          entry->GetVirtualURL(), security_info);
-  location_icon_view()->OnBubbleCreated(bubble->GetWidget());
+          this, gfx::Rect(), GetWidget()->GetNativeWindow(), profile(),
+          contents, entry->GetVirtualURL(), security_info);
+  bubble->SetHighlightedButton(location_icon_view());
   bubble->GetWidget()->Show();
   return true;
 }
@@ -471,7 +466,6 @@
     return;
 
   selected_keyword_view_->SetVisible(false);
-  location_icon_view_->SetVisible(false);
   keyword_hint_view_->SetVisible(false);
 
   const int edge_padding = GetLayoutConstant(LOCATION_BAR_ELEMENT_PADDING);
@@ -531,6 +525,7 @@
 
   location_icon_view_->SetLabel(base::string16());
   if (ShouldShowKeywordBubble()) {
+    location_icon_view_->SetVisible(false);
     leading_decorations.AddDecoration(
         vertical_padding, location_height, false, kLeadingDecorationMaxFraction,
         edge_padding, internal_padding, selected_keyword_view_);
diff --git a/chrome/browser/ui/views/location_bar/location_bar_view.h b/chrome/browser/ui/views/location_bar/location_bar_view.h
index e29b2fe0..64d636a 100644
--- a/chrome/browser/ui/views/location_bar/location_bar_view.h
+++ b/chrome/browser/ui/views/location_bar/location_bar_view.h
@@ -199,10 +199,6 @@
     return selected_keyword_view_;
   }
 
-  // The anchor view for security-related bubbles. That is, those anchored to
-  // the leading edge of the Omnibox, under the padlock.
-  views::View* GetSecurityBubbleAnchorView();
-
   // Show a page info dialog for |web_contents|.
   // Returns true if a dialog was shown, false otherwise.
   bool ShowPageInfoDialog(content::WebContents* web_contents);
diff --git a/chrome/browser/ui/views/location_bar/star_view.cc b/chrome/browser/ui/views/location_bar/star_view.cc
index a89fbfb64..b73b7ce 100644
--- a/chrome/browser/ui/views/location_bar/star_view.cc
+++ b/chrome/browser/ui/views/location_bar/star_view.cc
@@ -39,8 +39,6 @@
 void StarView::ShowPromo() {
   BookmarkPromoBubbleView* bookmark_promo_bubble =
       BookmarkPromoBubbleView::CreateOwned(this);
-
-  OnBubbleWidgetCreated(bookmark_promo_bubble->GetWidget());
   if (!bookmark_promo_observer_.IsObserving(
           bookmark_promo_bubble->GetWidget())) {
     bookmark_promo_observer_.Add(bookmark_promo_bubble->GetWidget());
diff --git a/chrome/browser/ui/views/location_bar/star_view.h b/chrome/browser/ui/views/location_bar/star_view.h
index ecd4499e..b4c6002 100644
--- a/chrome/browser/ui/views/location_bar/star_view.h
+++ b/chrome/browser/ui/views/location_bar/star_view.h
@@ -13,7 +13,7 @@
 class CommandUpdater;
 
 // The star icon to show a bookmark bubble.
-class StarView : public PageActionIconView {
+class StarView : public PageActionIconView, public views::WidgetObserver {
  public:
   StarView(CommandUpdater* command_updater,
            Browser* browser,
diff --git a/chrome/browser/ui/views/location_bar/zoom_bubble_view.cc b/chrome/browser/ui/views/location_bar/zoom_bubble_view.cc
index 1ec877d..9e8d887 100644
--- a/chrome/browser/ui/views/location_bar/zoom_bubble_view.cc
+++ b/chrome/browser/ui/views/location_bar/zoom_bubble_view.cc
@@ -163,19 +163,19 @@
                           ZoomBubbleView* zoom_bubble,
                           views::View* anchor_view,
                           content::WebContents* web_contents) {
-  BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser);
-  // If we do not have an anchor view, parent the bubble to the content area.
-  if (!anchor_view)
+  // If the anchor view exists the zoom icon should be highlighed.
+  if (anchor_view) {
+    zoom_bubble->SetHighlightedButton(
+        BrowserView::GetBrowserViewForBrowser(browser)
+            ->toolbar_button_provider()
+            ->GetPageActionIconContainerView()
+            ->GetPageActionIconView(PageActionIconType::kZoom));
+  } else {
+    // If we do not have an anchor view, parent the bubble to the content area.
     zoom_bubble->set_parent_window(web_contents->GetNativeView());
-
-  views::Widget* zoom_bubble_widget =
-      views::BubbleDialogDelegateView::CreateBubble(zoom_bubble);
-  if (zoom_bubble_widget && anchor_view) {
-    browser_view->toolbar_button_provider()
-        ->GetPageActionIconContainerView()
-        ->GetPageActionIconView(PageActionIconType::kZoom)
-        ->OnBubbleWidgetCreated(zoom_bubble_widget);
   }
+
+  views::BubbleDialogDelegateView::CreateBubble(zoom_bubble);
 }
 #endif
 
diff --git a/chrome/browser/ui/views/omnibox/omnibox_view_views.cc b/chrome/browser/ui/views/omnibox/omnibox_view_views.cc
index 9a25221b..7a2c1ba41 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_view_views.cc
+++ b/chrome/browser/ui/views/omnibox/omnibox_view_views.cc
@@ -638,6 +638,10 @@
   if (model()->user_input_in_progress())
     return false;
 
+  // Don't unelide if we are currently displaying Query in Omnibox search terms.
+  if (model()->GetQueryInOmniboxSearchTerms(nullptr /* search_terms */))
+    return false;
+
   // If everything is selected, the user likely does not intend to edit the URL.
   // But if the Home key is pressed, the user probably does want to interact
   // with the beginning of the URL - in which case we unelide.
diff --git a/chrome/browser/ui/views/omnibox/omnibox_view_views_unittest.cc b/chrome/browser/ui/views/omnibox/omnibox_view_views_unittest.cc
index 38acc8e..f50a6b7 100644
--- a/chrome/browser/ui/views/omnibox/omnibox_view_views_unittest.cc
+++ b/chrome/browser/ui/views/omnibox/omnibox_view_views_unittest.cc
@@ -19,6 +19,7 @@
 #include "chrome/browser/search_engines/template_url_service_factory_test_util.h"
 #include "chrome/browser/ui/omnibox/chrome_omnibox_client.h"
 #include "chrome/browser/ui/omnibox/chrome_omnibox_edit_controller.h"
+#include "chrome/browser/ui/omnibox/query_in_omnibox_factory.h"
 #include "chrome/test/base/testing_profile.h"
 #include "chrome/test/views/chrome_views_test_base.h"
 #include "chrome/test/views/scoped_macviews_browser_mode.h"
@@ -279,6 +280,8 @@
 #endif
   AutocompleteClassifierFactory::GetInstance()->SetTestingFactoryAndUse(
       &profile_, &AutocompleteClassifierFactory::BuildInstanceFor);
+  QueryInOmniboxFactory::GetInstance()->SetTestingFactoryAndUse(
+      &profile_, &QueryInOmniboxFactory::BuildInstanceFor);
   omnibox_view_ = new TestingOmniboxView(
       &omnibox_edit_controller_, std::make_unique<ChromeOmniboxClient>(
                                      &omnibox_edit_controller_, &profile_));
@@ -477,6 +480,10 @@
             {omnibox::kUIExperimentHideSteadyStateUrlSchemeAndSubdomains}) {}
 
  protected:
+  explicit OmniboxViewViewsSteadyStateElisionsTest(
+      const std::vector<base::Feature>& enabled_features)
+      : OmniboxViewViewsTest(enabled_features) {}
+
   const int kCharacterWidth = 10;
   const base::string16 kFullUrl = base::ASCIIToUTF16("https://www.example.com");
 
@@ -865,3 +872,42 @@
   EXPECT_TRUE(IsElidedUrlDisplayed());
   EXPECT_TRUE(omnibox_view()->IsSelectAll());
 }
+
+class OmniboxViewViewsSteadyStateElisionsAndQueryInOmniboxTest
+    : public OmniboxViewViewsSteadyStateElisionsTest {
+ public:
+  OmniboxViewViewsSteadyStateElisionsAndQueryInOmniboxTest()
+      : OmniboxViewViewsSteadyStateElisionsTest({
+            omnibox::kUIExperimentHideSteadyStateUrlSchemeAndSubdomains,
+            omnibox::kQueryInOmnibox,
+        }) {}
+};
+
+TEST_F(OmniboxViewViewsSteadyStateElisionsAndQueryInOmniboxTest,
+       DontUnelideQueryInOmniboxSearchTerms) {
+  const GURL kValidSearchResultsPage =
+      GURL("https://www.google.com/search?q=foo+query");
+  toolbar_model()->set_url(kValidSearchResultsPage);
+  toolbar_model()->set_security_level(security_state::SecurityLevel::SECURE);
+
+  omnibox_view()->model()->ResetDisplayTexts();
+  omnibox_view()->RevertAll();
+
+  // Sanity check that Query in Omnibox is working with Steady State Elisions.
+  EXPECT_EQ(base::ASCIIToUTF16("foo query"), omnibox_view()->text());
+
+  // Focus the Omnibox.
+  SendMouseClick(0);
+
+  // Right key should NOT unelide, and should correctly place the cursor at the
+  // end of the search query.
+  omnibox_textfield_view()->OnKeyPressed(
+      ui::KeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_RIGHT, 0));
+  EXPECT_EQ(base::ASCIIToUTF16("foo query"), omnibox_view()->text());
+  EXPECT_FALSE(omnibox_view()->model()->user_input_in_progress());
+
+  size_t start, end;
+  omnibox_view()->GetSelectionBounds(&start, &end);
+  EXPECT_EQ(9U, start);
+  EXPECT_EQ(9U, end);
+}
diff --git a/chrome/browser/ui/views/page_action/page_action_icon_view.cc b/chrome/browser/ui/views/page_action/page_action_icon_view.cc
index 232c83f..b5df57c 100644
--- a/chrome/browser/ui/views/page_action/page_action_icon_view.cc
+++ b/chrome/browser/ui/views/page_action/page_action_icon_view.cc
@@ -38,7 +38,6 @@
                                        PageActionIconView::Delegate* delegate,
                                        const gfx::FontList& font_list)
     : IconLabelBubbleView(font_list),
-      widget_observer_(this),
       icon_size_(GetLayoutConstant(LOCATION_BAR_ICON_SIZE)),
       command_updater_(command_updater),
       delegate_(delegate),
@@ -74,19 +73,6 @@
   return command_updater_->IsCommandEnabled(command_id_);
 }
 
-void PageActionIconView::SetHighlighted(bool bubble_visible) {
-  AnimateInkDrop(bubble_visible ? views::InkDropState::ACTIVATED
-                                : views::InkDropState::DEACTIVATED,
-                 nullptr);
-}
-
-void PageActionIconView::OnBubbleWidgetCreated(views::Widget* bubble_widget) {
-  widget_observer_.SetWidget(bubble_widget);
-
-  if (bubble_widget->IsVisible())
-    SetHighlighted(true);
-}
-
 bool PageActionIconView::Update() {
   return false;
 }
@@ -272,25 +258,3 @@
 content::WebContents* PageActionIconView::GetWebContents() const {
   return delegate_->GetWebContentsForPageActionIconView();
 }
-
-PageActionIconView::WidgetObserver::WidgetObserver(PageActionIconView* parent)
-    : parent_(parent), scoped_observer_(this) {}
-
-PageActionIconView::WidgetObserver::~WidgetObserver() = default;
-
-void PageActionIconView::WidgetObserver::SetWidget(views::Widget* widget) {
-  scoped_observer_.RemoveAll();
-  scoped_observer_.Add(widget);
-}
-
-void PageActionIconView::WidgetObserver::OnWidgetDestroying(
-    views::Widget* widget) {
-  scoped_observer_.Remove(widget);
-}
-
-void PageActionIconView::WidgetObserver::OnWidgetVisibilityChanged(
-    views::Widget* widget,
-    bool visible) {
-  // |widget| is a bubble that has just got shown / hidden.
-  parent_->SetHighlighted(visible);
-}
diff --git a/chrome/browser/ui/views/page_action/page_action_icon_view.h b/chrome/browser/ui/views/page_action/page_action_icon_view.h
index e8cb7a2..372b25e 100644
--- a/chrome/browser/ui/views/page_action/page_action_icon_view.h
+++ b/chrome/browser/ui/views/page_action/page_action_icon_view.h
@@ -16,7 +16,6 @@
 #include "ui/gfx/image/image_skia.h"
 #include "ui/views/animation/ink_drop_host_view.h"
 #include "ui/views/controls/image_view.h"
-#include "ui/views/widget/widget_observer.h"
 
 class CommandUpdater;
 
@@ -49,10 +48,6 @@
 
   void set_icon_size(int size) { icon_size_ = size; }
 
-  // Invoked when a bubble for this icon is created. The PageActionIconView
-  // changes highlights based on this widget's visibility.
-  void OnBubbleWidgetCreated(views::Widget* bubble_widget);
-
   // Returns the bubble instance for the icon.
   virtual views::BubbleDialogDelegateView* GetBubble() const = 0;
 
@@ -141,30 +136,6 @@
   bool active() const { return active_; }
 
  private:
-  class WidgetObserver : public views::WidgetObserver {
-   public:
-    explicit WidgetObserver(PageActionIconView* parent);
-    ~WidgetObserver() override;
-
-    void SetWidget(views::Widget* widget);
-
-   private:
-    // views::WidgetObserver:
-    void OnWidgetDestroying(views::Widget* widget) override;
-    void OnWidgetVisibilityChanged(views::Widget* widget,
-                                   bool visible) override;
-
-    PageActionIconView* const parent_;
-    ScopedObserver<views::Widget, views::WidgetObserver> scoped_observer_;
-    DISALLOW_COPY_AND_ASSIGN(WidgetObserver);
-  };
-
-  // Highlights the ink drop for the icon, used when the corresponding widget
-  // is visible.
-  void SetHighlighted(bool bubble_visible);
-
-  WidgetObserver widget_observer_;
-
   // The size of the icon image (excluding the ink drop).
   int icon_size_;
 
diff --git a/chrome/browser/ui/views/page_info/page_info_bubble_view.cc b/chrome/browser/ui/views/page_info/page_info_bubble_view.cc
index 3c6404b..bd95f70 100644
--- a/chrome/browser/ui/views/page_info/page_info_bubble_view.cc
+++ b/chrome/browser/ui/views/page_info/page_info_bubble_view.cc
@@ -932,11 +932,8 @@
       PageInfoBubbleView::CreatePageInfoBubble(
           configuration.anchor_view, anchor_rect, parent_window,
           browser->profile(), web_contents, virtual_url, security_info);
+  bubble->SetHighlightedButton(configuration.highlighted_button);
   bubble->SetArrow(configuration.bubble_arrow);
-  BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser);
-  auto* location_bar = browser_view->GetLocationBarView();
-  if (location_bar)
-    location_bar->location_icon_view()->OnBubbleCreated(bubble->GetWidget());
   bubble->GetWidget()->Show();
 }
 #endif
diff --git a/chrome/browser/ui/views/passwords/password_bubble_view_base.cc b/chrome/browser/ui/views/passwords/password_bubble_view_base.cc
index 0c8e4827b..072d44a 100644
--- a/chrome/browser/ui/views/passwords/password_bubble_view_base.cc
+++ b/chrome/browser/ui/views/passwords/password_bubble_view_base.cc
@@ -47,15 +47,13 @@
     g_manage_passwords_bubble_->set_parent_window(
         web_contents->GetNativeView());
 
-  views::Widget* bubble_widget =
-      views::BubbleDialogDelegateView::CreateBubble(g_manage_passwords_bubble_);
-
   if (anchor_view) {
-    browser_view->GetLocationBarView()
-        ->manage_passwords_icon_view()
-        ->OnBubbleWidgetCreated(bubble_widget);
+    g_manage_passwords_bubble_->SetHighlightedButton(
+        browser_view->GetLocationBarView()->manage_passwords_icon_view());
   }
 
+  views::BubbleDialogDelegateView::CreateBubble(g_manage_passwords_bubble_);
+
   // Adjust for fullscreen after creation as it relies on the content size.
   if (is_fullscreen) {
     g_manage_passwords_bubble_->AdjustForFullscreen(
diff --git a/chrome/browser/ui/views/permission_bubble/chooser_bubble_ui.cc b/chrome/browser/ui/views/permission_bubble/chooser_bubble_ui.cc
index 7044b50..5efe6af 100644
--- a/chrome/browser/ui/views/permission_bubble/chooser_bubble_ui.cc
+++ b/chrome/browser/ui/views/permission_bubble/chooser_bubble_ui.cc
@@ -164,6 +164,7 @@
 void ChooserBubbleUiViewDelegate::UpdateAnchor(Browser* browser) {
   AnchorConfiguration configuration = GetChooserAnchorConfiguration(browser);
   SetAnchorView(configuration.anchor_view);
+  SetHighlightedButton(configuration.highlighted_button);
   if (!configuration.anchor_view)
     SetAnchorRect(GetChooserAnchorRect(browser));
   SetArrow(configuration.bubble_arrow);
@@ -199,6 +200,7 @@
 
 void ChooserBubbleUi::Show(BubbleReference bubble_reference) {
   chooser_bubble_ui_view_delegate_->set_bubble_reference(bubble_reference);
+  chooser_bubble_ui_view_delegate_->UpdateAnchor(browser_);
   CreateAndShow(chooser_bubble_ui_view_delegate_);
   chooser_bubble_ui_view_delegate_->GetWidget()->AddObserver(this);
   chooser_bubble_ui_view_delegate_->UpdateTableView();
diff --git a/chrome/browser/ui/views/permission_bubble/permission_prompt_impl.cc b/chrome/browser/ui/views/permission_bubble/permission_prompt_impl.cc
index 8bcdde7..ee3a77b 100644
--- a/chrome/browser/ui/views/permission_bubble/permission_prompt_impl.cc
+++ b/chrome/browser/ui/views/permission_bubble/permission_prompt_impl.cc
@@ -258,6 +258,7 @@
   AnchorConfiguration configuration =
       GetPermissionAnchorConfiguration(owner_->browser());
   SetAnchorView(configuration.anchor_view);
+  SetHighlightedButton(configuration.highlighted_button);
   if (!configuration.anchor_view)
     SetAnchorRect(GetPermissionAnchorRect(owner_->browser()));
   SetArrow(configuration.bubble_arrow);
diff --git a/chrome/browser/ui/views/tabs/browser_tab_strip_controller.cc b/chrome/browser/ui/views/tabs/browser_tab_strip_controller.cc
index 068a5a43..f0ede22 100644
--- a/chrome/browser/ui/views/tabs/browser_tab_strip_controller.cc
+++ b/chrome/browser/ui/views/tabs/browser_tab_strip_controller.cc
@@ -16,7 +16,6 @@
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/extensions/tab_helper.h"
 #include "chrome/browser/favicon/favicon_utils.h"
-#include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/search/search.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_navigator_params.h"
@@ -346,11 +345,6 @@
     model_->delegate()->AddTabAt(match.destination_url, -1, true);
 }
 
-bool BrowserTabStripController::IsIncognito() {
-  return browser_view_->browser()->profile()->GetProfileType() ==
-      Profile::INCOGNITO_PROFILE;
-}
-
 void BrowserTabStripController::StackedLayoutMaybeChanged() {
   bool adjust_layout = false;
   bool stacked_layout = DetermineTabStripLayoutStacked(
@@ -386,6 +380,10 @@
   immersive_reveal_lock_.reset();
 }
 
+bool BrowserTabStripController::IsFrameCondensed() const {
+  return GetFrameView()->IsFrameCondensed();
+}
+
 bool BrowserTabStripController::HasVisibleBackgroundTabShapes() const {
   return GetFrameView()->HasVisibleBackgroundTabShapes(
       BrowserNonClientFrameView::kUseCurrent);
diff --git a/chrome/browser/ui/views/tabs/browser_tab_strip_controller.h b/chrome/browser/ui/views/tabs/browser_tab_strip_controller.h
index d2bf377..6d79dc4 100644
--- a/chrome/browser/ui/views/tabs/browser_tab_strip_controller.h
+++ b/chrome/browser/ui/views/tabs/browser_tab_strip_controller.h
@@ -70,12 +70,12 @@
   NewTabButtonPosition GetNewTabButtonPosition() const override;
   void CreateNewTab() override;
   void CreateNewTabWithLocation(const base::string16& loc) override;
-  bool IsIncognito() override;
   void StackedLayoutMaybeChanged() override;
   bool IsSingleTabModeAvailable() override;
   bool ShouldDrawStrokes() const override;
   void OnStartedDraggingTabs() override;
   void OnStoppedDraggingTabs() override;
+  bool IsFrameCondensed() const override;
   bool HasVisibleBackgroundTabShapes() const override;
   bool EverHasVisibleBackgroundTabShapes() const override;
   SkColor GetFrameColor() const override;
diff --git a/chrome/browser/ui/views/tabs/fake_base_tab_strip_controller.cc b/chrome/browser/ui/views/tabs/fake_base_tab_strip_controller.cc
index e972647..178eaeb 100644
--- a/chrome/browser/ui/views/tabs/fake_base_tab_strip_controller.cc
+++ b/chrome/browser/ui/views/tabs/fake_base_tab_strip_controller.cc
@@ -129,10 +129,6 @@
     const base::string16& location) {
 }
 
-bool FakeBaseTabStripController::IsIncognito() {
-  return false;
-}
-
 void FakeBaseTabStripController::StackedLayoutMaybeChanged() {
 }
 
@@ -144,10 +140,12 @@
   return false;
 }
 
-void FakeBaseTabStripController::OnStartedDraggingTabs() {
-}
+void FakeBaseTabStripController::OnStartedDraggingTabs() {}
 
-void FakeBaseTabStripController::OnStoppedDraggingTabs() {
+void FakeBaseTabStripController::OnStoppedDraggingTabs() {}
+
+bool FakeBaseTabStripController::IsFrameCondensed() const {
+  return false;
 }
 
 bool FakeBaseTabStripController::HasVisibleBackgroundTabShapes() const {
diff --git a/chrome/browser/ui/views/tabs/fake_base_tab_strip_controller.h b/chrome/browser/ui/views/tabs/fake_base_tab_strip_controller.h
index 1ee66e1..df1cd01 100644
--- a/chrome/browser/ui/views/tabs/fake_base_tab_strip_controller.h
+++ b/chrome/browser/ui/views/tabs/fake_base_tab_strip_controller.h
@@ -46,12 +46,12 @@
   NewTabButtonPosition GetNewTabButtonPosition() const override;
   void CreateNewTab() override;
   void CreateNewTabWithLocation(const base::string16& loc) override;
-  bool IsIncognito() override;
   void StackedLayoutMaybeChanged() override;
   bool IsSingleTabModeAvailable() override;
   bool ShouldDrawStrokes() const override;
   void OnStartedDraggingTabs() override;
   void OnStoppedDraggingTabs() override;
+  bool IsFrameCondensed() const override;
   bool HasVisibleBackgroundTabShapes() const override;
   bool EverHasVisibleBackgroundTabShapes() const override;
   SkColor GetFrameColor() const override;
diff --git a/chrome/browser/ui/views/tabs/new_tab_button.cc b/chrome/browser/ui/views/tabs/new_tab_button.cc
index f320d6d1..3eaaa6b 100644
--- a/chrome/browser/ui/views/tabs/new_tab_button.cc
+++ b/chrome/browser/ui/views/tabs/new_tab_button.cc
@@ -5,7 +5,6 @@
 #include "chrome/browser/ui/views/tabs/new_tab_button.h"
 
 #include "build/build_config.h"
-#include "chrome/app/vector_icons/vector_icons.h"
 #include "chrome/browser/themes/theme_properties.h"
 #include "chrome/browser/ui/browser_list.h"
 #include "chrome/browser/ui/layout_constants.h"
@@ -13,22 +12,16 @@
 #include "chrome/browser/ui/views/feature_promos/new_tab_promo_bubble_view.h"
 #include "chrome/browser/ui/views/tabs/browser_tab_strip_controller.h"
 #include "chrome/browser/ui/views/tabs/tab_strip.h"
-#include "chrome/grit/theme_resources.h"
 #include "components/feature_engagement/buildflags.h"
-#include "third_party/skia/include/core/SkColorFilter.h"
-#include "third_party/skia/include/core/SkMaskFilter.h"
-#include "third_party/skia/include/effects/SkLayerDrawLooper.h"
-#include "third_party/skia/include/pathops/SkPathOps.h"
 #include "ui/base/default_theme_provider.h"
 #include "ui/base/material_design/material_design_controller.h"
-#include "ui/gfx/color_palette.h"
 #include "ui/gfx/color_utils.h"
-#include "ui/gfx/paint_vector_icon.h"
 #include "ui/gfx/scoped_canvas.h"
 #include "ui/views/animation/flood_fill_ink_drop_ripple.h"
 #include "ui/views/animation/ink_drop_impl.h"
 #include "ui/views/animation/ink_drop_mask.h"
 #include "ui/views/widget/widget.h"
+
 #if defined(OS_WIN)
 #include "ui/display/win/screen_win.h"
 #include "ui/gfx/win/hwnd_util.h"
@@ -41,90 +34,37 @@
 #include "chrome/browser/ui/views/feature_promos/new_tab_promo_bubble_view.h"
 #endif
 
-using MD = ui::MaterialDesignController;
-
 namespace {
 
-constexpr int kDistanceBetweenIcons = 6;
-constexpr int kStrokeThickness = 1;
-
-// Returns the size of the button without any spacing/padding.
-gfx::Size GetButtonSize(bool is_incognito) {
-  const gfx::Size sizes[] = {
-      {36, 18}, {39, 21}, {(is_incognito ? 42 : 24), 24}, {28, 28}, {28, 28}};
-  return sizes[MD::GetMode()];
-}
-
-sk_sp<SkDrawLooper> CreateShadowDrawLooper(SkColor color) {
-  SkLayerDrawLooper::Builder looper_builder;
-  looper_builder.addLayer();
-
-  SkLayerDrawLooper::LayerInfo layer_info;
-  layer_info.fPaintBits |= SkLayerDrawLooper::kMaskFilter_Bit;
-  layer_info.fPaintBits |= SkLayerDrawLooper::kColorFilter_Bit;
-  layer_info.fColorMode = SkBlendMode::kDst;
-  layer_info.fOffset.set(0, 1);
-  SkPaint* layer_paint = looper_builder.addLayer(layer_info);
-  layer_paint->setMaskFilter(SkMaskFilter::MakeBlur(kNormal_SkBlurStyle, 0.5));
-  layer_paint->setColorFilter(
-      SkColorFilter::MakeModeFilter(color, SkBlendMode::kSrcIn));
-  return looper_builder.detach();
-}
-
-// Returns the ID of the resource that should be used for the button fill if
-// any. |has_custom_image| will be set to true if the images of either the
-// tab, the frame background, (or the toolbar if |is_non_refresh_touch_ui| is
-// true) have been customized.
-int GetButtonFillResourceIdIfAny(const TabStrip* tab_strip,
-                                 const ui::ThemeProvider* theme_provider,
-                                 bool is_non_refresh_touch_ui,
-                                 bool* has_custom_image) {
-  if (!is_non_refresh_touch_ui)
-    return tab_strip->GetBackgroundResourceId(has_custom_image);
-
-  constexpr int kTouchBackgroundId = IDR_THEME_TOOLBAR;
-  *has_custom_image = theme_provider->HasCustomImage(kTouchBackgroundId);
-  return kTouchBackgroundId;
-}
+constexpr gfx::Size kButtonSize(28, 28);
 
 }  // namespace
 
 NewTabButton::NewTabButton(TabStrip* tab_strip, views::ButtonListener* listener)
-    : views::ImageButton(listener),
-      tab_strip_(tab_strip),
-      is_incognito_(tab_strip->IsIncognito()) {
+    : views::ImageButton(listener), tab_strip_(tab_strip) {
   set_animate_on_state_change(true);
 #if defined(OS_LINUX) && !defined(OS_CHROMEOS)
   set_triggerable_event_flags(triggerable_event_flags() |
                               ui::EF_MIDDLE_MOUSE_BUTTON);
 #endif
 
-  if (MD::IsNewerMaterialUi()) {
-    // Initialize the ink drop mode for a ripple highlight on button press.
-    ink_drop_container_ = new views::InkDropContainerView();
-    AddChildView(ink_drop_container_);
-    ink_drop_container_->SetVisible(false);
+  // Initialize the ink drop mode for a ripple highlight on button press.
+  ink_drop_container_ = new views::InkDropContainerView();
+  AddChildView(ink_drop_container_);
+  ink_drop_container_->SetVisible(false);
+  SetInkDropMode(InkDropMode::ON_NO_GESTURE_HANDLER);
+  set_ink_drop_visible_opacity(0.08f);
 
-    SetInkDropMode(InkDropMode::ON_NO_GESTURE_HANDLER);
-    set_ink_drop_visible_opacity(0.08f);
+  SetFocusPainter(nullptr);
+  SetInstallFocusRingOnFocus(true);
 
-    SetFocusPainter(nullptr);
-    SetInstallFocusRingOnFocus(true);
-  }
-
-  // In newer material UI, the button is placed vertically exactly in the
-  // center of the tabstrip.  In older UI, the new tab button is placed at a
-  // fixed distance from the bottom of the tabstrip.
+  // The button is placed vertically exactly in the center of the tabstrip.
   const int extra_vertical_space = GetLayoutConstant(TAB_HEIGHT) -
                                    GetLayoutConstant(TABSTRIP_TOOLBAR_OVERLAP) -
-                                   GetButtonSize(is_incognito_).height();
-  constexpr int kNewTabButtonBottomOffset = 4;
-  const int top = MD::IsNewerMaterialUi()
-                      ? (extra_vertical_space / 2)
-                      : (extra_vertical_space - kNewTabButtonBottomOffset);
-  const int horizontal = MD::IsRefreshUi() ? 8 : 0;
-  SetBorder(
-      views::CreateEmptyBorder(gfx::Insets(top, horizontal, 0, horizontal)));
+                                   kButtonSize.height();
+  constexpr int kHorizontalInset = 8;
+  SetBorder(views::CreateEmptyBorder(gfx::Insets(
+      extra_vertical_space / 2, kHorizontalInset, 0, kHorizontalInset)));
 }
 
 NewTabButton::~NewTabButton() {
@@ -136,7 +76,6 @@
 void NewTabButton::ShowPromoForLastActiveBrowser() {
   BrowserView* browser = static_cast<BrowserView*>(
       BrowserList::GetInstance()->GetLastActive()->window());
-
   browser->tabstrip()->new_tab_button()->ShowPromo();
 }
 
@@ -161,8 +100,7 @@
 }
 
 void NewTabButton::FrameColorsChanged() {
-  if (MD::IsRefreshUi())
-    UpdateInkDropBaseColor();
+  UpdateInkDropBaseColor();
 }
 
 void NewTabButton::AnimateInkDropToStateForTesting(views::InkDropState state) {
@@ -171,21 +109,19 @@
 
 #if defined(OS_WIN)
 void NewTabButton::OnMouseReleased(const ui::MouseEvent& event) {
-  if (event.IsOnlyRightMouseButton()) {
-    gfx::Point point = event.location();
-    views::View::ConvertPointToScreen(this, &point);
-    point = display::win::ScreenWin::DIPToScreenPoint(point);
-    bool destroyed = false;
-    destroyed_ = &destroyed;
-    gfx::ShowSystemMenuAtPoint(views::HWNDForView(this), point);
-    if (destroyed)
-      return;
-
-    destroyed_ = NULL;
-    SetState(views::Button::STATE_NORMAL);
+  if (!event.IsOnlyRightMouseButton()) {
+    views::ImageButton::OnMouseReleased(event);
     return;
   }
-  views::ImageButton::OnMouseReleased(event);
+
+  gfx::Point point = event.location();
+  views::View::ConvertPointToScreen(this, &point);
+  point = display::win::ScreenWin::DIPToScreenPoint(point);
+  bool destroyed = false;
+  destroyed_ = &destroyed;
+  gfx::ShowSystemMenuAtPoint(views::HWNDForView(this), point);
+  if (!destroyed)
+    SetState(views::Button::STATE_NORMAL);
 }
 #endif
 
@@ -247,122 +183,26 @@
 
 void NewTabButton::PaintButtonContents(gfx::Canvas* canvas) {
   gfx::ScopedCanvas scoped_canvas(canvas);
-  const gfx::Rect contents_bounds = GetContentsBounds();
-  canvas->Translate(contents_bounds.OffsetFromOrigin());
-  const float scale = canvas->image_scale();
-  const bool pressed = state() == views::Button::STATE_PRESSED;
-  const SkColor stroke_color =
-      new_tab_promo_observer_.IsObservingSources()
-          ? color_utils::AlphaBlend(
-                SK_ColorBLACK,
-                GetNativeTheme()->GetSystemColor(
-                    ui::NativeTheme::kColorId_ProminentButtonColor),
-                0x70)
-          : tab_strip_->GetToolbarTopSeparatorColor();
-
-  // Fill.
-  SkPath fill, stroke;
-  const bool non_refresh_touch_ui =
-      MD::GetMode() == ui::MaterialDesignController::MATERIAL_TOUCH_OPTIMIZED;
-  fill =
-      MD::IsNewerMaterialUi()
-          ? GetNewerMaterialUiButtonPath(0, scale, false, non_refresh_touch_ui)
-          : GetMaterialUiButtonPath(0, contents_bounds.height(), scale, false,
-                                    true);
-  // The ink drop is used to represent the pressed state under Refresh.
-  PaintFill(!MD::IsRefreshUi() && pressed, scale, fill, canvas);
-
-  // Stroke.
-  if (!MD::IsRefreshUi())
-    GetBorderPath(0, scale, false, &stroke);
-
-  if (MD::IsNewerMaterialUi()) {
-    const int plus_icon_size = MD::IsTouchOptimizedUiEnabled() ? 14 : 12;
-    const int plus_icon_offset = GetCornerRadius() - (plus_icon_size / 2);
-
-    PaintPlusIcon(canvas, plus_icon_offset, plus_icon_size);
-
-    cc::PaintFlags paint_flags;
-    paint_flags.setAntiAlias(true);
-    if (ShouldDrawIncognitoIcon()) {
-      DCHECK(!incognito_icon_.isNull());
-      canvas->DrawImageInt(
-          incognito_icon_,
-          plus_icon_offset + plus_icon_size + kDistanceBetweenIcons,
-          plus_icon_offset, paint_flags);
-    }
-
-    if (!MD::IsRefreshUi()) {
-      // Draw stroke.
-      // In the touch-optimized UI design, the new tab button is rendered flat,
-      // regardless of whether pressed or not (i.e. we don't emulate a pushed
-      // button by drawing a drop shadow). Instead, we're using an ink drop
-      // ripple effect.
-      // Here we want to make sure the stroke width is 1px regardless of the
-      // device scale factor, so undo the scale.
-      canvas->UndoDeviceScaleFactor();
-      Op(stroke, fill, kDifference_SkPathOp, &stroke);
-      paint_flags.setColor(stroke_color);
-      canvas->DrawPath(stroke, paint_flags);
-    }
-  } else {
-    // We want to draw a drop shadow either inside or outside the stroke,
-    // depending on whether we're pressed; so, either clip out what's outside
-    // the stroke, or clip out the fill inside it.
-    canvas->UndoDeviceScaleFactor();
-    if (pressed)
-      canvas->ClipPath(stroke, true);
-    Op(stroke, fill, kDifference_SkPathOp, &stroke);
-    if (!pressed)
-      canvas->sk_canvas()->clipPath(fill, SkClipOp::kDifference, true);
-    // Now draw the stroke and shadow; the stroke will always be visible, while
-    // the shadow will be affected by the clip we set above.
-    cc::PaintFlags flags;
-    flags.setAntiAlias(true);
-    const float alpha = SkColorGetA(stroke_color);
-    const SkAlpha shadow_alpha =
-        base::saturated_cast<SkAlpha>(std::round(2.1875f * alpha));
-    flags.setLooper(
-        CreateShadowDrawLooper(SkColorSetA(stroke_color, shadow_alpha)));
-    const SkAlpha path_alpha = static_cast<SkAlpha>(
-        std::round((pressed ? 0.875f : 0.609375f) * alpha));
-    flags.setColor(SkColorSetA(stroke_color, path_alpha));
-    canvas->DrawPath(stroke, flags);
-  }
+  canvas->Translate(GetContentsBounds().OffsetFromOrigin());
+  PaintFill(canvas);
+  PaintPlusIcon(canvas);
 }
 
 void NewTabButton::Layout() {
   ImageButton::Layout();
 
-  if (MD::IsNewerMaterialUi()) {
-    if (ShouldDrawIncognitoIcon() && incognito_icon_.isNull())
-      InitIncognitoIcon();
+  // TODO(pkasting): Instead of setting this bounds rect, maybe have the
+  // container match the view bounds, then undo the coordinate transforms in
+  // the ink drop creation methods above.
+  const gfx::Rect contents_bounds = GetContentsBounds();
+  ink_drop_container_->SetBoundsRect(contents_bounds);
 
-    // TODO(pkasting): Instead of setting this bounds rect, maybe have the
-    // container match the view bounds, then undo the coordinate transforms in
-    // the ink drop creation methods above.
-    const gfx::Rect contents_bounds = GetContentsBounds();
-    ink_drop_container_->SetBoundsRect(contents_bounds);
-
-    SkPath path;
-    path.addOval(gfx::RectToSkRect(contents_bounds));
-    focus_ring()->SetPath(path);
-  }
-}
-
-void NewTabButton::OnThemeChanged() {
-  ImageButton::OnThemeChanged();
-
-  if (!MD::IsNewerMaterialUi())
-    return;
-
-  if (ShouldDrawIncognitoIcon())
-    InitIncognitoIcon();
-  UpdateInkDropBaseColor();
+  focus_ring()->SetPath(
+      GetBorderPath(GetContentsBounds().origin(), 1.0f, false));
 }
 
 gfx::Size NewTabButton::CalculatePreferredSize() const {
-  gfx::Size size = GetButtonSize(is_incognito_);
+  gfx::Size size = kButtonSize;
   const auto insets = GetInsets();
   size.Enlarge(insets.width(), insets.height());
   return size;
@@ -377,13 +217,11 @@
 bool NewTabButton::GetHitTestMask(gfx::Path* mask) const {
   DCHECK(mask);
 
-  SkPath border;
-  const gfx::Point contents_origin = GetContentsBounds().origin();
   const float scale = GetWidget()->GetCompositor()->device_scale_factor();
   // TODO(pkasting): Fitts' Law horizontally when appropriate.
-  GetBorderPath(contents_origin.y() * scale, scale,
-                tab_strip_->SizeTabButtonToTopOfTabStrip(), &border);
-  border.offset(contents_origin.x(), 0);
+  gfx::Path border =
+      GetBorderPath(GetContentsBounds().origin(), scale,
+                    tab_strip_->controller()->IsFrameCondensed());
   mask->addPath(border, SkMatrix::MakeScale(1 / scale));
   return true;
 }
@@ -396,138 +234,70 @@
 #endif
   new_tab_promo_observer_.Remove(widget);
   new_tab_promo_ = nullptr;
-  // When the promo widget is destroyed, the NewTabButton needs to be
-  // recolored.
+  // When the promo widget is destroyed, the NewTabButton needs to be recolored.
   SchedulePaint();
 }
 
-bool NewTabButton::ShouldDrawIncognitoIcon() const {
-  return is_incognito_ && MD::GetMode() == MD::MATERIAL_TOUCH_OPTIMIZED;
-}
-
 int NewTabButton::GetCornerRadius() const {
   return ChromeLayoutProvider::Get()->GetCornerRadiusMetric(
       views::EMPHASIS_MAXIMUM, GetContentsBounds().size());
 }
 
-void NewTabButton::GetBorderPath(float button_y,
-                                 float scale,
-                                 bool extend_to_top,
-                                 SkPath* path) const {
-  *path =
-      MD::IsNewerMaterialUi()
-          ? GetNewerMaterialUiButtonPath(button_y, scale, extend_to_top, false)
-          : GetMaterialUiButtonPath(button_y, GetContentsBounds().height(),
-                                    scale, extend_to_top, false);
-}
-
-void NewTabButton::PaintFill(bool pressed,
-                             float scale,
-                             const SkPath& fill,
-                             gfx::Canvas* canvas) const {
+void NewTabButton::PaintFill(gfx::Canvas* canvas) const {
   gfx::ScopedCanvas scoped_canvas(canvas);
   canvas->UndoDeviceScaleFactor();
   cc::PaintFlags flags;
   flags.setAntiAlias(true);
 
-  // For unpressed buttons, draw the fill and its shadow.
-  // Note that for newer UI, we always draw the fill since the button
-  // has a flat design. Hover highlights are handled by the ink drop.
-  const bool is_newer_ui = MD::IsNewerMaterialUi();
-  if (is_newer_ui || !pressed) {
-    const bool non_refresh_touch_ui =
-        MD::GetMode() == ui::MaterialDesignController::MATERIAL_TOUCH_OPTIMIZED;
-    // First we compute the background image coordinates and scale, in case we
-    // need to draw a custom background image.
-    const ui::ThemeProvider* tp = GetThemeProvider();
-
-    bool has_custom_image;
-    const int bg_id = GetButtonFillResourceIdIfAny(
-        tab_strip_, tp, non_refresh_touch_ui, &has_custom_image);
-    if (has_custom_image && !new_tab_promo_observer_.IsObservingSources()) {
-      // For non-refresh touch UI, the background is that of the active tab, so
-      // the positioning must match that in Tab::PaintTab().
-      const int offset_y =
-          non_refresh_touch_ui ? -tab_strip_->GetStrokeThickness() : 0;
+  bool has_custom_image;
+  const int bg_id = tab_strip_->GetBackgroundResourceId(&has_custom_image);
+  const float scale = canvas->image_scale();
+  if (has_custom_image && !new_tab_promo_observer_.IsObservingSources()) {
+    float x_scale = scale;
+    const gfx::Rect& contents_bounds = GetContentsBounds();
+    int x = GetMirroredX() + contents_bounds.x() + background_offset_;
+    if (base::i18n::IsRTL()) {
       // The new tab background is mirrored in RTL mode, but the theme
       // background should never be mirrored. Mirror it here to compensate.
-      float x_scale = 1.0f;
-      const gfx::Rect& contents_bounds = GetContentsBounds();
-      int x = GetMirroredX() + contents_bounds.x() + background_offset_;
-      if (base::i18n::IsRTL()) {
-        x_scale = -1.0f;
-        // Offset by |width| such that the same region is painted as if there
-        // was no flip.
-        x += contents_bounds.width();
-      }
-
-      const bool succeeded = canvas->InitPaintFlagsForTiling(
-          *tp->GetImageSkiaNamed(bg_id), x, contents_bounds.y() + offset_y,
-          x_scale * scale, scale, 0, 0, SkShader::kRepeat_TileMode,
-          SkShader::kRepeat_TileMode, &flags);
-      DCHECK(succeeded);
-    } else {
-      flags.setColor(GetButtonFillColor());
+      x_scale = -scale;
+      // Offset by |width| such that the same region is painted as if there
+      // was no flip.
+      x += contents_bounds.width();
     }
 
-    cc::PaintFlags shadow_flags = flags;
-    // For Refresh, don't draw a shadow.
-    if (!MD::IsRefreshUi()) {
-      const SkColor stroke_color = tab_strip_->GetToolbarTopSeparatorColor();
-      const SkAlpha alpha = static_cast<SkAlpha>(
-          std::round(SkColorGetA(stroke_color) * 0.59375f));
-      shadow_flags.setLooper(
-          CreateShadowDrawLooper(SkColorSetA(stroke_color, alpha)));
-    }
-    canvas->DrawPath(fill, shadow_flags);
-
-    if (is_newer_ui) {
-      // We don't have hover/pressed states in the newer UI design.
-      // Instead we are using an ink drop effect.
-      return;
-    }
+    canvas->InitPaintFlagsForTiling(
+        *GetThemeProvider()->GetImageSkiaNamed(bg_id), x, contents_bounds.y(),
+        x_scale, scale, 0, 0, SkShader::kRepeat_TileMode,
+        SkShader::kRepeat_TileMode, &flags);
+  } else {
+    flags.setColor(GetButtonFillColor());
   }
 
-  // Draw a white highlight on hover.
-  const SkAlpha hover_alpha =
-      static_cast<SkAlpha>(hover_animation().CurrentValueBetween(0x00, 0x4D));
-  if (hover_alpha != SK_AlphaTRANSPARENT) {
-    flags.setColor(SkColorSetA(SK_ColorWHITE, hover_alpha));
-    canvas->DrawPath(fill, flags);
-  }
-
-  // Most states' opacities are adjusted using an opacity recorder in
-  // TabStrip::PaintChildren(), but the pressed state is excluded there and
-  // instead rendered using a dark overlay here.  Avoiding the use of the
-  // opacity recorder keeps the stroke more visible in this state.
-  if (pressed) {
-    flags.setColor(SkColorSetA(SK_ColorBLACK, 0x14));
-    canvas->DrawPath(fill, flags);
-  }
+  canvas->DrawPath(GetBorderPath(gfx::Point(), scale, false), flags);
 }
 
-void NewTabButton::PaintPlusIcon(gfx::Canvas* canvas, int offset, int size) {
+void NewTabButton::PaintPlusIcon(gfx::Canvas* canvas) const {
+  cc::PaintFlags flags;
+  flags.setAntiAlias(true);
+  flags.setColor(tab_strip_->GetTabForegroundColor(TAB_INACTIVE));
+  flags.setStrokeCap(cc::PaintFlags::kRound_Cap);
   constexpr int kStrokeWidth = 2;
+  flags.setStrokeWidth(kStrokeWidth);
+
+  const int radius =
+      ui::MaterialDesignController::IsTouchOptimizedUiEnabled() ? 7 : 6;
+  const int offset = GetCornerRadius() - radius;
+  // The cap will be added outside the end of the stroke; inset to compensate.
   constexpr int kCapRadius = kStrokeWidth / 2;
-
-  cc::PaintFlags paint_flags;
-  paint_flags.setAntiAlias(true);
-  paint_flags.setColor(GetIconColor());
-  paint_flags.setStrokeCap(cc::PaintFlags::kRound_Cap);
-  paint_flags.setStrokeWidth(kStrokeWidth);
-
-  // With a round end-cap, the apparent line length will extend past the end
-  // points by one radius of the cap. Reduce the specified length to take this
-  // into account.
   const int start = offset + kCapRadius;
-  const int end = offset + size - kCapRadius;
-  const int center = offset + size / 2;
-  // Draw the horizontal leg of the plus (+) icon
-  canvas->DrawLine(gfx::PointF(start, center), gfx::PointF(end, center),
-                   paint_flags);
-  // Draw the vertical leg of the plus (+) icon
-  canvas->DrawLine(gfx::PointF(center, start), gfx::PointF(center, end),
-                   paint_flags);
+  const int end = offset + (radius * 2) - kCapRadius;
+  const int center = offset + radius;
+
+  // Horizontal stroke.
+  canvas->DrawLine(gfx::PointF(start, center), gfx::PointF(end, center), flags);
+
+  // Vertical stroke.
+  canvas->DrawLine(gfx::PointF(center, start), gfx::PointF(center, end), flags);
 }
 
 SkColor NewTabButton::GetButtonFillColor() const {
@@ -542,112 +312,30 @@
              : SK_ColorTRANSPARENT;
 }
 
-SkColor NewTabButton::GetIconColor() const {
-  return tab_strip_->GetTabForegroundColor(MD::IsRefreshUi() ? TAB_INACTIVE
-                                                             : TAB_ACTIVE);
-}
-
-void NewTabButton::InitIncognitoIcon() {
-  DCHECK(ShouldDrawIncognitoIcon());
-  incognito_icon_ =
-      gfx::CreateVectorIcon(kNewTabButtonIncognitoIcon, GetIconColor());
-}
-
-SkPath NewTabButton::GetNewerMaterialUiButtonPath(float button_y,
-                                                  float scale,
-                                                  bool extend_to_top,
-                                                  bool for_fill) const {
-  DCHECK(MD::IsNewerMaterialUi());
-
+gfx::Path NewTabButton::GetBorderPath(const gfx::Point& origin,
+                                      float scale,
+                                      bool extend_to_top) const {
+  gfx::PointF scaled_origin(origin);
+  scaled_origin.Scale(scale);
   const float radius = GetCornerRadius() * scale;
-  const float rect_width =
-      2 * radius +
-      (ShouldDrawIncognitoIcon()
-           ? scale * (incognito_icon_.width() + kDistanceBetweenIcons)
-           : 0);
 
-  const SkRect button_rect =
-      SkRect::MakeXYWH(0, button_y, rect_width, 2 * radius);
-  SkRRect rrect = SkRRect::MakeRectXY(button_rect, radius, radius);
-  // Inset by 1px for a fill path to give room for the stroke to show up. The
-  // stroke width is 1px regardless of the device scale factor.
-  if (for_fill)
-    rrect.inset(kStrokeThickness, kStrokeThickness);
-
-  SkPath path;
-  path.addRRect(rrect, SkPath::kCW_Direction);
-
+  gfx::Path path;
   if (extend_to_top) {
-    SkPath extension_path;
-    extension_path.addRect(
-        SkRect::MakeXYWH(0, 0, rect_width, button_y + radius),
-        SkPath::kCW_Direction);
-    Op(path, extension_path, kUnion_SkPathOp, &path);
-  }
-
-  path.close();
-  return path;
-}
-
-SkPath NewTabButton::GetMaterialUiButtonPath(int button_y,
-                                             int button_height,
-                                             float scale,
-                                             bool extend_to_top,
-                                             bool for_fill) const {
-  const float inverse_slope = Tab::GetInverseDiagonalSlope();
-  float bottom = (button_height - 2) * scale;
-  const float diag_height = bottom - 3.5 * scale;
-  const float diag_width = diag_height * inverse_slope;
-  const float right = diag_width + 4 * scale;
-  const int stroke_thickness = for_fill ? 0 : kStrokeThickness;
-  bottom += button_y + stroke_thickness;
-
-  SkPath path;
-  path.moveTo(right - stroke_thickness, bottom);
-  path.rCubicTo(-0.75 * scale, 0, -1.625 * scale, -0.5 * scale, -2 * scale,
-                -1.5 * scale);
-  path.rLineTo(-diag_width, -diag_height);
-  if (extend_to_top) {
-    // Create the vertical extension by extending the side diagonals at the
-    // upper left and lower right corners until they reach the top and bottom of
-    // the border, respectively (in other words, "un-round-off" those corners
-    // and turn them into sharp points).  Then extend upward from the corner
-    // points to the top of the bounds.
-    const float dy = scale + stroke_thickness * 2;
-    const float dx = inverse_slope * dy;
-    path.rLineTo(-dx, -dy);
-    path.rLineTo(0, -button_y - scale + stroke_thickness);
-    path.lineTo((width() - 2) * scale + stroke_thickness + dx, 0);
-    path.rLineTo(0, bottom);
+    path.moveTo(scaled_origin.x(), 0);
+    const float diameter = radius * 2;
+    path.rLineTo(diameter, 0);
+    path.rLineTo(0, scaled_origin.y() + radius);
+    path.rArcTo(radius, radius, 0, SkPath::kSmall_ArcSize,
+                SkPath::kCW_Direction, -diameter, 0);
+    path.close();
   } else {
-    if (for_fill) {
-      path.rCubicTo(0, -0.5 * scale, 0.25 * scale, -scale, scale, -scale);
-    } else {
-      path.rCubicTo(-0.5 * scale, -1.125 * scale, 0.5 * scale,
-                    -scale - 2 * stroke_thickness, scale,
-                    -scale - 2 * stroke_thickness);
-    }
-    path.lineTo((width() - 4) * scale - diag_width + stroke_thickness,
-                button_y + scale - stroke_thickness);
-    path.rCubicTo(0.75 * scale, 0, 1.625 * scale, 0.5 * scale, 2 * scale,
-                  1.5 * scale);
-    path.rLineTo(diag_width, diag_height);
-    if (for_fill) {
-      path.rCubicTo(0, 0.5 * scale, -0.25 * scale, scale, -scale, scale);
-    } else {
-      path.rCubicTo(0.5 * scale, 1.125 * scale, -0.5 * scale,
-                    scale + 2 * stroke_thickness, -scale,
-                    scale + 2 * stroke_thickness);
-    }
+    path.addCircle(scaled_origin.x() + radius, scaled_origin.y() + radius,
+                   radius);
   }
-  path.close();
-
   return path;
 }
 
 void NewTabButton::UpdateInkDropBaseColor() {
-  DCHECK(MD::IsNewerMaterialUi());
-
   set_ink_drop_base_color(color_utils::BlendTowardOppositeLuma(
       GetButtonFillColor(), SK_AlphaOPAQUE));
 }
diff --git a/chrome/browser/ui/views/tabs/new_tab_button.h b/chrome/browser/ui/views/tabs/new_tab_button.h
index f4326ea8..558a0e3 100644
--- a/chrome/browser/ui/views/tabs/new_tab_button.h
+++ b/chrome/browser/ui/views/tabs/new_tab_button.h
@@ -73,7 +73,6 @@
   std::unique_ptr<views::InkDropMask> CreateInkDropMask() const override;
   void PaintButtonContents(gfx::Canvas* canvas) override;
   void Layout() override;
-  void OnThemeChanged() override;
   gfx::Size CalculatePreferredSize() const override;
   void OnBoundsChanged(const gfx::Rect& previous_bounds) override;
 
@@ -83,54 +82,22 @@
   // views::WidgetObserver:
   void OnWidgetDestroying(views::Widget* widget) override;
 
-  // Returns whether this button should draw an incognito icon.
-  bool ShouldDrawIncognitoIcon() const;
-
   // Returns the radius to use for the button corners (in newer material UI).
   int GetCornerRadius() const;
 
-  // Computes a path corresponding to the button's outer border for a given
-  // |scale| and stores it in |path|.  |button_y| is used as the y-coordinate
-  // for the top of the button.  If |extend_to_top| is true, the path is
-  // extended vertically to y = 0.  The caller uses this for Fitts' Law purposes
-  // in maximized/fullscreen mode.
-  void GetBorderPath(float button_y,
-                     float scale,
-                     bool extend_to_top,
-                     SkPath* path) const;
-
-  // Paints the fill region of the button into |canvas|, according to the
-  // supplied values from GetImage() and the given |fill| path.
-  void PaintFill(bool pressed,
-                 float scale,
-                 const SkPath& fill,
-                 gfx::Canvas* canvas) const;
+  // Paints the fill region of the button into |canvas|.
+  void PaintFill(gfx::Canvas* canvas) const;
 
   // Paints a properly sized plus (+) icon into the center of the button.
-  void PaintPlusIcon(gfx::Canvas* canvas, int offset, int size);
+  void PaintPlusIcon(gfx::Canvas* canvas) const;
 
   SkColor GetButtonFillColor() const;
-  SkColor GetIconColor() const;
 
-  // In the touch-optimized UI, initializes the incognito button icon.
-  void InitIncognitoIcon();
-
-  // Returns the path for the newer material ui new tab button for the given
-  // |scale|. |button_y| is the button's top y-cordinate. If |for_fill| is true,
-  // the path will be shrunk by 1px from all sides to allow room for the stroke
-  // to show up. If |extend_to_top| is true, the path is extended vertically to
-  // y = 0.
-  SkPath GetNewerMaterialUiButtonPath(float button_y,
-                                      float scale,
-                                      bool extend_to_top,
-                                      bool for_fill) const;
-
-  // Similar, but for the non-new material ui button.
-  SkPath GetMaterialUiButtonPath(int button_y,
-                                 int button_height,
-                                 float scale,
-                                 bool extend_to_top,
-                                 bool for_fill) const;
+  // Returns the path for the given |origin| and |scale|.  If |extend_to_top| is
+  // true, the path is extended vertically to y = 0.
+  gfx::Path GetBorderPath(const gfx::Point& origin,
+                          float scale,
+                          bool extend_to_top) const;
 
   void UpdateInkDropBaseColor();
 
@@ -144,15 +111,6 @@
   // The offset used to paint the background image.
   int background_offset_;
 
-  // Whether this new tab button belongs to a tabstrip that is part of an
-  // incognito mode browser or not. Note that you can't drag a tab from one
-  // incognito browser to another non-incognito browser or vice versa.
-  const bool is_incognito_;
-
-  // In the touch-optimized UI, the new tab button has a plus icon, and an
-  // incognito icon if is in incognito mode.
-  gfx::ImageSkia incognito_icon_;
-
   // In touch-optimized UI, this view holds the ink drop layer so that it's
   // shifted down to the correct top offset of the button, since the actual
   // button's y-coordinate is 0 due to Fitt's Law needs.
diff --git a/chrome/browser/ui/views/tabs/tab.cc b/chrome/browser/ui/views/tabs/tab.cc
index 22cfe377a..53b5e8b0 100644
--- a/chrome/browser/ui/views/tabs/tab.cc
+++ b/chrome/browser/ui/views/tabs/tab.cc
@@ -435,6 +435,31 @@
   return path;
 }
 
+// Returns the inverse of the slope of the diagonal portion of the tab outer
+// border.  (This is a positive value, so it's specifically for the slope of the
+// leading edge.)
+//
+// This returns the inverse (dx/dy instead of dy/dx) because we use exact values
+// for the vertical distances between points and then compute the horizontal
+// deltas from those.
+float GetInverseDiagonalSlope() {
+  // In refresh, tab sides do not have slopes, so no one should call this.
+  DCHECK(!MD::IsRefreshUi());
+
+  // This is computed from the border path as follows:
+  // * The endcap width is enough for the whole stroke outer curve, i.e. the
+  //   side diagonal plus the curves on both its ends.
+  // * The bottom and top curve together are 4 DIP wide, so the diagonal is
+  //   (endcap width - 4) DIP wide.
+  // * The bottom and top curve are each 1.5 px high.  Additionally, there is an
+  //   extra 1 px below the bottom curve and (scale - 1) px above the top curve,
+  //   so the diagonal is ((height - 1.5 - 1.5) * scale - 1 - (scale - 1)) px
+  //   high.  Simplifying this gives (height - 4) * scale px, or (height - 4)
+  //   DIP.
+  return (GetTabEndcapWidthForPainting() - 4) /
+         (GetLayoutConstant(TAB_HEIGHT) - 4);
+}
+
 // Returns a path corresponding to the tab's outer border for a given tab
 // |scale| and |bounds|.  If |unscale_at_end| is true, this path will be
 // normalized to a 1x scale by scaling by 1/scale before returning.  If
@@ -466,7 +491,7 @@
       // Create the vertical extension by extending the side diagonals until
       // they reach the top of the bounds.
       const float dy = 2.5 * scale - stroke_thickness;
-      const float dx = Tab::GetInverseDiagonalSlope() * dy;
+      const float dx = GetInverseDiagonalSlope() * dy;
       path.rLineTo(dx, -dy);
       path.lineTo(right - (endcap_width - 2) * scale - dx, 0);
       path.rLineTo(dx, dy);
@@ -1217,25 +1242,6 @@
 }
 
 // static
-float Tab::GetInverseDiagonalSlope() {
-  // In refresh, tab sides do not have slopes, so no one should call this.
-  DCHECK(!MD::IsRefreshUi());
-
-  // This is computed from the border path as follows:
-  // * The endcap width is enough for the whole stroke outer curve, i.e. the
-  //   side diagonal plus the curves on both its ends.
-  // * The bottom and top curve together are 4 DIP wide, so the diagonal is
-  //   (endcap width - 4) DIP wide.
-  // * The bottom and top curve are each 1.5 px high.  Additionally, there is an
-  //   extra 1 px below the bottom curve and (scale - 1) px above the top curve,
-  //   so the diagonal is ((height - 1.5 - 1.5) * scale - 1 - (scale - 1)) px
-  //   high.  Simplifying this gives (height - 4) * scale px, or (height - 4)
-  //   DIP.
-  return (GetTabEndcapWidthForPainting() - 4) /
-         (GetLayoutConstant(TAB_HEIGHT) - 4);
-}
-
-// static
 int Tab::GetCornerRadius() {
   return ChromeLayoutProvider::Get()->GetCornerRadiusMetric(
       views::EMPHASIS_HIGH);
diff --git a/chrome/browser/ui/views/tabs/tab.h b/chrome/browser/ui/views/tabs/tab.h
index 415838c..3fb95d0 100644
--- a/chrome/browser/ui/views/tabs/tab.h
+++ b/chrome/browser/ui/views/tabs/tab.h
@@ -211,15 +211,6 @@
   // Returns the height of the separator between tabs.
   static int GetTabSeparatorHeight();
 
-  // Returns the inverse of the slope of the diagonal portion of the tab outer
-  // border.  (This is a positive value, so it's specifically for the slope of
-  // the leading edge.)
-  //
-  // This returns the inverse (dx/dy instead of dy/dx) because we use exact
-  // values for the vertical distances between points and then compute the
-  // horizontal deltas from those.
-  static float GetInverseDiagonalSlope();
-
   // Returns the radius of the outer corners of the tab shape.
   static int GetCornerRadius();
 
diff --git a/chrome/browser/ui/views/tabs/tab_close_button.cc b/chrome/browser/ui/views/tabs/tab_close_button.cc
index ae6b9330..85048e259 100644
--- a/chrome/browser/ui/views/tabs/tab_close_button.cc
+++ b/chrome/browser/ui/views/tabs/tab_close_button.cc
@@ -30,8 +30,6 @@
 #include "ui/aura/env.h"
 #endif
 
-using MD = ui::MaterialDesignController;
-
 namespace {
 constexpr int kGlyphWidth = 16;
 constexpr int kTouchGlyphWidth = 24;
@@ -55,7 +53,9 @@
 
 // static
 int TabCloseButton::GetWidth() {
-  return MD::IsTouchOptimizedUiEnabled() ? kTouchGlyphWidth : kGlyphWidth;
+  return ui::MaterialDesignController::IsTouchOptimizedUiEnabled()
+             ? kTouchGlyphWidth
+             : kGlyphWidth;
 }
 
 void TabCloseButton::SetIconColors(SkColor icon_color,
diff --git a/chrome/browser/ui/views/tabs/tab_controller.h b/chrome/browser/ui/views/tabs/tab_controller.h
index b1f277e..82766b6 100644
--- a/chrome/browser/ui/views/tabs/tab_controller.h
+++ b/chrome/browser/ui/views/tabs/tab_controller.h
@@ -93,9 +93,6 @@
   // a subset of the cases where ther is exactly one tab.
   virtual bool SingleTabMode() const = 0;
 
-  // Returns true if the tab is a part of an incognito profile.
-  virtual bool IsIncognito() const = 0;
-
   // Potentially starts a drag for the specified Tab.
   virtual void MaybeStartDrag(
       Tab* tab,
diff --git a/chrome/browser/ui/views/tabs/tab_strip.cc b/chrome/browser/ui/views/tabs/tab_strip.cc
index 79755dc..ce70ba0b 100644
--- a/chrome/browser/ui/views/tabs/tab_strip.cc
+++ b/chrome/browser/ui/views/tabs/tab_strip.cc
@@ -347,7 +347,7 @@
       (height() - Tab::GetTabSeparatorHeight()) / 2 - 1;
 
   // Disable drag handle extension when tab shapes are visible.
-  bool extend_drag_handle = !SizeTabButtonToTopOfTabStrip() &&
+  bool extend_drag_handle = !controller_->IsFrameCondensed() &&
                             !controller_->EverHasVisibleBackgroundTabShapes();
 
   // A hit on the tab is not in the caption unless it is in the thin strip
@@ -427,13 +427,6 @@
     tab_at(i)->Layout();
 }
 
-bool TabStrip::SizeTabButtonToTopOfTabStrip() {
-  // Extend the button to the screen edge in maximized and immersive fullscreen.
-  views::Widget* widget = GetWidget();
-  return browser_defaults::kSizeTabButtonToTopOfTabStrip ||
-         (widget && (widget->IsMaximized() || widget->IsFullscreen()));
-}
-
 void TabStrip::StartHighlight(int model_index) {
   tab_at(model_index)->StartPulse();
 }
@@ -979,11 +972,6 @@
          !tab_at(0)->data().pinned;
 }
 
-bool TabStrip::IsIncognito() const {
-  // There may be no controller in tests.
-  return controller_ && controller_->IsIncognito();
-}
-
 void TabStrip::MaybeStartDrag(
     Tab* tab,
     const ui::LocatedEvent& event,
diff --git a/chrome/browser/ui/views/tabs/tab_strip.h b/chrome/browser/ui/views/tabs/tab_strip.h
index c28d5c8..6dccaf9 100644
--- a/chrome/browser/ui/views/tabs/tab_strip.h
+++ b/chrome/browser/ui/views/tabs/tab_strip.h
@@ -131,10 +131,6 @@
   // Returns the bounds of the new tab button.
   gfx::Rect new_tab_button_bounds() const { return new_tab_button_bounds_; }
 
-  // Returns true if the new tab button should be sized to the top of the tab
-  // strip.
-  bool SizeTabButtonToTopOfTabStrip();
-
   // Starts highlighting the tab at the specified index.
   void StartHighlight(int model_index);
 
@@ -238,7 +234,6 @@
   bool IsFirstVisibleTab(const Tab* tab) const override;
   bool IsLastVisibleTab(const Tab* tab) const override;
   bool SingleTabMode() const override;
-  bool IsIncognito() const override;
   void MaybeStartDrag(
       Tab* tab,
       const ui::LocatedEvent& event,
diff --git a/chrome/browser/ui/views/tabs/tab_strip_controller.h b/chrome/browser/ui/views/tabs/tab_strip_controller.h
index 95f156b..f8ac993d 100644
--- a/chrome/browser/ui/views/tabs/tab_strip_controller.h
+++ b/chrome/browser/ui/views/tabs/tab_strip_controller.h
@@ -98,11 +98,6 @@
   // search-result page for |location|.
   virtual void CreateNewTabWithLocation(const base::string16& location) = 0;
 
-  // Returns true if the tab strip is in an incognito window.  This is used to
-  // determining which theme may have applied to it, so this determination
-  // should match the one in ThemeService::GetThemeProviderForProfile().
-  virtual bool IsIncognito() = 0;
-
   // Invoked if the stacked layout (on or off) might have changed.
   virtual void StackedLayoutMaybeChanged() = 0;
 
@@ -120,6 +115,11 @@
   // from this tabstrip but the user is still dragging the tabs.
   virtual void OnStoppedDraggingTabs() = 0;
 
+  // Determines whether the top frame is condensed vertically, as when the
+  // window is maximized. If true, the top frame is just the height of a tab,
+  // rather than having extra vertical space above the tabs.
+  virtual bool IsFrameCondensed() const = 0;
+
   // Returns whether the shapes of background tabs are visible against the
   // frame.
   virtual bool HasVisibleBackgroundTabShapes() const = 0;
diff --git a/chrome/browser/ui/views/tabs/tab_unittest.cc b/chrome/browser/ui/views/tabs/tab_unittest.cc
index 0f19b28..cfc09876 100644
--- a/chrome/browser/ui/views/tabs/tab_unittest.cc
+++ b/chrome/browser/ui/views/tabs/tab_unittest.cc
@@ -62,7 +62,6 @@
   bool IsFirstVisibleTab(const Tab* tab) const override { return false; }
   bool IsLastVisibleTab(const Tab* tab) const override { return false; }
   bool SingleTabMode() const override { return false; }
-  bool IsIncognito() const override { return false; }
   void MaybeStartDrag(
       Tab* tab,
       const ui::LocatedEvent& event,
@@ -83,9 +82,7 @@
       gfx::Path* clip) override {
     return true;
   }
-  int GetStrokeThickness() const override {
-    return ui::MaterialDesignController::IsRefreshUi() ? 0 : 1;
-  }
+  int GetStrokeThickness() const override { return 0; }
   bool CanPaintThrobberToLayer() const override {
     return paint_throbber_to_layer_;
   }
@@ -689,11 +686,6 @@
 }
 
 TEST_F(TabTest, ExtraAlertPaddingNotShownOnSmallActiveTab) {
-  if (!ui::MaterialDesignController::IsRefreshUi()) {
-    // Extra alert padding not shown pre-Refresh.
-    return;
-  }
-
   Widget widget;
   InitWidget(&widget);
 
diff --git a/chrome/browser/ui/views/toolbar/browser_actions_container.cc b/chrome/browser/ui/views/toolbar/browser_actions_container.cc
index ff55ed9..62b89c2 100644
--- a/chrome/browser/ui/views/toolbar/browser_actions_container.cc
+++ b/chrome/browser/ui/views/toolbar/browser_actions_container.cc
@@ -24,6 +24,7 @@
 #include "chrome/browser/ui/views/frame/browser_view.h"
 #include "chrome/browser/ui/views/toolbar/browser_app_menu_button.h"
 #include "chrome/browser/ui/views/toolbar/toolbar_actions_bar_bubble_views.h"
+#include "chrome/browser/ui/views/toolbar/toolbar_ink_drop_util.h"
 #include "chrome/browser/ui/views/toolbar/toolbar_view.h"
 #include "chrome/common/extensions/command.h"
 #include "chrome/grit/generated_resources.h"
@@ -424,11 +425,22 @@
       bounds.set_x(bounds.x() + GetResizeAreaWidth());
       view->SetBoundsRect(bounds);
       view->SetVisible(true);
-      if (!ShownInsideMenu()) {
-        view->AnimateInkDrop(toolbar_actions_bar()->is_highlighting()
-                                 ? views::InkDropState::ACTIVATED
-                                 : views::InkDropState::HIDDEN,
-                             nullptr);
+      // TODO(corising): Move setting background to
+      // ToolbarActionsBar::OnToolbarHighlightModeChanged when the files merge.
+      if (!ShownInsideMenu() && (toolbar_actions_bar()->is_highlighting() !=
+                                 (view->background() != nullptr))) {
+        // Sets background to reflect whether the item is being highlighted.
+        const gfx::Insets bg_insets(
+            (height() - GetLayoutConstant(LOCATION_BAR_HEIGHT)) / 2);
+        const int corner_radius = height() / 2;
+        const SkColor bg_color = SkColorSetA(view->GetInkDropBaseColor(),
+                                             kToolbarButtonBackgroundAlpha);
+        view->SetBackground(
+            toolbar_actions_bar()->is_highlighting()
+                ? views::CreateBackgroundFromPainter(
+                      views::Painter::CreateSolidRoundRectPainter(
+                          bg_color, corner_radius, bg_insets))
+                : nullptr);
       }
     }
   }
diff --git a/chrome/browser/ui/views/toolbar/toolbar_button.cc b/chrome/browser/ui/views/toolbar/toolbar_button.cc
index b77c45f..398bbc0 100644
--- a/chrome/browser/ui/views/toolbar/toolbar_button.cc
+++ b/chrome/browser/ui/views/toolbar/toolbar_button.cc
@@ -84,7 +84,8 @@
     // ToolbarButtons are always the height the location bar.
     const gfx::Insets bg_insets(
         (height() - GetLayoutConstant(LOCATION_BAR_HEIGHT)) / 2);
-    const SkColor bg_color = SkColorSetA(*highlight_color_, 32);
+    const SkColor bg_color =
+        SkColorSetA(*highlight_color_, kToolbarButtonBackgroundAlpha);
     SetBackground(views::CreateBackgroundFromPainter(
         views::Painter::CreateSolidRoundRectPainter(
             bg_color, ink_drop_large_corner_radius(), bg_insets)));
diff --git a/chrome/browser/ui/views/toolbar/toolbar_ink_drop_util.h b/chrome/browser/ui/views/toolbar/toolbar_ink_drop_util.h
index 0bb0e35..34b5a02 100644
--- a/chrome/browser/ui/views/toolbar/toolbar_ink_drop_util.h
+++ b/chrome/browser/ui/views/toolbar/toolbar_ink_drop_util.h
@@ -23,6 +23,7 @@
 
 constexpr float kToolbarInkDropVisibleOpacity = 0.06f;
 constexpr float kToolbarInkDropHighlightVisibleOpacity = 0.08f;
+constexpr SkAlpha kToolbarButtonBackgroundAlpha = 32;
 
 // The below utility functions are templated since we have two different types
 // of buttons on the toolbar (ToolbarButton and AppMenuButton) which don't share
diff --git a/chrome/browser/ui/views/toolbar/toolbar_view.cc b/chrome/browser/ui/views/toolbar/toolbar_view.cc
index 002cbff..2e209be 100644
--- a/chrome/browser/ui/views/toolbar/toolbar_view.cc
+++ b/chrome/browser/ui/views/toolbar/toolbar_view.cc
@@ -303,11 +303,9 @@
       location_bar()->Layout();
     }
 
-    views::Widget* bubble_widget = IntentPickerBubbleView::ShowBubble(
+    IntentPickerBubbleView::ShowBubble(
         intent_picker_view, GetWebContents(), std::move(app_info),
         disable_stay_in_chrome, std::move(callback));
-    if (bubble_widget && intent_picker_view)
-      intent_picker_view->OnBubbleWidgetCreated(bubble_widget);
   }
 }
 #endif  // defined(OS_CHROMEOS)
@@ -321,11 +319,9 @@
 
   std::unique_ptr<BubbleSyncPromoDelegate> delegate;
   delegate.reset(new BookmarkBubbleSignInDelegate(browser_));
-  views::Widget* bubble_widget = BookmarkBubbleView::ShowBubble(
-      anchor_view, gfx::Rect(), nullptr, observer, std::move(delegate),
-      browser_->profile(), url, already_bookmarked);
-  if (bubble_widget && star_view)
-    star_view->OnBubbleWidgetCreated(bubble_widget);
+  BookmarkBubbleView::ShowBubble(anchor_view, star_view, gfx::Rect(), nullptr,
+                                 observer, std::move(delegate),
+                                 browser_->profile(), url, already_bookmarked);
 }
 
 void ToolbarView::ShowTranslateBubble(
@@ -337,12 +333,11 @@
   PageActionIconView* translate_icon_view =
       location_bar()->translate_icon_view();
 
-  views::Widget* bubble_widget = TranslateBubbleView::ShowBubble(
-      anchor_view, gfx::Point(), web_contents, step, error_type,
-      is_user_gesture ? TranslateBubbleView::USER_GESTURE
-                      : TranslateBubbleView::AUTOMATIC);
-  if (bubble_widget && translate_icon_view)
-    translate_icon_view->OnBubbleWidgetCreated(bubble_widget);
+  TranslateBubbleView::ShowBubble(anchor_view, translate_icon_view,
+                                  gfx::Point(), web_contents, step, error_type,
+                                  is_user_gesture
+                                      ? TranslateBubbleView::USER_GESTURE
+                                      : TranslateBubbleView::AUTOMATIC);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
diff --git a/chrome/browser/ui/views/translate/translate_bubble_view.cc b/chrome/browser/ui/views/translate/translate_bubble_view.cc
index 4aaef02..418c1e7 100644
--- a/chrome/browser/ui/views/translate/translate_bubble_view.cc
+++ b/chrome/browser/ui/views/translate/translate_bubble_view.cc
@@ -98,6 +98,7 @@
 // static
 views::Widget* TranslateBubbleView::ShowBubble(
     views::View* anchor_view,
+    views::Button* highlighted_button,
     const gfx::Point& anchor_point,
     content::WebContents* web_contents,
     translate::TranslateStep step,
@@ -155,6 +156,9 @@
   }
 #endif
 
+  if (highlighted_button)
+    view->SetHighlightedButton(highlighted_button);
+
   views::Widget* bubble_widget =
       views::BubbleDialogDelegateView::CreateBubble(view);
   view->ShowForReason(reason);
diff --git a/chrome/browser/ui/views/translate/translate_bubble_view.h b/chrome/browser/ui/views/translate/translate_bubble_view.h
index 10c972e..e6c514c 100644
--- a/chrome/browser/ui/views/translate/translate_bubble_view.h
+++ b/chrome/browser/ui/views/translate/translate_bubble_view.h
@@ -60,6 +60,7 @@
   // |is_user_gesture| is true when the bubble is shown on the user's deliberate
   // action.
   static views::Widget* ShowBubble(views::View* anchor_view,
+                                   views::Button* highlighted_button,
                                    const gfx::Point& anchor_point,
                                    content::WebContents* web_contents,
                                    translate::TranslateStep step,
diff --git a/chrome/browser/ui/webui/devtools_ui.cc b/chrome/browser/ui/webui/devtools_ui.cc
index c7c8a1c..0aa64d348 100644
--- a/chrome/browser/ui/webui/devtools_ui.cc
+++ b/chrome/browser/ui/webui/devtools_ui.cc
@@ -10,9 +10,12 @@
 #include "base/command_line.h"
 #include "base/macros.h"
 #include "base/memory/ref_counted_memory.h"
+#include "base/path_service.h"
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
+#include "base/task/post_task.h"
 #include "chrome/browser/devtools/url_constants.h"
+#include "chrome/common/chrome_paths.h"
 #include "chrome/common/chrome_switches.h"
 #include "chrome/common/url_constants.h"
 #include "content/public/browser/browser_context.h"
@@ -127,6 +130,11 @@
       int load_flags,
       const GotDataCallback& callback);
 
+#if BUILDFLAG(DEBUG_DEVTOOLS)
+  void StartFileRequestForDebugDevtools(const std::string& path,
+                                        const GotDataCallback& callback);
+#endif
+
   struct PendingRequest {
     PendingRequest() = default;
     PendingRequest(PendingRequest&& other) = default;
@@ -162,8 +170,17 @@
   bundled_path_prefix += "/";
   if (base::StartsWith(path, bundled_path_prefix,
                        base::CompareCase::INSENSITIVE_ASCII)) {
-    StartBundledDataRequest(path.substr(bundled_path_prefix.length()),
-                            callback);
+    std::string path_without_params = PathWithoutParams(path);
+
+    DCHECK(base::StartsWith(path_without_params, bundled_path_prefix,
+                            base::CompareCase::INSENSITIVE_ASCII));
+    std::string path_under_bundled =
+        path_without_params.substr(bundled_path_prefix.length());
+#if BUILDFLAG(DEBUG_DEVTOOLS)
+    StartFileRequestForDebugDevtools(path_under_bundled, callback);
+#else
+    StartBundledDataRequest(path_under_bundled, callback);
+#endif
     return;
   }
 
@@ -236,12 +253,11 @@
 void DevToolsDataSource::StartBundledDataRequest(
     const std::string& path,
     const content::URLDataSource::GotDataCallback& callback) {
-  std::string filename = PathWithoutParams(path);
   base::StringPiece resource =
-      content::DevToolsFrontendHost::GetFrontendResource(filename);
+      content::DevToolsFrontendHost::GetFrontendResource(path);
 
   DLOG_IF(WARNING, resource.empty())
-      << "Unable to find dev tool resource: " << filename
+      << "Unable to find dev tool resource: " << path
       << ". If you compiled with debug_devtools=1, try running with "
          "--debug-devtools.";
   scoped_refptr<base::RefCountedStaticMemory> bytes(
@@ -339,6 +355,42 @@
                      base::Unretained(this), request_iter));
 }
 
+#if BUILDFLAG(DEBUG_DEVTOOLS)
+scoped_refptr<base::RefCountedMemory> ReadFile(const base::FilePath& path) {
+  std::string buffer;
+  if (!base::ReadFileToString(path, &buffer)) {
+    LOG(ERROR) << "Failed to read " << path;
+    return CreateNotFoundResponse();
+  }
+  return base::RefCountedString::TakeString(&buffer);
+}
+
+void DevToolsDataSource::StartFileRequestForDebugDevtools(
+    const std::string& path,
+    const GotDataCallback& callback) {
+  base::FilePath inspector_debug_dir;
+  if (!base::PathService::Get(chrome::DIR_INSPECTOR_DEBUG,
+                              &inspector_debug_dir)) {
+    callback.Run(CreateNotFoundResponse());
+    return;
+  }
+
+  DCHECK(!inspector_debug_dir.empty());
+
+  base::FilePath full_path = inspector_debug_dir.AppendASCII(path);
+
+  base::PostTaskWithTraitsAndReplyWithResult(
+      FROM_HERE,
+      {base::MayBlock(), base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN,
+       base::TaskPriority::USER_VISIBLE},
+      // The usage of BindRepeating below is only because the type of
+      // task callback needs to match that of response callback, which
+      // is currently a repeating callback.
+      base::BindRepeating(ReadFile, std::move(full_path)), callback);
+}
+
+#endif  // BUILDFLAG(DEBUG_DEVTOOLS)
+
 void DevToolsDataSource::OnLoadComplete(
     std::list<PendingRequest>::iterator request_iter,
     std::unique_ptr<std::string> response_body) {
diff --git a/chrome/child/BUILD.gn b/chrome/child/BUILD.gn
index d8fa2af..578cc25 100644
--- a/chrome/child/BUILD.gn
+++ b/chrome/child/BUILD.gn
@@ -10,10 +10,7 @@
     "v8_breakpad_support_win.h",
   ]
 
-  # TODO(thakis): Enable on Windows too, http://crbug.com/404525
-  if (!is_win) {
-    configs += [ "//build/config/compiler:wexit_time_destructors" ]
-  }
+  configs += [ "//build/config/compiler:wexit_time_destructors" ]
 
   deps = [
     "//base",
diff --git a/chrome/child/pdf_child_init.cc b/chrome/child/pdf_child_init.cc
index 0ba718cd..1a216f4 100644
--- a/chrome/child/pdf_child_init.cc
+++ b/chrome/child/pdf_child_init.cc
@@ -7,6 +7,7 @@
 #include "build/build_config.h"
 
 #if defined(OS_WIN)
+#include "base/no_destructor.h"
 #include "base/win/current_module.h"
 #include "base/win/iat_patch_function.h"
 #include "content/public/child/child_thread.h"
@@ -15,8 +16,6 @@
 namespace {
 
 #if defined(OS_WIN)
-base::win::IATPatchFunction g_iat_patch_createdca;
-
 HDC WINAPI CreateDCAPatch(LPCSTR driver_name,
                           LPCSTR device_name,
                           LPCSTR output,
@@ -37,7 +36,6 @@
                                         DWORD length);
 GetFontDataPtr g_original_get_font_data = nullptr;
 
-base::win::IATPatchFunction g_iat_patch_get_font_data;
 
 DWORD WINAPI GetFontDataPatch(HDC hdc,
                               DWORD table,
@@ -66,15 +64,19 @@
 void InitializePDF() {
 #if defined(OS_WIN)
   // Need to patch a few functions for font loading to work correctly. This can
-  // be removed once we switch PDF to use Skia.
+  // be removed once we switch PDF to use Skia
+  // (https://bugs.chromium.org/p/pdfium/issues/detail?id=11).
   HMODULE current_module = CURRENT_MODULE();
-  g_iat_patch_createdca.PatchFromModule(
-      current_module, "gdi32.dll", "CreateDCA",
-      reinterpret_cast<void*>(CreateDCAPatch));
-  g_iat_patch_get_font_data.PatchFromModule(
+
+  static base::NoDestructor<base::win::IATPatchFunction> patch_createdca;
+  patch_createdca->PatchFromModule(current_module, "gdi32.dll", "CreateDCA",
+                                   reinterpret_cast<void*>(CreateDCAPatch));
+
+  static base::NoDestructor<base::win::IATPatchFunction> patch_get_font_data;
+  patch_get_font_data->PatchFromModule(
       current_module, "gdi32.dll", "GetFontData",
       reinterpret_cast<void*>(GetFontDataPatch));
   g_original_get_font_data = reinterpret_cast<GetFontDataPtr>(
-      g_iat_patch_get_font_data.original_function());
+      patch_get_font_data->original_function());
 #endif  // defined(OS_WIN)
 }
diff --git a/chrome/chrome_watcher/chrome_watcher_main.cc b/chrome/chrome_watcher/chrome_watcher_main.cc
index 5d65ba0..2cfdd1d 100644
--- a/chrome/chrome_watcher/chrome_watcher_main.cc
+++ b/chrome/chrome_watcher/chrome_watcher_main.cc
@@ -69,14 +69,12 @@
 // Assertion handler for logging errors that occur when dialogs are
 // silenced.  To record a new error, pass the log string associated
 // with that error in the str parameter.
-MSVC_DISABLE_OPTIMIZE();
-void SilentRuntimeAssertHandler(const char* file,
-                                int line,
-                                const base::StringPiece message,
-                                const base::StringPiece stack_trace) {
+NOINLINE void SilentRuntimeAssertHandler(const char* file,
+                                         int line,
+                                         const base::StringPiece message,
+                                         const base::StringPiece stack_trace) {
   base::debug::BreakDebugger();
 }
-MSVC_ENABLE_OPTIMIZE();
 
 // Suppresses error/assertion dialogs and enables the logging of
 // those errors into silenced_errors_.
diff --git a/chrome/common/extensions/api/file_manager_private.idl b/chrome/common/extensions/api/file_manager_private.idl
index 2cf3f536..336bf0d8 100644
--- a/chrome/common/extensions/api/file_manager_private.idl
+++ b/chrome/common/extensions/api/file_manager_private.idl
@@ -741,6 +741,10 @@
 callback InstallLinuxPackageCallback = void(
     InstallLinuxPackageResponse response, DOMString failure_reason);
 
+// |thumbnailDataUrl| A data URL for the thumbnail as a PNG; |thumbnailDataUrl|
+// is empty if no thumbnail was available.
+callback GetThumbnailCallback = void(DOMString thumbnailDataUrl);
+
 interface Functions {
   // Logout the current user for navigating to the re-authentication screen for
   // the Google account.
@@ -1115,6 +1119,14 @@
   [nocompile]
   static void installLinuxPackage([instanceof=Entry] object entry,
                                   InstallLinuxPackageCallback callback);
+
+  // For a file in DriveFS, retrieves its thumbnail. If |cropToSquare| is true,
+  // returns a thumbnail appropriate for file list or grid views; otherwise,
+  // returns a thumbnail appropriate for quickview.
+  [nocompile]
+  static void getThumbnail([instanceOf=FileEntry] object entry,
+                           boolean cropToSquare,
+                           GetThumbnailCallback callback);
 };
 
 interface Events {
diff --git a/chrome/common/extensions/api/file_manager_private_internal.idl b/chrome/common/extensions/api/file_manager_private_internal.idl
index 22f87b6b..0d126488 100644
--- a/chrome/common/extensions/api/file_manager_private_internal.idl
+++ b/chrome/common/extensions/api/file_manager_private_internal.idl
@@ -35,6 +35,7 @@
   callback InstallLinuxPackageCallback =
       void(fileManagerPrivate.InstallLinuxPackageResponse response,
            optional DOMString failure_reason);
+  callback GetThumbnailCallback = void(DOMString ThumbnailDataUrl);
 
   interface Functions {
     static void resolveIsolatedEntries(DOMString[] urls,
@@ -103,5 +104,8 @@
                                                SimpleCallback callback);
     static void installLinuxPackage(DOMString url,
                                     InstallLinuxPackageCallback callback);
+    static void getThumbnail(DOMString url,
+                             boolean cropToSquare,
+                             GetThumbnailCallback callback);
   };
 };
diff --git a/chrome/installer/setup/BUILD.gn b/chrome/installer/setup/BUILD.gn
index 11c2fe6..3476a97a 100644
--- a/chrome/installer/setup/BUILD.gn
+++ b/chrome/installer/setup/BUILD.gn
@@ -58,6 +58,10 @@
       "brand_behaviors.h",
       "install.cc",
       "install.h",
+      "install_service_work_item.cc",
+      "install_service_work_item.h",
+      "install_service_work_item_impl.cc",
+      "install_service_work_item_impl.h",
       "install_worker.cc",
       "install_worker.h",
       "installer_crash_reporter_client.cc",
@@ -121,6 +125,7 @@
   test("setup_unittests") {
     sources = [
       "archive_patch_helper_unittest.cc",
+      "install_service_work_item_unittest.cc",
       "install_unittest.cc",
       "install_worker_unittest.cc",
       "installer_state_unittest.cc",
diff --git a/chrome/installer/setup/install_service_work_item.cc b/chrome/installer/setup/install_service_work_item.cc
new file mode 100644
index 0000000..cbfb0f95
--- /dev/null
+++ b/chrome/installer/setup/install_service_work_item.cc
@@ -0,0 +1,29 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/installer/setup/install_service_work_item.h"
+
+#include "chrome/installer/setup/install_service_work_item_impl.h"
+
+namespace installer {
+
+InstallServiceWorkItem::InstallServiceWorkItem(
+    const base::string16& service_name,
+    const base::string16& display_name,
+    const base::string16& service_cmd_line)
+    : impl_(std::make_unique<InstallServiceWorkItemImpl>(service_name,
+                                                         display_name,
+                                                         service_cmd_line)) {}
+
+InstallServiceWorkItem::~InstallServiceWorkItem() = default;
+
+bool InstallServiceWorkItem::DoImpl() {
+  return impl_->DoImpl();
+}
+
+void InstallServiceWorkItem::RollbackImpl() {
+  impl_->RollbackImpl();
+}
+
+}  // namespace installer
diff --git a/chrome/installer/setup/install_service_work_item.h b/chrome/installer/setup/install_service_work_item.h
new file mode 100644
index 0000000..ec480bf1
--- /dev/null
+++ b/chrome/installer/setup/install_service_work_item.h
@@ -0,0 +1,59 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// This module is responsible for installing a service, given a |service_name|,
+// |display_name|, and |service_cmd_line|. If the service already exists, a
+// light-weight upgrade of the service will be performed, to reduce the chances
+// of anti-virus flagging issues with deleting/installing a new service.
+// In the event that the upgrade fails, this module will install a new service
+// and mark the original service for deletion.
+
+#ifndef CHROME_INSTALLER_SETUP_INSTALL_SERVICE_WORK_ITEM_H_
+#define CHROME_INSTALLER_SETUP_INSTALL_SERVICE_WORK_ITEM_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "base/strings/string16.h"
+#include "chrome/installer/util/work_item.h"
+
+namespace installer {
+
+class InstallServiceWorkItemImpl;
+
+// A generic WorkItem subclass that installs a Windows Service for Chrome.
+class InstallServiceWorkItem : public WorkItem {
+ public:
+  // |service_name| is the name given to the service. In the case of a conflict
+  // when upgrading the service, this will be the prefix for a versioned name
+  // given to the service.
+  // An example |service_name| could be "elevationservice".
+  //
+  // |display_name| is the human-readable name that is visible in the Service
+  // control panel. For example, "Chrome Elevation Service".
+  //
+  // |service_cmd_line| is the command line with which the service is invoked by
+  // the SCM. For example,
+  // "C:\Program Files (x86)\Google\Chrome\ElevationService.exe" /svc
+  InstallServiceWorkItem(const base::string16& service_name,
+                         const base::string16& display_name,
+                         const base::string16& service_cmd_line);
+
+  ~InstallServiceWorkItem() override;
+
+ private:
+  friend class InstallServiceWorkItemTest;
+
+  // Overrides of WorkItem.
+  bool DoImpl() override;
+  void RollbackImpl() override;
+
+  std::unique_ptr<InstallServiceWorkItemImpl> impl_;
+
+  DISALLOW_COPY_AND_ASSIGN(InstallServiceWorkItem);
+};
+
+}  // namespace installer
+
+#endif  // CHROME_INSTALLER_SETUP_INSTALL_SERVICE_WORK_ITEM_H_
diff --git a/chrome/installer/setup/install_service_work_item_impl.cc b/chrome/installer/setup/install_service_work_item_impl.cc
new file mode 100644
index 0000000..3d983e8
--- /dev/null
+++ b/chrome/installer/setup/install_service_work_item_impl.cc
@@ -0,0 +1,355 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/installer/setup/install_service_work_item_impl.h"
+
+#include <string.h>
+#include <memory>
+#include <utility>
+
+#include "base/logging.h"
+#include "base/strings/stringprintf.h"
+#include "base/time/time.h"
+#include "base/win/registry.h"
+#include "chrome/install_static/install_util.h"
+
+using base::win::RegKey;
+
+namespace installer {
+
+namespace {
+
+constexpr uint32_t kServiceType = SERVICE_WIN32_OWN_PROCESS;
+constexpr uint32_t kServiceStartType = SERVICE_DEMAND_START;
+constexpr uint32_t kServiceErrorControl = SERVICE_ERROR_NORMAL;
+constexpr base::char16 kServiceDependencies[] = L"RPCSS\0";
+
+// For the service handle, all permissions that could possibly be used in all
+// Do/Rollback scenarios are requested since the handle is reused.
+constexpr uint32_t kServiceAccess =
+    DELETE | SERVICE_QUERY_CONFIG | SERVICE_CHANGE_CONFIG;
+
+}  // namespace
+
+InstallServiceWorkItemImpl::ServiceConfig::ServiceConfig()
+    : is_valid(false),
+      type(SERVICE_KERNEL_DRIVER),
+      start_type(SERVICE_DISABLED),
+      error_control(SERVICE_ERROR_CRITICAL) {}
+
+InstallServiceWorkItemImpl::ServiceConfig::ServiceConfig(
+    uint32_t service_type,
+    uint32_t service_start_type,
+    uint32_t service_error_control,
+    const base::string16& service_cmd_line,
+    const base::char16* dependencies_multi_sz)
+    : is_valid(true),
+      type(service_type),
+      start_type(service_start_type),
+      error_control(service_error_control),
+      cmd_line(service_cmd_line),
+      dependencies(MultiSzToVector(dependencies_multi_sz)) {}
+
+InstallServiceWorkItemImpl::ServiceConfig::ServiceConfig(ServiceConfig&& rhs) =
+    default;
+
+InstallServiceWorkItemImpl::ServiceConfig::~ServiceConfig() = default;
+
+InstallServiceWorkItemImpl::InstallServiceWorkItemImpl(
+    const base::string16& service_name,
+    const base::string16& display_name,
+    const base::string16& service_cmd_line)
+    : service_name_(service_name),
+      display_name_(display_name),
+      service_cmd_line_(service_cmd_line),
+      rollback_existing_service_(false),
+      rollback_new_service_(false),
+      original_service_still_exists_(false) {}
+
+InstallServiceWorkItemImpl::~InstallServiceWorkItemImpl() = default;
+
+bool InstallServiceWorkItemImpl::DoImpl() {
+  scm_.Set(::OpenSCManager(nullptr, nullptr,
+                           SC_MANAGER_CONNECT | SC_MANAGER_CREATE_SERVICE));
+  if (!scm_.IsValid()) {
+    DPLOG(ERROR) << "::OpenSCManager Failed";
+    return false;
+  }
+
+  if (!OpenService())
+    return InstallNewService();
+
+  // It is preferable to do a lightweight upgrade of the existing service,
+  // instead of deleting and recreating a new service, since it is less
+  // likely to fail. Less intrusive to the SCM and to AV/Anti-malware programs.
+  if (UpgradeService())
+    return true;
+
+  // Save the original service name. Then create a new service name so as to not
+  // conflict with the previous one to be safe, then install the new service.
+  original_service_name_ = GetCurrentServiceName();
+  LOG_IF(WARNING, !CreateAndSetServiceName());
+
+  ScopedScHandle original_service = std::move(service_);
+  if (InstallNewService()) {
+    // Delete the previous version of the service.
+    if (!DeleteService(std::move(original_service)))
+      original_service_still_exists_ = true;
+
+    return true;
+  }
+
+  return false;
+}
+
+void InstallServiceWorkItemImpl::RollbackImpl() {
+  DCHECK(!(rollback_existing_service_ && rollback_new_service_));
+
+  if (!rollback_existing_service_ && !rollback_new_service_)
+    return;
+
+  if (rollback_existing_service_) {
+    DCHECK(service_.IsValid());
+    DCHECK(original_service_config_.is_valid);
+    LOG_IF(WARNING, !RestoreOriginalServiceConfig());
+    return;
+  }
+
+  DCHECK(rollback_new_service_);
+  DCHECK(service_.IsValid());
+
+  // Delete the newly created service.
+  // TODO(ganesh): If this Delete fails, there will be an extra service. We need
+  // to use UMA to record occurrences. And have cleanup code that is run on
+  // subsequent updates to delete stale services.
+  LOG_IF(WARNING, !DeleteCurrentService());
+
+  if (original_service_name_.empty())
+    return;
+
+  if (original_service_still_exists_) {
+    // Set only the service name back to original_service_name_ and return.
+    LOG_IF(WARNING, !SetServiceName(original_service_name_));
+    return;
+  }
+
+  // Recreate original service with a new service name to avoid possible SCM
+  // issues with reusing the old name.
+  LOG_IF(WARNING, !CreateAndSetServiceName());
+  LOG_IF(WARNING, !ReinstallOriginalService());
+}
+
+bool InstallServiceWorkItemImpl::IsServiceCorrectlyConfigured(
+    const ServiceConfig& config) {
+  return config.type == kServiceType &&
+         config.start_type == kServiceStartType &&
+         config.error_control == kServiceErrorControl &&
+         !_wcsicmp(config.cmd_line.c_str(), service_cmd_line_.c_str()) &&
+         config.dependencies == MultiSzToVector(kServiceDependencies);
+}
+
+bool InstallServiceWorkItemImpl::DeleteCurrentService() {
+  return DeleteService(std::move(service_));
+}
+
+bool InstallServiceWorkItemImpl::OpenService() {
+  DCHECK(scm_.IsValid());
+  service_.Set(::OpenService(scm_.Get(), GetCurrentServiceName().c_str(),
+                             kServiceAccess));
+  return service_.IsValid();
+}
+
+bool InstallServiceWorkItemImpl::GetServiceConfig(ServiceConfig* config) const {
+  DCHECK(config);
+  DCHECK(service_.IsValid());
+
+  constexpr uint32_t kMaxQueryConfigBufferBytes = 8 * 1024;
+
+  // ::QueryServiceConfig expects a buffer of at most 8K bytes, according to
+  // documentation. While the size of the buffer can be dynamically computed,
+  // we just assume the maximum size for simplicity.
+  auto buffer = std::make_unique<uint8_t[]>(kMaxQueryConfigBufferBytes);
+  DWORD bytes_needed_ignored = 0;
+  QUERY_SERVICE_CONFIG* service_config =
+      reinterpret_cast<QUERY_SERVICE_CONFIG*>(buffer.get());
+  if (!::QueryServiceConfig(service_.Get(), service_config,
+                            kMaxQueryConfigBufferBytes,
+                            &bytes_needed_ignored)) {
+    DPLOG(ERROR) << "QueryServiceConfig failed";
+    return false;
+  }
+
+  *config = ServiceConfig(
+      service_config->dwServiceType, service_config->dwStartType,
+      service_config->dwErrorControl,
+      service_config->lpBinaryPathName ? service_config->lpBinaryPathName : L"",
+      service_config->lpDependencies ? service_config->lpDependencies : L"");
+  return true;
+}
+
+// Creates a unique name of the form "{prefix}1c9b3d6baf90df3" and stores it in
+// the registry under HKLM\Google\Chrome. Subsequent invocations of
+// GetCurrentServiceName() will return this new value.
+// The service_name_ is used as the "Name" entry in the registry under
+// HKLM\Software\Google\Update\ClientState\{appguid}. For example,
+// HKLM\Software\Google\Update\ClientState\{appguid}
+//  "Name"  "elevationservice", "Type" "REG_SZ", "Data" "elevationservice0394"
+bool InstallServiceWorkItemImpl::CreateAndSetServiceName() const {
+  const base::string16 versioned_service_name(GenerateVersionedServiceName());
+  return SetServiceName(versioned_service_name);
+}
+
+bool InstallServiceWorkItemImpl::SetServiceName(
+    const base::string16& service_name) const {
+  base::win::RegKey key;
+
+  // This assumes that a WorkItem to create the key has already executed before
+  // this WorkItem. this is generally true since one is added in
+  // AddUninstallShortcutWorkItems.
+  auto result = key.Open(HKEY_LOCAL_MACHINE,
+                         install_static::GetClientStateKeyPath().c_str(),
+                         KEY_SET_VALUE | KEY_WOW64_32KEY);
+  if (result != ERROR_SUCCESS) {
+    ::SetLastError(result);
+    DPLOG(ERROR) << "key.Open failed";
+    return false;
+  }
+
+  result = key.WriteValue(service_name_.c_str(), service_name.c_str());
+  if (result != ERROR_SUCCESS) {
+    ::SetLastError(result);
+    DPLOG(ERROR) << "key.WriteValue failed";
+    return false;
+  }
+
+  return true;
+}
+
+base::string16 InstallServiceWorkItemImpl::GetCurrentServiceName() const {
+  base::win::RegKey key;
+
+  LONG result = key.Open(HKEY_LOCAL_MACHINE,
+                         install_static::GetClientStateKeyPath().c_str(),
+                         KEY_QUERY_VALUE | KEY_WOW64_32KEY);
+  if (result != ERROR_SUCCESS)
+    return service_name_;
+
+  base::string16 versioned_service_name;
+  key.ReadValue(service_name_.c_str(), &versioned_service_name);
+  return versioned_service_name.empty() ? service_name_
+                                        : versioned_service_name;
+}
+
+std::vector<base::char16> InstallServiceWorkItemImpl::MultiSzToVector(
+    const base::char16* multi_sz) {
+  if (!multi_sz)
+    return std::vector<base::char16>();
+
+  if (!*multi_sz)
+    return std::vector<base::char16>(1, L'\0');
+
+  // Scan forward to the second terminating '\0' at the end of the list of
+  // strings in the multi-sz.
+  const base::char16* scan = multi_sz;
+  do {
+    scan += wcslen(scan) + 1;
+  } while (*scan);
+
+  return std::vector<base::char16>(multi_sz, scan + 1);
+}
+
+bool InstallServiceWorkItemImpl::InstallNewService() {
+  DCHECK(!service_.IsValid());
+  bool success = InstallService(
+      ServiceConfig(kServiceType, kServiceStartType, kServiceErrorControl,
+                    service_cmd_line_, kServiceDependencies));
+  if (success)
+    rollback_new_service_ = true;
+  return success;
+}
+
+bool InstallServiceWorkItemImpl::UpgradeService() {
+  DCHECK(service_.IsValid());
+  DCHECK(!original_service_config_.is_valid);
+
+  ServiceConfig config;
+  if (!GetServiceConfig(&config))
+    return false;
+
+  if (IsServiceCorrectlyConfigured(config))
+    return true;
+
+  original_service_config_ = std::move(config);
+
+  bool success = ChangeServiceConfig(
+      ServiceConfig(kServiceType, kServiceStartType, kServiceErrorControl,
+                    service_cmd_line_, kServiceDependencies));
+  if (success)
+    rollback_existing_service_ = true;
+
+  return success;
+}
+
+bool InstallServiceWorkItemImpl::ReinstallOriginalService() {
+  return InstallService(original_service_config_);
+}
+
+bool InstallServiceWorkItemImpl::RestoreOriginalServiceConfig() {
+  return ChangeServiceConfig(original_service_config_);
+}
+
+bool InstallServiceWorkItemImpl::InstallService(const ServiceConfig& config) {
+  ScopedScHandle service(::CreateService(
+      scm_.Get(), GetCurrentServiceName().c_str(), display_name_.c_str(),
+      kServiceAccess, config.type, config.start_type, config.error_control,
+      config.cmd_line.c_str(), nullptr, nullptr,
+      !config.dependencies.empty() ? config.dependencies.data() : nullptr,
+      nullptr, nullptr));
+  if (!service.IsValid()) {
+    DPLOG(WARNING) << "Failed to create service";
+    return false;
+  }
+
+  service_ = std::move(service);
+  return true;
+}
+
+bool InstallServiceWorkItemImpl::ChangeServiceConfig(
+    const ServiceConfig& config) {
+  DCHECK(service_.IsValid());
+
+  // Change the configuration of the existing service.
+  if (!::ChangeServiceConfig(
+          service_.Get(), config.type, config.start_type, config.error_control,
+          config.cmd_line.c_str(), nullptr, nullptr,
+          !config.dependencies.empty() ? config.dependencies.data() : nullptr,
+          nullptr, nullptr, nullptr)) {
+    DPLOG(WARNING) << "Failed to change service config";
+    return false;
+  }
+
+  return true;
+}
+
+bool InstallServiceWorkItemImpl::DeleteService(ScopedScHandle service) const {
+  if (!service.IsValid())
+    return false;
+
+  if (!::DeleteService(service.Get())) {
+    DWORD error = ::GetLastError();
+    DPLOG(WARNING) << "DeleteService failed";
+    return error == ERROR_SERVICE_MARKED_FOR_DELETE;
+  }
+
+  return true;
+}
+
+base::string16 InstallServiceWorkItemImpl::GenerateVersionedServiceName()
+    const {
+  const FILETIME filetime = base::Time::Now().ToFileTime();
+  return base::StringPrintf(L"%ls%x%x", service_name_.c_str(),
+                            filetime.dwHighDateTime, filetime.dwLowDateTime);
+}
+
+}  // namespace installer
diff --git a/chrome/installer/setup/install_service_work_item_impl.h b/chrome/installer/setup/install_service_work_item_impl.h
new file mode 100644
index 0000000..610e827
--- /dev/null
+++ b/chrome/installer/setup/install_service_work_item_impl.h
@@ -0,0 +1,166 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_INSTALLER_SETUP_INSTALL_SERVICE_WORK_ITEM_IMPL_H_
+#define CHROME_INSTALLER_SETUP_INSTALL_SERVICE_WORK_ITEM_IMPL_H_
+
+#include <windows.h>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/strings/string16.h"
+#include "base/win/scoped_handle.h"
+#include "base/win/windows_types.h"
+
+namespace installer {
+
+// Helper class for the implementation of InstallServiceWorkItem.
+class InstallServiceWorkItemImpl {
+ public:
+  struct ServiceConfig {
+    ServiceConfig();
+    ServiceConfig(uint32_t service_type,
+                  uint32_t service_start_type,
+                  uint32_t service_error_control,
+                  const base::string16& service_cmd_line,
+                  const base::char16* dependencies_multi_sz);
+    ServiceConfig(ServiceConfig&& rhs);
+
+    ServiceConfig& operator=(ServiceConfig&& rhs) = default;
+
+    ~ServiceConfig();
+
+    bool is_valid;
+    uint32_t type;
+    uint32_t start_type;
+    uint32_t error_control;
+    base::string16 cmd_line;
+    std::vector<base::char16> dependencies;
+
+    DISALLOW_COPY_AND_ASSIGN(ServiceConfig);
+  };
+
+  InstallServiceWorkItemImpl(const base::string16& service_name,
+                             const base::string16& display_name,
+                             const base::string16& service_cmd_line);
+
+  ~InstallServiceWorkItemImpl();
+
+  bool DoImpl();
+  void RollbackImpl();
+
+  // Member functions that help with service installation or upgrades.
+  bool IsServiceCorrectlyConfigured(const ServiceConfig& config);
+  bool DeleteCurrentService();
+
+  // Helper functions for service install/upgrade/delete/rollback.
+  bool OpenService();
+  bool GetServiceConfig(ServiceConfig* config) const;
+
+  // Stores in the registry a versioned service name generated by
+  // GenerateVersionedServiceName().
+  bool CreateAndSetServiceName() const;
+
+  // Returns the versioned service name if one exists in the registry under the
+  // named value service_name_. In other cases, it returns service_name_.
+  base::string16 GetCurrentServiceName() const;
+
+  // Copies and returns a vector containing a sequence of C-style strings
+  // terminated with '\0\0'. Return an empty vector if the input is nullptr.
+  static std::vector<base::char16> MultiSzToVector(
+      const base::char16* multi_sz);
+
+ private:
+  class ScHandleTraits {
+   public:
+    using Handle = SC_HANDLE;
+
+    static bool CloseHandle(SC_HANDLE handle) {
+      return ::CloseServiceHandle(handle) != FALSE;
+    }
+
+    static bool IsHandleValid(SC_HANDLE handle) { return handle != nullptr; }
+
+    static SC_HANDLE NullHandle() { return nullptr; }
+
+   private:
+    DISALLOW_IMPLICIT_CONSTRUCTORS(ScHandleTraits);
+  };
+
+  using ScopedScHandle =
+      base::win::GenericScopedHandle<ScHandleTraits,
+                                     base::win::DummyVerifierTraits>;
+
+  // Member functions that help with service installation or upgrades.
+  bool InstallNewService();
+
+  // Upgrades an existing service's configuration in-place. Returns true if the
+  // service was already properly configured, or if it was successfully
+  // upgraded; otherwise, returns false in case of any failure.
+  // Side-effects of this function:
+  //   * Saves the original service's config in |original_service_config_| if
+  //     the new service configuration will be different.
+  //     |original_service_config_| is used in rollback scenarios, specifically
+  //     in ReinstallOriginalService() and RestoreOriginalServiceConfig().
+  //   * Sets |rollback_existing_service_| to true if the service is
+  //     successfully upgraded, which is used by RollbackImpl().
+  bool UpgradeService();
+
+  // Member functions that help with rollbacks.
+  bool ReinstallOriginalService();
+  bool RestoreOriginalServiceConfig();
+
+  // Helper functions for service install/upgrade/delete/rollback.
+  bool InstallService(const ServiceConfig& config);
+  bool ChangeServiceConfig(const ServiceConfig& config);
+  bool DeleteService(ScopedScHandle service) const;
+
+  // Generates a versioned service name prefixed with service_name_ and suffixed
+  // with the current system time in hexadecimal format.
+  base::string16 GenerateVersionedServiceName() const;
+
+  // Persists the given service name in the registry.
+  bool SetServiceName(const base::string16& service_name) const;
+
+  // The service name, or in the case of a conflict, the prefix for the service
+  // name.
+  const base::string16 service_name_;
+
+  // The service name displayed to the user.
+  const base::string16 display_name_;
+
+  // The desired service command line.
+  const base::string16 service_cmd_line_;
+
+  ScopedScHandle scm_;
+  ScopedScHandle service_;
+
+  // Rollback-specific data.
+
+  // True if original_service_config_ and service_ are both valid, and the
+  // former should be applied to the latter on rollback.
+  bool rollback_existing_service_;
+
+  // True if service_ represents a newly-installed service that is to be deleted
+  // on rollback.
+  bool rollback_new_service_;
+
+  // The configuration of a pre-existing service on the machine that may have
+  // been modified or deleted.
+  ServiceConfig original_service_config_;
+
+  // The service name prior to any modifications; may be either |service_name_|
+  // or a value read from the registry.
+  base::string16 original_service_name_;
+
+  // True if a pre-existing service (named |original_service_name_|) could not
+  // be deleted and still exists on rollback.
+  bool original_service_still_exists_;
+
+  DISALLOW_COPY_AND_ASSIGN(InstallServiceWorkItemImpl);
+};
+
+}  // namespace installer
+
+#endif  // CHROME_INSTALLER_SETUP_INSTALL_SERVICE_WORK_ITEM_IMPL_H_
diff --git a/chrome/installer/setup/install_service_work_item_unittest.cc b/chrome/installer/setup/install_service_work_item_unittest.cc
new file mode 100644
index 0000000..59bdf11
--- /dev/null
+++ b/chrome/installer/setup/install_service_work_item_unittest.cc
@@ -0,0 +1,135 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/installer/setup/install_service_work_item.h"
+#include "chrome/installer/setup/install_service_work_item_impl.h"
+
+#include <memory>
+#include <vector>
+
+#include "base/stl_util.h"
+#include "base/win/registry.h"
+#include "chrome/install_static/install_util.h"
+#include "chrome/installer/util/work_item.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace installer {
+
+namespace {
+
+constexpr base::char16 kServiceName[] = L"InstallServiceWorkItemService";
+constexpr base::char16 kServiceDisplayName[] = L"InstallServiceWorkItemService";
+constexpr base::char16 kServiceCmdLine[] = L"c:\\windows\\system32\\cmd.exe";
+
+}  // namespace
+
+class InstallServiceWorkItemTest : public ::testing::Test {
+ protected:
+  static InstallServiceWorkItemImpl* GetImpl(InstallServiceWorkItem* item) {
+    DCHECK(item);
+    return item->impl_.get();
+  }
+  static bool IsServiceCorrectlyConfigured(InstallServiceWorkItem* item) {
+    DCHECK(item);
+    InstallServiceWorkItemImpl::ServiceConfig config;
+    if (!GetImpl(item)->GetServiceConfig(&config))
+      return false;
+
+    return GetImpl(item)->IsServiceCorrectlyConfigured(config);
+  }
+};
+
+TEST_F(InstallServiceWorkItemTest, Do_MultiSzToVector) {
+  constexpr base::char16 kZeroMultiSz[] = L"";
+  std::vector<base::char16> vec =
+      InstallServiceWorkItemImpl::MultiSzToVector(kZeroMultiSz);
+  EXPECT_TRUE(!memcmp(vec.data(), &kZeroMultiSz, sizeof(kZeroMultiSz)));
+  EXPECT_EQ(vec.size(), base::size(kZeroMultiSz));
+
+  vec = InstallServiceWorkItemImpl::MultiSzToVector(nullptr);
+  EXPECT_TRUE(vec.empty());
+
+  constexpr base::char16 kRpcMultiSz[] = L"RPCSS\0";
+  vec = InstallServiceWorkItemImpl::MultiSzToVector(kRpcMultiSz);
+  EXPECT_TRUE(!memcmp(vec.data(), &kRpcMultiSz, sizeof(kRpcMultiSz)));
+  EXPECT_EQ(vec.size(), base::size(kRpcMultiSz));
+
+  constexpr base::char16 kMultiSz[] = L"RPCSS\0LSASS\0";
+  vec = InstallServiceWorkItemImpl::MultiSzToVector(kMultiSz);
+  EXPECT_TRUE(!memcmp(vec.data(), &kMultiSz, sizeof(kMultiSz)));
+  EXPECT_EQ(vec.size(), base::size(kMultiSz));
+}
+
+TEST_F(InstallServiceWorkItemTest, Do_FreshInstall) {
+  auto item = std::make_unique<InstallServiceWorkItem>(
+      kServiceName, kServiceDisplayName, kServiceCmdLine);
+
+  ASSERT_TRUE(item->Do());
+  EXPECT_TRUE(GetImpl(item.get())->OpenService());
+  EXPECT_TRUE(IsServiceCorrectlyConfigured(item.get()));
+
+  item->Rollback();
+  EXPECT_FALSE(GetImpl(item.get())->OpenService());
+}
+
+TEST_F(InstallServiceWorkItemTest, Do_UpgradeNoChanges) {
+  auto item = std::make_unique<InstallServiceWorkItem>(
+      kServiceName, kServiceDisplayName, kServiceCmdLine);
+  ASSERT_TRUE(item->Do());
+
+  EXPECT_TRUE(IsServiceCorrectlyConfigured(item.get()));
+
+  // Same command line:
+  auto item_upgrade = std::make_unique<InstallServiceWorkItem>(
+      kServiceName, kServiceDisplayName, kServiceCmdLine);
+  EXPECT_TRUE(item_upgrade->Do());
+
+  item_upgrade->Rollback();
+  EXPECT_TRUE(GetImpl(item_upgrade.get())->OpenService());
+
+  EXPECT_TRUE(GetImpl(item_upgrade.get())->DeleteCurrentService());
+}
+
+TEST_F(InstallServiceWorkItemTest, Do_UpgradeChangedCmdLine) {
+  auto item = std::make_unique<InstallServiceWorkItem>(
+      kServiceName, kServiceDisplayName, kServiceCmdLine);
+  ASSERT_TRUE(item->Do());
+
+  EXPECT_TRUE(IsServiceCorrectlyConfigured(item.get()));
+
+  // New command line.
+  auto item_upgrade = std::make_unique<InstallServiceWorkItem>(
+      kServiceName, kServiceDisplayName, L"NewCmd.exe new cmd line");
+  EXPECT_TRUE(item_upgrade->Do());
+
+  item_upgrade->Rollback();
+  EXPECT_TRUE(GetImpl(item_upgrade.get())->OpenService());
+
+  EXPECT_TRUE(IsServiceCorrectlyConfigured(item.get()));
+  EXPECT_FALSE(IsServiceCorrectlyConfigured(item_upgrade.get()));
+
+  EXPECT_TRUE(GetImpl(item_upgrade.get())->DeleteCurrentService());
+}
+
+TEST_F(InstallServiceWorkItemTest, Do_ServiceName) {
+  base::win::RegKey key;
+  ASSERT_EQ(ERROR_SUCCESS,
+            key.Create(HKEY_LOCAL_MACHINE,
+                       install_static::GetClientStateKeyPath().c_str(),
+                       KEY_WRITE | KEY_WOW64_32KEY));
+  auto item = std::make_unique<InstallServiceWorkItem>(
+      kServiceName, kServiceDisplayName, kServiceCmdLine);
+
+  EXPECT_STREQ(kServiceName,
+               GetImpl(item.get())->GetCurrentServiceName().c_str());
+  EXPECT_TRUE(GetImpl(item.get())->CreateAndSetServiceName());
+  EXPECT_STRNE(kServiceName,
+               GetImpl(item.get())->GetCurrentServiceName().c_str());
+  EXPECT_EQ(0UL,
+            GetImpl(item.get())->GetCurrentServiceName().find(kServiceName));
+
+  EXPECT_EQ(ERROR_SUCCESS, key.DeleteKey(L""));
+}
+
+}  // namespace installer
diff --git a/chrome/installer/setup/run_all_unittests.cc b/chrome/installer/setup/run_all_unittests.cc
index 6c123f3..2617d756a 100644
--- a/chrome/installer/setup/run_all_unittests.cc
+++ b/chrome/installer/setup/run_all_unittests.cc
@@ -23,8 +23,7 @@
 
   install_static::ScopedInstallDetails scoped_install_details;
 
-  return base::LaunchUnitTests(
-      argc,
-      argv,
+  return base::LaunchUnitTestsSerially(
+      argc, argv,
       base::Bind(&base::TestSuite::Run, base::Unretained(&test_suite)));
 }
diff --git a/chrome/renderer/resources/extensions/file_manager_private_custom_bindings.js b/chrome/renderer/resources/extensions/file_manager_private_custom_bindings.js
index 72fac16..1330e2e 100644
--- a/chrome/renderer/resources/extensions/file_manager_private_custom_bindings.js
+++ b/chrome/renderer/resources/extensions/file_manager_private_custom_bindings.js
@@ -229,6 +229,12 @@
     var url = fileManagerPrivateNatives.GetEntryURL(entry);
     fileManagerPrivateInternal.installLinuxPackage(url, callback);
   });
+
+  apiFunctions.setHandleRequest('getThumbnail', function(
+        entry, cropToSquare, callback) {
+    var url = fileManagerPrivateNatives.GetEntryURL(entry);
+    fileManagerPrivateInternal.getThumbnail(url, cropToSquare, callback);
+  });
 });
 
 registerArgumentMassager('fileManagerPrivate.onDirectoryChanged',
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index adaa789..33a1ece 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -1529,7 +1529,6 @@
       ]
       if (!is_chromeos) {
         sources += [
-          "../browser/ui/views/frame/opaque_browser_frame_view_browsertest.cc",
           "../browser/ui/views/relaunch_notification/relaunch_recommended_bubble_view_browsertest.cc",
           "../browser/ui/views/relaunch_notification/relaunch_required_dialog_view_browsertest.cc",
         ]
diff --git a/chrome/test/data/android/render_tests/TileGridLayoutTest.ntp_tile_grid_layout.Nexus_5-19.png b/chrome/test/data/android/render_tests/TileGridLayoutTest.ntp_tile_grid_layout.Nexus_5-19.png
index 6c08d10..f58a848 100644
--- a/chrome/test/data/android/render_tests/TileGridLayoutTest.ntp_tile_grid_layout.Nexus_5-19.png
+++ b/chrome/test/data/android/render_tests/TileGridLayoutTest.ntp_tile_grid_layout.Nexus_5-19.png
Binary files differ
diff --git a/chrome/test/data/webui/extensions/runtime_host_permissions_test.js b/chrome/test/data/webui/extensions/runtime_host_permissions_test.js
index 201ab2c..308fe44ee 100644
--- a/chrome/test/data/webui/extensions/runtime_host_permissions_test.js
+++ b/chrome/test/data/webui/extensions/runtime_host_permissions_test.js
@@ -90,15 +90,84 @@
     }
 
     // Check that selecting different values correctly notifies the delegate.
-    return expectDelegateCallOnAccessChange(HostAccess.ON_SPECIFIC_SITES)
-        .then(() => {
-          return expectDelegateCallOnAccessChange(HostAccess.ON_ALL_SITES);
-        })
+    return expectDelegateCallOnAccessChange(HostAccess.ON_ALL_SITES)
         .then(() => {
           return expectDelegateCallOnAccessChange(HostAccess.ON_CLICK);
         });
   });
 
+  test('on select sites cancel', function() {
+    const permissions = {
+      simplePermissions: ['permission 1', 'permission 2'],
+      hostAccess: HostAccess.ON_CLICK,
+      runtimeHostPermissions: [],
+    };
+
+    element.permissions = permissions;
+    Polymer.dom.flush();
+
+    const selectHostAccess = element.$$('#host-access');
+    assertTrue(!!selectHostAccess);
+
+    selectHostAccess.value = HostAccess.ON_SPECIFIC_SITES;
+    selectHostAccess.dispatchEvent(new CustomEvent('change'));
+
+    Polymer.dom.flush();
+    const dialog = element.$$('extensions-runtime-hosts-dialog');
+    assertTrue(!!dialog);
+
+    expectTrue(dialog.updateHostAccess);
+
+    // Canceling the dialog should reset the selectHostAccess value to ON_CLICK,
+    // since no host was added.
+    assertTrue(dialog.isOpen());
+    let whenClosed = test_util.eventToPromise('close', dialog);
+    dialog.$$('.cancel-button').click();
+    return whenClosed.then(() => {
+      Polymer.dom.flush();
+      expectEquals(HostAccess.ON_CLICK, selectHostAccess.value);
+    });
+  });
+
+  test('on select sites accept', function() {
+    const permissions = {
+      simplePermissions: ['permission 1', 'permission 2'],
+      hostAccess: HostAccess.ON_CLICK,
+      runtimeHostPermissions: [],
+    };
+
+    element.set('permissions', permissions);
+    Polymer.dom.flush();
+
+    const selectHostAccess = element.$$('#host-access');
+    assertTrue(!!selectHostAccess);
+
+    selectHostAccess.value = HostAccess.ON_SPECIFIC_SITES;
+    selectHostAccess.dispatchEvent(
+        new CustomEvent('change', {target: selectHostAccess}));
+
+    Polymer.dom.flush();
+    const dialog = element.$$('extensions-runtime-hosts-dialog');
+    assertTrue(!!dialog);
+
+    expectTrue(dialog.updateHostAccess);
+
+    // Make the add button clickable by entering valid input.
+    const input = dialog.$$('cr-input');
+    input.value = 'https://example.com';
+    input.fire('input');
+
+    // Closing the dialog (as opposed to canceling) should keep the
+    // selectHostAccess value at ON_SPECIFIC_SITES.
+    assertTrue(dialog.isOpen());
+    let whenClosed = test_util.eventToPromise('close', dialog);
+    dialog.$$('.action-button').click();
+    return whenClosed.then(() => {
+      Polymer.dom.flush();
+      expectEquals(HostAccess.ON_SPECIFIC_SITES, selectHostAccess.value);
+    });
+  });
+
   test('clicking add host triggers dialog', function() {
     const permissions = {
       simplePermissions: [],
@@ -119,6 +188,7 @@
     assertTrue(!!dialog);
     expectTrue(dialog.$.dialog.open);
     expectEquals(null, dialog.currentSite);
+    expectFalse(dialog.updateHostAccess);
   });
 
   test('removing runtime host permissions', function() {
@@ -169,6 +239,7 @@
     const dialog = element.$$('extensions-runtime-hosts-dialog');
     assertTrue(!!dialog);
     expectTrue(dialog.$.dialog.open);
+    expectFalse(dialog.updateHostAccess);
     expectEquals('https://example.com', dialog.currentSite);
   });
 });
diff --git a/chrome/test/data/webui/extensions/runtime_hosts_dialog_test.js b/chrome/test/data/webui/extensions/runtime_hosts_dialog_test.js
index f29d75f..eb527e9 100644
--- a/chrome/test/data/webui/extensions/runtime_hosts_dialog_test.js
+++ b/chrome/test/data/webui/extensions/runtime_hosts_dialog_test.js
@@ -122,4 +122,24 @@
         'https://example.com:80/*',
         extensions.getPatternFromSite('https://example.com:80/*'));
   });
+
+  test('update site access', function() {
+    dialog.updateHostAccess = true;
+    const input = dialog.$$('cr-input');
+    const site = 'http://www.example.com';
+    input.value = site;
+    input.fire('input');
+    assertFalse(input.invalid);
+
+    const submit = dialog.$.submit;
+    assertFalse(submit.disabled);
+    submit.click();
+    return delegate.whenCalled('setItemHostAccess').then((args) => {
+      let id = args[0];
+      let access = args[1];
+      assertEquals(ITEM_ID, id);
+      assertEquals(
+          chrome.developerPrivate.HostAccess.ON_SPECIFIC_SITES, access);
+    });
+  });
 });
diff --git a/chrome/test/data/webui/print_preview/destination_search_test.js b/chrome/test/data/webui/print_preview/destination_search_test.js
index e9015680..24610514 100644
--- a/chrome/test/data/webui/print_preview/destination_search_test.js
+++ b/chrome/test/data/webui/print_preview/destination_search_test.js
@@ -103,7 +103,7 @@
         success: true,
       };
       if (cr.isChromeOS) {
-        nativeLayer.setSetupPrinterResponse(false, response);
+        nativeLayer.setSetupPrinterResponse(response);
       } else {
         nativeLayer.setLocalDestinationCapabilities(
             print_preview_test_utils.getCddTemplate(destId));
@@ -133,7 +133,7 @@
       const destId = '001122DEADBEEF';
       const originalDestination = destinationStore.selectedDestination;
       nativeLayer.setSetupPrinterResponse(
-          true, {printerId: destId, success: false});
+          {printerId: destId, success: false}, true);
       requestSetup(destId);
       return nativeLayer.whenCalled('setupPrinter').then(function(actualId) {
         assertEquals(destId, actualId);
@@ -154,7 +154,7 @@
             print_preview_test_utils.getCddTemplate(destId).capabilities,
         success: false,
       };
-      nativeLayer.setSetupPrinterResponse(false, response);
+      nativeLayer.setSetupPrinterResponse(response);
       requestSetup(destId);
       return nativeLayer.whenCalled('setupPrinter')
           .then(function(actualDestId) {
@@ -215,7 +215,7 @@
         policies: {allowedColorModes: print_preview.ColorMode.GRAY},
         success: true,
       };
-      nativeLayer.setSetupPrinterResponse(false, response);
+      nativeLayer.setSetupPrinterResponse(response);
       requestSetup(destId);
       return nativeLayer.whenCalled('setupPrinter').then(function(actualId) {
         assertEquals(destId, actualId);
diff --git a/chrome/test/data/webui/print_preview/native_layer_stub.js b/chrome/test/data/webui/print_preview/native_layer_stub.js
index 90caa2a9..09fa32c 100644
--- a/chrome/test/data/webui/print_preview/native_layer_stub.js
+++ b/chrome/test/data/webui/print_preview/native_layer_stub.js
@@ -184,7 +184,7 @@
     /**
      * @param {!print_preview.CapabilitiesResponse} response The
      *     response to send for the destination whose ID is in the response.
-     * @param {boolean?} opt_reject Whether to reject the callback for this
+     * @param {?boolean} opt_reject Whether to reject the callback for this
      *     destination. Defaults to false (will resolve callback) if not
      *     provided.
      */
@@ -195,12 +195,13 @@
     }
 
     /**
-     * @param {boolean} reject Whether printSetup requests should be rejected.
      * @param {!print_preview.PrinterSetupResponse} The response to send when
      *     |setupPrinter| is called.
+     * @param {?boolean} opt_reject Whether printSetup requests should be
+     *     rejected. Defaults to false (will resolve callback) if not provided.
      */
-    setSetupPrinterResponse(reject, response) {
-      this.shouldRejectPrinterSetup_ = reject;
+    setSetupPrinterResponse(response, opt_reject) {
+      this.shouldRejectPrinterSetup_ = opt_reject || false;
       this.setupPrinterResponse_ = response;
     }
 
diff --git a/chrome/test/data/webui/print_preview/policy_test.js b/chrome/test/data/webui/print_preview/policy_test.js
index 1fdd473..1688b8bc 100644
--- a/chrome/test/data/webui/print_preview/policy_test.js
+++ b/chrome/test/data/webui/print_preview/policy_test.js
@@ -77,7 +77,8 @@
     }
 
     function getCheckbox() {
-      return page.$$('print-preview-other-options-settings').$.headerFooter;
+      return page.$$('print-preview-other-options-settings')
+          .$$('#headerFooter');
     }
 
     /**
diff --git a/chrome/test/data/webui/print_preview/print_preview_destination_search_test.js b/chrome/test/data/webui/print_preview/print_preview_destination_search_test.js
index 01c49e7..13e7dab 100644
--- a/chrome/test/data/webui/print_preview/print_preview_destination_search_test.js
+++ b/chrome/test/data/webui/print_preview/print_preview_destination_search_test.js
@@ -131,10 +131,12 @@
     test('ResolutionFails', function() {
       const destId = '001122DEADBEEF';
       if (cr.isChromeOS) {
-        nativeLayer_.setSetupPrinterResponse(true, {
-          printerId: destId,
-          success: false,
-        });
+        nativeLayer_.setSetupPrinterResponse(
+            {
+              printerId: destId,
+              success: false,
+            },
+            true);
       } else {
         nativeLayer_.setLocalDestinationCapabilities(
             {
@@ -161,7 +163,7 @@
         success: true,
       };
       if (cr.isChromeOS)
-        nativeLayer_.setSetupPrinterResponse(false, response);
+        nativeLayer_.setSetupPrinterResponse(response);
       else
         nativeLayer_.setLocalDestinationCapabilities({
           printer: {
@@ -196,7 +198,7 @@
           capabilities: getCaps(),
           success: false,
         };
-        nativeLayer_.setSetupPrinterResponse(false, response);
+        nativeLayer_.setSetupPrinterResponse(response);
         requestSetup(destId, destinationSearch_);
         return nativeLayer_.whenCalled('setupPrinter')
             .then(function(actualDestId) {
diff --git a/chrome/test/data/webui/print_preview/settings_section_test.js b/chrome/test/data/webui/print_preview/settings_section_test.js
index 650b9951..f512002 100644
--- a/chrome/test/data/webui/print_preview/settings_section_test.js
+++ b/chrome/test/data/webui/print_preview/settings_section_test.js
@@ -395,13 +395,21 @@
       assertTrue(scalingElement.hidden);
     });
 
+    /**
+     * @param {!CrCheckboxElement} checkbox The checkbox to check
+     * @return {boolean} Whether the checkbox's parent section is hidden.
+     */
+    function isSectionHidden(checkbox) {
+      return checkbox.parentNode.parentNode.hidden;
+    }
+
     test(assert(TestNames.Other), function() {
       const optionsElement = page.$$('print-preview-other-options-settings');
-      const headerFooter = optionsElement.$.headerFooter;
-      const duplex = optionsElement.$.duplex;
-      const cssBackground = optionsElement.$.cssBackground;
-      const rasterize = optionsElement.$.rasterize;
-      const selectionOnly = optionsElement.$.selectionOnly;
+      const headerFooter = optionsElement.$$('#headerFooter');
+      const duplex = optionsElement.$$('#duplex');
+      const cssBackground = optionsElement.$$('#cssBackground');
+      const rasterize = optionsElement.$$('#rasterize');
+      const selectionOnly = optionsElement.$$('#selectionOnly');
 
       // Start with HTML + duplex capability.
       initDocumentInfo(false, false);
@@ -411,16 +419,16 @@
 
       // Expanding more settings will show the section.
       toggleMoreSettings();
-      assertFalse(headerFooter.hidden);
-      assertFalse(duplex.hidden);
-      assertFalse(cssBackground.hidden);
-      assertTrue(rasterize.hidden);
-      assertTrue(selectionOnly.hidden);
+      assertFalse(isSectionHidden(headerFooter));
+      assertFalse(isSectionHidden(duplex));
+      assertFalse(isSectionHidden(cssBackground));
+      assertTrue(isSectionHidden(rasterize));
+      assertTrue(isSectionHidden(selectionOnly));
 
       // Add a selection - should show selection only.
       initDocumentInfo(false, true);
       assertFalse(optionsElement.hidden);
-      assertFalse(selectionOnly.hidden);
+      assertFalse(isSectionHidden(selectionOnly));
 
       // Remove duplex capability.
       capabilities =
@@ -429,7 +437,7 @@
       page.set('destination_.capabilities', capabilities);
       Polymer.dom.flush();
       assertFalse(optionsElement.hidden);
-      assertTrue(duplex.hidden);
+      assertTrue(isSectionHidden(duplex));
 
       // PDF
       initDocumentInfo(true, false);
@@ -439,17 +447,17 @@
         assertTrue(optionsElement.hidden);
       } else {
         // All sections hidden except rasterize
-        assertTrue(headerFooter.hidden);
-        assertTrue(duplex.hidden);
-        assertTrue(cssBackground.hidden);
-        assertEquals(cr.isWindows || cr.isMac, rasterize.hidden);
-        assertTrue(selectionOnly.hidden);
+        assertTrue(isSectionHidden(headerFooter));
+        assertTrue(isSectionHidden(duplex));
+        assertTrue(isSectionHidden(cssBackground));
+        assertEquals(cr.isWindows || cr.isMac, isSectionHidden(rasterize));
+        assertTrue(isSectionHidden(selectionOnly));
       }
 
       // Add a selection - should do nothing for PDFs.
       initDocumentInfo(true, true);
       assertEquals(cr.isWindows || cr.isMac, optionsElement.hidden);
-      assertTrue(selectionOnly.hidden);
+      assertTrue(isSectionHidden(selectionOnly));
 
       // Add duplex.
       capabilities =
@@ -457,12 +465,12 @@
       page.set('destination_.capabilities', capabilities);
       Polymer.dom.flush();
       assertFalse(optionsElement.hidden);
-      assertFalse(duplex.hidden);
+      assertFalse(isSectionHidden(duplex));
     });
 
     test(assert(TestNames.HeaderFooter), function() {
       const optionsElement = page.$$('print-preview-other-options-settings');
-      const headerFooter = optionsElement.$.headerFooter;
+      const headerFooter = optionsElement.$$('#headerFooter');
 
       // HTML page to show Header/Footer option.
       initDocumentInfo(false, false);
@@ -472,13 +480,13 @@
 
       toggleMoreSettings();
       assertFalse(optionsElement.hidden);
-      assertFalse(headerFooter.hidden);
+      assertFalse(isSectionHidden(headerFooter));
 
       // Set margins to NONE
       page.set(
           'settings.margins.value',
           print_preview.ticket_items.MarginsTypeValue.NO_MARGINS);
-      assertTrue(headerFooter.hidden);
+      assertTrue(isSectionHidden(headerFooter));
 
       // Custom margins of 0.
       page.set(
@@ -487,25 +495,25 @@
       page.set(
           'settings.customMargins.vaue',
           {marginTop: 0, marginLeft: 0, marginRight: 0, marginBottom: 0});
-      assertTrue(headerFooter.hidden);
+      assertTrue(isSectionHidden(headerFooter));
 
       // Custom margins of 36 -> header/footer available
       page.set(
           'settings.customMargins.value',
           {marginTop: 36, marginLeft: 36, marginRight: 36, marginBottom: 36});
-      assertFalse(headerFooter.hidden);
+      assertFalse(isSectionHidden(headerFooter));
 
       // Zero top and bottom -> header/footer unavailable
       page.set(
           'settings.customMargins.value',
           {marginTop: 0, marginLeft: 36, marginRight: 36, marginBottom: 0});
-      assertTrue(headerFooter.hidden);
+      assertTrue(isSectionHidden(headerFooter));
 
       // Zero top and nonzero bottom -> header/footer available
       page.set(
           'settings.customMargins.value',
           {marginTop: 0, marginLeft: 36, marginRight: 36, marginBottom: 36});
-      assertFalse(headerFooter.hidden);
+      assertFalse(isSectionHidden(headerFooter));
 
       // Small paper sizes
       capabilities =
@@ -532,14 +540,14 @@
           print_preview.ticket_items.MarginsTypeValue.DEFAULT);
 
       // Header/footer should be available for default big label
-      assertFalse(headerFooter.hidden);
+      assertFalse(isSectionHidden(headerFooter));
 
       page.set(
           'settings.mediaSize.value',
           capabilities.printer.media_size.option[0]);
 
       // Header/footer should not be available for small label
-      assertTrue(headerFooter.hidden);
+      assertTrue(isSectionHidden(headerFooter));
     });
 
     test(assert(TestNames.SetPages), function() {
@@ -934,7 +942,7 @@
       const testOptionCheckbox = (settingName, defaultValue) => {
         const element = optionsElement.$$('#' + settingName);
         const optionSetting = page.settings[settingName];
-        assertFalse(element.hidden);
+        assertFalse(isSectionHidden(element));
         assertEquals(defaultValue, element.checked);
         assertEquals(defaultValue, optionSetting.value);
         element.checked = !defaultValue;
diff --git a/chrome/test/data/webui/signin/sync_confirmation_test.js b/chrome/test/data/webui/signin/sync_confirmation_test.js
index 74a19fd..24588c9d 100644
--- a/chrome/test/data/webui/signin/sync_confirmation_test.js
+++ b/chrome/test/data/webui/signin/sync_confirmation_test.js
@@ -54,7 +54,7 @@
     const STANDARD_CONSENT_DESCRIPTION_TEXT = [
       'Get Google smarts in Chrome',
       'Your bookmarks, passwords, history, and more on all your devices',
-      'Personalized Google services like Google Pay',
+      'More personal Google services, like better page suggestions',
       'Improve Chrome and its security by sending system and usage ' +
           'information to Google',
       'Google may use content on sites you visit, plus browser activity and ' +
diff --git a/chromecast/browser/BUILD.gn b/chromecast/browser/BUILD.gn
index 4358b4f..2714494 100644
--- a/chromecast/browser/BUILD.gn
+++ b/chromecast/browser/BUILD.gn
@@ -334,6 +334,7 @@
 # This target should only include interfaces which are required for unit tests.
 cast_source_set("public") {
   sources = [
+    "cast_content_window.cc",
     "cast_content_window.h",
     "cast_web_view.cc",
     "cast_web_view.h",
diff --git a/chromecast/browser/android/cast_content_window_android.cc b/chromecast/browser/android/cast_content_window_android.cc
index 7db3a61..44d3806 100644
--- a/chromecast/browser/android/cast_content_window_android.cc
+++ b/chromecast/browser/android/cast_content_window_android.cc
@@ -114,6 +114,9 @@
 void CastContentWindowAndroid::NotifyVisibilityChange(
     VisibilityType visibility_type) {
   delegate_->OnVisibilityChange(visibility_type);
+  for (auto& observer : observer_list_) {
+    observer.OnVisibilityChange(visibility_type);
+  }
 }
 
 void CastContentWindowAndroid::RequestMoveOut() {
diff --git a/chromecast/browser/cast_content_window.cc b/chromecast/browser/cast_content_window.cc
new file mode 100644
index 0000000..05444229
--- /dev/null
+++ b/chromecast/browser/cast_content_window.cc
@@ -0,0 +1,23 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chromecast/browser/cast_content_window.h"
+
+namespace chromecast {
+namespace shell {
+
+CastContentWindow::CastContentWindow() = default;
+
+CastContentWindow::~CastContentWindow() = default;
+
+void CastContentWindow::AddObserver(Observer* observer) {
+  observer_list_.AddObserver(observer);
+}
+
+void CastContentWindow::RemoveObserver(Observer* observer) {
+  observer_list_.RemoveObserver(observer);
+}
+
+}  // namespace shell
+}  // namespace chromecast
diff --git a/chromecast/browser/cast_content_window.h b/chromecast/browser/cast_content_window.h
index 18f86ea..9fc4a75 100644
--- a/chromecast/browser/cast_content_window.h
+++ b/chromecast/browser/cast_content_window.h
@@ -10,6 +10,8 @@
 
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
+#include "base/observer_list.h"
+#include "base/observer_list_types.h"
 #include "chromecast/graphics/cast_window_manager.h"
 #include "chromecast/graphics/gestures/cast_gesture_handler.h"
 #include "content/public/browser/web_contents.h"
@@ -143,11 +145,21 @@
     CreateParams() = default;
   };
 
+  class Observer : public base::CheckedObserver {
+   public:
+    // Notify visibility change for this window.
+    virtual void OnVisibilityChange(VisibilityType visibility_type) {}
+
+   protected:
+    ~Observer() override {}
+  };
+
   // Creates the platform specific CastContentWindow. |delegate| should outlive
   // the created CastContentWindow.
   static std::unique_ptr<CastContentWindow> Create(const CreateParams& params);
 
-  virtual ~CastContentWindow() {}
+  CastContentWindow();
+  virtual ~CastContentWindow();
 
   // Creates a full-screen window for |web_contents| and displays it if screen
   // access has been granted.
@@ -184,6 +196,13 @@
   // Cast activity or application calls it to request for moving out of the
   // screen.
   virtual void RequestMoveOut() = 0;
+
+  // Observer interface:
+  void AddObserver(Observer* observer);
+  void RemoveObserver(Observer* observer);
+
+ protected:
+  base::ObserverList<Observer> observer_list_;
 };
 
 }  // namespace shell
diff --git a/chromecast/browser/cast_content_window_aura.cc b/chromecast/browser/cast_content_window_aura.cc
index 29b8884..cf42edc14 100644
--- a/chromecast/browser/cast_content_window_aura.cc
+++ b/chromecast/browser/cast_content_window_aura.cc
@@ -143,6 +143,9 @@
 void CastContentWindowAura::NotifyVisibilityChange(
     VisibilityType visibility_type) {
   delegate_->OnVisibilityChange(visibility_type);
+  for (auto& observer : observer_list_) {
+    observer.OnVisibilityChange(visibility_type);
+  }
 }
 
 void CastContentWindowAura::RequestMoveOut(){};
diff --git a/chromecast/browser/cast_web_view_factory.cc b/chromecast/browser/cast_web_view_factory.cc
index 1cea0ee..f678811 100644
--- a/chromecast/browser/cast_web_view_factory.cc
+++ b/chromecast/browser/cast_web_view_factory.cc
@@ -21,6 +21,9 @@
 CastWebViewFactory::~CastWebViewFactory() = default;
 
 void CastWebViewFactory::OnPageDestroyed(CastWebView* web_view) {
+  for (auto& observer : observer_list_) {
+    observer.OnCastWebViewDestroyed(web_view);
+  }
   web_view->RemoveObserver(this);
   active_webviews_.erase(std::find_if(
       active_webviews_.begin(), active_webviews_.end(),
@@ -47,7 +50,18 @@
     active_webviews_.push_back({webview.get(), next_id_++});
     webview->AddObserver(this);
   }
+  for (auto& observer : observer_list_) {
+    observer.OnCastWebViewCreated(webview.get());
+  }
   return webview;
 }
 
+void CastWebViewFactory::AddObserver(Observer* observer) {
+  observer_list_.AddObserver(observer);
+}
+
+void CastWebViewFactory::RemoveObserver(Observer* observer) {
+  observer_list_.RemoveObserver(observer);
+}
+
 }  // namespace chromecast
diff --git a/chromecast/browser/cast_web_view_factory.h b/chromecast/browser/cast_web_view_factory.h
index 1406c0ec..b556a8b 100644
--- a/chromecast/browser/cast_web_view_factory.h
+++ b/chromecast/browser/cast_web_view_factory.h
@@ -11,6 +11,8 @@
 #include "base/callback_forward.h"
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
+#include "base/observer_list.h"
+#include "base/observer_list_types.h"
 #include "chromecast/browser/cast_web_view.h"
 #include "url/gurl.h"
 
@@ -34,6 +36,15 @@
 
 class CastWebViewFactory : public CastWebView::Observer {
  public:
+  class Observer : public base::CheckedObserver {
+   public:
+    virtual void OnCastWebViewCreated(CastWebView* web_view) {}
+    virtual void OnCastWebViewDestroyed(CastWebView* web_view) {}
+
+   protected:
+    ~Observer() override {}
+  };
+
   explicit CastWebViewFactory(content::BrowserContext* browser_context);
   ~CastWebViewFactory() override;
 
@@ -44,6 +55,9 @@
       const extensions::Extension* extension,
       const GURL& initial_url);
 
+  void AddObserver(Observer* observer);
+  void RemoveObserver(Observer* observer);
+
   const std::vector<ActiveWebview>& active_webviews() const {
     return active_webviews_;
   }
@@ -55,6 +69,7 @@
 
   content::BrowserContext* const browser_context_;
   base::RepeatingCallback<void(CastWebView*, int)> register_callback_;
+  base::ObserverList<Observer> observer_list_;
 
   std::vector<ActiveWebview> active_webviews_;
   int next_id_ = 1;
diff --git a/chromecast/media/cma/backend/audio_decoder_for_mixer.cc b/chromecast/media/cma/backend/audio_decoder_for_mixer.cc
index 61e2fbb..acf770a 100644
--- a/chromecast/media/cma/backend/audio_decoder_for_mixer.cc
+++ b/chromecast/media/cma/backend/audio_decoder_for_mixer.cc
@@ -467,6 +467,8 @@
   UpdateStatistics(delta);
 
   if (config.samples_per_second != config_.samples_per_second) {
+    LOG(INFO) << "Input sample rate changed from " << config_.samples_per_second
+              << " to " << config.samples_per_second;
     // Sample rate from actual stream doesn't match supposed sample rate from
     // the container. Update the mixer and rate shifter. Note that for now we
     // assume that this can only happen at start of stream (ie, on the first
@@ -482,22 +484,6 @@
   } else {
     int input_frames = decoded->data_size() / (kNumChannels * sizeof(float));
 
-    if (config.samples_per_second != config_.samples_per_second) {
-      LOG(WARNING) << "mixer_input sample_rate changed to: "
-                   << config.samples_per_second;
-      CreateRateShifter(config.samples_per_second);
-      mixer_input_.reset();
-      mixer_input_.reset(new BufferingMixerSource(
-          this, config.samples_per_second, backend_->Primary(),
-          backend_->DeviceId(), backend_->ContentType(),
-          ToPlayoutChannel(backend_->AudioChannel()), playback_start_pts_,
-          start_playback_asap_));
-      mixer_input_->SetVolumeMultiplier(volume_multiplier_);
-      pending_output_frames_ = kNoPendingOutput;
-      config_.samples_per_second = config.samples_per_second;
-      last_mixer_delay_ = RenderingDelay();
-    }
-
     last_push_pts_ = decoded->timestamp();
     last_push_pts_length_ =
         input_frames * kMicrosecondsPerSecond / config_.samples_per_second;
diff --git a/chromecast/media/cma/backend/av_sync_dummy.cc b/chromecast/media/cma/backend/av_sync_dummy.cc
index 04739f7..50386e91 100644
--- a/chromecast/media/cma/backend/av_sync_dummy.cc
+++ b/chromecast/media/cma/backend/av_sync_dummy.cc
@@ -9,35 +9,11 @@
 namespace chromecast {
 namespace media {
 
-class AvSyncDummy : public AvSync {
- public:
-  AvSyncDummy();
-
-  // AvSync implementation:
-  void NotifyStart(int64_t timestamp, int64_t pts) override;
-  void NotifyStop() override;
-  void NotifyPause() override;
-  void NotifyResume() override;
-  void NotifyPlaybackRateChange(float rate) override;
-};
-
 std::unique_ptr<AvSync> AvSync::Create(
-    const scoped_refptr<base::SingleThreadTaskRunner> task_runner,
+    scoped_refptr<base::SingleThreadTaskRunner> task_runner,
     MediaPipelineBackendForMixer* const backend) {
-  return std::make_unique<AvSyncDummy>();
+  return nullptr;
 }
 
-AvSyncDummy::AvSyncDummy() {}
-
-void AvSyncDummy::NotifyStart(int64_t timestamp, int64_t pts) {}
-
-void AvSyncDummy::NotifyStop() {}
-
-void AvSyncDummy::NotifyPause() {}
-
-void AvSyncDummy::NotifyResume() {}
-
-void AvSyncDummy::NotifyPlaybackRateChange(float rate) {}
-
 }  // namespace media
 }  // namespace chromecast
diff --git a/chromecast/media/cma/decoder/cast_audio_decoder.cc b/chromecast/media/cma/decoder/cast_audio_decoder.cc
index 19a2b2b..fa64a2e 100644
--- a/chromecast/media/cma/decoder/cast_audio_decoder.cc
+++ b/chromecast/media/cma/decoder/cast_audio_decoder.cc
@@ -57,17 +57,18 @@
                   ::media::ChannelLayout output_channel_layout) {
     DCHECK(task_runner_->RunsTasksInCurrentSequence());
     DCHECK(!initialized_);
-    config_ = config;
-    output_channel_layout_ = output_channel_layout;
 
-    // Media pipeline should already decrypt the stream with the selected key
-    // system. If media pipeline fails to decrypt the stream for some reason,
-    // the decoder will report kDecodeError when decoding.
-    config_.encryption_scheme = Unencrypted();
+    input_channel_count_ = config_.channel_number;
+    output_channel_layout_ = output_channel_layout;
+    config_ = config;
+    if (config.is_encrypted()) {
+      LOG(ERROR) << "Cannot decode encrypted audio";
+      OnInitialized(false);
+      return;
+    }
 
     // Remix to desired channel layout; update config_ to match what this class
     // outputs.
-    input_channel_count_ = config_.channel_number;
     CreateMixerIfNeeded(config_.channel_number);
     config_.channel_number =
         ::media::ChannelLayoutToChannelCount(output_channel_layout_);
@@ -75,8 +76,7 @@
     decoder_ = std::make_unique<::media::FFmpegAudioDecoder>(task_runner_,
                                                              &media_log_);
     decoder_->Initialize(
-        media::DecoderConfigAdapter::ToMediaAudioDecoderConfig(config_),
-        nullptr,
+        media::DecoderConfigAdapter::ToMediaAudioDecoderConfig(config), nullptr,
         base::BindRepeating(&CastAudioDecoderImpl::OnInitialized, weak_this_),
         base::BindRepeating(&CastAudioDecoderImpl::OnDecoderOutput, weak_this_),
         ::media::AudioDecoder::WaitingForDecryptionKeyCB());
@@ -169,7 +169,7 @@
                 << (config_.is_encrypted() ? "true" : "false");
       LOG(INFO) << "\tCodec: " << config_.codec;
       LOG(INFO) << "\tSample format: " << config_.sample_format;
-      LOG(INFO) << "\tChannels: " << config_.channel_number;
+      LOG(INFO) << "\tChannels: " << input_channel_count_;
       LOG(INFO) << "\tSample rate: " << config_.samples_per_second;
     }
 
diff --git a/chromeos/components/drivefs/fake_drivefs.cc b/chromeos/components/drivefs/fake_drivefs.cc
index fce9ae6..7a564e71 100644
--- a/chromeos/components/drivefs/fake_drivefs.cc
+++ b/chromeos/components/drivefs/fake_drivefs.cc
@@ -194,4 +194,10 @@
   std::move(callback).Run(drive::FILE_ERROR_OK);
 }
 
+void FakeDriveFs::GetThumbnail(const base::FilePath& path,
+                               bool crop_to_square,
+                               GetThumbnailCallback callback) {
+  std::move(callback).Run(base::nullopt);
+}
+
 }  // namespace drivefs
diff --git a/chromeos/components/drivefs/fake_drivefs.h b/chromeos/components/drivefs/fake_drivefs.h
index c7220f6..a1bce9d 100644
--- a/chromeos/components/drivefs/fake_drivefs.h
+++ b/chromeos/components/drivefs/fake_drivefs.h
@@ -58,6 +58,10 @@
 
   void ResetCache(ResetCacheCallback callback) override;
 
+  void GetThumbnail(const base::FilePath& path,
+                    bool crop_to_square,
+                    GetThumbnailCallback callback) override;
+
   const base::FilePath mount_path_;
 
   std::map<base::FilePath, FileMetadata> metadata_;
diff --git a/chromeos/components/drivefs/mojom/drivefs.mojom b/chromeos/components/drivefs/mojom/drivefs.mojom
index f561c34..2a617e8 100644
--- a/chromeos/components/drivefs/mojom/drivefs.mojom
+++ b/chromeos/components/drivefs/mojom/drivefs.mojom
@@ -36,6 +36,13 @@
 
   // Reset DriveFS cache.
   ResetCache() => (FileError error);
+
+  // Returns a PNG containing a thumbnail for |path|. If |crop_to_square|, a
+  // 360x360 thumbnail, cropped to fit a square is returned; otherwise a
+  // thumbnail up to 500x500, maintaining aspect ration, is returned. If |path|
+  // does not exist or does not have a thumbnail, |thumbnail| will be null.
+  GetThumbnail(mojo_base.mojom.FilePath path, bool crop_to_square) => (
+      array<uint8>? thumbnail);
 };
 
 // Implemented by Chrome, used from DriveFS.
diff --git a/chromeos/components/tether/asynchronous_shutdown_object_container_impl.cc b/chromeos/components/tether/asynchronous_shutdown_object_container_impl.cc
index 8b01a7b..67c1a9f9 100644
--- a/chromeos/components/tether/asynchronous_shutdown_object_container_impl.cc
+++ b/chromeos/components/tether/asynchronous_shutdown_object_container_impl.cc
@@ -5,6 +5,7 @@
 #include "chromeos/components/tether/asynchronous_shutdown_object_container_impl.h"
 
 #include "base/memory/ptr_util.h"
+#include "chromeos/chromeos_features.h"
 #include "chromeos/components/tether/ble_advertisement_device_queue.h"
 #include "chromeos/components/tether/ble_advertiser_impl.h"
 #include "chromeos/components/tether/ble_connection_manager.h"
@@ -89,36 +90,55 @@
             managed_network_configuration_handler,
         NetworkConnectionHandler* network_connection_handler,
         PrefService* pref_service)
-    : adapter_(adapter),
+    : adapter_(base::FeatureList::IsEnabled(chromeos::features::kMultiDeviceApi)
+                   ? nullptr
+                   : adapter),
       tether_host_fetcher_(tether_host_fetcher),
       local_device_data_provider_(
           std::make_unique<cryptauth::LocalDeviceDataProvider>(
               cryptauth_service)),
       ble_service_data_helper_(
-          BleServiceDataHelperImpl::Factory::Get()->BuildInstance(
-              tether_host_fetcher_,
-              local_device_data_provider_.get(),
-              device_sync_client)),
+          base::FeatureList::IsEnabled(chromeos::features::kMultiDeviceApi)
+              ? nullptr
+              : BleServiceDataHelperImpl::Factory::Get()->BuildInstance(
+                    tether_host_fetcher_,
+                    local_device_data_provider_.get(),
+                    device_sync_client)),
       ble_advertisement_device_queue_(
-          std::make_unique<BleAdvertisementDeviceQueue>()),
+          base::FeatureList::IsEnabled(chromeos::features::kMultiDeviceApi)
+              ? nullptr
+              : std::make_unique<BleAdvertisementDeviceQueue>()),
       ble_synchronizer_(
-          secure_channel::BleSynchronizer::Factory::Get()->BuildInstance(
-              adapter)),
-      ble_advertiser_(BleAdvertiserImpl::Factory::NewInstance(
-          ble_service_data_helper_.get(),
-          ble_synchronizer_.get())),
+          base::FeatureList::IsEnabled(chromeos::features::kMultiDeviceApi)
+              ? nullptr
+              : secure_channel::BleSynchronizer::Factory::Get()->BuildInstance(
+                    adapter)),
+      ble_advertiser_(
+          base::FeatureList::IsEnabled(chromeos::features::kMultiDeviceApi)
+              ? nullptr
+              : BleAdvertiserImpl::Factory::NewInstance(
+                    ble_service_data_helper_.get(),
+                    ble_synchronizer_.get())),
       ble_scanner_(
-          BleScannerImpl::Factory::NewInstance(adapter,
-                                               ble_service_data_helper_.get(),
-                                               ble_synchronizer_.get(),
-                                               tether_host_fetcher_)),
-      ble_connection_manager_(std::make_unique<BleConnectionManager>(
-          adapter,
-          ble_advertisement_device_queue_.get(),
-          ble_advertiser_.get(),
-          ble_scanner_.get())),
+          base::FeatureList::IsEnabled(chromeos::features::kMultiDeviceApi)
+              ? nullptr
+              : BleScannerImpl::Factory::NewInstance(
+                    adapter,
+                    ble_service_data_helper_.get(),
+                    ble_synchronizer_.get(),
+                    tether_host_fetcher_)),
+      ble_connection_manager_(
+          base::FeatureList::IsEnabled(chromeos::features::kMultiDeviceApi)
+              ? nullptr
+              : std::make_unique<BleConnectionManager>(
+                    adapter,
+                    ble_advertisement_device_queue_.get(),
+                    ble_advertiser_.get(),
+                    ble_scanner_.get())),
       ble_connection_metrics_logger_(
-          std::make_unique<BleConnectionMetricsLogger>()),
+          base::FeatureList::IsEnabled(chromeos::features::kMultiDeviceApi)
+              ? nullptr
+              : std::make_unique<BleConnectionMetricsLogger>()),
       disconnect_tethering_request_sender_(
           DisconnectTetheringRequestSenderImpl::Factory::NewInstance(
               device_sync_client,
@@ -133,17 +153,21 @@
           network_state_handler,
           pref_service,
           network_configuration_remover_.get())) {
-  ble_connection_manager_->AddMetricsObserver(
-      ble_connection_metrics_logger_.get());
+  if (!base::FeatureList::IsEnabled(chromeos::features::kMultiDeviceApi)) {
+    ble_connection_manager_->AddMetricsObserver(
+        ble_connection_metrics_logger_.get());
+  }
 }
 
 AsynchronousShutdownObjectContainerImpl::
     ~AsynchronousShutdownObjectContainerImpl() {
-  ble_connection_manager_->RemoveMetricsObserver(
-      ble_connection_metrics_logger_.get());
+  if (!base::FeatureList::IsEnabled(chromeos::features::kMultiDeviceApi)) {
+    ble_connection_manager_->RemoveMetricsObserver(
+        ble_connection_metrics_logger_.get());
+    ble_advertiser_->RemoveObserver(this);
+    ble_scanner_->RemoveObserver(this);
+  }
 
-  ble_advertiser_->RemoveObserver(this);
-  ble_scanner_->RemoveObserver(this);
   disconnect_tethering_request_sender_->RemoveObserver(this);
 }
 
@@ -155,8 +179,10 @@
   // The objects below require asynchronous shutdowns, so start observering
   // these objects. Once they notify observers that they are finished shutting
   // down, the asynchronous shutdown will complete.
-  ble_advertiser_->AddObserver(this);
-  ble_scanner_->AddObserver(this);
+  if (!base::FeatureList::IsEnabled(chromeos::features::kMultiDeviceApi)) {
+    ble_advertiser_->AddObserver(this);
+    ble_scanner_->AddObserver(this);
+  }
   disconnect_tethering_request_sender_->AddObserver(this);
 
   ShutdownIfPossible();
@@ -169,7 +195,9 @@
 
 BleConnectionManager*
 AsynchronousShutdownObjectContainerImpl::ble_connection_manager() {
-  return ble_connection_manager_.get();
+  return base::FeatureList::IsEnabled(chromeos::features::kMultiDeviceApi)
+             ? nullptr
+             : ble_connection_manager_.get();
 }
 
 DisconnectTetheringRequestSender*
@@ -208,8 +236,10 @@
   if (AreAsynchronousOperationsActive())
     return;
 
-  ble_advertiser_->RemoveObserver(this);
-  ble_scanner_->RemoveObserver(this);
+  if (!base::FeatureList::IsEnabled(chromeos::features::kMultiDeviceApi)) {
+    ble_advertiser_->RemoveObserver(this);
+    ble_scanner_->RemoveObserver(this);
+  }
   disconnect_tethering_request_sender_->RemoveObserver(this);
 
   shutdown_complete_callback_.Run();
@@ -219,7 +249,8 @@
     AreAsynchronousOperationsActive() {
   // All of the asynchronous shutdown procedures depend on Bluetooth. If
   // Bluetooth is off, there is no way to complete these tasks.
-  if (!adapter_->IsPowered())
+  if (!base::FeatureList::IsEnabled(chromeos::features::kMultiDeviceApi) &&
+      !adapter_->IsPowered())
     return false;
 
   // If there are pending disconnection requests, they must be sent before the
@@ -229,13 +260,15 @@
   }
 
   // The BLE scanner must shut down completely before the component shuts down.
-  if (ble_scanner_->ShouldDiscoverySessionBeActive() !=
-      ble_scanner_->IsDiscoverySessionActive()) {
+  if (!base::FeatureList::IsEnabled(chromeos::features::kMultiDeviceApi) &&
+      ble_scanner_->ShouldDiscoverySessionBeActive() !=
+          ble_scanner_->IsDiscoverySessionActive()) {
     return true;
   }
 
   // The BLE advertiser must unregister all of its advertisements.
-  if (ble_advertiser_->AreAdvertisementsRegistered())
+  if (!base::FeatureList::IsEnabled(chromeos::features::kMultiDeviceApi) &&
+      ble_advertiser_->AreAdvertisementsRegistered())
     return true;
 
   return false;
@@ -246,8 +279,10 @@
     std::unique_ptr<BleScanner> ble_scanner,
     std::unique_ptr<DisconnectTetheringRequestSender>
         disconnect_tethering_request_sender) {
-  ble_advertiser_ = std::move(ble_advertiser);
-  ble_scanner_ = std::move(ble_scanner);
+  if (!base::FeatureList::IsEnabled(chromeos::features::kMultiDeviceApi)) {
+    ble_advertiser_ = std::move(ble_advertiser);
+    ble_scanner_ = std::move(ble_scanner);
+  }
   disconnect_tethering_request_sender_ =
       std::move(disconnect_tethering_request_sender);
 }
diff --git a/chromeos/components/tether/host_connection_metrics_logger.cc b/chromeos/components/tether/host_connection_metrics_logger.cc
index 3a20b002..575878f 100644
--- a/chromeos/components/tether/host_connection_metrics_logger.cc
+++ b/chromeos/components/tether/host_connection_metrics_logger.cc
@@ -6,6 +6,7 @@
 
 #include "base/metrics/histogram_macros.h"
 #include "base/time/default_clock.h"
+#include "chromeos/chromeos_features.h"
 
 namespace chromeos {
 
@@ -17,12 +18,14 @@
     : connection_manager_(connection_manager),
       active_host_(active_host),
       clock_(base::DefaultClock::GetInstance()) {
-  connection_manager_->AddMetricsObserver(this);
+  if (!base::FeatureList::IsEnabled(chromeos::features::kMultiDeviceApi))
+    connection_manager_->AddMetricsObserver(this);
   active_host_->AddObserver(this);
 }
 
 HostConnectionMetricsLogger::~HostConnectionMetricsLogger() {
-  connection_manager_->RemoveMetricsObserver(this);
+  if (!base::FeatureList::IsEnabled(chromeos::features::kMultiDeviceApi))
+    connection_manager_->RemoveMetricsObserver(this);
   active_host_->RemoveObserver(this);
 }
 
@@ -112,6 +115,8 @@
 void HostConnectionMetricsLogger::OnAdvertisementReceived(
     const std::string& device_id,
     bool is_background_advertisement) {
+  DCHECK(!base::FeatureList::IsEnabled(chromeos::features::kMultiDeviceApi));
+
   device_id_to_received_background_advertisement_[device_id] =
       is_background_advertisement;
 }
@@ -143,7 +148,8 @@
       device_id_to_received_background_advertisement_[active_host_device_id_];
   active_host_device_id_.clear();
 
-  if (is_background_advertisement) {
+  if (is_background_advertisement ||
+      base::FeatureList::IsEnabled(chromeos::features::kMultiDeviceApi)) {
     UMA_HISTOGRAM_ENUMERATION(
         "InstantTethering.ConnectionToHostResult.SuccessRate.Background",
         event_type, ConnectionToHostResult_SuccessEventType::SUCCESS_MAX);
@@ -202,7 +208,8 @@
       clock_->Now() - connect_to_host_start_time_;
   connect_to_host_start_time_ = base::Time();
 
-  if (is_background_advertisement) {
+  if (is_background_advertisement ||
+      base::FeatureList::IsEnabled(chromeos::features::kMultiDeviceApi)) {
     UMA_HISTOGRAM_MEDIUM_TIMES(
         "InstantTethering.Performance.ConnectToHostDuration.Background",
         connect_to_host_duration);
diff --git a/chromeos/components/tether/host_connection_metrics_logger.h b/chromeos/components/tether/host_connection_metrics_logger.h
index a6a28df..6204c63 100644
--- a/chromeos/components/tether/host_connection_metrics_logger.h
+++ b/chromeos/components/tether/host_connection_metrics_logger.h
@@ -70,6 +70,8 @@
                            RecordConnectionResultSuccess);
   FRIEND_TEST_ALL_PREFIXES(HostConnectionMetricsLoggerTest,
                            RecordConnectionResultSuccess_Background);
+  FRIEND_TEST_ALL_PREFIXES(HostConnectionMetricsLoggerTest,
+                           RecordConnectionResultSuccess_MultiDeviceApiEnabled);
   FRIEND_TEST_ALL_PREFIXES(
       HostConnectionMetricsLoggerTest,
       RecordConnectionResultSuccess_Background_DifferentDevice);
@@ -77,6 +79,8 @@
                            RecordConnectionResultFailure);
   FRIEND_TEST_ALL_PREFIXES(HostConnectionMetricsLoggerTest,
                            RecordConnectionResultFailure_Background);
+  FRIEND_TEST_ALL_PREFIXES(HostConnectionMetricsLoggerTest,
+                           RecordConnectionResultFailure_MultiDeviceApiEnabled);
   FRIEND_TEST_ALL_PREFIXES(
       HostConnectionMetricsLoggerTest,
       RecordConnectionResultFailure_Background_DifferentDevice);
@@ -108,6 +112,8 @@
   FRIEND_TEST_ALL_PREFIXES(HostConnectionMetricsLoggerTest,
                            RecordConnectToHostDuration_Background);
   FRIEND_TEST_ALL_PREFIXES(HostConnectionMetricsLoggerTest,
+                           RecordConnectToHostDuration_MultiDeviceApiEnabled);
+  FRIEND_TEST_ALL_PREFIXES(HostConnectionMetricsLoggerTest,
                            RecordConnectionResultFailureNoResponse);
   FRIEND_TEST_ALL_PREFIXES(
       HostConnectionMetricsLoggerTest,
diff --git a/chromeos/components/tether/host_connection_metrics_logger_unittest.cc b/chromeos/components/tether/host_connection_metrics_logger_unittest.cc
index f7641cb..02406c6 100644
--- a/chromeos/components/tether/host_connection_metrics_logger_unittest.cc
+++ b/chromeos/components/tether/host_connection_metrics_logger_unittest.cc
@@ -7,7 +7,9 @@
 #include <memory>
 
 #include "base/test/metrics/histogram_tester.h"
+#include "base/test/scoped_feature_list.h"
 #include "base/test/simple_test_clock.h"
+#include "chromeos/chromeos_features.h"
 #include "chromeos/components/tether/fake_active_host.h"
 #include "chromeos/components/tether/fake_ble_connection_manager.h"
 #include "components/cryptauth/remote_device_ref.h"
@@ -40,6 +42,10 @@
     metrics_logger_->SetClockForTesting(&test_clock_);
   }
 
+  void SetMultiDeviceApiEnabled() {
+    scoped_feature_list_.InitAndEnableFeature(features::kMultiDeviceApi);
+  }
+
   void VerifyProvisioningFailure(
       HostConnectionMetricsLogger::
           ConnectionToHostResult_ProvisioningFailureEventType event_type) {
@@ -88,8 +94,12 @@
   void VerifyConnectToHostDuration(bool is_background_advertisement) {
     std::string device_id = test_devices_[0].GetDeviceId();
 
-    SetActiveHostToConnectingAndReceiveAdvertisement(
-        device_id, is_background_advertisement);
+    if (base::FeatureList::IsEnabled(chromeos::features::kMultiDeviceApi)) {
+      SetActiveHostToConnecting(device_id);
+    } else {
+      SetActiveHostToConnectingAndReceiveAdvertisement(
+          device_id, is_background_advertisement);
+    }
 
     test_clock_.Advance(kConnectToHostTime);
 
@@ -116,6 +126,10 @@
     fake_active_host_->SetActiveHostConnecting(device_id, kTetherNetworkGuid);
   }
 
+  void SetActiveHostToConnecting(const std::string& device_id) {
+    fake_active_host_->SetActiveHostConnecting(device_id, kTetherNetworkGuid);
+  }
+
   const cryptauth::RemoteDeviceRefList test_devices_;
 
   std::unique_ptr<FakeBleConnectionManager> fake_ble_connection_manager_;
@@ -125,6 +139,8 @@
   base::HistogramTester histogram_tester_;
   base::SimpleTestClock test_clock_;
 
+  base::test::ScopedFeatureList scoped_feature_list_;
+
  private:
   DISALLOW_COPY_AND_ASSIGN(HostConnectionMetricsLoggerTest);
 };
@@ -181,6 +197,25 @@
 }
 
 TEST_F(HostConnectionMetricsLoggerTest,
+       RecordConnectionResultSuccess_MultiDeviceApiEnabled) {
+  SetMultiDeviceApiEnabled();
+
+  SetActiveHostToConnecting(test_devices_[0].GetDeviceId());
+
+  metrics_logger_->RecordConnectionToHostResult(
+      HostConnectionMetricsLogger::ConnectionToHostResult::
+          CONNECTION_RESULT_SUCCESS,
+      test_devices_[0].GetDeviceId());
+
+  VerifySuccess(HostConnectionMetricsLogger::
+                    ConnectionToHostResult_SuccessEventType::SUCCESS,
+                true /* is_background_advertisement */);
+  VerifyProvisioningFailure(
+      HostConnectionMetricsLogger::
+          ConnectionToHostResult_ProvisioningFailureEventType::OTHER);
+}
+
+TEST_F(HostConnectionMetricsLoggerTest,
        RecordConnectionResultSuccess_Background_DifferentDevice) {
   SetActiveHostToConnectingAndReceiveAdvertisement(
       test_devices_[0].GetDeviceId(), true /* is_background_advertisement */);
@@ -240,6 +275,28 @@
 }
 
 TEST_F(HostConnectionMetricsLoggerTest,
+       RecordConnectionResultFailure_MultiDeviceApiEnabled) {
+  SetMultiDeviceApiEnabled();
+
+  SetActiveHostToConnecting(test_devices_[0].GetDeviceId());
+
+  metrics_logger_->RecordConnectionToHostResult(
+      HostConnectionMetricsLogger::ConnectionToHostResult::
+          CONNECTION_RESULT_FAILURE_UNKNOWN_ERROR,
+      test_devices_[0].GetDeviceId());
+
+  VerifyFailure(HostConnectionMetricsLogger::
+                    ConnectionToHostResult_FailureEventType::UNKNOWN_ERROR);
+
+  VerifySuccess(HostConnectionMetricsLogger::
+                    ConnectionToHostResult_SuccessEventType::FAILURE,
+                true /* is_background_advertisement */);
+  VerifyProvisioningFailure(
+      HostConnectionMetricsLogger::
+          ConnectionToHostResult_ProvisioningFailureEventType::OTHER);
+}
+
+TEST_F(HostConnectionMetricsLoggerTest,
        RecordConnectionResultFailure_Background_DifferentDevice) {
   SetActiveHostToConnectingAndReceiveAdvertisement(
       test_devices_[0].GetDeviceId(), true /* is_background_advertisement */);
@@ -465,6 +522,11 @@
 }
 
 TEST_F(HostConnectionMetricsLoggerTest,
+       RecordConnectToHostDuration_MultiDeviceApiEnabled) {
+  VerifyConnectToHostDuration(true /* is_background_advertisement */);
+}
+
+TEST_F(HostConnectionMetricsLoggerTest,
        RecordConnectionResultFailureNoResponse) {
   SetActiveHostToConnectingAndReceiveAdvertisement(
       test_devices_[0].GetDeviceId(), false /* is_background_advertisement */);
diff --git a/chromeos/network/dhcp_pac_file_fetcher_chromeos.cc b/chromeos/network/dhcp_pac_file_fetcher_chromeos.cc
index c3a4f42..7fcb8a0 100644
--- a/chromeos/network/dhcp_pac_file_fetcher_chromeos.cc
+++ b/chromeos/network/dhcp_pac_file_fetcher_chromeos.cc
@@ -4,6 +4,7 @@
 
 #include "chromeos/network/dhcp_pac_file_fetcher_chromeos.h"
 
+#include "base/callback_helpers.h"
 #include "base/location.h"
 #include "base/task_runner_util.h"
 #include "chromeos/network/network_event_log.h"
@@ -50,15 +51,12 @@
   if (!network_handler_task_runner_.get())
     return net::ERR_PAC_NOT_IN_DHCP;
   CHECK(callback);
-  // DhcpPacFileFetcher only allows one Fetch in progress at a time.
-  CHECK(!callback_);
-  callback_ = std::move(callback);
   base::PostTaskAndReplyWithResult(
       network_handler_task_runner_.get(), FROM_HERE,
       base::BindOnce(&GetPacUrlFromDefaultNetwork),
       base::BindOnce(&DhcpPacFileFetcherChromeos::ContinueFetch,
                      weak_ptr_factory_.GetWeakPtr(), utf16_text,
-                     traffic_annotation));
+                     std::move(callback), traffic_annotation));
   return net::ERR_IO_PENDING;
 }
 
@@ -82,26 +80,22 @@
 
 void DhcpPacFileFetcherChromeos::ContinueFetch(
     base::string16* utf16_text,
+    net::CompletionOnceCallback callback,
     const net::NetworkTrafficAnnotationTag traffic_annotation,
     std::string pac_url) {
   NET_LOG_EVENT("DhcpPacFileFetcher", pac_url);
   pac_url_ = GURL(pac_url);
   if (pac_url_.is_empty()) {
-    std::move(callback_).Run(net::ERR_PAC_NOT_IN_DHCP);
+    std::move(callback).Run(net::ERR_PAC_NOT_IN_DHCP);
     return;
   }
 
-  int result = pac_file_fetcher_->Fetch(
-      pac_url_, utf16_text,
-      base::BindOnce(&DhcpPacFileFetcherChromeos::OnFetchCompleted,
-                     weak_ptr_factory_.GetWeakPtr()),
-      traffic_annotation);
-  if (result != net::ERR_IO_PENDING)
-    std::move(callback_).Run(result);
-}
-
-void DhcpPacFileFetcherChromeos::OnFetchCompleted(int result) {
-  std::move(callback_).Run(result);
+  auto repeating_callback =
+      base::AdaptCallbackForRepeating(std::move(callback));
+  int res = pac_file_fetcher_->Fetch(pac_url_, utf16_text, repeating_callback,
+                                     traffic_annotation);
+  if (res != net::ERR_IO_PENDING)
+    repeating_callback.Run(res);
 }
 
 }  // namespace chromeos
diff --git a/chromeos/network/dhcp_pac_file_fetcher_chromeos.h b/chromeos/network/dhcp_pac_file_fetcher_chromeos.h
index 91c3a2e..25acd50c7 100644
--- a/chromeos/network/dhcp_pac_file_fetcher_chromeos.h
+++ b/chromeos/network/dhcp_pac_file_fetcher_chromeos.h
@@ -38,7 +38,11 @@
       net::URLRequestContext* url_request_context);
   ~DhcpPacFileFetcherChromeos() override;
 
-  // net::DhcpPacFileFetcher
+  // net::DhcpPacFileFetcher implementation
+
+  // TODO(https://crbug.com/882996)  Even though it is documented at
+  // DhcpPacFileFetcher::Fetch() that only one fetch is allowed to be
+  // outstanding at any given time, crashes show that this is not obeyed.
   int Fetch(base::string16* utf16_text,
             net::CompletionOnceCallback callback,
             const net::NetLogWithSource& net_log,
@@ -50,12 +54,10 @@
 
  private:
   void ContinueFetch(base::string16* utf16_text,
+                     net::CompletionOnceCallback callback,
                      const net::NetworkTrafficAnnotationTag traffic_annotation,
                      std::string pac_url);
 
-  void OnFetchCompleted(int result);
-
-  net::CompletionOnceCallback callback_;
   std::unique_ptr<net::PacFileFetcher> pac_file_fetcher_;
   scoped_refptr<base::SingleThreadTaskRunner> network_handler_task_runner_;
 
diff --git a/chromeos/services/assistant/BUILD.gn b/chromeos/services/assistant/BUILD.gn
index 75af4bed..01a1f89 100644
--- a/chromeos/services/assistant/BUILD.gn
+++ b/chromeos/services/assistant/BUILD.gn
@@ -68,9 +68,13 @@
     ]
 
     deps += [
+      "//chromeos/assistant/internal",
       "//chromeos/assistant/internal:build_libassistant",
       "//chromeos/assistant/internal/action",
       "//chromeos/assistant/internal/proto/google3",
+      "//chromeos/resources",
+      "//chromeos/services/assistant/public/proto",
+      "//chromeos/strings",
       "//libassistant/contrib/core",
       "//libassistant/contrib/platform/audio",
       "//libassistant/shared/internal_api/c:api_wrappers_entrypoint",
@@ -101,6 +105,7 @@
     "//base/test:test_support",
     "//chromeos",
     "//mojo/public/cpp/bindings:bindings",
+    "//services/device/public/mojom",
     "//services/identity/public/mojom",
     "//services/service_manager/public/cpp",
     "//services/service_manager/public/cpp:service_test_support",
@@ -116,6 +121,8 @@
       "platform/audio_output_provider_impl_unittest.cc",
       "platform/system_provider_impl_unittest.cc",
     ]
+
+    deps += [ "//libassistant/shared/public" ]
   }
 }
 
diff --git a/components/autofill/core/common/autofill_features.cc b/components/autofill/core/common/autofill_features.cc
index 6393be1..676e4c3 100644
--- a/components/autofill/core/common/autofill_features.cc
+++ b/components/autofill/core/common/autofill_features.cc
@@ -268,9 +268,14 @@
     "AutofillVoteUsingInvalidProfileData", base::FEATURE_ENABLED_BY_DEFAULT};
 
 // Controls whether password generation is offered automatically on fields
-// percieved as eligible for generation.
+// perceived as eligible for generation.
+#if defined(OS_ANDROID)
+const base::Feature kAutomaticPasswordGeneration = {
+    "AutomaticPasswordGeneration", base::FEATURE_DISABLED_BY_DEFAULT};
+#else
 const base::Feature kAutomaticPasswordGeneration = {
     "AutomaticPasswordGeneration", base::FEATURE_ENABLED_BY_DEFAULT};
+#endif
 
 // Controls whether or not the autofill UI triggers on a single click.
 const base::Feature kSingleClickAutofill{"SingleClickAutofill",
diff --git a/components/autofill/ios/browser/BUILD.gn b/components/autofill/ios/browser/BUILD.gn
index 3a3c11e..22dff94 100644
--- a/components/autofill/ios/browser/BUILD.gn
+++ b/components/autofill/ios/browser/BUILD.gn
@@ -13,6 +13,10 @@
     "autofill_driver_ios.h",
     "autofill_driver_ios.mm",
     "autofill_driver_ios_bridge.h",
+    "autofill_driver_ios_webframe.h",
+    "autofill_driver_ios_webframe.mm",
+    "autofill_driver_ios_webstate.h",
+    "autofill_driver_ios_webstate.mm",
     "autofill_switches.cc",
     "autofill_switches.h",
     "autofill_util.h",
diff --git a/components/autofill/ios/browser/autofill_agent.mm b/components/autofill/ios/browser/autofill_agent.mm
index bb3abbc4..0b2641a7 100644
--- a/components/autofill/ios/browser/autofill_agent.mm
+++ b/components/autofill/ios/browser/autofill_agent.mm
@@ -32,6 +32,7 @@
 #include "components/autofill/core/common/form_data.h"
 #include "components/autofill/core/common/form_field_data.h"
 #include "components/autofill/ios/browser/autofill_driver_ios.h"
+#include "components/autofill/ios/browser/autofill_switches.h"
 #include "components/autofill/ios/browser/autofill_util.h"
 #import "components/autofill/ios/browser/form_suggestion.h"
 #import "components/autofill/ios/browser/js_autofill_manager.h"
@@ -44,6 +45,9 @@
 #import "ios/web/public/web_state/js/crw_js_injection_receiver.h"
 #import "ios/web/public/web_state/navigation_context.h"
 #include "ios/web/public/web_state/url_verification_constants.h"
+#include "ios/web/public/web_state/web_frame.h"
+#include "ios/web/public/web_state/web_frame_util.h"
+#import "ios/web/public/web_state/web_frames_manager.h"
 #import "ios/web/public/web_state/web_state.h"
 #include "ui/gfx/geometry/rect.h"
 #include "url/gurl.h"
@@ -131,6 +135,7 @@
                fieldIdentifier:(NSString*)fieldIdentifier
                           type:(NSString*)type
                     typedValue:(NSString*)typedValue
+                       frameID:(NSString*)frameID
                       webState:(web::WebState*)webState
              completionHandler:(SuggestionsAvailableCompletion)completion;
 
@@ -257,11 +262,13 @@
 // Returns nullptr if there is no autofill manager associated anymore, this can
 // happen when |close| has been called on the |webState|. Also returns nullptr
 // if detachFromWebState has been called.
-- (autofill::AutofillManager*)autofillManagerFromWebState:
-    (web::WebState*)webState {
+- (autofill::AutofillManager*)
+autofillManagerFromWebState:(web::WebState*)webState
+                   webFrame:(web::WebFrame*)webFrame {
   if (!webState || !webStateObserverBridge_)
     return nullptr;
-  return autofill::AutofillDriverIOS::FromWebState(webState)
+  return autofill::AutofillDriverIOS::FromWebStateAndWebFrame(webState,
+                                                              webFrame)
       ->autofill_manager();
 }
 
@@ -360,10 +367,14 @@
                fieldIdentifier:(NSString*)fieldIdentifier
                           type:(NSString*)type
                     typedValue:(NSString*)typedValue
+                       frameID:(NSString*)frameID
                       webState:(web::WebState*)webState
              completionHandler:(SuggestionsAvailableCompletion)completion {
+  web::WebFrame* frame =
+      web::WebFramesManager::FromWebState(webState)->GetFrameWithId(
+          base::SysNSStringToUTF8(frameID));
   autofill::AutofillManager* autofillManager =
-      [self autofillManagerFromWebState:webState];
+      [self autofillManagerFromWebState:webState webFrame:frame];
   if (!autofillManager)
     return;
 
@@ -393,6 +404,7 @@
                                  fieldType:(NSString*)fieldType
                                       type:(NSString*)type
                                 typedValue:(NSString*)typedValue
+                                   frameID:(NSString*)frameID
                                isMainFrame:(BOOL)isMainFrame
                             hasUserGesture:(BOOL)hasUserGesture
                                   webState:(web::WebState*)webState
@@ -427,6 +439,7 @@
                        fieldIdentifier:fieldIdentifier
                                   type:type
                             typedValue:typedValue
+                               frameID:frameID
                               webState:webState
                      completionHandler:completion];
     }
@@ -451,12 +464,13 @@
                          fieldType:(NSString*)fieldType
                               type:(NSString*)type
                         typedValue:(NSString*)typedValue
+                           frameID:(NSString*)frameID
                           webState:(web::WebState*)webState
                  completionHandler:(SuggestionsReadyCompletion)completion {
   DCHECK(mostRecentSuggestions_)
       << "Requestor should have called "
       << "|checkIfSuggestionsAvailableForForm:fieldName:fieldIdentifier:type:"
-      << "completionHandler:| "
+      << "typedValue:frameID:isMainFrame:completionHandler:| "
       << "and waited for the result before calling "
       << "|retrieveSuggestionsForForm:field:type:completionHandler:|.";
   completion(mostRecentSuggestions_, self);
@@ -466,6 +480,7 @@
                   fieldName:(NSString*)fieldName
             fieldIdentifier:(NSString*)fieldIdentifier
                        form:(NSString*)formName
+                    frameID:(NSString*)frameID
           completionHandler:(SuggestionHandledCompletion)completion {
   [[UIDevice currentDevice] playInputClick];
   suggestionHandledCompletion_ = [completion copy];
@@ -514,12 +529,14 @@
   // Ignore navigations within the same document, e.g., history.pushState().
   if (navigation->IsSameDocument())
     return;
-
-  // Reset AutofillManager before processing the new page.
-  autofill::AutofillManager* autofillManager =
-      [self autofillManagerFromWebState:webState];
-  DCHECK(autofillManager);
-  autofillManager->Reset();
+  if (!autofill::switches::IsAutofillIFrameMessagingEnabled()) {
+    // Reset AutofillManager before processing the new page.
+    web::WebFrame* frame = web::GetMainWebFrame(webState);
+    autofill::AutofillManager* autofillManager =
+        [self autofillManagerFromWebState:webState webFrame:frame];
+    DCHECK(autofillManager);
+    autofillManager->Reset();
+  }
 
   // Mark the page as not processed.
   pageProcessed_ = NO;
@@ -552,17 +569,20 @@
   [jsAutofillManager_ toggleTrackingUserEditedFields:
                           base::FeatureList::IsEnabled(
                               autofill::features::kAutofillPrefilledFields)];
-  [self scanFormsInPage:webState pageURL:pageURL];
+  web::WebFrame* frame = web::GetMainWebFrame(webState);
+  [self scanFormsInPage:webState inFrame:frame pageURL:pageURL];
 }
 
-- (void)scanFormsInPage:(web::WebState*)webState pageURL:(const GURL&)pageURL {
+- (void)scanFormsInPage:(web::WebState*)webState
+                inFrame:(web::WebFrame*)webFrame
+                pageURL:(const GURL&)pageURL {
   __weak AutofillAgent* weakSelf = self;
   id completionHandler = ^(BOOL success, const FormDataVector& forms) {
     AutofillAgent* strongSelf = weakSelf;
     if (!strongSelf || !success)
       return;
     autofill::AutofillManager* autofillManager =
-        [strongSelf autofillManagerFromWebState:webState];
+        [strongSelf autofillManagerFromWebState:webState webFrame:webFrame];
     if (!autofillManager || forms.empty())
       return;
     [strongSelf notifyAutofillManager:autofillManager ofFormsSeen:forms];
@@ -596,6 +616,10 @@
   if (params.input_missing)
     return;
 
+  if (!params.is_main_frame) {
+    return;
+  }
+
   web::URLVerificationTrustLevel trustLevel;
   const GURL pageURL(webState->GetCurrentURL(&trustLevel));
 
@@ -603,7 +627,7 @@
   // not a particular form. The whole page need to be reparsed to find the new
   // forms.
   if (params.type == "form_changed") {
-    [self scanFormsInPage:webState pageURL:pageURL];
+    [self scanFormsInPage:webState inFrame:frame pageURL:pageURL];
     return;
   }
 
@@ -624,7 +648,7 @@
 
     DCHECK_EQ(webState_, webState);
     autofill::AutofillManager* autofillManager =
-        [weakSelf autofillManagerFromWebState:webState];
+        [weakSelf autofillManagerFromWebState:webState webFrame:frame];
     if (!autofillManager)
       return;
 
@@ -663,7 +687,7 @@
     if (!strongSelf || !success)
       return;
     autofill::AutofillManager* autofillManager =
-        [strongSelf autofillManagerFromWebState:webState];
+        [strongSelf autofillManagerFromWebState:webState webFrame:frame];
     if (!autofillManager || forms.empty())
       return;
     if (forms.size() > 1) {
diff --git a/components/autofill/ios/browser/autofill_agent_unittests.mm b/components/autofill/ios/browser/autofill_agent_unittests.mm
index 2a8016a..59ca15b3f 100644
--- a/components/autofill/ios/browser/autofill_agent_unittests.mm
+++ b/components/autofill/ios/browser/autofill_agent_unittests.mm
@@ -14,6 +14,7 @@
 #include "components/autofill/core/common/form_data.h"
 #import "components/autofill/ios/browser/js_autofill_manager.h"
 #include "components/prefs/pref_service.h"
+#include "ios/web/public/test/fakes/fake_web_frame.h"
 #import "ios/web/public/test/fakes/test_web_state.h"
 #import "ios/web/public/web_state/js/crw_js_injection_receiver.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -45,7 +46,11 @@
         [OCMockObject mockForClass:[CRWJSInjectionReceiver class]];
     test_web_state_.SetJSInjectionReceiver(mock_js_injection_receiver_);
     test_web_state_.SetContentIsHTML(true);
-    test_web_state_.SetCurrentURL(GURL("https://example.com"));
+    GURL url("https://example.com");
+    test_web_state_.SetCurrentURL(url);
+    test_web_state_.CreateWebFramesManager();
+    auto main_frame = std::make_unique<web::FakeWebFrame>("frameID", true, url);
+    test_web_state_.AddWebFrame(std::move(main_frame));
 
     prefs_ = autofill::test::PrefServiceForTesting();
     autofill::prefs::SetAutofillEnabled(prefs_.get(), true);
@@ -173,6 +178,7 @@
                                             fieldType:@"text"
                                                  type:@"focus"
                                            typedValue:@""
+                                              frameID:@"frameID"
                                           isMainFrame:YES
                                        hasUserGesture:YES
                                              webState:&test_web_state_
@@ -197,6 +203,7 @@
                                             fieldType:@"text"
                                                  type:@"focus"
                                            typedValue:@""
+                                              frameID:@"frameID"
                                           isMainFrame:YES
                                        hasUserGesture:YES
                                              webState:&test_web_state_
@@ -220,6 +227,7 @@
                                             fieldType:@"text"
                                                  type:@"focus"
                                            typedValue:@""
+                                              frameID:@"frameID"
                                           isMainFrame:YES
                                        hasUserGesture:NO
                                              webState:&test_web_state_
@@ -274,6 +282,7 @@
                                     fieldType:@"text"
                                          type:@"focus"
                                    typedValue:@""
+                                      frameID:@"frameID"
                                      webState:&test_web_state_
                             completionHandler:completionHandler];
   test_web_state_.WasShown();
@@ -333,6 +342,7 @@
                                     fieldType:@"text"
                                          type:@"focus"
                                    typedValue:@""
+                                      frameID:@"frameID"
                                      webState:&test_web_state_
                             completionHandler:completionHandler];
   test_web_state_.WasShown();
diff --git a/components/autofill/ios/browser/autofill_driver_ios.h b/components/autofill/ios/browser/autofill_driver_ios.h
index 471cf9c2..24b55bb 100644
--- a/components/autofill/ios/browser/autofill_driver_ios.h
+++ b/components/autofill/ios/browser/autofill_driver_ios.h
@@ -11,9 +11,9 @@
 #include "components/autofill/core/browser/autofill_driver.h"
 #include "components/autofill/core/browser/autofill_external_delegate.h"
 #include "components/autofill/core/browser/autofill_manager.h"
-#include "ios/web/public/web_state/web_state_user_data.h"
 
 namespace web {
+class WebFrame;
 class WebState;
 }
 
@@ -23,18 +23,20 @@
 
 // Class that drives autofill flow on iOS. There is one instance per
 // WebContents.
-class AutofillDriverIOS : public AutofillDriver,
-                          public web::WebStateUserData<AutofillDriverIOS> {
+class AutofillDriverIOS : public AutofillDriver {
  public:
   ~AutofillDriverIOS() override;
 
-  static void CreateForWebStateAndDelegate(
+  static void PrepareForWebStateWebFrameAndDelegate(
       web::WebState* web_state,
       AutofillClient* client,
       id<AutofillDriverIOSBridge> bridge,
       const std::string& app_locale,
       AutofillManager::AutofillDownloadManagerState enable_download_manager);
 
+  static AutofillDriverIOS* FromWebStateAndWebFrame(web::WebState* web_state,
+                                                    web::WebFrame* web_frame);
+
   // AutofillDriver:
   bool IsIncognito() const override;
   net::URLRequestContextGetter* GetURLRequestContext() override;
@@ -62,16 +64,22 @@
   gfx::RectF TransformBoundingBoxToViewportCoordinates(
       const gfx::RectF& bounding_box) override;
 
- private:
+ protected:
   AutofillDriverIOS(
       web::WebState* web_state,
+      web::WebFrame* web_frame,
       AutofillClient* client,
       id<AutofillDriverIOSBridge> bridge,
       const std::string& app_locale,
       AutofillManager::AutofillDownloadManagerState enable_download_manager);
 
+ private:
   // The WebState with which this object is associated.
-  web::WebState* web_state_;
+  web::WebState* web_state_ = nullptr;
+
+  // The WebState with which this object is associated.
+  // nullptr if frame messaging is disabled.
+  web::WebFrame* web_frame_ = nullptr;
 
   // AutofillDriverIOSBridge instance that is passed in.
   __unsafe_unretained id<AutofillDriverIOSBridge> bridge_;
diff --git a/components/autofill/ios/browser/autofill_driver_ios.mm b/components/autofill/ios/browser/autofill_driver_ios.mm
index 1df6ec5..18ca936 100644
--- a/components/autofill/ios/browser/autofill_driver_ios.mm
+++ b/components/autofill/ios/browser/autofill_driver_ios.mm
@@ -7,9 +7,12 @@
 #include "base/memory/ptr_util.h"
 #include "components/autofill/core/browser/form_structure.h"
 #include "components/autofill/ios/browser/autofill_driver_ios_bridge.h"
+#include "components/autofill/ios/browser/autofill_driver_ios_webframe.h"
+#include "components/autofill/ios/browser/autofill_driver_ios_webstate.h"
+#include "components/autofill/ios/browser/autofill_switches.h"
 #include "ios/web/public/browser_state.h"
 #import "ios/web/public/origin_util.h"
-#include "ios/web/public/web_state/web_state.h"
+#import "ios/web/public/web_state/web_state.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 #include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
 #include "ui/gfx/geometry/rect_f.h"
@@ -18,33 +21,48 @@
 #error "This file requires ARC support."
 #endif
 
-DEFINE_WEB_STATE_USER_DATA_KEY(autofill::AutofillDriverIOS);
-
 namespace autofill {
 
 // static
-void AutofillDriverIOS::CreateForWebStateAndDelegate(
+void AutofillDriverIOS::PrepareForWebStateWebFrameAndDelegate(
     web::WebState* web_state,
     AutofillClient* client,
     id<AutofillDriverIOSBridge> bridge,
     const std::string& app_locale,
     AutofillManager::AutofillDownloadManagerState enable_download_manager) {
-  if (FromWebState(web_state))
-    return;
+  if (autofill::switches::IsAutofillIFrameMessagingEnabled()) {
+    AutofillDriverIOSWebFrameFactory::CreateForWebStateAndDelegate(
+        web_state, client, bridge, app_locale, enable_download_manager);
+    // By the time this method is called, no web_frame is available. This method
+    // only prepare the factory and the AutofillDriverIOS will be created in the
+    // first call to FromWebStateAndWebFrame.
+  } else {
+    AutofillDriverIOSWebState::CreateForWebStateAndDelegate(
+        web_state, client, bridge, app_locale, enable_download_manager);
+  }
+}
 
-  web_state->SetUserData(
-      UserDataKey(),
-      base::WrapUnique(new AutofillDriverIOS(
-          web_state, client, bridge, app_locale, enable_download_manager)));
+// static
+AutofillDriverIOS* AutofillDriverIOS::FromWebStateAndWebFrame(
+    web::WebState* web_state,
+    web::WebFrame* web_frame) {
+  if (autofill::switches::IsAutofillIFrameMessagingEnabled()) {
+    return AutofillDriverIOSWebFrameFactory::FromWebState(web_state)
+        ->AutofillDriverIOSFromWebFrame(web_frame);
+  } else {
+    return AutofillDriverIOSWebState::FromWebState(web_state);
+  }
 }
 
 AutofillDriverIOS::AutofillDriverIOS(
     web::WebState* web_state,
+    web::WebFrame* web_frame,
     AutofillClient* client,
     id<AutofillDriverIOSBridge> bridge,
     const std::string& app_locale,
     AutofillManager::AutofillDownloadManagerState enable_download_manager)
     : web_state_(web_state),
+      web_frame_(web_frame),
       bridge_(bridge),
       autofill_manager_(this, client, app_locale, enable_download_manager),
       autofill_external_delegate_(&autofill_manager_, this) {
@@ -75,7 +93,7 @@
     int query_id,
     RendererFormDataAction action,
     const FormData& data) {
-  [bridge_ onFormDataFilled:query_id result:data];
+  [bridge_ onFormDataFilled:query_id inFrame:web_frame_ result:data];
 }
 
 void AutofillDriverIOS::PropagateAutofillPredictions(
@@ -86,7 +104,8 @@
 void AutofillDriverIOS::SendAutofillTypePredictionsToRenderer(
     const std::vector<FormStructure*>& forms) {
   [bridge_ sendAutofillTypePredictionsToRenderer:
-               FormStructure::GetFieldTypePredictions(forms)];
+               FormStructure::GetFieldTypePredictions(forms)
+                                         toFrame:web_frame_];
 }
 
 void AutofillDriverIOS::RendererShouldAcceptDataListSuggestion(
diff --git a/components/autofill/ios/browser/autofill_driver_ios_bridge.h b/components/autofill/ios/browser/autofill_driver_ios_bridge.h
index ad9f314..8aa8933 100644
--- a/components/autofill/ios/browser/autofill_driver_ios_bridge.h
+++ b/components/autofill/ios/browser/autofill_driver_ios_bridge.h
@@ -15,14 +15,20 @@
 struct FormData;
 }
 
+namespace web {
+class WebFrame;
+}
+
 // Interface used to pipe form data from AutofillDriverIOS to the embedder.
 @protocol AutofillDriverIOSBridge
 
 - (void)onFormDataFilled:(uint16_t)query_id
+                 inFrame:(web::WebFrame*)frame
                   result:(const autofill::FormData&)result;
 
 - (void)sendAutofillTypePredictionsToRenderer:
-    (const std::vector<autofill::FormDataPredictions>&)forms;
+            (const std::vector<autofill::FormDataPredictions>&)forms
+                                      toFrame:(web::WebFrame*)frame;
 
 @end
 
diff --git a/components/autofill/ios/browser/autofill_driver_ios_webframe.h b/components/autofill/ios/browser/autofill_driver_ios_webframe.h
new file mode 100644
index 0000000..5eea70d
--- /dev/null
+++ b/components/autofill/ios/browser/autofill_driver_ios_webframe.h
@@ -0,0 +1,84 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_AUTOFILL_IOS_BROWSER_AUTOFILL_DRIVER_IOS_WEBFRAME_H_
+#define COMPONENTS_AUTOFILL_IOS_BROWSER_AUTOFILL_DRIVER_IOS_WEBFRAME_H_
+
+#include "components/autofill/ios/browser/autofill_driver_ios.h"
+#include "ios/web/public/web_state/web_frame_user_data.h"
+#include "ios/web/public/web_state/web_state_user_data.h"
+
+namespace web {
+class WebFrame;
+class WebState;
+}  // namespace web
+
+namespace autofill {
+
+class AutofillDriverIOSWebFrame;
+
+// This factory will keep the parameters needed to create an
+// AutofillDriverIOSWebFrame. These parameters only depend on the web_state, so
+// there is one AutofillDriverIOSWebFrameFactory per WebState.
+class AutofillDriverIOSWebFrameFactory
+    : public web::WebStateUserData<AutofillDriverIOSWebFrameFactory> {
+ public:
+  // Creates a AutofillDriverIOSWebFrameFactory that will store all the
+  // needed to create a AutofillDriverIOS.
+  static void CreateForWebStateAndDelegate(
+      web::WebState* web_state,
+      AutofillClient* client,
+      id<AutofillDriverIOSBridge> bridge,
+      const std::string& app_locale,
+      AutofillManager::AutofillDownloadManagerState enable_download_manager);
+  ~AutofillDriverIOSWebFrameFactory() override;
+
+  // Returns a AutofillDriverIOSFromWebFrame for |web_frame|, creating it if
+  // needed.
+  AutofillDriverIOSWebFrame* AutofillDriverIOSFromWebFrame(
+      web::WebFrame* web_frame);
+
+ private:
+  AutofillDriverIOSWebFrameFactory(
+      web::WebState* web_state,
+      AutofillClient* client,
+      id<AutofillDriverIOSBridge> bridge,
+      const std::string& app_locale,
+      AutofillManager::AutofillDownloadManagerState enable_download_manager);
+  web::WebState* web_state_ = nullptr;
+  AutofillClient* client_ = nullptr;
+  id<AutofillDriverIOSBridge> bridge_ = nil;
+  std::string app_locale_;
+  AutofillManager::AutofillDownloadManagerState enable_download_manager_;
+};
+
+// TODO(crbug.com/883203): Merge with AutofillDriverIOS class once WebFrame is
+// released.
+class AutofillDriverIOSWebFrame
+    : public AutofillDriverIOS,
+      public web::WebFrameUserData<AutofillDriverIOSWebFrame> {
+ public:
+  // Creates a AutofillDriverIOSWebFrame for |web_frame|.
+  static void CreateForWebFrameAndDelegate(
+      web::WebState* web_state,
+      web::WebFrame* web_frame,
+      AutofillClient* client,
+      id<AutofillDriverIOSBridge> bridge,
+      const std::string& app_locale,
+      AutofillManager::AutofillDownloadManagerState enable_download_manager);
+
+  ~AutofillDriverIOSWebFrame() override;
+
+ private:
+  AutofillDriverIOSWebFrame(
+      web::WebState* web_state,
+      web::WebFrame* web_frame,
+      AutofillClient* client,
+      id<AutofillDriverIOSBridge> bridge,
+      const std::string& app_locale,
+      AutofillManager::AutofillDownloadManagerState enable_download_manager);
+};
+}  // namespace autofill
+
+#endif  // COMPONENTS_AUTOFILL_CONTENT_BROWSER_AUTOFILL_DRIVER_IOS_WEBSTATE_H_
diff --git a/components/autofill/ios/browser/autofill_driver_ios_webframe.mm b/components/autofill/ios/browser/autofill_driver_ios_webframe.mm
new file mode 100644
index 0000000..2f2d98c
--- /dev/null
+++ b/components/autofill/ios/browser/autofill_driver_ios_webframe.mm
@@ -0,0 +1,84 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/autofill/ios/browser/autofill_driver_ios_webframe.h"
+
+#include "ios/web/public/web_state/web_state.h"
+
+namespace autofill {
+
+// static
+void AutofillDriverIOSWebFrameFactory::CreateForWebStateAndDelegate(
+    web::WebState* web_state,
+    AutofillClient* client,
+    id<AutofillDriverIOSBridge> bridge,
+    const std::string& app_locale,
+    AutofillManager::AutofillDownloadManagerState enable_download_manager) {
+  if (FromWebState(web_state))
+    return;
+
+  web_state->SetUserData(
+      UserDataKey(),
+      base::WrapUnique(new AutofillDriverIOSWebFrameFactory(
+          web_state, client, bridge, app_locale, enable_download_manager)));
+}
+
+AutofillDriverIOSWebFrameFactory::AutofillDriverIOSWebFrameFactory(
+    web::WebState* web_state,
+    AutofillClient* client,
+    id<AutofillDriverIOSBridge> bridge,
+    const std::string& app_locale,
+    AutofillManager::AutofillDownloadManagerState enable_download_manager)
+    : web_state_(web_state),
+      client_(client),
+      bridge_(bridge),
+      app_locale_(app_locale),
+      enable_download_manager_(enable_download_manager) {}
+
+AutofillDriverIOSWebFrameFactory::~AutofillDriverIOSWebFrameFactory() {}
+
+AutofillDriverIOSWebFrame*
+AutofillDriverIOSWebFrameFactory::AutofillDriverIOSFromWebFrame(
+    web::WebFrame* web_frame) {
+  AutofillDriverIOSWebFrame::CreateForWebFrameAndDelegate(
+      web_state_, web_frame, client_, bridge_, app_locale_,
+      enable_download_manager_);
+  return AutofillDriverIOSWebFrame::FromWebFrame(web_frame);
+}
+
+// static
+void AutofillDriverIOSWebFrame::CreateForWebFrameAndDelegate(
+    web::WebState* web_state,
+    web::WebFrame* web_frame,
+    AutofillClient* client,
+    id<AutofillDriverIOSBridge> bridge,
+    const std::string& app_locale,
+    AutofillManager::AutofillDownloadManagerState enable_download_manager) {
+  if (FromWebFrame(web_frame))
+    return;
+
+  web_frame->SetUserData(UserDataKey(),
+                         base::WrapUnique(new AutofillDriverIOSWebFrame(
+                             web_state, web_frame, client, bridge, app_locale,
+                             enable_download_manager)));
+}
+
+AutofillDriverIOSWebFrame::AutofillDriverIOSWebFrame(
+    web::WebState* web_state,
+    web::WebFrame* web_frame,
+    AutofillClient* client,
+    id<AutofillDriverIOSBridge> bridge,
+    const std::string& app_locale,
+    AutofillManager::AutofillDownloadManagerState enable_download_manager)
+
+    : AutofillDriverIOS(web_state,
+                        web_frame,
+                        client,
+                        bridge,
+                        app_locale,
+                        enable_download_manager) {}
+
+AutofillDriverIOSWebFrame::~AutofillDriverIOSWebFrame() {}
+
+}  //  namespace autofill
diff --git a/components/autofill/ios/browser/autofill_driver_ios_webstate.h b/components/autofill/ios/browser/autofill_driver_ios_webstate.h
new file mode 100644
index 0000000..d6fc069
--- /dev/null
+++ b/components/autofill/ios/browser/autofill_driver_ios_webstate.h
@@ -0,0 +1,41 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_AUTOFILL_IOS_BROWSER_AUTOFILL_DRIVER_IOS_WEBSTATE_H_
+#define COMPONENTS_AUTOFILL_IOS_BROWSER_AUTOFILL_DRIVER_IOS_WEBSTATE_H_
+
+#include "components/autofill/ios/browser/autofill_driver_ios.h"
+#include "ios/web/public/web_state/web_state_user_data.h"
+
+namespace web {
+class WebState;
+}  // namespace web
+
+namespace autofill {
+
+// TODO(crbug.com/883203): remove class once WebFrame is released.
+class AutofillDriverIOSWebState
+    : public AutofillDriverIOS,
+      public web::WebStateUserData<AutofillDriverIOSWebState> {
+ public:
+  static void CreateForWebStateAndDelegate(
+      web::WebState* web_state,
+      AutofillClient* client,
+      id<AutofillDriverIOSBridge> bridge,
+      const std::string& app_locale,
+      AutofillManager::AutofillDownloadManagerState enable_download_manager);
+
+  ~AutofillDriverIOSWebState() override;
+
+ protected:
+  AutofillDriverIOSWebState(
+      web::WebState* web_state,
+      AutofillClient* client,
+      id<AutofillDriverIOSBridge> bridge,
+      const std::string& app_locale,
+      AutofillManager::AutofillDownloadManagerState enable_download_manager);
+};
+}  // namespace autofill
+
+#endif  // COMPONENTS_AUTOFILL_CONTENT_BROWSER_AUTOFILL_DRIVER_IOS_WEBSTATE_H_
diff --git a/components/autofill/ios/browser/autofill_driver_ios_webstate.mm b/components/autofill/ios/browser/autofill_driver_ios_webstate.mm
new file mode 100644
index 0000000..d69d3b5
--- /dev/null
+++ b/components/autofill/ios/browser/autofill_driver_ios_webstate.mm
@@ -0,0 +1,46 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/autofill/ios/browser/autofill_driver_ios_webstate.h"
+
+#include "ios/web/public/web_state/web_state.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+namespace autofill {
+
+// static
+void AutofillDriverIOSWebState::CreateForWebStateAndDelegate(
+    web::WebState* web_state,
+    AutofillClient* client,
+    id<AutofillDriverIOSBridge> bridge,
+    const std::string& app_locale,
+    AutofillManager::AutofillDownloadManagerState enable_download_manager) {
+  if (FromWebState(web_state))
+    return;
+
+  web_state->SetUserData(
+      UserDataKey(),
+      base::WrapUnique(new AutofillDriverIOSWebState(
+          web_state, client, bridge, app_locale, enable_download_manager)));
+}
+
+AutofillDriverIOSWebState::AutofillDriverIOSWebState(
+    web::WebState* web_state,
+    AutofillClient* client,
+    id<AutofillDriverIOSBridge> bridge,
+    const std::string& app_locale,
+    AutofillManager::AutofillDownloadManagerState enable_download_manager)
+    : AutofillDriverIOS(web_state,
+                        /*web_frame=*/nullptr,
+                        client,
+                        bridge,
+                        app_locale,
+                        enable_download_manager) {}
+
+AutofillDriverIOSWebState::~AutofillDriverIOSWebState() {}
+
+}  //  namespace autofill
diff --git a/components/autofill/ios/browser/fake_autofill_agent.h b/components/autofill/ios/browser/fake_autofill_agent.h
index 5f1cff6..fab2a603 100644
--- a/components/autofill/ios/browser/fake_autofill_agent.h
+++ b/components/autofill/ios/browser/fake_autofill_agent.h
@@ -21,12 +21,14 @@
 // with |fieldName| and |fieldIdentifier|.
 - (void)addSuggestion:(FormSuggestion*)suggestion
           forFormName:(NSString*)formName
-      fieldIdentifier:(NSString*)fieldIdentifier;
+      fieldIdentifier:(NSString*)fieldIdentifier
+              frameID:(NSString*)frameID;
 
 // Returns the last selected |suggestion| for form with |formName| and field
 // with |fieldName| and |fieldIdentifier|.
 - (FormSuggestion*)selectedSuggestionForFormName:(NSString*)formName
-                                 fieldIdentifier:(NSString*)fieldIdentifier;
+                                 fieldIdentifier:(NSString*)fieldIdentifier
+                                         frameID:(NSString*)frameID;
 
 @end
 
diff --git a/components/autofill/ios/browser/fake_autofill_agent.mm b/components/autofill/ios/browser/fake_autofill_agent.mm
index 072e818..f7d8540cc 100644
--- a/components/autofill/ios/browser/fake_autofill_agent.mm
+++ b/components/autofill/ios/browser/fake_autofill_agent.mm
@@ -34,9 +34,11 @@
 
 - (void)addSuggestion:(FormSuggestion*)suggestion
           forFormName:(NSString*)formName
-      fieldIdentifier:(NSString*)fieldIdentifier {
-  NSString* key =
-      [self keyForFormName:formName fieldIdentifier:fieldIdentifier];
+      fieldIdentifier:(NSString*)fieldIdentifier
+              frameID:(NSString*)frameID {
+  NSString* key = [self keyForFormName:formName
+                       fieldIdentifier:fieldIdentifier
+                               frameID:frameID];
   NSMutableArray* suggestions = _suggestionsByFormAndFieldName[key];
   if (!suggestions) {
     suggestions = [NSMutableArray array];
@@ -46,9 +48,11 @@
 }
 
 - (FormSuggestion*)selectedSuggestionForFormName:(NSString*)formName
-                                 fieldIdentifier:(NSString*)fieldIdentifier {
-  NSString* key =
-      [self keyForFormName:formName fieldIdentifier:fieldIdentifier];
+                                 fieldIdentifier:(NSString*)fieldIdentifier
+                                         frameID:(NSString*)frameID {
+  NSString* key = [self keyForFormName:formName
+                       fieldIdentifier:fieldIdentifier
+                               frameID:frameID];
   return _selectedSuggestionByFormAndFieldName[key];
 }
 
@@ -60,6 +64,7 @@
                                  fieldType:(NSString*)fieldType
                                       type:(NSString*)type
                                 typedValue:(NSString*)typedValue
+                                   frameID:(NSString*)frameID
                                isMainFrame:(BOOL)isMainFrame
                             hasUserGesture:(BOOL)hasUserGesture
                                   webState:(web::WebState*)webState
@@ -67,8 +72,9 @@
                              (SuggestionsAvailableCompletion)completion {
   base::PostTaskWithTraits(
       FROM_HERE, {web::WebThread::UI}, base::BindOnce(^{
-        NSString* key =
-            [self keyForFormName:formName fieldIdentifier:fieldIdentifier];
+        NSString* key = [self keyForFormName:formName
+                             fieldIdentifier:fieldIdentifier
+                                     frameID:frameID];
         completion([_suggestionsByFormAndFieldName[key] count] ? YES : NO);
       }));
 }
@@ -79,12 +85,14 @@
                          fieldType:(NSString*)fieldType
                               type:(NSString*)type
                         typedValue:(NSString*)typedValue
+                           frameID:(NSString*)frameID
                           webState:(web::WebState*)webState
                  completionHandler:(SuggestionsReadyCompletion)completion {
   base::PostTaskWithTraits(
       FROM_HERE, {web::WebThread::UI}, base::BindOnce(^{
-        NSString* key =
-            [self keyForFormName:formName fieldIdentifier:fieldIdentifier];
+        NSString* key = [self keyForFormName:formName
+                             fieldIdentifier:fieldIdentifier
+                                     frameID:frameID];
         completion(_suggestionsByFormAndFieldName[key], self);
       }));
 }
@@ -93,11 +101,13 @@
                   fieldName:(NSString*)fieldName
             fieldIdentifier:(NSString*)fieldIdentifier
                        form:(NSString*)formName
+                    frameID:(NSString*)frameID
           completionHandler:(SuggestionHandledCompletion)completion {
   base::PostTaskWithTraits(
       FROM_HERE, {web::WebThread::UI}, base::BindOnce(^{
-        NSString* key =
-            [self keyForFormName:formName fieldIdentifier:fieldIdentifier];
+        NSString* key = [self keyForFormName:formName
+                             fieldIdentifier:fieldIdentifier
+                                     frameID:frameID];
         _selectedSuggestionByFormAndFieldName[key] = suggestion;
         completion();
       }));
@@ -106,9 +116,11 @@
 #pragma mark - Private Methods
 
 - (NSString*)keyForFormName:(NSString*)formName
-            fieldIdentifier:(NSString*)fieldIdentifier {
+            fieldIdentifier:(NSString*)fieldIdentifier
+                    frameID:(NSString*)frameID {
   // Uniqueness ensured because spaces are not allowed in html name attributes.
-  return [NSString stringWithFormat:@"%@ %@", formName, fieldIdentifier];
+  return [NSString
+      stringWithFormat:@"%@ %@ %@", formName, fieldIdentifier, frameID];
 }
 
 @end
diff --git a/components/autofill/ios/browser/form_suggestion_provider.h b/components/autofill/ios/browser/form_suggestion_provider.h
index e06720de..51cbf2c1a 100644
--- a/components/autofill/ios/browser/form_suggestion_provider.h
+++ b/components/autofill/ios/browser/form_suggestion_provider.h
@@ -32,6 +32,7 @@
                                  fieldType:(NSString*)fieldType
                                       type:(NSString*)type
                                 typedValue:(NSString*)typedValue
+                                   frameID:(NSString*)frameID
                                isMainFrame:(BOOL)isMainFrame
                             hasUserGesture:(BOOL)hasUserGesture
                                   webState:(web::WebState*)webState
@@ -47,6 +48,7 @@
                          fieldType:(NSString*)fieldType
                               type:(NSString*)type
                         typedValue:(NSString*)typedValue
+                           frameID:(NSString*)frameID
                           webState:(web::WebState*)webState
                  completionHandler:(SuggestionsReadyCompletion)completion;
 
@@ -56,6 +58,7 @@
                   fieldName:(NSString*)fieldName
             fieldIdentifier:(NSString*)fieldIdentifier
                        form:(NSString*)formName
+                    frameID:(NSString*)frameID
           completionHandler:(SuggestionHandledCompletion)completion;
 
 @end
diff --git a/components/autofill/ios/form_util/form_activity_params.h b/components/autofill/ios/form_util/form_activity_params.h
index e7ab0ec..d3ed7df 100644
--- a/components/autofill/ios/form_util/form_activity_params.h
+++ b/components/autofill/ios/form_util/form_activity_params.h
@@ -24,6 +24,8 @@
 // type: "focus"
 // value: "LouisLane" (assuming that was the password typed)
 // input_missing:  false
+// frame_id: will be the unique ID generated in for the frame containing the
+// form (see __gCrWeb.message.getFrameId for details).
 struct FormActivityParams {
   FormActivityParams();
   FormActivityParams(const FormActivityParams& other);
@@ -35,6 +37,7 @@
   std::string field_type;
   std::string type;
   std::string value;
+  std::string frame_id;
 
   // |input_missing| is set to true if at least one of the members above isn't
   // set.
diff --git a/components/autofill/ios/form_util/form_activity_tab_helper.mm b/components/autofill/ios/form_util/form_activity_tab_helper.mm
index 8f922908..d2bf89a 100644
--- a/components/autofill/ios/form_util/form_activity_tab_helper.mm
+++ b/components/autofill/ios/form_util/form_activity_tab_helper.mm
@@ -9,6 +9,8 @@
 #include "base/values.h"
 #include "components/autofill/ios/form_util/form_activity_observer.h"
 #include "components/autofill/ios/form_util/form_activity_params.h"
+#include "ios/web/public/features.h"
+#include "ios/web/public/web_state/web_frame.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
@@ -99,6 +101,13 @@
   }
 
   params.is_main_frame = form_in_main_frame;
+  if (!sender_frame &&
+      base::FeatureList::IsEnabled(web::features::kWebFrameMessaging)) {
+    return false;
+  }
+  if (sender_frame) {
+    params.frame_id = sender_frame->GetFrameId();
+  }
   for (auto& observer : observers_)
     observer.FormActivityRegistered(web_state_, sender_frame, params);
   return true;
diff --git a/components/autofill/ios/form_util/form_activity_tab_helper_unittest.mm b/components/autofill/ios/form_util/form_activity_tab_helper_unittest.mm
index 79bfcbb..80e6bed 100644
--- a/components/autofill/ios/form_util/form_activity_tab_helper_unittest.mm
+++ b/components/autofill/ios/form_util/form_activity_tab_helper_unittest.mm
@@ -13,6 +13,7 @@
 #import "ios/web/public/test/web_js_test.h"
 #import "ios/web/public/test/web_test_with_web_state.h"
 #include "ios/web/public/web_state/web_frame.h"
+#include "ios/web/public/web_state/web_frame_util.h"
 #import "ios/web/public/web_state/web_frames_manager.h"
 #include "testing/platform_test.h"
 
@@ -62,13 +63,11 @@
   const std::string kTestFormName("form-name");
   bool has_user_gesture = false;
   bool form_in_main_frame = true;
-  web::WebFramesManager* manager =
-      web::WebFramesManager::FromWebState(web_state());
   EXPECT_TRUE(base::test::ios::WaitUntilConditionOrTimeout(
       base::test::ios::kWaitForJSCompletionTimeout, ^bool {
-        return manager->GetMainWebFrame() != nullptr;
+        return web::GetMainWebFrame(web_state()) != nullptr;
       }));
-  web::WebFrame* main_frame = manager->GetMainWebFrame();
+  web::WebFrame* main_frame = web::GetMainWebFrame(web_state());
 
   ExecuteJavaScript(@"document.getElementById('submit').click();");
   ASSERT_TRUE(observer_->submit_document_info());
@@ -87,13 +86,11 @@
       @"<form name='form-name'>"
        "<input type='input' name='field-name' id='fieldid'/>"
        "</form>");
-  web::WebFramesManager* manager =
-      web::WebFramesManager::FromWebState(web_state());
   EXPECT_TRUE(base::test::ios::WaitUntilConditionOrTimeout(
       base::test::ios::kWaitForJSCompletionTimeout, ^bool {
-        return manager->GetMainWebFrame() != nullptr;
+        return web::GetMainWebFrame(web_state()) != nullptr;
       }));
-  web::WebFrame* main_frame = manager->GetMainWebFrame();
+  web::WebFrame* main_frame = web::GetMainWebFrame(web_state());
   ASSERT_FALSE(observer_->form_activity_info());
   // First call will set document.activeElement (which is usually set by user
   // action. Second call will trigger the message.
diff --git a/components/autofill/ios/form_util/test_form_activity_tab_helper.mm b/components/autofill/ios/form_util/test_form_activity_tab_helper.mm
index ce2ec51..1b5d0d5f 100644
--- a/components/autofill/ios/form_util/test_form_activity_tab_helper.mm
+++ b/components/autofill/ios/form_util/test_form_activity_tab_helper.mm
@@ -8,7 +8,7 @@
 #include "components/autofill/ios/form_util/form_activity_observer.h"
 #include "components/autofill/ios/form_util/form_activity_params.h"
 #include "components/autofill/ios/form_util/form_activity_tab_helper.h"
-#include "ios/web/public/web_state/web_state.h"
+#import "ios/web/public/web_state/web_state.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
diff --git a/components/autofill_assistant/browser/actions/action_delegate.h b/components/autofill_assistant/browser/actions/action_delegate.h
index 5024a30..dda8e84 100644
--- a/components/autofill_assistant/browser/actions/action_delegate.h
+++ b/components/autofill_assistant/browser/actions/action_delegate.h
@@ -63,6 +63,14 @@
   virtual void FocusElement(const std::vector<std::string>& selectors,
                             base::OnceCallback<void(bool)> callback) = 0;
 
+  // Get the value of all fields in |selector_list| and return the result
+  // through |callback|. The list of returned values will have the same size as
+  // |selectors_list|, and will be the empty string in case of error or empty
+  // value.
+  virtual void GetFieldsValue(
+      const std::vector<std::vector<std::string>>& selectors_list,
+      base::OnceCallback<void(const std::vector<std::string>&)> callback) = 0;
+
   // Return the current ClientMemory.
   virtual ClientMemory* GetClientMemory() = 0;
 
diff --git a/components/autofill_assistant/browser/actions/autofill_action.cc b/components/autofill_assistant/browser/actions/autofill_action.cc
index a683e59..dc518f42 100644
--- a/components/autofill_assistant/browser/actions/autofill_action.cc
+++ b/components/autofill_assistant/browser/actions/autofill_action.cc
@@ -26,7 +26,23 @@
          proto.use_address().form_field_element().selectors()) {
       data_.selectors.emplace_back(selector);
     }
+    for (const auto& required_address_field :
+         proto.use_address().required_fields()) {
+      // Some UseAddressProto::RequiredField don't have a selector to only
+      // specify an address_field, in which case we ignore it.
+      if (required_address_field.element().selectors().empty()) {
+        continue;
+      }
+
+      data_.required_fields_selectors.emplace_back(std::vector<std::string>());
+      for (const auto& selector :
+           required_address_field.element().selectors()) {
+        data_.required_fields_selectors.back().emplace_back(selector);
+      }
+    }
   } else {
+    // TODO(crbug.com/806868): Also read the required fields when filling a
+    // credit card form.
     DCHECK(proto.has_use_card());
     data_.is_autofill_card = true;
     data_.prompt = proto.use_card().prompt();
@@ -125,24 +141,64 @@
 
 void AutofillAction::FillFormWithData(std::string guid,
                                       ActionDelegate* delegate,
-                                      ProcessActionCallback callback) {
+                                      ProcessActionCallback action_callback) {
   DCHECK(!data_.selectors.empty());
   if (data_.is_autofill_card) {
     delegate->FillAddressForm(
         guid, data_.selectors,
-        base::BindOnce(&::autofill_assistant::AutofillAction::OnFillForm,
-                       weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+        base::BindOnce(&AutofillAction::OnFormFilled,
+                       weak_ptr_factory_.GetWeakPtr(), delegate,
+                       std::move(action_callback)));
     return;
   }
-  delegate->FillCardForm(
-      guid, data_.selectors,
-      base::BindOnce(&::autofill_assistant::AutofillAction::OnFillForm,
-                     weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+  delegate->FillCardForm(guid, data_.selectors,
+                         base::BindOnce(&AutofillAction::OnFormFilled,
+                                        weak_ptr_factory_.GetWeakPtr(),
+                                        delegate, std::move(action_callback)));
 }
 
-void AutofillAction::OnFillForm(ProcessActionCallback callback, bool status) {
-  UpdateProcessedAction(status);
-  std::move(callback).Run(std::move(processed_action_proto_));
+void AutofillAction::OnFormFilled(ActionDelegate* delegate,
+                                  ProcessActionCallback action_callback,
+                                  bool successful) {
+  // In case Autofill failed, we stop the action.
+  if (!successful) {
+    UpdateProcessedAction(false);
+    std::move(action_callback).Run(std::move(processed_action_proto_));
+    // TODO(crbug.com/806868): Tell the user to fill the form manually.
+    return;
+  }
+
+  // If there are no required fields, finish the action successfully.
+  if (data_.required_fields_selectors.empty()) {
+    UpdateProcessedAction(true);
+    std::move(action_callback).Run(std::move(processed_action_proto_));
+    return;
+  }
+
+  // Otherwise, we check that all required fields have a value.
+  delegate->GetFieldsValue(
+      data_.required_fields_selectors,
+      base::BindOnce(&AutofillAction::OnGetRequiredFieldsValue,
+                     weak_ptr_factory_.GetWeakPtr(),
+                     std::move(action_callback)));
+}
+
+void AutofillAction::OnGetRequiredFieldsValue(
+    ProcessActionCallback action_callback,
+    const std::vector<std::string>& values) {
+  for (const std::string& value : values) {
+    if (value.empty()) {
+      // Validation failed: at least one required field is empty.
+      // TODO(crbug.com/806868): Tell the user to fill the form manually.
+      // TODO(crbug.com/806868): Use fallback and retry validation.
+      UpdateProcessedAction(false);
+      std::move(action_callback).Run(std::move(processed_action_proto_));
+      return;
+    }
+  }
+
+  UpdateProcessedAction(true);
+  std::move(action_callback).Run(std::move(processed_action_proto_));
 }
 
 }  // namespace autofill_assistant.
diff --git a/components/autofill_assistant/browser/actions/autofill_action.h b/components/autofill_assistant/browser/actions/autofill_action.h
index 2ff7ad7..3781443 100644
--- a/components/autofill_assistant/browser/actions/autofill_action.h
+++ b/components/autofill_assistant/browser/actions/autofill_action.h
@@ -35,6 +35,7 @@
     std::string name;
     std::string prompt;
     std::vector<std::string> selectors;
+    std::vector<std::vector<std::string>> required_fields_selectors;
 
     // True if autofilling a card, otherwise we are autofilling an address.
     bool is_autofill_card;
@@ -45,13 +46,20 @@
                       ProcessActionCallback callback,
                       const std::string& guid);
 
-  void OnFillForm(ProcessActionCallback callback, bool status);
-
   // Fill the form using data with GUID |guid|. Return whether filling succeeded
   // or not through |callback|.
   void FillFormWithData(std::string guid,
                         ActionDelegate* delegate,
-                        ProcessActionCallback callback);
+                        ProcessActionCallback action_callback);
+
+  // Called when the form has been filled.
+  void OnFormFilled(ActionDelegate* delegate,
+                    ProcessActionCallback action_callback,
+                    bool successful);
+
+  // Called when we get the value of the required fields.
+  void OnGetRequiredFieldsValue(ProcessActionCallback action_callback,
+                                const std::vector<std::string>& values);
 
   Data data_;
   base::WeakPtrFactory<AutofillAction> weak_ptr_factory_;
diff --git a/components/autofill_assistant/browser/controller.cc b/components/autofill_assistant/browser/controller.cc
index 2b265ac..5c76e6f 100644
--- a/components/autofill_assistant/browser/controller.cc
+++ b/components/autofill_assistant/browser/controller.cc
@@ -5,7 +5,6 @@
 #include "components/autofill_assistant/browser/controller.h"
 
 #include <utility>
-#include <vector>
 
 #include "components/autofill_assistant/browser/protocol_utils.h"
 #include "components/autofill_assistant/browser/ui_controller.h"
@@ -50,7 +49,8 @@
       web_controller_(std::move(web_controller)),
       service_(std::move(service)),
       script_tracker_(std::make_unique<ScriptTracker>(this, this)),
-      memory_(std::make_unique<ClientMemory>()) {
+      memory_(std::make_unique<ClientMemory>()),
+      allow_autostart_(true) {
   GetUiController()->SetUiDelegate(this);
   GetUiController()->ShowOverlay();
   if (!web_contents->IsLoading()) {
@@ -114,6 +114,7 @@
   DCHECK(!script_path.empty());
 
   GetUiController()->ShowOverlay();
+  allow_autostart_ = false;  // Only ever autostart the very first script.
   script_tracker_->ExecuteScript(
       script_path, base::BindOnce(&Controller::OnScriptExecuted,
                                   // script_tracker_ is owned by Controller.
@@ -128,6 +129,9 @@
   switch (type) {
     case blink::WebInputEvent::kGestureLongTap:
     case blink::WebInputEvent::kGestureTap:
+      // Disable autostart after interaction with the web page.
+      allow_autostart_ = false;
+
       if (!script_tracker_->running())
         script_tracker_->CheckScripts();
       break;
@@ -144,6 +148,16 @@
   if (script_tracker_->running())
     return;
 
+  // Under specific conditions, we can directly run a script without first
+  // displaying it. This is meant to work only at the very beginning, when
+  // no scripts have run, there has been no interaction with the webpage and
+  // only if there's exactly one runnable script, flagged for autostart.
+  if (allow_autostart_ && runnable_scripts.size() == 1 &&
+      runnable_scripts[0].autostart) {
+    OnScriptSelected(runnable_scripts[0].path);
+    return;
+  }
+
   GetUiController()->UpdateScripts(runnable_scripts);
 }
 
diff --git a/components/autofill_assistant/browser/controller.h b/components/autofill_assistant/browser/controller.h
index d5bae30..d941b62 100644
--- a/components/autofill_assistant/browser/controller.h
+++ b/components/autofill_assistant/browser/controller.h
@@ -7,6 +7,7 @@
 
 #include <memory>
 #include <string>
+#include <vector>
 
 #include "base/macros.h"
 #include "components/autofill_assistant/browser/client.h"
@@ -81,6 +82,7 @@
   // Domain of the last URL the controller requested scripts from.
   std::string script_domain_;
   std::unique_ptr<ClientMemory> memory_;
+  bool allow_autostart_;
 
   DISALLOW_COPY_AND_ASSIGN(Controller);
 };
diff --git a/components/autofill_assistant/browser/controller_unittest.cc b/components/autofill_assistant/browser/controller_unittest.cc
index 5604795..cf6168f 100644
--- a/components/autofill_assistant/browser/controller_unittest.cc
+++ b/components/autofill_assistant/browser/controller_unittest.cc
@@ -32,7 +32,7 @@
 
 class FakeClient : public Client {
  public:
-  FakeClient(std::unique_ptr<UiController> ui_controller)
+  explicit FakeClient(std::unique_ptr<UiController> ui_controller)
       : ui_controller_(std::move(ui_controller)) {}
 
   // Implements Client
@@ -72,10 +72,6 @@
     ON_CALL(*mock_service_, OnGetActions(_, _))
         .WillByDefault(RunOnceCallback<1>(true, ""));
 
-    // Element "exists" exists.
-    ON_CALL(*mock_web_controller_, OnElementExists(ElementsAre("exists"), _))
-        .WillByDefault(RunOnceCallback<1>(true));
-
     // Make WebController::GetUrl accessible.
     ON_CALL(*mock_web_controller_, GetUrl()).WillByDefault(ReturnRef(url_));
 
@@ -88,16 +84,13 @@
   }
 
  protected:
-  static void AddRunnableScript(SupportsScriptResponseProto* response,
-                                const std::string& name,
-                                const std::string& path) {
+  static SupportedScriptProto* AddRunnableScript(
+      SupportsScriptResponseProto* response,
+      const std::string& name_and_path) {
     SupportedScriptProto* script = response->add_scripts();
-    script->set_path(path);
-    script->mutable_presentation()->set_name(name);
-    script->mutable_presentation()
-        ->mutable_precondition()
-        ->add_elements_exist()
-        ->add_selectors("exists");
+    script->set_path(name_and_path);
+    script->mutable_presentation()->set_name(name_and_path);
+    return script;
   }
 
   // Updates the current url of the controller and forces a refresh, without
@@ -107,6 +100,10 @@
     controller_->DidFinishLoad(nullptr, url);
   }
 
+  void SimulateUserInteraction(const blink::WebInputEvent::Type type) {
+    controller_->DidGetUserInteraction(type);
+  }
+
   // Sets up the next call to the service for scripts to return |response|.
   void SetNextScriptResponse(const SupportsScriptResponseProto& response) {
     std::string response_str;
@@ -130,8 +127,8 @@
   // Going to the URL triggers a whole flow:
   // 1. loading scripts
   SupportsScriptResponseProto script_response;
-  AddRunnableScript(&script_response, "script1 name", "script1");
-  AddRunnableScript(&script_response, "script2 name", "script2");
+  AddRunnableScript(&script_response, "script1");
+  AddRunnableScript(&script_response, "script2");
   SetNextScriptResponse(script_response);
 
   // 2. checking the scripts
@@ -177,4 +174,48 @@
   SimulateNavigateToUrl(GURL("http://b.example.com/path2"));
 }
 
+TEST_F(ControllerTest, Autostart) {
+  SupportsScriptResponseProto script_response;
+  AddRunnableScript(&script_response, "runnable")
+      ->mutable_presentation()
+      ->set_autostart(true);
+  SetNextScriptResponse(script_response);
+
+  EXPECT_CALL(*mock_service_, OnGetActions(StrEq("runnable"), _))
+      .WillOnce(RunOnceCallback<1>(true, ""));
+  EXPECT_CALL(*mock_ui_controller_, UpdateScripts(SizeIs(0)));
+
+  SimulateNavigateToUrl(GURL("http://a.example.com/path"));
+}
+
+TEST_F(ControllerTest, AutostartFallsBackToUpdateScriptAfterTap) {
+  SupportsScriptResponseProto script_response;
+  AddRunnableScript(&script_response, "runnable")
+      ->mutable_presentation()
+      ->set_autostart(true);
+  SetNextScriptResponse(script_response);
+
+  EXPECT_CALL(*mock_ui_controller_, UpdateScripts(SizeIs(1)));
+  EXPECT_CALL(*mock_service_, OnGetActions(StrEq("runnable"), _)).Times(0);
+
+  SimulateUserInteraction(blink::WebInputEvent::kGestureTap);
+  SimulateNavigateToUrl(GURL("http://a.example.com/path"));
+}
+
+TEST_F(ControllerTest, AutostartFallsBackToUpdateScriptAfterExecution) {
+  SupportsScriptResponseProto script_response;
+  AddRunnableScript(&script_response, "runnable")
+      ->mutable_presentation()
+      ->set_autostart(true);
+  SetNextScriptResponse(script_response);
+
+  EXPECT_CALL(*mock_service_, OnGetActions(StrEq("script1"), _));
+  GetUiDelegate()->OnScriptSelected("script1");
+
+  EXPECT_CALL(*mock_ui_controller_, UpdateScripts(SizeIs(1)));
+  EXPECT_CALL(*mock_service_, OnGetActions(StrEq("runnable"), _)).Times(0);
+
+  SimulateNavigateToUrl(GURL("http://a.example.com/path"));
+}
+
 }  // namespace autofill_assistant
diff --git a/components/autofill_assistant/browser/protocol_utils.cc b/components/autofill_assistant/browser/protocol_utils.cc
index 837de34a..e1342d9 100644
--- a/components/autofill_assistant/browser/protocol_utils.cc
+++ b/components/autofill_assistant/browser/protocol_utils.cc
@@ -53,6 +53,7 @@
 
     const auto& presentation = script_proto.presentation();
     script->handle.name = presentation.name();
+    script->handle.autostart = presentation.autostart();
     script->precondition =
         ScriptPrecondition::FromProto(presentation.precondition());
     script->priority = presentation.priority();
diff --git a/components/autofill_assistant/browser/protocol_utils_unittest.cc b/components/autofill_assistant/browser/protocol_utils_unittest.cc
index 3c5c1c21..b1080a7 100644
--- a/components/autofill_assistant/browser/protocol_utils_unittest.cc
+++ b/components/autofill_assistant/browser/protocol_utils_unittest.cc
@@ -41,5 +41,26 @@
   EXPECT_NE(nullptr, scripts[0]->precondition);
 }
 
+TEST(ProtocolUtilsTest, OneFullyFeaturedScript) {
+  SupportsScriptResponseProto proto;
+
+  SupportedScriptProto* script = proto.add_scripts();
+  script->set_path("path");
+  auto* presentation = script->mutable_presentation();
+  presentation->set_name("name");
+  presentation->set_autostart(true);
+  presentation->mutable_precondition()->add_domain("www.example.com");
+
+  std::vector<std::unique_ptr<Script>> scripts;
+  std::string proto_str;
+  proto.SerializeToString(&proto_str);
+  EXPECT_TRUE(ProtocolUtils::ParseScripts(proto_str, &scripts));
+  ASSERT_THAT(scripts, SizeIs(1));
+  EXPECT_EQ("path", scripts[0]->handle.path);
+  EXPECT_EQ("name", scripts[0]->handle.name);
+  EXPECT_TRUE(scripts[0]->handle.autostart);
+  EXPECT_NE(nullptr, scripts[0]->precondition);
+}
+
 }  // namespace
 }  // namespace autofill_assistant
diff --git a/components/autofill_assistant/browser/script.cc b/components/autofill_assistant/browser/script.cc
index ced1730..8f35cbb2 100644
--- a/components/autofill_assistant/browser/script.cc
+++ b/components/autofill_assistant/browser/script.cc
@@ -6,7 +6,7 @@
 
 namespace autofill_assistant {
 
-ScriptHandle::ScriptHandle() = default;
+ScriptHandle::ScriptHandle() : autostart(false) {}
 
 ScriptHandle::~ScriptHandle() = default;
 
diff --git a/components/autofill_assistant/browser/script.h b/components/autofill_assistant/browser/script.h
index 0f5b489..a446f6b 100644
--- a/components/autofill_assistant/browser/script.h
+++ b/components/autofill_assistant/browser/script.h
@@ -19,6 +19,10 @@
 
   std::string name;
   std::string path;
+
+  // When set to true this script can be run in 'autostart mode'. Script won't
+  // be shown.
+  bool autostart;
 };
 
 // Script represents a sequence of actions.
diff --git a/components/autofill_assistant/browser/script_executor.cc b/components/autofill_assistant/browser/script_executor.cc
index 6bdfc903..17e7a3c 100644
--- a/components/autofill_assistant/browser/script_executor.cc
+++ b/components/autofill_assistant/browser/script_executor.cc
@@ -84,6 +84,13 @@
   delegate_->GetWebController()->FocusElement(selectors, std::move(callback));
 }
 
+void ScriptExecutor::GetFieldsValue(
+    const std::vector<std::vector<std::string>>& selectors_list,
+    base::OnceCallback<void(const std::vector<std::string>&)> callback) {
+  delegate_->GetWebController()->GetFieldsValue(selectors_list,
+                                                std::move(callback));
+}
+
 ClientMemory* ScriptExecutor::GetClientMemory() {
   return delegate_->GetClientMemory();
 }
diff --git a/components/autofill_assistant/browser/script_executor.h b/components/autofill_assistant/browser/script_executor.h
index 65098a8a..2f7d6d1 100644
--- a/components/autofill_assistant/browser/script_executor.h
+++ b/components/autofill_assistant/browser/script_executor.h
@@ -51,7 +51,10 @@
                     base::OnceCallback<void(bool)> callback) override;
   void FocusElement(const std::vector<std::string>& selectors,
                     base::OnceCallback<void(bool)> callback) override;
-
+  void GetFieldsValue(
+      const std::vector<std::vector<std::string>>& selectors_list,
+      base::OnceCallback<void(const std::vector<std::string>&)> callback)
+      override;
   ClientMemory* GetClientMemory() override;
 
  private:
diff --git a/components/autofill_assistant/browser/script_tracker.cc b/components/autofill_assistant/browser/script_tracker.cc
index af408646..c2b9ab4 100644
--- a/components/autofill_assistant/browser/script_tracker.cc
+++ b/components/autofill_assistant/browser/script_tracker.cc
@@ -109,8 +109,9 @@
   }
   pending_runnable_scripts_.clear();
 
-  if (runnables_changed)
+  if (runnables_changed) {
     listener_->OnRunnableScriptsChanged(runnable_scripts_);
+  }
 }
 
 bool ScriptTracker::RunnablesHaveChanged() {
diff --git a/components/autofill_assistant/browser/script_tracker_unittest.cc b/components/autofill_assistant/browser/script_tracker_unittest.cc
index ecc45a4..1fc49142 100644
--- a/components/autofill_assistant/browser/script_tracker_unittest.cc
+++ b/components/autofill_assistant/browser/script_tracker_unittest.cc
@@ -6,6 +6,7 @@
 
 #include <utility>
 
+#include "base/bind_helpers.h"
 #include "base/test/mock_callback.h"
 #include "components/autofill_assistant/browser/client_memory.h"
 #include "components/autofill_assistant/browser/mock_run_once_callback.h"
@@ -70,10 +71,10 @@
     tracker_.SetAndCheckScripts(std::move(scripts));
   }
 
-  static void AddScript(SupportsScriptResponseProto* response,
-                        const std::string& name,
-                        const std::string& path,
-                        const std::string& selector) {
+  static SupportedScriptProto* AddScript(SupportsScriptResponseProto* response,
+                                         const std::string& name,
+                                         const std::string& path,
+                                         const std::string& selector) {
     SupportedScriptProto* script = response->add_scripts();
     script->set_path(path);
     script->mutable_presentation()->set_name(name);
@@ -81,6 +82,13 @@
         ->mutable_precondition()
         ->add_elements_exist()
         ->add_selectors(selector);
+    return script;
+  }
+
+  static SupportedScriptProto* AddRunnableScript(
+      SupportsScriptResponseProto* response,
+      const std::string& name_and_path) {
+    return AddScript(response, name_and_path, name_and_path, "exists");
   }
 
   const std::vector<ScriptHandle>& runnable_scripts() {
diff --git a/components/autofill_assistant/browser/service.proto b/components/autofill_assistant/browser/service.proto
index 0a38e91..33c7e42 100644
--- a/components/autofill_assistant/browser/service.proto
+++ b/components/autofill_assistant/browser/service.proto
@@ -42,6 +42,10 @@
     // means a script with priority 0 should be displayed before a script with
     // priority 1.
     optional int32 priority = 5;
+
+    // When set to true this script can be run in 'autostart mode'. Script won't
+    // be shown.
+    optional bool autostart = 8;
   }
   optional PresentationProto presentation = 2;
 }
@@ -215,6 +219,9 @@
 
 // Fill a form with an address if there is, otherwise fail this action.
 message UseAddressProto {
+  // Message used to indicate which form fields should be filled.
+  message RequiredField { optional ElementReferenceProto element = 2; }
+
   // An optional name to allow to handle multiple addresses selection (for
   // instance a billing and a delivery address).
   optional string name = 1;
@@ -225,6 +232,9 @@
 
   // Reference to an element in the form that should be filled.
   required ElementReferenceProto form_field_element = 4;
+
+  // An optional list of fields that should be filled by this action.
+  repeated RequiredField required_fields = 6;
 }
 
 // Fill a form with a credit card if there is, otherwise fail this action.
diff --git a/components/autofill_assistant/browser/web_controller.cc b/components/autofill_assistant/browser/web_controller.cc
index fbfcb81..958881f 100644
--- a/components/autofill_assistant/browser/web_controller.cc
+++ b/components/autofill_assistant/browser/web_controller.cc
@@ -322,4 +322,15 @@
   std::move(callback).Run(true);
 }
 
+void WebController::GetFieldsValue(
+    const std::vector<std::vector<std::string>>& selectors_list,
+    base::OnceCallback<void(const std::vector<std::string>&)> callback) {
+  // TODO(crbug.com/806868): Implement get fields value operation.
+  std::vector<std::string> values;
+  for (size_t i = 0; i < selectors_list.size(); i++) {
+    values.emplace_back("");
+  }
+  std::move(callback).Run(values);
+}
+
 }  // namespace autofill_assistant.
diff --git a/components/autofill_assistant/browser/web_controller.h b/components/autofill_assistant/browser/web_controller.h
index 6055532..ae1d0188 100644
--- a/components/autofill_assistant/browser/web_controller.h
+++ b/components/autofill_assistant/browser/web_controller.h
@@ -70,6 +70,14 @@
   virtual void FocusElement(const std::vector<std::string>& selectors,
                             base::OnceCallback<void(bool)> callback);
 
+  // Get the value of all fields in |selector_list| and return the result
+  // through |callback|. The list of returned values will have the same size as
+  // |selectors_list|, and will be the empty string in case of error or empty
+  // value.
+  virtual void GetFieldsValue(
+      const std::vector<std::vector<std::string>>& selectors_list,
+      base::OnceCallback<void(const std::vector<std::string>&)> callback);
+
  private:
   void OnFindElementForClick(base::OnceCallback<void(bool)> callback,
                              std::string object_id);
diff --git a/components/cronet/ios/Cronet.mm b/components/cronet/ios/Cronet.mm
index 82e3c66..9d0994b 100644
--- a/components/cronet/ios/Cronet.mm
+++ b/components/cronet/ios/Cronet.mm
@@ -198,8 +198,12 @@
 }
 
 + (void)setMetricsEnabled:(BOOL)metricsEnabled {
-  [self checkNotStarted];
-  gMetricsEnabled = metricsEnabled;
+  // https://crbug.com/878589
+  // Don't collect NSURLSessionTaskMetrics until iOS 10.2 to avoid crash in iOS.
+  if (@available(iOS 10.2, *)) {
+    [self checkNotStarted];
+    gMetricsEnabled = metricsEnabled;
+  }
 }
 
 + (BOOL)addQuicHint:(NSString*)host port:(int)port altPort:(int)altPort {
diff --git a/components/cronet/ios/test/cronet_metrics_test.mm b/components/cronet/ios/test/cronet_metrics_test.mm
index c7c6eb9..b7647b0 100644
--- a/components/cronet/ios/test/cronet_metrics_test.mm
+++ b/components/cronet/ios/test/cronet_metrics_test.mm
@@ -66,7 +66,7 @@
 
 // Tests that metrics data is sane for a QUIC request.
 TEST_F(CronetEnabledMetricsTest, ProtocolIsQuic) {
-  if (@available(iOS 10, *)) {
+  if (@available(iOS 10.2, *)) {
     NSURL* url = net::NSURLWithGURL(net::QuicSimpleTestServer::GetSimpleURL());
 
     __block BOOL block_used = NO;
@@ -125,7 +125,7 @@
 
 // Tests that metrics data is sane for an HTTP/1.1 request.
 TEST_F(CronetEnabledMetricsTest, ProtocolIsNotQuic) {
-  if (@available(iOS 10, *)) {
+  if (@available(iOS 10.2, *)) {
     NSURL* url = net::NSURLWithGURL(GURL(TestServer::GetSimpleURL()));
 
     __block BOOL block_used = NO;
@@ -154,7 +154,7 @@
 
 // Tests that Cronet provides similar metrics data to iOS.
 TEST_F(CronetEnabledMetricsTest, PlatformComparison) {
-  if (@available(iOS 10, *)) {
+  if (@available(iOS 10.2, *)) {
     NSURL* url = net::NSURLWithGURL(GURL(TestServer::GetSimpleURL()));
 
     // Perform a connection using Cronet.
@@ -209,7 +209,7 @@
 // Tests that the metrics API behaves sanely when making a request to an
 // invalid URL.
 TEST_F(CronetEnabledMetricsTest, InvalidURL) {
-  if (@available(iOS 10, *)) {
+  if (@available(iOS 10.2, *)) {
     NSURL* url = net::NSURLWithGURL(GURL("http://notfound.example.com"));
 
     __block BOOL block_used = NO;
@@ -245,7 +245,7 @@
 
 // Tests that the metrics API behaves sanely when the request is canceled.
 TEST_F(CronetEnabledMetricsTest, CanceledRequest) {
-  if (@available(iOS 10, *)) {
+  if (@available(iOS 10.2, *)) {
     NSURL* url = net::NSURLWithGURL(net::QuicSimpleTestServer::GetSimpleURL());
 
     __block BOOL block_used = NO;
@@ -266,7 +266,7 @@
 
 // Tests the metrics data for a reused connection is correct.
 TEST_F(CronetEnabledMetricsTest, ReusedConnection) {
-  if (@available(iOS 10, *)) {
+  if (@available(iOS 10.2, *)) {
     NSURL* url = net::NSURLWithGURL(net::QuicSimpleTestServer::GetSimpleURL());
 
     __block BOOL block_used = NO;
@@ -324,7 +324,7 @@
 // and contains 0 records at the end of the request. This is a regression test
 // for http://crbug/834401.
 TEST_F(CronetEnabledMetricsTest, SessionWithoutDelegate) {
-  if (@available(iOS 10, *)) {
+  if (@available(iOS 10.2, *)) {
     NSURLSessionConfiguration* default_config =
         [NSURLSessionConfiguration defaultSessionConfiguration];
     [Cronet installIntoSessionConfiguration:default_config];
@@ -365,7 +365,7 @@
 
 // Tests that the metrics disable switch works.
 TEST_F(CronetDisabledMetricsTest, MetricsDisabled) {
-  if (@available(iOS 10, *)) {
+  if (@available(iOS 10.2, *)) {
     NSURL* url = net::NSURLWithGURL(net::QuicSimpleTestServer::GetSimpleURL());
 
     __block BOOL block_used = NO;
diff --git a/components/cronet/ios/test/cronet_quic_test.mm b/components/cronet/ios/test/cronet_quic_test.mm
index feae7918..5997946 100644
--- a/components/cronet/ios/test/cronet_quic_test.mm
+++ b/components/cronet/ios/test/cronet_quic_test.mm
@@ -100,7 +100,7 @@
   EXPECT_EQ(nil, [delegate_ error]);
   EXPECT_EQ(net::QuicSimpleTestServer::GetSimpleBodyValue(),
             base::SysNSStringToUTF8(delegate_.responseBody));
-  if (@available(iOS 10, *)) {
+  if (@available(iOS 10.2, *)) {
     NSURLSessionTaskTransactionMetrics* metrics =
         delegate_.taskMetrics.transactionMetrics[0];
     EXPECT_TRUE([metrics.networkProtocolName containsString:@"quic"])
diff --git a/components/metrics/file_metrics_provider.cc b/components/metrics/file_metrics_provider.cc
index fc14caa..45198c7 100644
--- a/components/metrics/file_metrics_provider.cc
+++ b/components/metrics/file_metrics_provider.cc
@@ -8,6 +8,7 @@
 
 #include "base/command_line.h"
 #include "base/containers/flat_map.h"
+#include "base/feature_list.h"
 #include "base/files/file.h"
 #include "base/files/file_enumerator.h"
 #include "base/files/file_util.h"
@@ -32,6 +33,9 @@
 
 namespace {
 
+const base::Feature kCacheFileMetricData = {"CacheFileMetricData",
+                                            base::FEATURE_ENABLED_BY_DEFAULT};
+
 // These structures provide values used to define how files are opened and
 // accessed. It obviates the need for multiple code-paths within several of
 // the methods.
@@ -490,7 +494,7 @@
   }
 
   // Map the file and validate it.
-  std::unique_ptr<base::PersistentMemoryAllocator> memory_allocator =
+  std::unique_ptr<base::FilePersistentMemoryAllocator> memory_allocator =
       std::make_unique<base::FilePersistentMemoryAllocator>(
           std::move(mapped), 0, 0, base::StringPiece(), read_only);
   if (memory_allocator->GetMemoryState() ==
@@ -500,6 +504,11 @@
   if (memory_allocator->IsCorrupt())
     return ACCESS_RESULT_DATA_CORRUPTION;
 
+  // Cache the file data while running in a background thread so that there
+  // shouldn't be any I/O when the data is accessed from the main thread.
+  if (base::FeatureList::IsEnabled(kCacheFileMetricData))
+    memory_allocator->Cache();
+
   // Create an allocator for the mapped file. Ownership passes to the allocator.
   source->allocator = std::make_unique<base::PersistentHistogramAllocator>(
       std::move(memory_allocator));
diff --git a/components/mirroring/browser/single_client_video_capture_host_unittest.cc b/components/mirroring/browser/single_client_video_capture_host_unittest.cc
index 10082f5a..dbf0185 100644
--- a/components/mirroring/browser/single_client_video_capture_host_unittest.cc
+++ b/components/mirroring/browser/single_client_video_capture_host_unittest.cc
@@ -158,7 +158,7 @@
   return media::mojom::VideoFrameInfo::New(
       base::TimeDelta(), base::Value(base::Value::Type::DICTIONARY),
       media::PIXEL_FORMAT_I420, gfx::Size(320, 180), gfx::Rect(320, 180),
-      gfx::ColorSpace::CreateREC709());
+      gfx::ColorSpace::CreateREC709(), nullptr);
 }
 
 }  // namespace
diff --git a/components/mirroring/service/fake_video_capture_host.cc b/components/mirroring/service/fake_video_capture_host.cc
index 12dbdac..201edc7 100644
--- a/components/mirroring/service/fake_video_capture_host.cc
+++ b/components/mirroring/service/fake_video_capture_host.cc
@@ -52,7 +52,7 @@
       0, media::mojom::VideoFrameInfo::New(
              base::TimeDelta(), metadata.GetInternalValues().Clone(),
              media::PIXEL_FORMAT_I420, size, gfx::Rect(size),
-             gfx::ColorSpace::CreateREC709()));
+             gfx::ColorSpace::CreateREC709(), nullptr));
 }
 
 }  // namespace mirroring
diff --git a/components/mirroring/service/video_capture_client_unittest.cc b/components/mirroring/service/video_capture_client_unittest.cc
index babb7f6..3f11bf2 100644
--- a/components/mirroring/service/video_capture_client_unittest.cc
+++ b/components/mirroring/service/video_capture_client_unittest.cc
@@ -33,7 +33,7 @@
   return media::mojom::VideoFrameInfo::New(
       base::TimeDelta(), metadata.GetInternalValues().Clone(),
       media::PIXEL_FORMAT_I420, size, gfx::Rect(size),
-      gfx::ColorSpace::CreateREC709());
+      gfx::ColorSpace::CreateREC709(), nullptr);
 }
 
 }  // namespace
diff --git a/components/omnibox/browser/omnibox_edit_model.cc b/components/omnibox/browser/omnibox_edit_model.cc
index d5dfd64bd..4673923e 100644
--- a/components/omnibox/browser/omnibox_edit_model.cc
+++ b/components/omnibox/browser/omnibox_edit_model.cc
@@ -232,14 +232,9 @@
   ToolbarModel* toolbar_model = controller()->GetToolbarModel();
   url_for_editing_ = toolbar_model->GetFormattedFullURL();
 
-  QueryInOmnibox* query_in_omnibox = client()->GetQueryInOmnibox();
-  base::string16 search_query;
-  if (query_in_omnibox &&
-      query_in_omnibox->GetDisplaySearchTerms(
-          toolbar_model->GetSecurityLevel(false /* ignore_editing */),
-          toolbar_model->GetURL(), &search_query)) {
-    DCHECK(!search_query.empty());
-    display_text_ = search_query;
+  if (GetQueryInOmniboxSearchTerms(&display_text_)) {
+    // The search query has been inserted into |display_text_|.
+    DCHECK(!display_text_.empty());
   } else if (OmniboxFieldTrial::
                  IsHideSteadyStateUrlSchemeAndSubdomainsEnabled()) {
     display_text_ = toolbar_model->GetURLForDisplay();
@@ -1309,6 +1304,18 @@
   OnPopupDataChanged(inline_autocompletion, nullptr, keyword, is_keyword_hint);
 }
 
+bool OmniboxEditModel::GetQueryInOmniboxSearchTerms(
+    base::string16* search_terms) {
+  QueryInOmnibox* query_in_omnibox = client()->GetQueryInOmnibox();
+  if (!query_in_omnibox)
+    return false;
+
+  ToolbarModel* toolbar_model = controller()->GetToolbarModel();
+  return query_in_omnibox->GetDisplaySearchTerms(
+      toolbar_model->GetSecurityLevel(false /* ignore_editing */),
+      toolbar_model->GetURL(), search_terms);
+}
+
 // static
 const char OmniboxEditModel::kCutOrCopyAllTextHistogram[] =
     "Omnibox.CutOrCopyAllText";
diff --git a/components/omnibox/browser/omnibox_edit_model.h b/components/omnibox/browser/omnibox_edit_model.h
index 7456757..33434b3 100644
--- a/components/omnibox/browser/omnibox_edit_model.h
+++ b/components/omnibox/browser/omnibox_edit_model.h
@@ -361,6 +361,10 @@
   // Called when the current match has changed in the OmniboxController.
   void OnCurrentMatchChanged();
 
+  // Convenience method for QueryInOmnibox::GetDisplaySearchTerms.
+  // Returns true if Query in Omnibox is active. |search_terms| may be nullptr.
+  bool GetQueryInOmniboxSearchTerms(base::string16* search_terms);
+
   // Used for testing purposes only.
   base::string16 GetUserTextForTesting() const { return user_text_; }
 
diff --git a/components/omnibox/browser/omnibox_edit_model_unittest.cc b/components/omnibox/browser/omnibox_edit_model_unittest.cc
index 7d87fd5..05cc86f 100644
--- a/components/omnibox/browser/omnibox_edit_model_unittest.cc
+++ b/components/omnibox/browser/omnibox_edit_model_unittest.cc
@@ -249,6 +249,10 @@
     model()->ResetDisplayTexts();
     EXPECT_EQ(base::ASCIIToUTF16("https://www.example.com/"),
               model()->GetPermanentDisplayText());
+
+    base::string16 search_terms;
+    EXPECT_FALSE(model()->GetQueryInOmniboxSearchTerms(&search_terms));
+    EXPECT_TRUE(search_terms.empty());
   }
 
 // TODO(tommycli): For now, it's not possible to enable Steady State Elisions
@@ -266,6 +270,10 @@
     model()->ResetDisplayTexts();
     EXPECT_EQ(base::ASCIIToUTF16("example.com"),
               model()->GetPermanentDisplayText());
+
+    base::string16 search_terms;
+    EXPECT_FALSE(model()->GetQueryInOmniboxSearchTerms(&search_terms));
+    EXPECT_TRUE(search_terms.empty());
   }
 #endif  // !defined(OS_IOS)
 
@@ -275,6 +283,11 @@
   client->SetFakeSearchTermsForQueryInOmnibox(base::ASCIIToUTF16("foobar"));
   model()->ResetDisplayTexts();
   EXPECT_EQ(base::ASCIIToUTF16("foobar"), model()->GetPermanentDisplayText());
+
+  base::string16 search_terms;
+  EXPECT_TRUE(model()->GetQueryInOmniboxSearchTerms(&search_terms));
+  EXPECT_FALSE(search_terms.empty());
+  EXPECT_EQ(base::ASCIIToUTF16("foobar"), search_terms);
 }
 
 TEST_F(OmniboxEditModelTest, DisablePasteAndGoForLongTexts) {
diff --git a/components/omnibox/browser/omnibox_view.cc b/components/omnibox/browser/omnibox_view.cc
index 6bcca5f..a8230e23 100644
--- a/components/omnibox/browser/omnibox_view.cc
+++ b/components/omnibox/browser/omnibox_view.cc
@@ -19,6 +19,7 @@
 #include "components/omnibox/browser/omnibox_edit_controller.h"
 #include "components/omnibox/browser/omnibox_edit_model.h"
 #include "components/omnibox/browser/omnibox_field_trial.h"
+#include "components/omnibox/browser/query_in_omnibox.h"
 #include "components/toolbar/toolbar_model.h"
 #include "extensions/common/constants.h"
 #include "ui/base/l10n/l10n_util.h"
@@ -119,6 +120,15 @@
   return gfx::ImageSkia();
 #else   // !defined(OS_IOS)
   if (!IsEditingOrEmpty()) {
+    // Query in Omnibox.
+    if (model_ &&
+        model_->GetQueryInOmniboxSearchTerms(nullptr /* search_terms */)) {
+      gfx::Image icon = model_->client()->GetFaviconForDefaultSearchProvider(
+          std::move(on_icon_fetched));
+      if (!icon.IsEmpty())
+        return model_->client()->GetSizedIcon(icon).AsImageSkia();
+    }
+
     return gfx::CreateVectorIcon(
         controller_->GetToolbarModel()->GetVectorIcon(), dip_size, color);
   }
diff --git a/components/plugins/renderer/webview_plugin.cc b/components/plugins/renderer/webview_plugin.cc
index 6a416f0..3bebe40 100644
--- a/components/plugins/renderer/webview_plugin.cc
+++ b/components/plugins/renderer/webview_plugin.cc
@@ -313,6 +313,11 @@
   return true;
 }
 
+blink::WebLayerTreeView*
+WebViewPlugin::WebViewHelper::InitializeLayerTreeView() {
+  return nullptr;
+}
+
 void WebViewPlugin::WebViewHelper::DidInvalidateRect(const WebRect& rect) {
   if (plugin_->container_)
     plugin_->container_->InvalidateRect(rect);
diff --git a/components/plugins/renderer/webview_plugin.h b/components/plugins/renderer/webview_plugin.h
index ada465ce..e0aa3f9a 100644
--- a/components/plugins/renderer/webview_plugin.h
+++ b/components/plugins/renderer/webview_plugin.h
@@ -175,6 +175,7 @@
     // TODO(ojan): Remove this override and have this class use a non-null
     // layerTreeView.
     bool AllowsBrokenNullLayerTreeView() const override;
+    blink::WebLayerTreeView* InitializeLayerTreeView() override;
     void DidInvalidateRect(const blink::WebRect&) override;
     void DidChangeCursor(const blink::WebCursorInfo& cursor) override;
     void ScheduleAnimation() override;
diff --git a/components/printing/renderer/print_render_frame_helper.cc b/components/printing/renderer/print_render_frame_helper.cc
index b0a0ed1..b5451b9 100644
--- a/components/printing/renderer/print_render_frame_helper.cc
+++ b/components/printing/renderer/print_render_frame_helper.cc
@@ -631,10 +631,21 @@
    private:
     blink::WebLocalFrame* frame_;
   };
+
   HeaderAndFooterClient frame_client;
   blink::WebLocalFrame* frame = blink::WebLocalFrame::CreateMainFrame(
       web_view, &frame_client, nullptr, nullptr);
-  blink::WebWidgetClient web_widget_client;
+
+  class NonCompositingWebWidgetClient : public blink::WebWidgetClient {
+   public:
+    // blink::WebWidgetClient implementation.
+    bool AllowsBrokenNullLayerTreeView() const override { return true; }
+    blink::WebLayerTreeView* InitializeLayerTreeView() override {
+      return nullptr;
+    }
+  };
+
+  NonCompositingWebWidgetClient web_widget_client;
   blink::WebFrameWidget::Create(&web_widget_client, frame);
 
   base::Value html(ui::ResourceBundle::GetSharedInstance().GetRawDataResource(
@@ -720,6 +731,7 @@
   // TODO(ojan): Remove this override and have this class use a non-null
   // layerTreeView.
   bool AllowsBrokenNullLayerTreeView() const override;
+  blink::WebLayerTreeView* InitializeLayerTreeView() override;
   WebWidgetClient* WidgetClient() override { return this; }
 
   // blink::WebLocalFrameClient:
@@ -877,6 +889,11 @@
   return true;
 }
 
+blink::WebLayerTreeView*
+PrepareFrameAndViewForPrint::InitializeLayerTreeView() {
+  return nullptr;
+}
+
 void PrepareFrameAndViewForPrint::DidStopLoading() {
   DCHECK(!on_ready_.is_null());
   // Don't call callback here, because it can delete |this| and WebView that is
diff --git a/components/safe_browsing/browser/safe_browsing_url_request_context_getter.cc b/components/safe_browsing/browser/safe_browsing_url_request_context_getter.cc
index cbb6c93..cfebb6c1 100644
--- a/components/safe_browsing/browser/safe_browsing_url_request_context_getter.cc
+++ b/components/safe_browsing/browser/safe_browsing_url_request_context_getter.cc
@@ -11,6 +11,7 @@
 #include "content/public/browser/cookie_store_factory.h"
 #include "net/cookies/cookie_store.h"
 #include "net/extras/sqlite/sqlite_channel_id_store.h"
+#include "net/extras/sqlite/sqlite_persistent_cookie_store.h"
 #include "net/http/http_network_layer.h"
 #include "net/http/http_transaction_factory.h"
 #include "net/ssl/channel_id_service.h"
@@ -47,7 +48,7 @@
         system_context_getter_->GetURLRequestContext());
     scoped_refptr<base::SequencedTaskRunner> background_task_runner =
         base::CreateSequencedTaskRunnerWithTraits(
-            {base::MayBlock(), base::TaskPriority::BEST_EFFORT,
+            {base::MayBlock(), net::GetCookieStoreBackgroundSequencePriority(),
              base::TaskShutdownBehavior::BLOCK_SHUTDOWN});
     // Set up the ChannelIDService
     scoped_refptr<net::SQLiteChannelIDStore> channel_id_db =
diff --git a/components/subresource_filter/core/common/PRESUBMIT.py b/components/subresource_filter/core/common/PRESUBMIT.py
index aa43e9a3..b50dc3d 100644
--- a/components/subresource_filter/core/common/PRESUBMIT.py
+++ b/components/subresource_filter/core/common/PRESUBMIT.py
@@ -16,17 +16,11 @@
    - components/subresource_filter/core/common/flat/indexed_ruleset.fbs
   and kIndexedFormatVersion constant stays intact, this check returns a
   presubmit warning to make sure the value should not be updated.
-
-  Additionally, checks to ensure the format version in
-  tools/perf/core/default_local_state.json stays up to date with
-  kIndexedFormatVersion.
   """
 
   indexed_ruleset_changed = False
   indexed_ruleset_version_changed = False
 
-  new_indexed_ruleset_version = None
-
   for affected_file in input_api.AffectedFiles():
     path = affected_file.LocalPath()
     basename = input_api.basename(path)
@@ -38,29 +32,11 @@
       for (_, line) in affected_file.ChangedContents():
         if 'const int RulesetIndexer::kIndexedFormatVersion =' in line:
           indexed_ruleset_version_changed = True
-          new_indexed_ruleset_version = int(line.split()[-1].replace(';',''))
           break
 
   # If the indexed ruleset version changed, ensure the perf benchmarks are using
   # the new format.
   out = []
-  if indexed_ruleset_version_changed:
-    assert new_indexed_ruleset_version is not None
-    current_path = input_api.PresubmitLocalPath()
-    local_state_path = input_api.os_path.join(
-        current_path, '..', '..', '..', '..', 'tools', 'perf', 'core',
-        'default_local_state.json')
-
-    assert input_api.os_path.exists(local_state_path)
-    with open(local_state_path, 'r') as f:
-      json_state = input_api.json.load(f)
-      version = json_state['subresource_filter']['ruleset_version']['format']
-      if new_indexed_ruleset_version != version:
-        out.append(output_api.PresubmitPromptWarning(
-            'Please make sure that kIndexedFormatVersion (%d) and '
-            'the format version in tools/perf/core/default_local_state.json '
-            '(%d) are in sync' % (new_indexed_ruleset_version, version)))
-
   if indexed_ruleset_changed and not indexed_ruleset_version_changed:
     out.append(output_api.PresubmitPromptWarning(
         'Please make sure that IndexedRuleset modifications in '
diff --git a/components/subresource_filter/core/common/indexed_ruleset.cc b/components/subresource_filter/core/common/indexed_ruleset.cc
index 0c3010c..5321788 100644
--- a/components/subresource_filter/core/common/indexed_ruleset.cc
+++ b/components/subresource_filter/core/common/indexed_ruleset.cc
@@ -52,9 +52,6 @@
 
 // RulesetIndexer --------------------------------------------------------------
 
-// static
-// Keep this in sync with the version number in
-// tools/perf/core/default_local_state.json.
 const int RulesetIndexer::kIndexedFormatVersion = 23;
 
 // This static assert is meant to catch cases where
diff --git a/components/subresource_filter/tools/BUILD.gn b/components/subresource_filter/tools/BUILD.gn
index 172441c..54b7ba5 100644
--- a/components/subresource_filter/tools/BUILD.gn
+++ b/components/subresource_filter/tools/BUILD.gn
@@ -63,6 +63,7 @@
     ]
     deps = [
       ":tools_lib",
+      "../core/common",
       "//base",
     ]
   }
@@ -109,6 +110,10 @@
     args = [
       rebase_path(inputs[0], root_build_dir),
       rebase_path("$target_gen_dir/GeneratedRulesetData", root_build_dir),
+      "--version_output=" +
+          rebase_path("$target_gen_dir/default_local_state.json",
+                      root_build_dir),
+      "--content_version=1000",
     ]
   }
 }
diff --git a/components/subresource_filter/tools/indexing_tool.cc b/components/subresource_filter/tools/indexing_tool.cc
index 70552b4..9a7ba20 100644
--- a/components/subresource_filter/tools/indexing_tool.cc
+++ b/components/subresource_filter/tools/indexing_tool.cc
@@ -9,6 +9,7 @@
 #include "base/files/file.h"
 #include "base/files/file_util.h"
 #include "base/numerics/safe_conversions.h"
+#include "base/strings/stringprintf.h"
 #include "components/subresource_filter/core/browser/copying_file_stream.h"
 #include "components/subresource_filter/core/common/indexed_ruleset.h"
 #include "components/subresource_filter/core/common/unindexed_ruleset.h"
@@ -17,7 +18,8 @@
 namespace subresource_filter {
 
 bool IndexAndWriteRuleset(const base::FilePath& unindexed_path,
-                          const base::FilePath& indexed_path) {
+                          const base::FilePath& indexed_path,
+                          int* out_checksum) {
   if (!base::PathExists(unindexed_path) ||
       !base::DirectoryExists(indexed_path.DirName())) {
     return false;
@@ -46,7 +48,28 @@
   base::WriteFile(indexed_path, reinterpret_cast<const char*>(indexer.data()),
                   base::checked_cast<int>(indexer.size()));
 
+  if (out_checksum)
+    *out_checksum = indexer.GetChecksum();
+
   return true;
 }
 
+void WriteVersionMetadata(const base::FilePath& path,
+                          const std::string& content_version,
+                          int checksum) {
+  const char* version_format = R"({
+  "subresource_filter": {
+    "ruleset_version": {
+      "content": "%s",
+      "format": %d,
+      "checksum": %d
+    }
+  }
+})";
+  std::string version = base::StringPrintf(
+      version_format, content_version.c_str(),
+      subresource_filter::RulesetIndexer::kIndexedFormatVersion, checksum);
+  base::WriteFile(path, version.data(), version.size());
+}
+
 }  // namespace subresource_filter
diff --git a/components/subresource_filter/tools/indexing_tool.h b/components/subresource_filter/tools/indexing_tool.h
index fbb232e..72d41d7 100644
--- a/components/subresource_filter/tools/indexing_tool.h
+++ b/components/subresource_filter/tools/indexing_tool.h
@@ -12,9 +12,17 @@
 
 // Given |unindexed_path|, which is a path to an unindexed ruleset, writes the
 // indexed (flatbuffer) version to |indexed_path|. Returns false if there was
-// something wrong with the given paths.
+// something wrong with the given paths. If successful, stores the checksum of
+// the ruleset in the outparam.
 bool IndexAndWriteRuleset(const base::FilePath& unindexed_path,
-                          const base::FilePath& indexed_path);
+                          const base::FilePath& indexed_path,
+                          int* out_checksum = nullptr);
+
+// Write version JSON to |path|. This matches the version JSON found in
+// preferences.
+void WriteVersionMetadata(const base::FilePath& path,
+                          const std::string& content_version,
+                          int checksum);
 
 }  // namespace subresource_filter
 
diff --git a/components/subresource_filter/tools/indexing_tool_main.cc b/components/subresource_filter/tools/indexing_tool_main.cc
index 8b07dd2..fa2da76 100644
--- a/components/subresource_filter/tools/indexing_tool_main.cc
+++ b/components/subresource_filter/tools/indexing_tool_main.cc
@@ -10,11 +10,18 @@
 
 const char kHelpMsg[] = R"(
   subresource_indexing_tool <unindexed_ruleset_file> <output_file>
+  [--version_output=<version_output> --content_version=<content_version>]
 
   subresource_indexing_tool will open the |unindexed_ruleset_file| and output
   an indexed version in |output_file|.
+
+  Optionally, this tool can also output version metadata json, given a content
+  version. This metadata matches how versions are stored in Chrome prefs.
 )";
 
+const char kSwitchVersionOutput[] = "version_output";
+const char kSwitchContentVersion[] = "content_version";
+
 void PrintHelp() {
   printf("%s\n\n", kHelpMsg);
 }
@@ -33,9 +40,26 @@
   base::FilePath unindexed_path(args[0]);
   base::FilePath indexed_path(args[1]);
 
-  if (!subresource_filter::IndexAndWriteRuleset(unindexed_path, indexed_path)) {
+  int checksum = 0;
+  if (!subresource_filter::IndexAndWriteRuleset(unindexed_path, indexed_path,
+                                                &checksum)) {
     LOG(ERROR) << "There was an error. Be sure that the first argument points "
                   "to a valid unindexed file and that the second argument is "
                   "in an existing directory.";
+    return 1;
   }
+
+  DCHECK_NE(0, checksum);
+
+  if (!command_line.HasSwitch(kSwitchVersionOutput))
+    return 0;
+
+  LOG_IF(FATAL, !command_line.HasSwitch(kSwitchContentVersion))
+      << "content_version must be present if version_output is";
+  std::string content_version =
+      command_line.GetSwitchValueASCII(kSwitchContentVersion);
+  subresource_filter::WriteVersionMetadata(
+      command_line.GetSwitchValuePath(kSwitchVersionOutput), content_version,
+      checksum);
+  return 0;
 }
diff --git a/components/subresource_filter/tools/indexing_tool_unittest.cc b/components/subresource_filter/tools/indexing_tool_unittest.cc
index 86becab..79434da 100644
--- a/components/subresource_filter/tools/indexing_tool_unittest.cc
+++ b/components/subresource_filter/tools/indexing_tool_unittest.cc
@@ -10,9 +10,12 @@
 
 #include "base/files/file_util.h"
 #include "base/files/scoped_temp_dir.h"
+#include "base/json/json_reader.h"
 #include "base/macros.h"
 #include "base/numerics/safe_conversions.h"
 #include "base/strings/string_number_conversions.h"
+#include "base/values.h"
+#include "components/subresource_filter/core/common/indexed_ruleset.h"
 #include "components/subresource_filter/core/common/test_ruleset_creator.h"
 #include "components/subresource_filter/core/common/test_ruleset_utils.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -112,6 +115,39 @@
   EXPECT_EQ(test_ruleset_pair_.indexed.contents, indexed_data);
 }
 
+TEST_F(IndexingToolTest, VersionMetadata) {
+  base::FilePath unindexed_path = GetUniquePath();
+  base::FilePath indexed_path = GetUniquePath();
+  base::FilePath version_path = GetUniquePath();
+
+  CreateSimpleRuleset();
+  WriteUnindexedRulesetToFile(unindexed_path);
+
+  // Convert the unindexed data to indexed data, and write the result to
+  // indexed_path.
+  int checksum = 0;
+  EXPECT_TRUE(IndexAndWriteRuleset(unindexed_path, indexed_path, &checksum));
+  EXPECT_NE(0, checksum);
+  WriteVersionMetadata(version_path, "1.2.3", checksum);
+  std::string version_json;
+  EXPECT_TRUE(base::ReadFileToString(version_path, &version_json));
+  std::unique_ptr<base::DictionaryValue> json =
+      base::DictionaryValue::From(base::JSONReader::Read(version_json));
+
+  std::string actual_content =
+      json->FindPath({"subresource_filter", "ruleset_version", "content"})
+          ->GetString();
+  EXPECT_EQ("1.2.3", actual_content);
+  int actual_format =
+      json->FindPath({"subresource_filter", "ruleset_version", "format"})
+          ->GetInt();
+  EXPECT_EQ(RulesetIndexer::kIndexedFormatVersion, actual_format);
+  int actual_checksum =
+      json->FindPath({"subresource_filter", "ruleset_version", "checksum"})
+          ->GetInt();
+  EXPECT_EQ(checksum, actual_checksum);
+}
+
 }  // namespace
 
 }  // namespace subresource_filter
diff --git a/components/sync/driver/model_type_controller.cc b/components/sync/driver/model_type_controller.cc
index 68327a8..5b9003d 100644
--- a/components/sync/driver/model_type_controller.cc
+++ b/components/sync/driver/model_type_controller.cc
@@ -76,12 +76,13 @@
     const ConfigureContext& configure_context,
     const ModelLoadCallback& model_load_callback) {
   DCHECK(CalledOnValidThread());
-  DCHECK(!model_load_callback.is_null());
-  DCHECK_EQ(NOT_RUNNING, state_);
+  CHECK(!model_load_callback.is_null());
+  CHECK_EQ(NOT_RUNNING, state_);
 
   auto it = delegate_map_.find(configure_context.storage_option);
-  DCHECK(it != delegate_map_.end());
+  CHECK(it != delegate_map_.end());
   delegate_ = it->second.get();
+  CHECK(delegate_);
 
   DVLOG(1) << "Sync starting for " << ModelTypeToString(type());
   state_ = MODEL_STARTING;
@@ -95,8 +96,8 @@
   request.authenticated_account_id = configure_context.authenticated_account_id;
   request.cache_guid = configure_context.cache_guid;
 
-  DCHECK(!request.authenticated_account_id.empty());
-  DCHECK(!request.cache_guid.empty());
+  CHECK(!request.authenticated_account_id.empty());
+  CHECK(!request.cache_guid.empty());
 
   // Ask the delegate to actually start the datatype.
   delegate_->OnSyncStarting(
@@ -109,7 +110,7 @@
 void ModelTypeController::LoadModelsDone(ConfigureResult result,
                                          const SyncError& error) {
   DCHECK(CalledOnValidThread());
-  DCHECK_NE(NOT_RUNNING, state_);
+  CHECK_NE(NOT_RUNNING, state_);
 
   if (state_ == STOPPING) {
     DCHECK(!model_stop_callbacks_.empty());
@@ -136,7 +137,7 @@
   }
 
   if (IsSuccessfulResult(result)) {
-    DCHECK_EQ(MODEL_STARTING, state_);
+    CHECK_EQ(MODEL_STARTING, state_);
     state_ = MODEL_LOADED;
     DVLOG(1) << "Sync start completed for " << ModelTypeToString(type());
   } else {
@@ -167,7 +168,7 @@
     return;
   DCHECK(configurer);
   DCHECK(activation_response_);
-  DCHECK_EQ(MODEL_LOADED, state_);
+  CHECK_EQ(MODEL_LOADED, state_);
   // Inform the DataTypeManager whether our initial download is complete.
   set_downloaded.Run(
       activation_response_->model_type_state.initial_sync_done());
@@ -182,7 +183,7 @@
     const StartCallback& start_callback) {
   DCHECK(CalledOnValidThread());
   DCHECK(!start_callback.is_null());
-  DCHECK_EQ(MODEL_LOADED, state_);
+  CHECK_EQ(MODEL_LOADED, state_);
 
   state_ = RUNNING;
   DVLOG(1) << "Sync running for " << ModelTypeToString(type());
@@ -195,11 +196,11 @@
 void ModelTypeController::ActivateDataType(ModelTypeConfigurer* configurer) {
   DCHECK(CalledOnValidThread());
   DCHECK(configurer);
-  DCHECK_EQ(RUNNING, state_);
+  CHECK_EQ(RUNNING, state_);
   // In contrast with directory datatypes, non-blocking data types should be
   // activated in RegisterWithBackend. activation_response_ should be
   // passed to backend before call to ActivateDataType.
-  DCHECK(!activation_response_);
+  CHECK(!activation_response_);
 }
 
 void ModelTypeController::DeactivateDataType(ModelTypeConfigurer* configurer) {
@@ -229,15 +230,15 @@
       return;
 
     case STOPPING:
-      DCHECK(!model_stop_callbacks_.empty());
+      CHECK(!model_stop_callbacks_.empty());
       model_stop_metadata_fate_ =
           TakeStrictestMetadataFate(model_stop_metadata_fate_, metadata_fate);
       model_stop_callbacks_.push_back(std::move(callback));
       break;
 
     case MODEL_STARTING:
-      DCHECK(!model_load_callback_.is_null());
-      DCHECK(model_stop_callbacks_.empty());
+      CHECK(!model_load_callback_.is_null());
+      CHECK(model_stop_callbacks_.empty());
       DLOG(WARNING) << "Deferring stop for " << ModelTypeToString(type())
                     << " because it's still starting";
       model_stop_metadata_fate_ = metadata_fate;
diff --git a/components/sync/model_impl/client_tag_based_model_type_processor.cc b/components/sync/model_impl/client_tag_based_model_type_processor.cc
index bc5d0f0..419e9fe 100644
--- a/components/sync/model_impl/client_tag_based_model_type_processor.cc
+++ b/components/sync/model_impl/client_tag_based_model_type_processor.cc
@@ -87,13 +87,15 @@
     const DataTypeActivationRequest& request,
     StartCallback start_callback) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  DVLOG(1) << "Sync is starting for " << ModelTypeToString(type_);
   // TODO(crbug.com/876490): Here and elsewhere in this file, CHECKs have been
   // introduced to investigate some crashes in the wild. Let's downgrade all
   // CHECKs to DCHECKs as soon as the investigation is completed.
-  CHECK(!IsConnected());
+  CHECK(this);
   CHECK(request.error_handler);
   CHECK(start_callback);
-  DVLOG(1) << "Sync is starting for " << ModelTypeToString(type_);
+  CHECK(!start_callback_);
+  CHECK(!IsConnected());
 
   start_callback_ = std::move(start_callback);
   activation_request_ = request;
@@ -218,6 +220,7 @@
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   // Disabling sync for a type never happens before the model is ready to sync.
   CHECK(model_ready_to_sync_);
+  CHECK(!start_callback_);
 
   switch (metadata_fate) {
     case KEEP_METADATA: {
@@ -997,13 +1000,17 @@
     }
   }
 
-  // Report failed loading of entities to UMA (if they are still tracked here).
+  // Report failed loading of entities to UMA.
   for (const std::string& storage_key : storage_keys_to_load) {
-    if (GetEntityForStorageKey(storage_key)) {
-      UMA_HISTOGRAM_ENUMERATION("Sync.ModelTypeOrphanMetadata",
-                                ModelTypeToHistogramInt(type_),
-                                static_cast<int>(MODEL_TYPE_COUNT));
+    ProcessorEntityTracker* entity = GetEntityForStorageKey(storage_key);
+    if (entity == nullptr || entity->metadata().is_deleted()) {
+      // Skip entities that are not tracked any more or already marked for
+      // deletion.
+      continue;
     }
+    UMA_HISTOGRAM_ENUMERATION("Sync.ModelTypeOrphanMetadata",
+                              ModelTypeToHistogramInt(type_),
+                              static_cast<int>(MODEL_TYPE_COUNT));
   }
 }
 
diff --git a/components/sync/model_impl/client_tag_based_model_type_processor_unittest.cc b/components/sync/model_impl/client_tag_based_model_type_processor_unittest.cc
index 5ae8b14c..84ce432 100644
--- a/components/sync/model_impl/client_tag_based_model_type_processor_unittest.cc
+++ b/components/sync/model_impl/client_tag_based_model_type_processor_unittest.cc
@@ -1921,23 +1921,10 @@
   ResetState(/*keep_db=*/true);
 
   // Initializing the processor will trigger it to commit again. It does not
-  // have a copy of the data so it will ask the bridge. And it will report an
-  // orphan.
+  // have a copy of the data so it will ask the bridge.
   base::HistogramTester histogram_tester;
-  bridge()->ExpectSynchronousDataCallback();
   InitializeToReadyState();
 
-  histogram_tester.ExpectBucketCount(
-      "Sync.ModelTypeOrphanMetadata",
-      /*bucket=*/ModelTypeToHistogramInt(GetModelType()), /*count=*/1);
-
-  // Do it again, explicitly. The processor cannot return the entity as a
-  // LocalChange (because it cannot load its data) so it needs to ask the
-  // bridge.
-  CommitRequestDataList commit_request;
-  type_processor()->GetLocalChanges(
-      INT_MAX, base::BindOnce(&CaptureCommitRequest, &commit_request));
-
   // The bridge has not passed the data back to the processor, we untrack the
   // entity.
   type_processor()->UntrackEntityForStorageKey(kKey1);
@@ -1945,10 +1932,35 @@
   // Make the bridge pass the data back to the processor. Because the entity is
   // already deleted in the processor, no further orphan gets reported.
   std::move(bridge()->GetDataCallback()).Run();
-  EXPECT_EQ(0U, commit_request.size());
-  histogram_tester.ExpectBucketCount(
-      "Sync.ModelTypeOrphanMetadata",
-      /*bucket=*/ModelTypeToHistogramInt(GetModelType()), /*count=*/1);
+  histogram_tester.ExpectTotalCount("Sync.ModelTypeOrphanMetadata",
+                                    /*count=*/0);
+}
+
+TEST_F(ClientTagBasedModelTypeProcessorTest,
+       ShouldNotReportOrphanMetadataInGetLocalChangesWhenDataIsAlreadyDeleted) {
+  InitializeToReadyState();
+  bridge()->WriteItem(kKey1, kValue1);
+
+  // Loose the entity in the bridge (keeping the metadata around as an orphan).
+  bridge()->MimicBugToLooseItemWithoutNotifyingProcessor(kKey1);
+
+  // Reset "the browser" so that the processor looses the copy of the data.
+  ResetState(/*keep_db=*/true);
+
+  // Initializing the processor will trigger it to commit again. It does not
+  // have a copy of the data so it will ask the bridge.
+  base::HistogramTester histogram_tester;
+  InitializeToReadyState();
+
+  // The bridge has not passed the data back to the processor, we delete the
+  // entity.
+  bridge()->DeleteItem(kKey1);
+
+  // Make the bridge pass the data back to the processor. Because the entity is
+  // already deleted in the processor, no further orphan gets reported.
+  std::move(bridge()->GetDataCallback()).Run();
+  histogram_tester.ExpectTotalCount("Sync.ModelTypeOrphanMetadata",
+                                    /*count=*/0);
 }
 
 TEST_F(ClientTagBasedModelTypeProcessorTest,
diff --git a/components/sync/model_impl/proxy_model_type_controller_delegate.cc b/components/sync/model_impl/proxy_model_type_controller_delegate.cc
index 128657d..cd29793 100644
--- a/components/sync/model_impl/proxy_model_type_controller_delegate.cc
+++ b/components/sync/model_impl/proxy_model_type_controller_delegate.cc
@@ -17,6 +17,7 @@
     const DataTypeActivationRequest& request,
     ModelTypeControllerDelegate::StartCallback callback_bound_to_ui_thread,
     base::WeakPtr<ModelTypeControllerDelegate> delegate) {
+  CHECK(delegate);
   delegate->OnSyncStarting(request, std::move(callback_bound_to_ui_thread));
 }
 
@@ -24,6 +25,7 @@
     ProxyModelTypeControllerDelegate::AllNodesCallback
         callback_bound_to_ui_thread,
     base::WeakPtr<ModelTypeControllerDelegate> delegate) {
+  CHECK(delegate);
   delegate->GetAllNodesForDebugging(std::move(callback_bound_to_ui_thread));
 }
 
@@ -31,6 +33,7 @@
     ProxyModelTypeControllerDelegate::StatusCountersCallback
         callback_bound_to_ui_thread,
     base::WeakPtr<ModelTypeControllerDelegate> delegate) {
+  CHECK(delegate);
   delegate->GetStatusCountersForDebugging(
       std::move(callback_bound_to_ui_thread));
 }
@@ -38,11 +41,13 @@
 void StopSyncHelperOnModelThread(
     SyncStopMetadataFate metadata_fate,
     base::WeakPtr<ModelTypeControllerDelegate> delegate) {
+  CHECK(delegate);
   delegate->OnSyncStopping(metadata_fate);
 }
 
 void RecordMemoryUsageAndCountsHistogramsHelperOnModelThread(
     base::WeakPtr<ModelTypeControllerDelegate> delegate) {
+  CHECK(delegate);
   delegate->RecordMemoryUsageAndCountsHistograms();
 }
 
diff --git a/components/viz/common/frame_sinks/begin_frame_source.cc b/components/viz/common/frame_sinks/begin_frame_source.cc
index 857aca0c..ff9b0b78 100644
--- a/components/viz/common/frame_sinks/begin_frame_source.cc
+++ b/components/viz/common/frame_sinks/begin_frame_source.cc
@@ -187,9 +187,7 @@
 void DelayBasedBeginFrameSource::OnUpdateVSyncParameters(
     base::TimeTicks timebase,
     base::TimeDelta interval) {
-  if (!authoritative_interval_.is_zero()) {
-    interval = authoritative_interval_;
-  } else if (interval.is_zero()) {
+  if (interval.is_zero()) {
     // TODO(brianderson): We should not be receiving 0 intervals.
     interval = BeginFrameArgs::DefaultInterval();
   }
@@ -198,12 +196,6 @@
   time_source_->SetTimebaseAndInterval(timebase, interval);
 }
 
-void DelayBasedBeginFrameSource::SetAuthoritativeVSyncInterval(
-    base::TimeDelta interval) {
-  authoritative_interval_ = interval;
-  OnUpdateVSyncParameters(last_timebase_, interval);
-}
-
 BeginFrameArgs DelayBasedBeginFrameSource::CreateBeginFrameArgs(
     base::TimeTicks frame_time) {
   uint64_t sequence_number = next_sequence_number_++;
diff --git a/components/viz/common/frame_sinks/begin_frame_source.h b/components/viz/common/frame_sinks/begin_frame_source.h
index 677ed18a..8b8a775 100644
--- a/components/viz/common/frame_sinks/begin_frame_source.h
+++ b/components/viz/common/frame_sinks/begin_frame_source.h
@@ -175,8 +175,6 @@
 
   virtual void OnUpdateVSyncParameters(base::TimeTicks timebase,
                                        base::TimeDelta interval) = 0;
-  // This overrides any past or future interval from updating vsync parameters.
-  virtual void SetAuthoritativeVSyncInterval(base::TimeDelta interval) = 0;
 };
 
 // A frame source which calls BeginFrame (at the next possible time) as soon as
@@ -198,7 +196,6 @@
   // SyntheticBeginFrameSource implementation.
   void OnUpdateVSyncParameters(base::TimeTicks timebase,
                                base::TimeDelta interval) override {}
-  void SetAuthoritativeVSyncInterval(base::TimeDelta interval) override {}
 
   // DelayBasedTimeSourceClient implementation.
   void OnTimerTick() override;
@@ -232,7 +229,6 @@
   // SyntheticBeginFrameSource implementation.
   void OnUpdateVSyncParameters(base::TimeTicks timebase,
                                base::TimeDelta interval) override;
-  void SetAuthoritativeVSyncInterval(base::TimeDelta interval) override;
 
   // DelayBasedTimeSourceClient implementation.
   void OnTimerTick() override;
@@ -243,7 +239,6 @@
   std::unique_ptr<DelayBasedTimeSource> time_source_;
   std::unordered_set<BeginFrameObserver*> observers_;
   base::TimeTicks last_timebase_;
-  base::TimeDelta authoritative_interval_;
   BeginFrameArgs last_begin_frame_args_;
   uint64_t next_sequence_number_;
 
diff --git a/components/viz/common/frame_sinks/begin_frame_source_unittest.cc b/components/viz/common/frame_sinks/begin_frame_source_unittest.cc
index f917015..6d5633a 100644
--- a/components/viz/common/frame_sinks/begin_frame_source_unittest.cc
+++ b/components/viz/common/frame_sinks/begin_frame_source_unittest.cc
@@ -431,33 +431,6 @@
   task_runner_->FastForwardTo(TicksFromMicroseconds(60000));
 }
 
-TEST_F(DelayBasedBeginFrameSourceTest, AuthoritativeVSyncChanges) {
-  source_->OnUpdateVSyncParameters(TicksFromMicroseconds(500),
-                                   base::TimeDelta::FromMicroseconds(10000));
-  EXPECT_BEGIN_FRAME_SOURCE_PAUSED(*obs_, false);
-  EXPECT_BEGIN_FRAME_USED_MISSED(*obs_, source_->source_id(), 1, 500, 10500,
-                                 10000);
-  source_->AddObserver(obs_.get());
-
-  EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 2, 10500, 20500, 10000);
-  EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 3, 20500, 30500, 10000);
-  task_runner_->FastForwardTo(TicksFromMicroseconds(20501));
-
-  // This will keep the same timebase, so 500, 9999
-  source_->SetAuthoritativeVSyncInterval(
-      base::TimeDelta::FromMicroseconds(9999));
-  EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 4, 30500, 40496, 9999);
-  EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 5, 40496, 50495, 9999);
-  task_runner_->FastForwardTo(TicksFromMicroseconds(40497));
-
-  // Change the vsync params, but the new interval will be ignored.
-  source_->OnUpdateVSyncParameters(TicksFromMicroseconds(400),
-                                   base::TimeDelta::FromMicroseconds(1));
-  EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 6, 50495, 60394, 9999);
-  EXPECT_BEGIN_FRAME_USED(*obs_, source_->source_id(), 7, 60394, 70393, 9999);
-  task_runner_->FastForwardTo(TicksFromMicroseconds(60395));
-}
-
 TEST_F(DelayBasedBeginFrameSourceTest, MultipleObservers) {
   NiceMock<MockBeginFrameObserver> obs1, obs2;
 
diff --git a/components/viz/common/hit_test/hit_test_region_list.h b/components/viz/common/hit_test/hit_test_region_list.h
index 155fd28..1ef8c4c 100644
--- a/components/viz/common/hit_test/hit_test_region_list.h
+++ b/components/viz/common/hit_test/hit_test_region_list.h
@@ -14,26 +14,26 @@
 
 namespace viz {
 
-struct HitTestRegionFlags {
+enum HitTestRegionFlags : uint32_t {
   // Region maps to this surface (me).
-  enum : uint32_t { kHitTestMine = 0x01 };
+  kHitTestMine = 0x01,
   // Region ignored for hit testing (transparent backgrounds & hover:none).
-  enum : uint32_t { kHitTestIgnore = 0x02 };
+  kHitTestIgnore = 0x02,
   // Region maps to child surface (OOPIF).
-  enum : uint32_t { kHitTestChildSurface = 0x04 };
+  kHitTestChildSurface = 0x04,
   // Irregular boundary - send HitTestRequest to resolve.
-  enum : uint32_t { kHitTestAsk = 0x08 };
+  kHitTestAsk = 0x08,
 
   // TODO(varkha): Add other kHitTest* flags as necessary for other event
   // sources such as mouse-wheel, stylus or perhaps even mouse-move.
 
   // Hit-testing for mouse events.
-  enum : uint32_t { kHitTestMouse = 0x10 };
+  kHitTestMouse = 0x10,
   // Hit-testing for touch events.
-  enum : uint32_t { kHitTestTouch = 0x20 };
+  kHitTestTouch = 0x20,
 
   // Client hasn't submitted its own hit-test data yet.
-  enum : uint32_t { kHitTestNotActive = 0x40 };
+  kHitTestNotActive = 0x40,
 };
 
 struct HitTestRegion {
diff --git a/components/viz/service/frame_sinks/root_compositor_frame_sink_impl.cc b/components/viz/service/frame_sinks/root_compositor_frame_sink_impl.cc
index dedcdc3..384d279c 100644
--- a/components/viz/service/frame_sinks/root_compositor_frame_sink_impl.cc
+++ b/components/viz/service/frame_sinks/root_compositor_frame_sink_impl.cc
@@ -54,10 +54,18 @@
     external_begin_frame_source =
         std::make_unique<ExternalBeginFrameSourceAndroid>(restart_id);
 #else
-    synthetic_begin_frame_source = std::make_unique<DelayBasedBeginFrameSource>(
-        std::make_unique<DelayBasedTimeSource>(
-            base::ThreadTaskRunnerHandle::Get().get()),
-        restart_id);
+    if (params->disable_frame_rate_limit) {
+      synthetic_begin_frame_source =
+          std::make_unique<BackToBackBeginFrameSource>(
+              std::make_unique<DelayBasedTimeSource>(
+                  base::ThreadTaskRunnerHandle::Get().get()));
+    } else {
+      synthetic_begin_frame_source =
+          std::make_unique<DelayBasedBeginFrameSource>(
+              std::make_unique<DelayBasedTimeSource>(
+                  base::ThreadTaskRunnerHandle::Get().get()),
+              restart_id);
+    }
 #endif
   }
 
@@ -127,12 +135,6 @@
   display_->SetOutputIsSecure(secure);
 }
 
-void RootCompositorFrameSinkImpl::SetAuthoritativeVSyncInterval(
-    base::TimeDelta interval) {
-  if (synthetic_begin_frame_source_)
-    synthetic_begin_frame_source_->SetAuthoritativeVSyncInterval(interval);
-}
-
 void RootCompositorFrameSinkImpl::SetDisplayVSyncParameters(
     base::TimeTicks timebase,
     base::TimeDelta interval) {
diff --git a/components/viz/service/frame_sinks/root_compositor_frame_sink_impl.h b/components/viz/service/frame_sinks/root_compositor_frame_sink_impl.h
index 7908859..8c717154 100644
--- a/components/viz/service/frame_sinks/root_compositor_frame_sink_impl.h
+++ b/components/viz/service/frame_sinks/root_compositor_frame_sink_impl.h
@@ -47,7 +47,6 @@
   void SetDisplayColorSpace(const gfx::ColorSpace& blending_color_space,
                             const gfx::ColorSpace& device_color_space) override;
   void SetOutputIsSecure(bool secure) override;
-  void SetAuthoritativeVSyncInterval(base::TimeDelta interval) override;
   void SetDisplayVSyncParameters(base::TimeTicks timebase,
                                  base::TimeDelta interval) override;
   void ForceImmediateDrawAndSwapIfPossible() override;
diff --git a/components/viz/test/test_layer_tree_frame_sink.cc b/components/viz/test/test_layer_tree_frame_sink.cc
index 52c91ec..da33573 100644
--- a/components/viz/test/test_layer_tree_frame_sink.cc
+++ b/components/viz/test/test_layer_tree_frame_sink.cc
@@ -90,7 +90,8 @@
       begin_frame_source_ = std::make_unique<DelayBasedBeginFrameSource>(
           std::make_unique<DelayBasedTimeSource>(compositor_task_runner_.get()),
           BeginFrameSource::kNotRestartableId);
-      begin_frame_source_->SetAuthoritativeVSyncInterval(
+      begin_frame_source_->OnUpdateVSyncParameters(
+          base::TimeTicks::Now(),
           base::TimeDelta::FromMilliseconds(1000.f / refresh_rate_));
       display_begin_frame_source_ = begin_frame_source_.get();
     }
diff --git a/content/app/content_main_runner_impl.cc b/content/app/content_main_runner_impl.cc
index 300e3bb..8540e79 100644
--- a/content/app/content_main_runner_impl.cc
+++ b/content/app/content_main_runner_impl.cc
@@ -912,8 +912,10 @@
     delegate_->ProcessExiting(process_type);
   }
 
+#if !defined(CHROME_MULTIPLE_DLL_CHILD)
   // The message loop needs to be destroyed before |exit_manager_|.
   main_message_loop_.reset();
+#endif  // !defined(CHROME_MULTIPLE_DLL_CHILD)
 
 #if defined(OS_WIN)
 #ifdef _CRTDBG_MAP_ALLOC
diff --git a/content/app/content_main_runner_impl.h b/content/app/content_main_runner_impl.h
index aff316c..118b91b 100644
--- a/content/app/content_main_runner_impl.h
+++ b/content/app/content_main_runner_impl.h
@@ -72,9 +72,11 @@
 
   CreatedMainPartsClosure* created_main_parts_closure_ = nullptr;
 
+#if !defined(CHROME_MULTIPLE_DLL_CHILD)
   std::unique_ptr<base::MessageLoop> main_message_loop_;
 
   std::unique_ptr<StartupDataImpl> startup_data_;
+#endif  // !defined(CHROME_MULTIPLE_DLL_CHILD)
 
   DISALLOW_COPY_AND_ASSIGN(ContentMainRunnerImpl);
 };
diff --git a/content/browser/accessibility/accessibility_event_recorder.cc b/content/browser/accessibility/accessibility_event_recorder.cc
index e251ea4..fdc0e52 100644
--- a/content/browser/accessibility/accessibility_event_recorder.cc
+++ b/content/browser/accessibility/accessibility_event_recorder.cc
@@ -11,8 +11,7 @@
 namespace content {
 
 AccessibilityEventRecorder::AccessibilityEventRecorder(
-    BrowserAccessibilityManager* manager,
-    base::ProcessId pid)
+    BrowserAccessibilityManager* manager)
     : manager_(manager) {}
 
 AccessibilityEventRecorder::~AccessibilityEventRecorder() = default;
@@ -21,8 +20,9 @@
 // static
 AccessibilityEventRecorder& AccessibilityEventRecorder::GetInstance(
     BrowserAccessibilityManager* manager,
-    base::ProcessId pid) {
-  static base::NoDestructor<AccessibilityEventRecorder> instance(manager, pid);
+    base::ProcessId pid,
+    const base::StringPiece& application_name_match_pattern) {
+  static base::NoDestructor<AccessibilityEventRecorder> instance(manager);
   return *instance;
 }
 #endif
diff --git a/content/browser/accessibility/accessibility_event_recorder.h b/content/browser/accessibility/accessibility_event_recorder.h
index 4d03a1d..23d32f7b 100644
--- a/content/browser/accessibility/accessibility_event_recorder.h
+++ b/content/browser/accessibility/accessibility_event_recorder.h
@@ -40,7 +40,9 @@
   // Get the right platform-specific subclass.
   static AccessibilityEventRecorder& GetInstance(
       BrowserAccessibilityManager* manager = nullptr,
-      base::ProcessId pid = 0);
+      base::ProcessId pid = 0,
+      const base::StringPiece& application_name_match_pattern =
+          base::StringPiece());
   virtual ~AccessibilityEventRecorder();
 
   void set_only_web_events(bool only_web_events) {
@@ -55,8 +57,7 @@
   const std::vector<std::string>& event_logs() { return event_logs_; }
 
  protected:
-  AccessibilityEventRecorder(BrowserAccessibilityManager* manager,
-                             base::ProcessId pid);
+  AccessibilityEventRecorder(BrowserAccessibilityManager* manager);
 
   void OnEvent(const std::string& event);
 
diff --git a/content/browser/accessibility/accessibility_event_recorder_auralinux.cc b/content/browser/accessibility/accessibility_event_recorder_auralinux.cc
index b20feea..59122f8 100644
--- a/content/browser/accessibility/accessibility_event_recorder_auralinux.cc
+++ b/content/browser/accessibility/accessibility_event_recorder_auralinux.cc
@@ -6,9 +6,13 @@
 
 #include <atk/atk.h>
 #include <atk/atkutil.h>
+#include <atspi/atspi.h>
 
 #include "base/process/process_handle.h"
+#include "base/stl_util.h"
+#include "base/strings/pattern.h"
 #include "base/strings/stringprintf.h"
+#include "content/browser/accessibility/accessibility_tree_formatter_utils_auralinux.h"
 #include "content/browser/accessibility/browser_accessibility_auralinux.h"
 #include "content/browser/accessibility/browser_accessibility_manager.h"
 
@@ -18,82 +22,114 @@
 
 namespace content {
 
+// This class has two distinct event recording code paths. When we are
+// recording events in-process (typically this is used for
+// DumpAccessibilityEvents tests), we use ATK's global event handlers. Since
+// ATK doesn't support intercepting events from other processes, if we have a
+// non-zero PID or an accessibility application name pattern, we use AT-SPI2
+// directly to intercept events. Since AT-SPI2 should be capable of
+// intercepting events in-process as well, eventually it would be nice to
+// remove the ATK code path entirely.
 class AccessibilityEventRecorderAuraLinux : public AccessibilityEventRecorder {
  public:
   explicit AccessibilityEventRecorderAuraLinux(
       BrowserAccessibilityManager* manager,
-      base::ProcessId pid);
+      base::ProcessId pid,
+      const base::StringPiece& application_name_match_pattern);
   ~AccessibilityEventRecorderAuraLinux() override;
 
-  void ProcessEvent(const char* event,
-                    unsigned int n_params,
-                    const GValue* params);
+  void ProcessATKEvent(const char* event,
+                       unsigned int n_params,
+                       const GValue* params);
+  void ProcessATSPIEvent(const AtspiEvent* event);
 
  private:
-  void AddGlobalListener(const char* event_name);
-  void AddGlobalListeners();
-  void RemoveGlobalListeners();
+  bool ShouldUseATSPI();
+
+  void AddATKEventListener(const char* event_name);
+  void AddATKEventListeners();
+  void RemoveATKEventListeners();
   bool IncludeState(AtkStateType state_type);
 
-  std::vector<unsigned int> listener_ids_;
+  void AddATSPIEventListeners();
+  void RemoveATSPIEventListeners();
+
+  AtspiEventListener* atspi_event_listener_ = nullptr;
+  base::ProcessId pid_;
+  base::StringPiece application_name_match_pattern_;
+  std::vector<unsigned int> atk_listener_ids_;
 };
 
 // static
-gboolean OnEventReceived(GSignalInvocationHint* hint,
-                         unsigned int n_params,
-                         const GValue* params,
-                         gpointer data) {
+gboolean OnATKEventReceived(GSignalInvocationHint* hint,
+                            unsigned int n_params,
+                            const GValue* params,
+                            gpointer data) {
   GSignalQuery query;
   g_signal_query(hint->signal_id, &query);
 
   static_cast<AccessibilityEventRecorderAuraLinux&>(
       AccessibilityEventRecorder::GetInstance())
-      .ProcessEvent(query.signal_name, n_params, params);
+      .ProcessATKEvent(query.signal_name, n_params, params);
   return true;
 }
 
 // static
 AccessibilityEventRecorder& AccessibilityEventRecorder::GetInstance(
     BrowserAccessibilityManager* manager,
-    base::ProcessId pid) {
+    base::ProcessId pid,
+    const base::StringPiece& application_name_match_pattern) {
   static base::NoDestructor<AccessibilityEventRecorderAuraLinux> instance(
-      manager, pid);
+      manager, pid, application_name_match_pattern);
   return *instance;
 }
 
+bool AccessibilityEventRecorderAuraLinux::ShouldUseATSPI() {
+  return pid_ != base::GetCurrentProcId() ||
+         !application_name_match_pattern_.empty();
+}
+
 AccessibilityEventRecorderAuraLinux::AccessibilityEventRecorderAuraLinux(
     BrowserAccessibilityManager* manager,
-    base::ProcessId pid)
-    : AccessibilityEventRecorder(manager, pid) {
-  AddGlobalListeners();
+    base::ProcessId pid,
+    const base::StringPiece& application_name_match_pattern)
+    : AccessibilityEventRecorder(manager),
+      pid_(pid),
+      application_name_match_pattern_(application_name_match_pattern) {
+  if (ShouldUseATSPI())
+    AddATSPIEventListeners();
+  else
+    AddATKEventListeners();
 }
 
-AccessibilityEventRecorderAuraLinux::~AccessibilityEventRecorderAuraLinux() {}
+AccessibilityEventRecorderAuraLinux::~AccessibilityEventRecorderAuraLinux() {
+  RemoveATSPIEventListeners();
+}
 
-void AccessibilityEventRecorderAuraLinux::AddGlobalListener(
+void AccessibilityEventRecorderAuraLinux::AddATKEventListener(
     const char* event_name) {
-  unsigned id = atk_add_global_event_listener(OnEventReceived, event_name);
+  unsigned id = atk_add_global_event_listener(OnATKEventReceived, event_name);
   if (!id)
     LOG(FATAL) << "atk_add_global_event_listener failed for " << event_name;
 
-  listener_ids_.push_back(id);
+  atk_listener_ids_.push_back(id);
 }
 
-void AccessibilityEventRecorderAuraLinux::AddGlobalListeners() {
+void AccessibilityEventRecorderAuraLinux::AddATKEventListeners() {
   GObject* gobject = G_OBJECT(g_object_new(G_TYPE_OBJECT, nullptr, nullptr));
   g_object_unref(atk_no_op_object_new(gobject));
   g_object_unref(gobject);
 
-  AddGlobalListener("ATK:AtkObject:state-change");
-  AddGlobalListener("ATK:AtkObject:focus-event");
-  AddGlobalListener("ATK:AtkObject:property-change");
+  AddATKEventListener("ATK:AtkObject:state-change");
+  AddATKEventListener("ATK:AtkObject:focus-event");
+  AddATKEventListener("ATK:AtkObject:property-change");
 }
 
-void AccessibilityEventRecorderAuraLinux::RemoveGlobalListeners() {
-  for (const auto& id : listener_ids_)
+void AccessibilityEventRecorderAuraLinux::RemoveATKEventListeners() {
+  for (const auto& id : atk_listener_ids_)
     atk_remove_global_event_listener(id);
 
-  listener_ids_.clear();
+  atk_listener_ids_.clear();
 }
 
 // Pruning states which are not supported on older bots makes it possible to
@@ -114,12 +150,13 @@
   }
 }
 
-void AccessibilityEventRecorderAuraLinux::ProcessEvent(const char* event,
-                                                       unsigned int n_params,
-                                                       const GValue* params) {
+void AccessibilityEventRecorderAuraLinux::ProcessATKEvent(
+    const char* event,
+    unsigned int n_params,
+    const GValue* params) {
   // If we don't have a root object, it means the tree is being destroyed.
   if (!manager_->GetRoot()) {
-    RemoveGlobalListeners();
+    RemoveATKEventListeners();
     return;
   }
 
@@ -165,4 +202,180 @@
   OnEvent(log);
 }
 
+// This list is composed of the sorted event names taken from the list provided
+// in the libatspi documentation at:
+// https://developer.gnome.org/libatspi/stable/AtspiEventListener.html#atspi-event-listener-register
+const char* const kEventNames[] = {
+    "object:active-descendant-changed",
+    "object:children-changed",
+    "object:column-deleted",
+    "object:column-inserted",
+    "object:column-reordered",
+    "object:model-changed",
+    "object:property-change",
+    "object:property-change:accessible-description",
+    "object:property-change:accessible-name",
+    "object:property-change:accessible-parent",
+    "object:property-change:accessible-role",
+    "object:property-change:accessible-table-caption",
+    "object:property-change:accessible-table-column-description",
+    "object:property-change:accessible-table-column-header",
+    "object:property-change:accessible-table-row-description",
+    "object:property-change:accessible-table-row-header",
+    "object:property-change:accessible-table-summary",
+    "object:property-change:accessible-value",
+    "object:row-deleted",
+    "object:row-inserted",
+    "object:row-reordered",
+    "object:selection-changed",
+    "object:state-changed",
+    "object:text-caret-moved",
+    "object:text-changed",
+    "object:text-selection-changed",
+    "object:visible-data-changed",
+    "window:activate",
+    "window:close",
+    "window:create",
+    "window:deactivate",
+    "window:desktop-create",
+    "window:desktop-destroy",
+    "window:lower",
+    "window:maximize",
+    "window:minimize",
+    "window:move",
+    "window:raise",
+    "window:reparent",
+    "window:resize",
+    "window:restore",
+    "window:restyle",
+    "window:shade",
+    "window:unshade",
+};
+
+static void OnATSPIEventReceived(AtspiEvent* event, void* data) {
+  static_cast<AccessibilityEventRecorderAuraLinux*>(data)->ProcessATSPIEvent(
+      event);
+  g_boxed_free(ATSPI_TYPE_EVENT, static_cast<void*>(event));
+}
+
+void AccessibilityEventRecorderAuraLinux::AddATSPIEventListeners() {
+  atspi_init();
+  atspi_event_listener_ =
+      atspi_event_listener_new(OnATSPIEventReceived, this, nullptr);
+
+  GError* error = nullptr;
+  for (size_t i = 0; i < base::size(kEventNames); i++) {
+    atspi_event_listener_register(atspi_event_listener_, kEventNames[i],
+                                  &error);
+    if (error) {
+      LOG(ERROR) << "Could not register event listener for " << kEventNames[i];
+      g_clear_error(&error);
+    }
+  }
+}
+
+void AccessibilityEventRecorderAuraLinux::RemoveATSPIEventListeners() {
+  if (!atspi_event_listener_)
+    return;
+
+  GError* error = nullptr;
+  for (size_t i = 0; i < base::size(kEventNames); i++) {
+    atspi_event_listener_deregister(atspi_event_listener_, kEventNames[i],
+                                    nullptr);
+    if (error) {
+      LOG(ERROR) << "Could not deregister event listener for "
+                 << kEventNames[i];
+      g_clear_error(&error);
+    }
+  }
+
+  g_object_unref(atspi_event_listener_);
+  atspi_event_listener_ = nullptr;
+}
+
+void AccessibilityEventRecorderAuraLinux::ProcessATSPIEvent(
+    const AtspiEvent* event) {
+  GError* error = nullptr;
+
+  if (!application_name_match_pattern_.empty()) {
+    AtspiAccessible* application =
+        atspi_accessible_get_application(event->source, &error);
+    if (error || !application)
+      return;
+
+    char* application_name = atspi_accessible_get_name(application, &error);
+    g_object_unref(application);
+    if (error || !application_name) {
+      g_clear_error(&error);
+      return;
+    }
+
+    if (!base::MatchPattern(application_name,
+                            application_name_match_pattern_)) {
+      return;
+    }
+    free(application_name);
+  }
+
+  if (pid_) {
+    int pid = atspi_accessible_get_process_id(event->source, &error);
+    if (!error && pid != pid_)
+      return;
+    g_clear_error(&error);
+  }
+
+  std::stringstream output;
+  output << event->type << " ";
+
+  GHashTable* attributes =
+      atspi_accessible_get_attributes(event->source, &error);
+  std::string html_tag, html_class, html_id;
+  if (!error && attributes) {
+    if (char* tag = static_cast<char*>(g_hash_table_lookup(attributes, "tag")))
+      html_tag = tag;
+    if (char* id = static_cast<char*>(g_hash_table_lookup(attributes, "id")))
+      html_id = id;
+    if (char* class_chars =
+            static_cast<char*>(g_hash_table_lookup(attributes, "class")))
+      html_class = std::string(".") + class_chars;
+    g_hash_table_unref(attributes);
+  }
+  g_clear_error(&error);
+
+  if (!html_tag.empty())
+    output << "<" << html_tag << html_id << html_class << ">";
+
+  AtspiRole role = atspi_accessible_get_role(event->source, &error);
+  output << "role=";
+  if (!error)
+    output << ATSPIRoleToString(role);
+  else
+    output << "#error";
+  g_clear_error(&error);
+
+  char* name = atspi_accessible_get_name(event->source, &error);
+  output << " name=";
+  if (!error && name)
+    output << name;
+  else
+    output << "#error";
+  g_clear_error(&error);
+  free(name);
+
+  AtspiStateSet* atspi_states = atspi_accessible_get_state_set(event->source);
+  GArray* state_array = atspi_state_set_get_states(atspi_states);
+  std::vector<std::string> states;
+  for (unsigned i = 0; i < state_array->len; i++) {
+    AtspiStateType state_type = g_array_index(state_array, AtspiStateType, i);
+    states.push_back(ATSPIStateToString(state_type));
+  }
+  g_array_free(state_array, TRUE);
+  g_object_unref(atspi_states);
+  output << " ";
+  std::copy(states.begin(), states.end(),
+            std::ostream_iterator<std::string>(output, ", "));
+
+  OnEvent(output.str());
+}
+
 }  // namespace content
diff --git a/content/browser/accessibility/accessibility_event_recorder_mac.mm b/content/browser/accessibility/accessibility_event_recorder_mac.mm
index 0245a037..fa07508a 100644
--- a/content/browser/accessibility/accessibility_event_recorder_mac.mm
+++ b/content/browser/accessibility/accessibility_event_recorder_mac.mm
@@ -8,6 +8,7 @@
 
 #include <string>
 
+#include "base/logging.h"
 #include "base/mac/foundation_util.h"
 #include "base/mac/scoped_cftyperef.h"
 #include "base/strings/stringprintf.h"
@@ -63,7 +64,14 @@
 // static
 AccessibilityEventRecorder& AccessibilityEventRecorder::GetInstance(
     BrowserAccessibilityManager* manager,
-    base::ProcessId pid) {
+    base::ProcessId pid,
+    const base::StringPiece& application_name_match_pattern) {
+  if (!application_name_match_pattern.empty()) {
+    LOG(ERROR) << "Recording accessibility events from an application name "
+                  "match pattern not supported on this platform yet.";
+    NOTREACHED();
+  }
+
   static base::NoDestructor<AccessibilityEventRecorderMac> instance(manager,
                                                                     pid);
   return *instance;
@@ -72,8 +80,7 @@
 AccessibilityEventRecorderMac::AccessibilityEventRecorderMac(
     BrowserAccessibilityManager* manager,
     base::ProcessId pid)
-    : AccessibilityEventRecorder(manager, pid),
-      observer_run_loop_source_(NULL) {
+    : AccessibilityEventRecorder(manager), observer_run_loop_source_(NULL) {
   if (kAXErrorSuccess != AXObserverCreate(pid, EventReceivedThunk,
                                           observer_ref_.InitializeInto())) {
     LOG(FATAL) << "Failed to create AXObserverRef";
diff --git a/content/browser/accessibility/accessibility_event_recorder_win.cc b/content/browser/accessibility/accessibility_event_recorder_win.cc
index 07c26ad..ff9e5f9 100644
--- a/content/browser/accessibility/accessibility_event_recorder_win.cc
+++ b/content/browser/accessibility/accessibility_event_recorder_win.cc
@@ -87,8 +87,10 @@
                                          DWORD event_time);
 
  private:
-  AccessibilityEventRecorderWin(BrowserAccessibilityManager* manager,
-                                base::ProcessId pid);
+  AccessibilityEventRecorderWin(
+      BrowserAccessibilityManager* manager,
+      base::ProcessId pid,
+      const base::StringPiece& application_name_match_pattern);
 
   // Called by the thunk registered by SetWinEventHook. Retrieves accessibility
   // info about the node the event was fired on and appends a string to
@@ -115,9 +117,16 @@
 // static
 AccessibilityEventRecorder& AccessibilityEventRecorder::GetInstance(
     BrowserAccessibilityManager* manager,
-    base::ProcessId pid) {
-  static base::NoDestructor<AccessibilityEventRecorderWin> instance(manager,
-                                                                    pid);
+    base::ProcessId pid,
+    const base::StringPiece& application_name_match_pattern) {
+  if (!application_name_match_pattern.empty()) {
+    LOG(ERROR) << "Recording accessibility events from an application name "
+                  "match pattern not supported on this platform yet.";
+    NOTREACHED();
+  }
+
+  static base::NoDestructor<AccessibilityEventRecorderWin> instance(
+      manager, pid, application_name_match_pattern);
   return *instance;
 }
 
@@ -137,8 +146,9 @@
 
 AccessibilityEventRecorderWin::AccessibilityEventRecorderWin(
     BrowserAccessibilityManager* manager,
-    base::ProcessId pid)
-    : AccessibilityEventRecorder(manager, pid) {
+    base::ProcessId pid,
+    const base::StringPiece& application_name_match_pattern)
+    : AccessibilityEventRecorder(manager) {
   // For now, just use out of context events when running as a utility to watch
   // events (no BrowserAccessibilityManager), because otherwise Chrome events
   // are not getting reported. Being in context is better so that for
diff --git a/content/browser/accessibility/accessibility_tree_formatter_utils_auralinux.cc b/content/browser/accessibility/accessibility_tree_formatter_utils_auralinux.cc
index 5d754c4..2e54f10 100644
--- a/content/browser/accessibility/accessibility_tree_formatter_utils_auralinux.cc
+++ b/content/browser/accessibility/accessibility_tree_formatter_utils_auralinux.cc
@@ -82,4 +82,136 @@
                                     state);
 }
 
+CONTENT_EXPORT const char* ATSPIRoleToString(AtspiRole role) {
+  static const PlatformConstantToNameEntry role_table[] = {
+      QUOTE(ATSPI_ROLE_ACCELERATOR_LABEL),
+      QUOTE(ATSPI_ROLE_ALERT),
+      QUOTE(ATSPI_ROLE_ANIMATION),
+      QUOTE(ATSPI_ROLE_APPLICATION),
+      QUOTE(ATSPI_ROLE_ARROW),
+      QUOTE(ATSPI_ROLE_ARTICLE),
+      QUOTE(ATSPI_ROLE_AUDIO),
+      QUOTE(ATSPI_ROLE_AUTOCOMPLETE),
+      QUOTE(ATSPI_ROLE_BLOCK_QUOTE),
+      QUOTE(ATSPI_ROLE_CALENDAR),
+      QUOTE(ATSPI_ROLE_CANVAS),
+      QUOTE(ATSPI_ROLE_CAPTION),
+      QUOTE(ATSPI_ROLE_CHART),
+      QUOTE(ATSPI_ROLE_CHECK_BOX),
+      QUOTE(ATSPI_ROLE_CHECK_MENU_ITEM),
+      QUOTE(ATSPI_ROLE_COLOR_CHOOSER),
+      QUOTE(ATSPI_ROLE_COLUMN_HEADER),
+      QUOTE(ATSPI_ROLE_COMBO_BOX),
+      QUOTE(ATSPI_ROLE_COMMENT),
+      QUOTE(ATSPI_ROLE_DATE_EDITOR),
+      QUOTE(ATSPI_ROLE_DEFINITION),
+      QUOTE(ATSPI_ROLE_DESCRIPTION_LIST),
+      QUOTE(ATSPI_ROLE_DESCRIPTION_TERM),
+      QUOTE(ATSPI_ROLE_DESCRIPTION_VALUE),
+      QUOTE(ATSPI_ROLE_DESKTOP_FRAME),
+      QUOTE(ATSPI_ROLE_DESKTOP_ICON),
+      QUOTE(ATSPI_ROLE_DIAL),
+      QUOTE(ATSPI_ROLE_DIALOG),
+      QUOTE(ATSPI_ROLE_DIRECTORY_PANE),
+      QUOTE(ATSPI_ROLE_DOCUMENT_EMAIL),
+      QUOTE(ATSPI_ROLE_DOCUMENT_FRAME),
+      QUOTE(ATSPI_ROLE_DOCUMENT_PRESENTATION),
+      QUOTE(ATSPI_ROLE_DOCUMENT_SPREADSHEET),
+      QUOTE(ATSPI_ROLE_DOCUMENT_TEXT),
+      QUOTE(ATSPI_ROLE_DOCUMENT_WEB),
+      QUOTE(ATSPI_ROLE_DRAWING_AREA),
+      QUOTE(ATSPI_ROLE_EDITBAR),
+      QUOTE(ATSPI_ROLE_EMBEDDED),
+      QUOTE(ATSPI_ROLE_ENTRY),
+      QUOTE(ATSPI_ROLE_EXTENDED),
+      QUOTE(ATSPI_ROLE_FILE_CHOOSER),
+      QUOTE(ATSPI_ROLE_FILLER),
+      QUOTE(ATSPI_ROLE_FOCUS_TRAVERSABLE),
+      QUOTE(ATSPI_ROLE_FONT_CHOOSER),
+      QUOTE(ATSPI_ROLE_FOOTER),
+      QUOTE(ATSPI_ROLE_FOOTNOTE),
+      QUOTE(ATSPI_ROLE_FORM),
+      QUOTE(ATSPI_ROLE_FRAME),
+      QUOTE(ATSPI_ROLE_GLASS_PANE),
+      QUOTE(ATSPI_ROLE_GROUPING),
+      QUOTE(ATSPI_ROLE_HEADER),
+      QUOTE(ATSPI_ROLE_HEADING),
+      QUOTE(ATSPI_ROLE_HTML_CONTAINER),
+      QUOTE(ATSPI_ROLE_ICON),
+      QUOTE(ATSPI_ROLE_IMAGE),
+      QUOTE(ATSPI_ROLE_IMAGE_MAP),
+      QUOTE(ATSPI_ROLE_INFO_BAR),
+      QUOTE(ATSPI_ROLE_INPUT_METHOD_WINDOW),
+      QUOTE(ATSPI_ROLE_INTERNAL_FRAME),
+      QUOTE(ATSPI_ROLE_INVALID),
+      QUOTE(ATSPI_ROLE_LABEL),
+      QUOTE(ATSPI_ROLE_LANDMARK),
+      QUOTE(ATSPI_ROLE_LAYERED_PANE),
+      QUOTE(ATSPI_ROLE_LEVEL_BAR),
+      QUOTE(ATSPI_ROLE_LINK),
+      QUOTE(ATSPI_ROLE_LIST),
+      QUOTE(ATSPI_ROLE_LIST_BOX),
+      QUOTE(ATSPI_ROLE_LIST_ITEM),
+      QUOTE(ATSPI_ROLE_LOG),
+      QUOTE(ATSPI_ROLE_MARQUEE),
+      QUOTE(ATSPI_ROLE_MATH),
+      QUOTE(ATSPI_ROLE_MATH_FRACTION),
+      QUOTE(ATSPI_ROLE_MATH_ROOT),
+      QUOTE(ATSPI_ROLE_MENU),
+      QUOTE(ATSPI_ROLE_MENU_BAR),
+      QUOTE(ATSPI_ROLE_MENU_ITEM),
+      QUOTE(ATSPI_ROLE_NOTIFICATION),
+      QUOTE(ATSPI_ROLE_OPTION_PANE),
+      QUOTE(ATSPI_ROLE_PAGE),
+      QUOTE(ATSPI_ROLE_PAGE_TAB),
+      QUOTE(ATSPI_ROLE_PAGE_TAB_LIST),
+      QUOTE(ATSPI_ROLE_PANEL),
+      QUOTE(ATSPI_ROLE_PARAGRAPH),
+      QUOTE(ATSPI_ROLE_PASSWORD_TEXT),
+      QUOTE(ATSPI_ROLE_POPUP_MENU),
+      QUOTE(ATSPI_ROLE_PROGRESS_BAR),
+      QUOTE(ATSPI_ROLE_PUSH_BUTTON),
+      QUOTE(ATSPI_ROLE_RADIO_BUTTON),
+      QUOTE(ATSPI_ROLE_RADIO_MENU_ITEM),
+      QUOTE(ATSPI_ROLE_RATING),
+      QUOTE(ATSPI_ROLE_REDUNDANT_OBJECT),
+      QUOTE(ATSPI_ROLE_ROOT_PANE),
+      QUOTE(ATSPI_ROLE_ROW_HEADER),
+      QUOTE(ATSPI_ROLE_RULER),
+      QUOTE(ATSPI_ROLE_SCROLL_BAR),
+      QUOTE(ATSPI_ROLE_SCROLL_PANE),
+      QUOTE(ATSPI_ROLE_SECTION),
+      QUOTE(ATSPI_ROLE_SEPARATOR),
+      QUOTE(ATSPI_ROLE_SLIDER),
+      QUOTE(ATSPI_ROLE_SPIN_BUTTON),
+      QUOTE(ATSPI_ROLE_SPLIT_PANE),
+      QUOTE(ATSPI_ROLE_STATIC),
+      QUOTE(ATSPI_ROLE_STATUS_BAR),
+      QUOTE(ATSPI_ROLE_SUBSCRIPT),
+      QUOTE(ATSPI_ROLE_SUPERSCRIPT),
+      QUOTE(ATSPI_ROLE_TABLE),
+      QUOTE(ATSPI_ROLE_TABLE_CELL),
+      QUOTE(ATSPI_ROLE_TABLE_COLUMN_HEADER),
+      QUOTE(ATSPI_ROLE_TABLE_ROW),
+      QUOTE(ATSPI_ROLE_TABLE_ROW_HEADER),
+      QUOTE(ATSPI_ROLE_TEAROFF_MENU_ITEM),
+      QUOTE(ATSPI_ROLE_TERMINAL),
+      QUOTE(ATSPI_ROLE_TEXT),
+      QUOTE(ATSPI_ROLE_TIMER),
+      QUOTE(ATSPI_ROLE_TITLE_BAR),
+      QUOTE(ATSPI_ROLE_TOGGLE_BUTTON),
+      QUOTE(ATSPI_ROLE_TOOL_BAR),
+      QUOTE(ATSPI_ROLE_TOOL_TIP),
+      QUOTE(ATSPI_ROLE_TREE),
+      QUOTE(ATSPI_ROLE_TREE_ITEM),
+      QUOTE(ATSPI_ROLE_TREE_TABLE),
+      QUOTE(ATSPI_ROLE_UNKNOWN),
+      QUOTE(ATSPI_ROLE_VIDEO),
+      QUOTE(ATSPI_ROLE_VIEWPORT),
+      QUOTE(ATSPI_ROLE_WINDOW),
+  };
+
+  return GetNameForPlatformConstant(role_table, base::size(role_table), role);
+}
+
 }  // namespace content
diff --git a/content/browser/accessibility/accessibility_tree_formatter_utils_auralinux.h b/content/browser/accessibility/accessibility_tree_formatter_utils_auralinux.h
index 3877ce82..29dad1d 100644
--- a/content/browser/accessibility/accessibility_tree_formatter_utils_auralinux.h
+++ b/content/browser/accessibility/accessibility_tree_formatter_utils_auralinux.h
@@ -12,6 +12,7 @@
 namespace content {
 
 CONTENT_EXPORT const char* ATSPIStateToString(AtspiStateType state);
+CONTENT_EXPORT const char* ATSPIRoleToString(AtspiRole role);
 
 }  // namespace content
 
diff --git a/content/browser/accessibility/browser_accessibility_auralinux_unittest.cc b/content/browser/accessibility/browser_accessibility_auralinux_unittest.cc
index 3d31036a..dfe6170 100644
--- a/content/browser/accessibility/browser_accessibility_auralinux_unittest.cc
+++ b/content/browser/accessibility/browser_accessibility_auralinux_unittest.cc
@@ -113,6 +113,157 @@
   EXPECT_STREQ((text1_name + text2_name).c_str(), text);
   g_free(text);
 
+  ASSERT_TRUE(ATK_IS_HYPERTEXT(root_atk_object));
+  AtkHypertext* atk_hypertext = ATK_HYPERTEXT(root_atk_object);
+
+  // There should be no hyperlinks in the node and trying to get one should
+  // always return -1.
+  EXPECT_EQ(0, atk_hypertext_get_n_links(atk_hypertext));
+  EXPECT_EQ(-1, atk_hypertext_get_link_index(atk_hypertext, 0));
+  EXPECT_EQ(-1, atk_hypertext_get_link_index(atk_hypertext, -1));
+  EXPECT_EQ(-1, atk_hypertext_get_link_index(atk_hypertext, 1));
+
+  g_object_unref(root_atk_object);
+
+  manager.reset();
+}
+
+TEST_F(BrowserAccessibilityAuraLinuxTest, TestComplexHypertext) {
+  const std::string text1_name = "One two three.";
+  const std::string combo_box_name = "City:";
+  const std::string combo_box_value = "Happyland";
+  const std::string text2_name = " Four five six.";
+  const std::string check_box_name = "I agree";
+  const std::string check_box_value = "Checked";
+  const std::string button_text_name = "Red";
+  const std::string link_text_name = "Blue";
+  // Each control (combo / check box, button and link) will be represented by an
+  // embedded object character.
+  const base::string16 string16_embed(
+      1, ui::AXPlatformNodeAuraLinux::kEmbeddedCharacter);
+  const std::string embed = base::UTF16ToUTF8(string16_embed);
+  const std::string root_hypertext =
+      text1_name + embed + text2_name + embed + embed + embed;
+
+  ui::AXNodeData text1;
+  text1.id = 11;
+  text1.role = ax::mojom::Role::kStaticText;
+  text1.SetName(text1_name);
+
+  ui::AXNodeData combo_box;
+  combo_box.id = 12;
+  combo_box.role = ax::mojom::Role::kTextFieldWithComboBox;
+  combo_box.AddState(ax::mojom::State::kEditable);
+  combo_box.SetName(combo_box_name);
+  combo_box.SetValue(combo_box_value);
+
+  ui::AXNodeData text2;
+  text2.id = 13;
+  text2.role = ax::mojom::Role::kStaticText;
+  text2.SetName(text2_name);
+
+  ui::AXNodeData check_box;
+  check_box.id = 14;
+  check_box.role = ax::mojom::Role::kCheckBox;
+  check_box.SetCheckedState(ax::mojom::CheckedState::kTrue);
+  check_box.SetName(check_box_name);
+  check_box.SetValue(check_box_value);
+
+  ui::AXNodeData button, button_text;
+  button.id = 15;
+  button_text.id = 17;
+  button_text.SetName(button_text_name);
+  button.role = ax::mojom::Role::kButton;
+  button_text.role = ax::mojom::Role::kStaticText;
+  button.child_ids.push_back(button_text.id);
+
+  ui::AXNodeData link, link_text;
+  link.id = 16;
+  link_text.id = 18;
+  link_text.SetName(link_text_name);
+  link.role = ax::mojom::Role::kLink;
+  link_text.role = ax::mojom::Role::kStaticText;
+  link.child_ids.push_back(link_text.id);
+
+  ui::AXNodeData root;
+  root.id = 1;
+  root.role = ax::mojom::Role::kRootWebArea;
+  root.child_ids.push_back(text1.id);
+  root.child_ids.push_back(combo_box.id);
+  root.child_ids.push_back(text2.id);
+  root.child_ids.push_back(check_box.id);
+  root.child_ids.push_back(button.id);
+  root.child_ids.push_back(link.id);
+
+  std::unique_ptr<BrowserAccessibilityManager> manager(
+      BrowserAccessibilityManager::Create(
+          MakeAXTreeUpdate(root, text1, combo_box, text2, check_box, button,
+                           button_text, link, link_text),
+          nullptr, new BrowserAccessibilityFactory()));
+
+  ui::AXPlatformNodeAuraLinux* root_obj =
+      ToBrowserAccessibilityAuraLinux(manager->GetRoot())->GetNode();
+  AtkObject* root_atk_object(root_obj->GetNativeViewAccessible());
+
+  ASSERT_TRUE(ATK_IS_OBJECT(root_atk_object));
+  ASSERT_TRUE(ATK_IS_TEXT(root_atk_object));
+  g_object_ref(root_atk_object);
+  AtkText* atk_text = ATK_TEXT(root_atk_object);
+
+  EXPECT_EQ(g_utf8_strlen(root_hypertext.c_str(), -1),
+            atk_text_get_character_count(atk_text));
+
+  gchar* text = atk_text_get_text(atk_text, 0, -1);
+  EXPECT_STREQ(root_hypertext.c_str(), text);
+  g_free(text);
+
+  ASSERT_TRUE(ATK_IS_HYPERTEXT(root_atk_object));
+  AtkHypertext* atk_hypertext = ATK_HYPERTEXT(root_atk_object);
+
+  EXPECT_EQ(4, atk_hypertext_get_n_links(atk_hypertext));
+
+  auto verify_atk_link_text = [&](const char* expected_text, int link_index) {
+    AtkHyperlink* link = atk_hypertext_get_link(atk_hypertext, link_index);
+    ASSERT_NE(nullptr, link);
+    ASSERT_TRUE(ATK_IS_HYPERLINK(link));
+
+    AtkObject* object = atk_hyperlink_get_object(link, 0);
+    ASSERT_TRUE(ATK_IS_TEXT(object));
+
+    char* text = atk_text_get_text(ATK_TEXT(object), 0, -1);
+    EXPECT_STREQ(expected_text, text);
+    g_free(text);
+  };
+
+  AtkHyperlink* combo_box_link = atk_hypertext_get_link(atk_hypertext, 0);
+  ASSERT_NE(nullptr, combo_box_link);
+  ASSERT_TRUE(ATK_IS_HYPERLINK(combo_box_link));
+
+  // Get the text of the combo box. It should be its value.
+  verify_atk_link_text(combo_box_value.c_str(), 0);
+
+  // Get the text of the check box. It should be its name.
+  verify_atk_link_text(check_box_name.c_str(), 1);
+
+  // Get the text of the button.
+  verify_atk_link_text(button_text_name.c_str(), 2);
+
+  // Get the text of the link.
+  verify_atk_link_text(link_text_name.c_str(), 3);
+
+  // Now test that all the object indices map back to the correct link indices.
+  EXPECT_EQ(-1, atk_hypertext_get_link_index(atk_hypertext, -1));
+  EXPECT_EQ(-1, atk_hypertext_get_link_index(atk_hypertext, 0));
+  EXPECT_EQ(-1, atk_hypertext_get_link_index(atk_hypertext, 1));
+  EXPECT_EQ(-1, atk_hypertext_get_link_index(atk_hypertext, 5));
+  EXPECT_EQ(-1, atk_hypertext_get_link_index(atk_hypertext, 13));
+  EXPECT_EQ(0, atk_hypertext_get_link_index(atk_hypertext, 14));
+  EXPECT_EQ(1, atk_hypertext_get_link_index(atk_hypertext, 30));
+  EXPECT_EQ(2, atk_hypertext_get_link_index(atk_hypertext, 31));
+  EXPECT_EQ(3, atk_hypertext_get_link_index(atk_hypertext, 32));
+  EXPECT_EQ(-1, atk_hypertext_get_link_index(atk_hypertext, 33));
+  EXPECT_EQ(-1, atk_hypertext_get_link_index(atk_hypertext, 34));
+
   g_object_unref(root_atk_object);
 
   manager.reset();
diff --git a/content/browser/browser_plugin/browser_plugin_guest.cc b/content/browser/browser_plugin/browser_plugin_guest.cc
index 410a7378..fbff8d9 100644
--- a/content/browser/browser_plugin/browser_plugin_guest.cc
+++ b/content/browser/browser_plugin/browser_plugin_guest.cc
@@ -1130,10 +1130,11 @@
 }
 #endif
 
-void BrowserPluginGuest::OnShowWidget(int route_id,
+void BrowserPluginGuest::OnShowWidget(int widget_route_id,
                                       const gfx::Rect& initial_rect) {
   int process_id = GetWebContents()->GetMainFrame()->GetProcess()->GetID();
-  GetWebContents()->ShowCreatedWidget(process_id, route_id, initial_rect);
+  GetWebContents()->ShowCreatedWidget(process_id, widget_route_id,
+                                      initial_rect);
 }
 
 void BrowserPluginGuest::OnTakeFocus(bool reverse) {
diff --git a/content/browser/browser_plugin/browser_plugin_guest.h b/content/browser/browser_plugin/browser_plugin_guest.h
index 54c90d3..4dad9c9 100644
--- a/content/browser/browser_plugin/browser_plugin_guest.h
+++ b/content/browser/browser_plugin/browser_plugin_guest.h
@@ -358,7 +358,7 @@
   void OnShowPopup(RenderFrameHost* render_frame_host,
                    const FrameHostMsg_ShowPopup_Params& params);
 #endif
-  void OnShowWidget(int route_id, const gfx::Rect& initial_rect);
+  void OnShowWidget(int widget_route_id, const gfx::Rect& initial_rect);
   void OnTakeFocus(bool reverse);
   void OnUpdateFrameName(int frame_id,
                          bool is_top_level,
diff --git a/content/browser/compositor/gpu_process_transport_factory.cc b/content/browser/compositor/gpu_process_transport_factory.cc
index 642fff3..8744b057 100644
--- a/content/browser/compositor/gpu_process_transport_factory.cc
+++ b/content/browser/compositor/gpu_process_transport_factory.cc
@@ -841,18 +841,6 @@
     data->display->SetColorSpace(blending_color_space, output_color_space);
 }
 
-void GpuProcessTransportFactory::SetAuthoritativeVSyncInterval(
-    ui::Compositor* compositor,
-    base::TimeDelta interval) {
-  PerCompositorDataMap::iterator it = per_compositor_data_.find(compositor);
-  if (it == per_compositor_data_.end())
-    return;
-  PerCompositorData* data = it->second.get();
-  DCHECK(data);
-  if (data->synthetic_begin_frame_source)
-    data->synthetic_begin_frame_source->SetAuthoritativeVSyncInterval(interval);
-}
-
 void GpuProcessTransportFactory::SetDisplayVSyncParameters(
     ui::Compositor* compositor,
     base::TimeTicks timebase,
diff --git a/content/browser/compositor/gpu_process_transport_factory.h b/content/browser/compositor/gpu_process_transport_factory.h
index 08384de8..b9f2cf3 100644
--- a/content/browser/compositor/gpu_process_transport_factory.h
+++ b/content/browser/compositor/gpu_process_transport_factory.h
@@ -96,8 +96,6 @@
   void SetDisplayColorSpace(ui::Compositor* compositor,
                             const gfx::ColorSpace& blending_color_space,
                             const gfx::ColorSpace& output_color_space) override;
-  void SetAuthoritativeVSyncInterval(ui::Compositor* compositor,
-                                     base::TimeDelta interval) override;
   void SetDisplayVSyncParameters(ui::Compositor* compositor,
                                  base::TimeTicks timebase,
                                  base::TimeDelta interval) override;
diff --git a/content/browser/compositor/test/test_image_transport_factory.h b/content/browser/compositor/test/test_image_transport_factory.h
index b065cd5..e347dc4 100644
--- a/content/browser/compositor/test/test_image_transport_factory.h
+++ b/content/browser/compositor/test/test_image_transport_factory.h
@@ -71,8 +71,6 @@
       ui::Compositor* compositor,
       const gfx::ColorSpace& blending_color_space,
       const gfx::ColorSpace& output_color_space) override {}
-  void SetAuthoritativeVSyncInterval(ui::Compositor* compositor,
-                                     base::TimeDelta interval) override {}
   void SetDisplayVSyncParameters(ui::Compositor* compositor,
                                  base::TimeTicks timebase,
                                  base::TimeDelta interval) override {}
diff --git a/content/browser/devtools/devtools_video_consumer_unittest.cc b/content/browser/devtools/devtools_video_consumer_unittest.cc
index cbad616..f2f45b3 100644
--- a/content/browser/devtools/devtools_video_consumer_unittest.cc
+++ b/content/browser/devtools/devtools_video_consumer_unittest.cc
@@ -181,7 +181,8 @@
 
     media::mojom::VideoFrameInfoPtr info = media::mojom::VideoFrameInfo::New(
         base::TimeDelta(), base::Value(base::Value::Type::DICTIONARY), kFormat,
-        kResolution, gfx::Rect(kResolution), gfx::ColorSpace::CreateREC709());
+        kResolution, gfx::Rect(kResolution), gfx::ColorSpace::CreateREC709(),
+        nullptr);
 
     consumer_->OnFrameCaptured(std::move(data), std::move(info),
                                gfx::Rect(kResolution), gfx::Rect(kResolution),
diff --git a/content/browser/media/android/media_resource_getter_impl.cc b/content/browser/media/android/media_resource_getter_impl.cc
index 74a94dfa..cab2d99 100644
--- a/content/browser/media/android/media_resource_getter_impl.cc
+++ b/content/browser/media/android/media_resource_getter_impl.cc
@@ -141,6 +141,11 @@
 net::AuthCredentials MediaResourceGetterTask::RequestAuthCredentials(
     const GURL& url) const {
   DCHECK(GetTaskRunner()->BelongsToCurrentThread());
+  if (!url.IsStandard()) {
+    // Non-standard URLs, such as data, will not be found in HTTP auth cache
+    // anyway, because they have no valid origin, so don't waste the time.
+    return net::AuthCredentials();
+  }
   net::HttpTransactionFactory* factory =
       context_getter_->GetURLRequestContext()->http_transaction_factory();
   if (!factory)
diff --git a/content/browser/media/capture/frame_sink_video_capture_device_unittest.cc b/content/browser/media/capture/frame_sink_video_capture_device_unittest.cc
index 0abbde3..dc637df7 100644
--- a/content/browser/media/capture/frame_sink_video_capture_device_unittest.cc
+++ b/content/browser/media/capture/frame_sink_video_capture_device_unittest.cc
@@ -372,7 +372,7 @@
                   kMinCapturePeriod * frame_number,
                   base::Value(base::Value::Type::DICTIONARY), kFormat,
                   kResolution, gfx::Rect(kResolution),
-                  gfx::ColorSpace::CreateREC709()),
+                  gfx::ColorSpace::CreateREC709(), nullptr),
               gfx::Rect(kResolution), gfx::Rect(kResolution),
               viz::mojom::FrameSinkVideoConsumerFrameCallbacksPtr(
                   std::move(callbacks_info)));
diff --git a/content/browser/memory/memory_monitor_win.cc b/content/browser/memory/memory_monitor_win.cc
index bf336c1..979bb28 100644
--- a/content/browser/memory/memory_monitor_win.cc
+++ b/content/browser/memory/memory_monitor_win.cc
@@ -15,10 +15,6 @@
 
 const int kKBperMB = 1024;
 
-// A global static instance of the default delegate. Used by default by
-// MemoryMonitorWin.
-MemoryMonitorDelegate g_memory_monitor_win_delegate;
-
 }  // namespace
 
 // A system is considered 'large memory' if it has more than 1.5GB of system
@@ -73,7 +69,7 @@
 
 // Implementation of factory function defined in memory_monitor.h.
 std::unique_ptr<MemoryMonitor> CreateMemoryMonitor() {
-  return MemoryMonitorWin::Create(&g_memory_monitor_win_delegate);
+  return MemoryMonitorWin::Create(MemoryMonitorDelegate::GetInstance());
 }
 
 }  // namespace content
diff --git a/content/browser/net/quota_policy_cookie_store.cc b/content/browser/net/quota_policy_cookie_store.cc
index ecf284f..d099db4 100644
--- a/content/browser/net/quota_policy_cookie_store.cc
+++ b/content/browser/net/quota_policy_cookie_store.cc
@@ -88,7 +88,7 @@
 
     if (!background_task_runner.get()) {
       background_task_runner = base::CreateSequencedTaskRunnerWithTraits(
-          {base::MayBlock(), base::TaskPriority::BEST_EFFORT,
+          {base::MayBlock(), net::GetCookieStoreBackgroundSequencePriority(),
            base::TaskShutdownBehavior::BLOCK_SHUTDOWN});
     }
 
diff --git a/content/browser/renderer_host/delegated_frame_host.cc b/content/browser/renderer_host/delegated_frame_host.cc
index 3f10204a..001bfc90 100644
--- a/content/browser/renderer_host/delegated_frame_host.cc
+++ b/content/browser/renderer_host/delegated_frame_host.cc
@@ -412,14 +412,6 @@
 void DelegatedFrameHost::OnLostSharedContext() {}
 
 void DelegatedFrameHost::OnLostVizProcess() {
-  // With OOP-D renderer surface was destroyed if the GPU process crashed. Reset
-  // the fallback Surface but leave the primary so we embed the renderer surface
-  // again.
-  if (HasFallbackSurface()) {
-    client_->DelegatedFrameHostGetLayer()->SetFallbackSurfaceId(
-        viz::SurfaceId());
-  }
-
   if (HasSavedFrame())
     frame_evictor_->DiscardedFrame();
 }
diff --git a/content/browser/renderer_host/media/video_capture_browsertest.cc b/content/browser/renderer_host/media/video_capture_browsertest.cc
index 7306b1e..547e7b5 100644
--- a/content/browser/renderer_host/media/video_capture_browsertest.cc
+++ b/content/browser/renderer_host/media/video_capture_browsertest.cc
@@ -35,10 +35,9 @@
 class MockVideoCaptureControllerEventHandler
     : public VideoCaptureControllerEventHandler {
  public:
-  MOCK_METHOD4(DoOnNewBuffer,
+  MOCK_METHOD3(DoOnNewBuffer,
                void(VideoCaptureControllerID id,
                     media::mojom::VideoBufferHandlePtr* buffer_handle,
-                    int length,
                     int buffer_id));
   MOCK_METHOD2(OnBufferDestroyed,
                void(VideoCaptureControllerID, int buffer_id));
@@ -55,9 +54,8 @@
 
   void OnNewBuffer(VideoCaptureControllerID id,
                    media::mojom::VideoBufferHandlePtr buffer_handle,
-                   int length,
                    int buffer_id) override {
-    DoOnNewBuffer(id, &buffer_handle, length, buffer_id);
+    DoOnNewBuffer(id, &buffer_handle, buffer_id);
   }
 };
 
@@ -305,7 +303,7 @@
           must_wait_for_gpu_decode_to_start = false;
         }));
   }
-  EXPECT_CALL(mock_controller_event_handler_, DoOnNewBuffer(_, _, _, _))
+  EXPECT_CALL(mock_controller_event_handler_, DoOnNewBuffer(_, _, _))
       .Times(AtLeast(1));
   EXPECT_CALL(mock_controller_event_handler_, OnBufferReady(_, _, _))
       .WillRepeatedly(Invoke(
diff --git a/content/browser/renderer_host/media/video_capture_buffer_pool_unittest.cc b/content/browser/renderer_host/media/video_capture_buffer_pool_unittest.cc
index eb50966..32a3cbd 100644
--- a/content/browser/renderer_host/media/video_capture_buffer_pool_unittest.cc
+++ b/content/browser/renderer_host/media/video_capture_buffer_pool_unittest.cc
@@ -77,9 +77,9 @@
     DVLOG(1) << media::VideoPixelFormatToString(pixel_format) << " "
              << dimensions.ToString();
     const int arbitrary_frame_feedback_id = 0;
-    const int buffer_id = pool_->ReserveForProducer(dimensions, pixel_format,
-                                                    arbitrary_frame_feedback_id,
-                                                    &buffer_id_to_drop);
+    const int buffer_id = pool_->ReserveForProducer(
+        dimensions, pixel_format, nullptr, arbitrary_frame_feedback_id,
+        &buffer_id_to_drop);
     if (buffer_id == media::VideoCaptureBufferPool::kInvalidId)
       return std::unique_ptr<Buffer>();
     EXPECT_EQ(expected_dropped_id_, buffer_id_to_drop);
diff --git a/content/browser/renderer_host/media/video_capture_controller.cc b/content/browser/renderer_host/media/video_capture_controller.cc
index 3eb1cbac..0602d0a0 100644
--- a/content/browser/renderer_host/media/video_capture_controller.cc
+++ b/content/browser/renderer_host/media/video_capture_controller.cc
@@ -511,13 +511,9 @@
       if (!base::ContainsValue(client->known_buffer_context_ids,
                                buffer_context_id)) {
         client->known_buffer_context_ids.push_back(buffer_context_id);
-        const size_t mapped_size =
-            media::VideoCaptureFormat(frame_info->coded_size, 0.0f,
-                                      frame_info->pixel_format)
-                .ImageAllocationSize();
         client->event_handler->OnNewBuffer(
             client->controller_id, buffer_context_iter->CloneBufferHandle(),
-            mapped_size, buffer_context_id);
+            buffer_context_id);
       }
 
       if (!base::ContainsValue(client->buffers_in_use, buffer_context_id))
diff --git a/content/browser/renderer_host/media/video_capture_controller_event_handler.h b/content/browser/renderer_host/media/video_capture_controller_event_handler.h
index a2564af..8c849cbc 100644
--- a/content/browser/renderer_host/media/video_capture_controller_event_handler.h
+++ b/content/browser/renderer_host/media/video_capture_controller_event_handler.h
@@ -38,7 +38,6 @@
 
   virtual void OnNewBuffer(VideoCaptureControllerID id,
                            media::mojom::VideoBufferHandlePtr buffer_handle,
-                           int length,
                            int buffer_id) = 0;
 
   // A previously created buffer has been freed and will no longer be used.
diff --git a/content/browser/renderer_host/media/video_capture_controller_unittest.cc b/content/browser/renderer_host/media/video_capture_controller_unittest.cc
index 38ab4b8..930a450 100644
--- a/content/browser/renderer_host/media/video_capture_controller_unittest.cc
+++ b/content/browser/renderer_host/media/video_capture_controller_unittest.cc
@@ -75,7 +75,6 @@
   }
   void OnNewBuffer(VideoCaptureControllerID id,
                    media::mojom::VideoBufferHandlePtr buffer_handle,
-                   int length,
                    int buffer_id) override {
     DoBufferCreated(id, buffer_id);
   }
diff --git a/content/browser/renderer_host/media/video_capture_host.cc b/content/browser/renderer_host/media/video_capture_host.cc
index d35e6a28..d9c05530 100644
--- a/content/browser/renderer_host/media/video_capture_host.cc
+++ b/content/browser/renderer_host/media/video_capture_host.cc
@@ -118,7 +118,6 @@
 void VideoCaptureHost::OnNewBuffer(
     VideoCaptureControllerID controller_id,
     media::mojom::VideoBufferHandlePtr buffer_handle,
-    int length,
     int buffer_id) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
   if (controllers_.find(controller_id) == controllers_.end())
diff --git a/content/browser/renderer_host/media/video_capture_host.h b/content/browser/renderer_host/media/video_capture_host.h
index 5fdecfe6c..afaf18d 100644
--- a/content/browser/renderer_host/media/video_capture_host.h
+++ b/content/browser/renderer_host/media/video_capture_host.h
@@ -57,7 +57,6 @@
                media::VideoCaptureError error) override;
   void OnNewBuffer(VideoCaptureControllerID id,
                    media::mojom::VideoBufferHandlePtr buffer_handle,
-                   int length,
                    int buffer_id) override;
   void OnBufferDestroyed(VideoCaptureControllerID id,
                          int buffer_id) override;
diff --git a/content/browser/renderer_host/media/video_capture_manager_unittest.cc b/content/browser/renderer_host/media/video_capture_manager_unittest.cc
index ce592438..ca47e6d8 100644
--- a/content/browser/renderer_host/media/video_capture_manager_unittest.cc
+++ b/content/browser/renderer_host/media/video_capture_manager_unittest.cc
@@ -160,7 +160,6 @@
 
   void OnNewBuffer(VideoCaptureControllerID id,
                    media::mojom::VideoBufferHandlePtr buffer_handle,
-                   int length,
                    int buffer_id) override {}
   void OnBufferDestroyed(VideoCaptureControllerID id, int buffer_id) override {}
   void OnBufferReady(
diff --git a/content/browser/renderer_host/render_view_host_delegate.h b/content/browser/renderer_host/render_view_host_delegate.h
index 4c5332e5..f529058 100644
--- a/content/browser/renderer_host/render_view_host_delegate.h
+++ b/content/browser/renderer_host/render_view_host_delegate.h
@@ -125,22 +125,23 @@
   // process |render_process_id|, but it should not be shown yet. That should
   // happen in response to ShowCreatedWidget.
   virtual void CreateNewWidget(int32_t render_process_id,
-                               int32_t route_id,
+                               int32_t widget_route_id,
                                mojom::WidgetPtr widget) {}
 
   // Creates a full screen RenderWidget. Similar to above.
   virtual void CreateNewFullscreenWidget(int32_t render_process_id,
-                                         int32_t route_id,
+                                         int32_t widget_route_id,
                                          mojom::WidgetPtr widget) {}
 
   // Show the newly created widget with the specified bounds.
   // The widget is identified by the route_id passed to CreateNewWidget.
   virtual void ShowCreatedWidget(int process_id,
-                                 int route_id,
+                                 int widget_route_id,
                                  const gfx::Rect& initial_rect) {}
 
   // Show the newly created full screen widget. Similar to above.
-  virtual void ShowCreatedFullscreenWidget(int process_id, int route_id) {}
+  virtual void ShowCreatedFullscreenWidget(int process_id,
+                                           int widget_route_id) {}
 
   // Returns the SessionStorageNamespace the render view should use. Might
   // create the SessionStorageNamespace on the fly.
diff --git a/content/browser/renderer_host/render_view_host_impl.cc b/content/browser/renderer_host/render_view_host_impl.cc
index 0b1d1ec..1e913d80 100644
--- a/content/browser/renderer_host/render_view_host_impl.cc
+++ b/content/browser/renderer_host/render_view_host_impl.cc
@@ -811,27 +811,29 @@
   delete this;
 }
 
-void RenderViewHostImpl::CreateNewWidget(int32_t route_id,
+void RenderViewHostImpl::CreateNewWidget(int32_t widget_route_id,
                                          mojom::WidgetPtr widget) {
-  delegate_->CreateNewWidget(GetProcess()->GetID(), route_id,
+  delegate_->CreateNewWidget(GetProcess()->GetID(), widget_route_id,
                              std::move(widget));
 }
 
-void RenderViewHostImpl::CreateNewFullscreenWidget(int32_t route_id,
+void RenderViewHostImpl::CreateNewFullscreenWidget(int32_t widget_route_id,
                                                    mojom::WidgetPtr widget) {
-  delegate_->CreateNewFullscreenWidget(GetProcess()->GetID(), route_id,
+  delegate_->CreateNewFullscreenWidget(GetProcess()->GetID(), widget_route_id,
                                        std::move(widget));
 }
 
-void RenderViewHostImpl::OnShowWidget(int route_id,
+void RenderViewHostImpl::OnShowWidget(int widget_route_id,
                                       const gfx::Rect& initial_rect) {
-  delegate_->ShowCreatedWidget(GetProcess()->GetID(), route_id, initial_rect);
-  Send(new ViewMsg_SetBounds_ACK(route_id));
+  delegate_->ShowCreatedWidget(GetProcess()->GetID(), widget_route_id,
+                               initial_rect);
+  Send(new ViewMsg_SetBounds_ACK(widget_route_id));
 }
 
-void RenderViewHostImpl::OnShowFullscreenWidget(int route_id) {
-  delegate_->ShowCreatedFullscreenWidget(GetProcess()->GetID(), route_id);
-  Send(new ViewMsg_SetBounds_ACK(route_id));
+void RenderViewHostImpl::OnShowFullscreenWidget(int widget_route_id) {
+  delegate_->ShowCreatedFullscreenWidget(GetProcess()->GetID(),
+                                         widget_route_id);
+  Send(new ViewMsg_SetBounds_ACK(widget_route_id));
 }
 
 void RenderViewHostImpl::OnUpdateTargetURL(const GURL& url) {
diff --git a/content/browser/renderer_host/render_view_host_impl.h b/content/browser/renderer_host/render_view_host_impl.h
index 3bed4ed..5df82a9 100644
--- a/content/browser/renderer_host/render_view_host_impl.h
+++ b/content/browser/renderer_host/render_view_host_impl.h
@@ -242,8 +242,8 @@
                   WindowOpenDisposition disposition,
                   const gfx::Rect& initial_rect,
                   bool user_gesture);
-  void OnShowWidget(int route_id, const gfx::Rect& initial_rect);
-  void OnShowFullscreenWidget(int route_id);
+  void OnShowWidget(int widget_route_id, const gfx::Rect& initial_rect);
+  void OnShowFullscreenWidget(int widget_route_id);
   void OnUpdateTargetURL(const GURL& url);
   void OnClose();
   void OnDocumentAvailableInMainFrame(bool uses_temporary_zoom_level);
diff --git a/content/browser/site_per_process_hit_test_browsertest.cc b/content/browser/site_per_process_hit_test_browsertest.cc
index fc3d7e0..c3dd8b9 100644
--- a/content/browser/site_per_process_hit_test_browsertest.cc
+++ b/content/browser/site_per_process_hit_test_browsertest.cc
@@ -2938,8 +2938,16 @@
 
 }  // namespace
 
+#if defined(OS_WIN)
+// https://crbug.com/882458
+#define MAYBE_CursorUpdateReceivedFromCrossSiteIframe \
+  DISABLED_CursorUpdateReceivedFromCrossSiteIframe
+#else
+#define MAYBE_CursorUpdateReceivedFromCrossSiteIframe \
+  CursorUpdateReceivedFromCrossSiteIframe
+#endif
 IN_PROC_BROWSER_TEST_P(SitePerProcessHitTestBrowserTest,
-                       CursorUpdateReceivedFromCrossSiteIframe) {
+                       MAYBE_CursorUpdateReceivedFromCrossSiteIframe) {
   CursorUpdateReceivedFromCrossSiteIframeHelper(shell(),
                                                 embedded_test_server());
 }
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index 5d4b922..386454d 100644
--- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc
@@ -2750,16 +2750,16 @@
 }
 
 void WebContentsImpl::CreateNewWidget(int32_t render_process_id,
-                                      int32_t route_id,
+                                      int32_t widget_route_id,
                                       mojom::WidgetPtr widget) {
-  CreateNewWidget(render_process_id, route_id, /*is_fullscreen=*/false,
+  CreateNewWidget(render_process_id, widget_route_id, /*is_fullscreen=*/false,
                   std::move(widget));
 }
 
 void WebContentsImpl::CreateNewFullscreenWidget(int32_t render_process_id,
-                                                int32_t route_id,
+                                                int32_t widget_route_id,
                                                 mojom::WidgetPtr widget) {
-  CreateNewWidget(render_process_id, route_id, /*is_fullscreen=*/true,
+  CreateNewWidget(render_process_id, widget_route_id, /*is_fullscreen=*/true,
                   std::move(widget));
 }
 
@@ -2824,14 +2824,14 @@
 }
 
 void WebContentsImpl::ShowCreatedWidget(int process_id,
-                                        int route_id,
+                                        int widget_route_id,
                                         const gfx::Rect& initial_rect) {
-  ShowCreatedWidget(process_id, route_id, false, initial_rect);
+  ShowCreatedWidget(process_id, widget_route_id, false, initial_rect);
 }
 
 void WebContentsImpl::ShowCreatedFullscreenWidget(int process_id,
-                                                  int route_id) {
-  ShowCreatedWidget(process_id, route_id, true, gfx::Rect());
+                                                  int widget_route_id) {
+  ShowCreatedWidget(process_id, widget_route_id, true, gfx::Rect());
 }
 
 void WebContentsImpl::ShowCreatedWidget(int process_id,
@@ -5051,12 +5051,6 @@
   if (showing_context_menu_)
     return;
 
-  // If the WebContents is not visible, don't show the context menu. This can
-  // happen if the renderer takes a while to return the right-click event back
-  // to the browser process and in the meantime the user switches tabs.
-  if (GetVisibility() != Visibility::VISIBLE)
-    return;
-
   ContextMenuParams context_menu_params(params);
   // Allow WebContentsDelegates to handle the context menu operation first.
   if (delegate_ && delegate_->HandleContextMenu(context_menu_params))
diff --git a/content/browser/web_contents/web_contents_impl.h b/content/browser/web_contents/web_contents_impl.h
index ce86f42..0b8df04 100644
--- a/content/browser/web_contents/web_contents_impl.h
+++ b/content/browser/web_contents/web_contents_impl.h
@@ -631,12 +631,13 @@
                        int32_t route_id,
                        mojom::WidgetPtr widget) override;
   void CreateNewFullscreenWidget(int32_t render_process_id,
-                                 int32_t route_id,
+                                 int32_t widget_route_id,
                                  mojom::WidgetPtr widget) override;
   void ShowCreatedWidget(int process_id,
-                         int route_id,
+                         int widget_route_id,
                          const gfx::Rect& initial_rect) override;
-  void ShowCreatedFullscreenWidget(int process_id, int route_id) override;
+  void ShowCreatedFullscreenWidget(int process_id,
+                                   int widget_route_id) override;
   void RequestMediaAccessPermission(const MediaStreamRequest& request,
                                     MediaResponseCallback callback) override;
   bool CheckMediaAccessPermission(RenderFrameHost* render_frame_host,
diff --git a/content/browser/webrtc/webrtc_video_capture_browsertest.cc b/content/browser/webrtc/webrtc_video_capture_browsertest.cc
index 75ceb0e..9e38210 100644
--- a/content/browser/webrtc/webrtc_video_capture_browsertest.cc
+++ b/content/browser/webrtc/webrtc_video_capture_browsertest.cc
@@ -36,7 +36,7 @@
 
 static const char kVideoCaptureHtmlFile[] = "/media/video_capture_test.html";
 static const char kStartVideoCaptureAndVerifySize[] =
-    "startVideoCaptureAndVerifySize()";
+    "startVideoCaptureAndVerifySize(320, 200)";
 static const char kWaitForVideoToTurnBlack[] = "waitForVideoToTurnBlack()";
 static const char kVerifyHasReceivedTrackEndedEvent[] =
     "verifyHasReceivedTrackEndedEvent()";
diff --git a/content/browser/webrtc/webrtc_video_capture_service_browsertest.cc b/content/browser/webrtc/webrtc_video_capture_service_browsertest.cc
index 032f9094..337c91dd 100644
--- a/content/browser/webrtc/webrtc_video_capture_service_browsertest.cc
+++ b/content/browser/webrtc/webrtc_video_capture_service_browsertest.cc
@@ -6,6 +6,7 @@
 #include "base/run_loop.h"
 #include "base/test/scoped_feature_list.h"
 #include "build/build_config.h"
+#include "cc/base/math_util.h"
 #include "components/viz/common/gpu/context_provider.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/common/content_features.h"
@@ -55,13 +56,15 @@
 };
 
 static const char kVideoCaptureHtmlFile[] = "/media/video_capture_test.html";
-static const char kStartVideoCaptureAndVerifySize[] =
-    "startVideoCaptureFromDeviceNamedVirtualDeviceAndVerifySize()";
+static const char kStartVideoCaptureAndVerify[] =
+    "startVideoCaptureFromVirtualDeviceAndVerifyUniformColorVideoWithSize(%d, "
+    "%d)";
 
 static const char kVirtualDeviceId[] = "/virtual/device";
 static const char kVirtualDeviceName[] = "Virtual Device";
 
-static const gfx::Size kDummyFrameDimensions(320, 200);
+static const gfx::Size kDummyFrameCodedSize(320, 200);
+static const gfx::Rect kDummyFrameVisibleRect(94, 36, 178, 150);
 static const int kDummyFrameRate = 5;
 
 }  // namespace
@@ -76,6 +79,7 @@
   virtual void RegisterVirtualDeviceAtFactory(
       video_capture::mojom::DeviceFactoryPtr* factory,
       const media::VideoCaptureDeviceInfo& info) = 0;
+  virtual gfx::Size GetVideoSize() = 0;
   virtual void PushNextFrame(base::TimeDelta timestamp) = 0;
   virtual void ShutDown() = 0;
 };
@@ -123,6 +127,8 @@
     frame_being_consumed_[1] = false;
   }
 
+  gfx::Size GetVideoSize() override { return kDummyFrameCodedSize; }
+
   void PushNextFrame(base::TimeDelta timestamp) override {
     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
     if (frame_being_consumed_[dummy_frame_index_]) {
@@ -145,9 +151,8 @@
     media::mojom::VideoFrameInfoPtr info = media::mojom::VideoFrameInfo::New();
     info->timestamp = timestamp;
     info->pixel_format = media::PIXEL_FORMAT_ARGB;
-    info->coded_size = kDummyFrameDimensions;
-    info->visible_rect = gfx::Rect(kDummyFrameDimensions.width(),
-                                   kDummyFrameDimensions.height());
+    info->coded_size = kDummyFrameCodedSize;
+    info->visible_rect = gfx::Rect(kDummyFrameCodedSize);
     info->metadata = metadata.GetInternalValues().Clone();
 
     frame_being_consumed_[dummy_frame_index_] = true;
@@ -169,8 +174,8 @@
                            uint8_t value_for_all_rgb_bytes,
                            std::vector<gpu::MailboxHolder>* target) {
     const int32_t kBytesPerRGBPixel = 3;
-    int32_t frame_size_in_bytes = kDummyFrameDimensions.width() *
-                                  kDummyFrameDimensions.height() *
+    int32_t frame_size_in_bytes = kDummyFrameCodedSize.width() *
+                                  kDummyFrameCodedSize.height() *
                                   kBytesPerRGBPixel;
     std::unique_ptr<uint8_t[]> dummy_frame_data(
         new uint8_t[frame_size_in_bytes]);
@@ -191,9 +196,9 @@
       gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
       gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
       gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
-      gl->TexImage2D(GL_TEXTURE_2D, 0, GL_RGB, kDummyFrameDimensions.width(),
-                     kDummyFrameDimensions.height(), 0, GL_RGB,
-                     GL_UNSIGNED_BYTE, dummy_frame_data.get());
+      gl->TexImage2D(GL_TEXTURE_2D, 0, GL_RGB, kDummyFrameCodedSize.width(),
+                     kDummyFrameCodedSize.height(), 0, GL_RGB, GL_UNSIGNED_BYTE,
+                     dummy_frame_data.get());
       gl->BindTexture(GL_TEXTURE_2D, 0);
 
       gpu::Mailbox mailbox;
@@ -225,12 +230,15 @@
 // A VirtualDeviceExerciser for exercising
 // DeviceFactory.AddSharedMemoryVirtualDevice().
 // It generates (dummy) I420 frame data by setting all bytes equal to the
-// current frame count.
+// current frame count. Padding bytes are set to 0.
 class SharedMemoryDeviceExerciser : public VirtualDeviceExerciser,
                                     public video_capture::mojom::Producer {
  public:
-  SharedMemoryDeviceExerciser()
-      : producer_binding_(this), weak_factory_(this) {}
+  explicit SharedMemoryDeviceExerciser(
+      media::mojom::PlaneStridesPtr strides = nullptr)
+      : strides_(std::move(strides)),
+        producer_binding_(this),
+        weak_factory_(this) {}
 
   // VirtualDeviceExerciser implementation.
   void Initialize() override {}
@@ -245,9 +253,14 @@
         kSendBufferHandlesToProducerAsRawFileDescriptors,
         mojo::MakeRequest(&virtual_device_));
   }
+  gfx::Size GetVideoSize() override {
+    return gfx::Size(kDummyFrameVisibleRect.width(),
+                     kDummyFrameVisibleRect.height());
+  }
   void PushNextFrame(base::TimeDelta timestamp) override {
     virtual_device_->RequestFrameBuffer(
-        kDummyFrameDimensions, media::VideoPixelFormat::PIXEL_FORMAT_I420,
+        kDummyFrameCodedSize, media::VideoPixelFormat::PIXEL_FORMAT_I420,
+        strides_.Clone(),
         base::BindOnce(&SharedMemoryDeviceExerciser::OnFrameBufferReceived,
                        weak_factory_.GetWeakPtr(), timestamp));
   }
@@ -287,22 +300,91 @@
     media::mojom::VideoFrameInfoPtr info = media::mojom::VideoFrameInfo::New();
     info->timestamp = timestamp;
     info->pixel_format = media::PIXEL_FORMAT_I420;
-    info->coded_size = kDummyFrameDimensions;
-    info->visible_rect = gfx::Rect(kDummyFrameDimensions.width(),
-                                   kDummyFrameDimensions.height());
+    info->coded_size = kDummyFrameCodedSize;
+    info->visible_rect = kDummyFrameVisibleRect;
     info->metadata = metadata.GetInternalValues().Clone();
+    info->strides = strides_.Clone();
 
     auto outgoing_buffer = outgoing_buffer_id_to_buffer_map_.at(buffer_id)
                                ->GetHandleForInProcessAccess();
 
     static int frame_count = 0;
     frame_count++;
-    memset(outgoing_buffer->data(), frame_count % 256,
-           outgoing_buffer->mapped_size());
+    const uint8_t dummy_value = frame_count % 256;
+
+    // Reset the whole buffer to 0
+    memset(outgoing_buffer->data(), 0, outgoing_buffer->mapped_size());
+
+    // Set all bytes affecting |info->visible_rect| to |dummy_value|.
+    const int kYStride = info->strides ? info->strides->stride_by_plane[0]
+                                       : info->coded_size.width();
+    const int kYColsToSkipAtStart = info->visible_rect.x();
+    const int kYVisibleColCount = info->visible_rect.width();
+    const int kYCodedRowCount = info->coded_size.height();
+    const int kYRowsToSkipAtStart = info->visible_rect.y();
+    const int kYVisibleRowCount = info->visible_rect.height();
+
+    const int kUStride = info->strides ? info->strides->stride_by_plane[1]
+                                       : info->coded_size.width() / 2;
+    const int kUColsToSkipAtStart =
+        cc::MathUtil::UncheckedRoundDown(info->visible_rect.x(), 2) / 2;
+    const int kUVisibleColCount =
+        (cc::MathUtil::UncheckedRoundUp(info->visible_rect.right(), 2) / 2) -
+        kUColsToSkipAtStart;
+    const int kUCodedRowCount = info->coded_size.height() / 2;
+    const int kURowsToSkipAtStart =
+        cc::MathUtil::UncheckedRoundDown(info->visible_rect.y(), 2) / 2;
+    const int kUVisibleRowCount =
+        (cc::MathUtil::UncheckedRoundUp(info->visible_rect.bottom(), 2) / 2) -
+        kURowsToSkipAtStart;
+
+    const int kVStride = info->strides ? info->strides->stride_by_plane[2]
+                                       : info->coded_size.width() / 2;
+
+    uint8_t* write_ptr = outgoing_buffer->data();
+    FillVisiblePortionOfPlane(&write_ptr, dummy_value, kYCodedRowCount,
+                              kYRowsToSkipAtStart, kYVisibleRowCount, kYStride,
+                              kYColsToSkipAtStart, kYVisibleColCount);
+    FillVisiblePortionOfPlane(&write_ptr, dummy_value, kUCodedRowCount,
+                              kURowsToSkipAtStart, kUVisibleRowCount, kUStride,
+                              kUColsToSkipAtStart, kUVisibleColCount);
+    FillVisiblePortionOfPlane(&write_ptr, dummy_value, kUCodedRowCount,
+                              kURowsToSkipAtStart, kUVisibleRowCount, kVStride,
+                              kUColsToSkipAtStart, kUVisibleColCount);
 
     virtual_device_->OnFrameReadyInBuffer(buffer_id, std::move(info));
   }
 
+  void FillVisiblePortionOfPlane(uint8_t** write_ptr,
+                                 uint8_t fill_value,
+                                 int row_count,
+                                 int rows_to_skip_at_start,
+                                 int visible_row_count,
+                                 int col_count,
+                                 int cols_to_skip_at_start,
+                                 int visible_col_count) {
+    const int kColsToSkipAtEnd =
+        col_count - visible_col_count - cols_to_skip_at_start;
+    const int kRowsToSkipAtEnd =
+        row_count - visible_row_count - rows_to_skip_at_start;
+
+    // Skip rows at start
+    (*write_ptr) += col_count * rows_to_skip_at_start;
+    // Fill rows
+    for (int i = 0; i < visible_row_count; i++) {
+      // Skip cols at start
+      (*write_ptr) += cols_to_skip_at_start;
+      // Fill visible bytes
+      memset(*write_ptr, fill_value, visible_col_count);
+      (*write_ptr) += visible_col_count;
+      // Skip cols at end
+      (*write_ptr) += kColsToSkipAtEnd;
+    }
+    // Skip rows at end
+    (*write_ptr) += col_count * kRowsToSkipAtEnd;
+  }
+
+  media::mojom::PlaneStridesPtr strides_;
   mojo::Binding<video_capture::mojom::Producer> producer_binding_;
   video_capture::mojom::SharedMemoryVirtualDevicePtr virtual_device_;
   std::map<int32_t /*buffer_id*/,
@@ -337,6 +419,7 @@
     info.descriptor.set_display_name(kVirtualDeviceName);
     info.descriptor.capture_api = media::VideoCaptureApi::VIRTUAL_DEVICE;
 
+    video_size_ = device_exerciser->GetVideoSize();
     device_exerciser->RegisterVirtualDeviceAtFactory(&factory_, info);
 
     main_task_runner_->PostTask(
@@ -384,10 +467,12 @@
     GURL url(embedded_test_server()->GetURL(kVideoCaptureHtmlFile));
     NavigateToURL(shell(), url);
 
+    std::string javascript_to_execute = base::StringPrintf(
+        kStartVideoCaptureAndVerify, video_size_.width(), video_size_.height());
     std::string result;
     // Start video capture and wait until it started rendering
-    ASSERT_TRUE(ExecuteScriptAndExtractString(
-        shell(), kStartVideoCaptureAndVerifySize, &result));
+    ASSERT_TRUE(
+        ExecuteScriptAndExtractString(shell(), javascript_to_execute, &result));
     ASSERT_EQ("OK", result);
 
     std::move(finish_test_cb).Run();
@@ -433,6 +518,7 @@
   base::test::ScopedFeatureList scoped_feature_list_;
   video_capture::mojom::DeviceFactoryProviderPtr provider_;
   video_capture::mojom::DeviceFactoryPtr factory_;
+  gfx::Size video_size_;
   base::TimeTicks first_frame_time_;
   base::WeakPtrFactory<WebRtcVideoCaptureServiceBrowserTest> weak_factory_;
 
@@ -473,6 +559,25 @@
   run_loop.Run();
 }
 
+IN_PROC_BROWSER_TEST_F(
+    WebRtcVideoCaptureServiceBrowserTest,
+    PaddedI420FramesSentThroughSharedMemoryVirtualDeviceGetDisplayedOnPage) {
+  Initialize();
+  auto device_exerciser = std::make_unique<SharedMemoryDeviceExerciser>(
+      media::mojom::PlaneStrides::New(
+          std::vector<uint32_t>({1024u, 512u, 1024u, 0u})));
+  device_exerciser->Initialize();
+
+  base::RunLoop run_loop;
+  virtual_device_thread_.task_runner()->PostTask(
+      FROM_HERE,
+      base::BindOnce(&WebRtcVideoCaptureServiceBrowserTest::
+                         AddVirtualDeviceAndStartCapture,
+                     base::Unretained(this), device_exerciser.get(),
+                     media::BindToCurrentLoop(run_loop.QuitClosure())));
+  run_loop.Run();
+}
+
 }  // namespace content
 
 #endif  // defined(CAN_USE_IMAGE_TRANSPORT_FACTORY)
diff --git a/content/child/dwrite_font_proxy/dwrite_font_proxy_win.cc b/content/child/dwrite_font_proxy/dwrite_font_proxy_win.cc
index 6387f299..7019d1f3 100644
--- a/content/child/dwrite_font_proxy/dwrite_font_proxy_win.cc
+++ b/content/child/dwrite_font_proxy/dwrite_font_proxy_win.cc
@@ -13,6 +13,7 @@
 #include "base/feature_list.h"
 #include "base/logging.h"
 #include "base/metrics/histogram_macros.h"
+#include "base/no_destructor.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/task/post_task.h"
 #include "content/child/dwrite_font_proxy/dwrite_localized_strings_win.h"
@@ -381,9 +382,10 @@
     }
     SetProxy(std::move(dwrite_font_proxy));
   }
-  static base::ThreadLocalBoolean font_proxy_method_in_flight;
-
-  return FontProxyScopeWrapper(font_proxy_.get(), &font_proxy_method_in_flight);
+  static base::NoDestructor<base::ThreadLocalBoolean>
+      font_proxy_method_in_flight;
+  return FontProxyScopeWrapper(font_proxy_.get(),
+                               font_proxy_method_in_flight.get());
 }
 
 DWriteFontFamilyProxy::DWriteFontFamilyProxy() = default;
diff --git a/content/child/font_warmup_win.cc b/content/child/font_warmup_win.cc
index be505c8..93b698c3 100644
--- a/content/child/font_warmup_win.cc
+++ b/content/child/font_warmup_win.cc
@@ -14,6 +14,7 @@
 #include "base/logging.h"
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
+#include "base/no_destructor.h"
 #include "base/numerics/safe_conversions.h"
 #include "base/numerics/safe_math.h"
 #include "base/strings/utf_string_conversions.h"
@@ -40,12 +41,6 @@
 // The Skia font manager, used for the life of the process (leaked at the end).
 SkFontMgr* g_warmup_fontmgr = nullptr;
 
-base::win::IATPatchFunction g_iat_patch_open_sc_manager;
-base::win::IATPatchFunction g_iat_patch_close_service_handle;
-base::win::IATPatchFunction g_iat_patch_open_service;
-base::win::IATPatchFunction g_iat_patch_start_service;
-base::win::IATPatchFunction g_iat_patch_nt_connect_port;
-
 // These are from ntddk.h
 #if !defined(STATUS_ACCESS_DENIED)
 #define STATUS_ACCESS_DENIED ((NTSTATUS)0xC0000022L)
@@ -400,27 +395,33 @@
 
   is_patched = true;
 
-  DWORD patched = g_iat_patch_open_sc_manager.Patch(
+  static base::NoDestructor<base::win::IATPatchFunction> patch_open_sc_manager;
+  DWORD patched = patch_open_sc_manager->Patch(
       L"dwrite.dll", service_provider_dll, "OpenSCManagerW",
       reinterpret_cast<void*>(OpenSCManagerWPatch));
   DCHECK(patched == 0);
 
-  patched = g_iat_patch_close_service_handle.Patch(
+  static base::NoDestructor<base::win::IATPatchFunction>
+      patch_close_service_handle;
+  patched = patch_close_service_handle->Patch(
       L"dwrite.dll", service_provider_dll, "CloseServiceHandle",
       reinterpret_cast<void*>(CloseServiceHandlePatch));
   DCHECK(patched == 0);
 
-  patched = g_iat_patch_open_service.Patch(
+  static base::NoDestructor<base::win::IATPatchFunction> patch_open_service;
+  patched = patch_open_service->Patch(
       L"dwrite.dll", service_provider_dll, "OpenServiceW",
       reinterpret_cast<void*>(OpenServiceWPatch));
   DCHECK(patched == 0);
 
-  patched = g_iat_patch_start_service.Patch(
+  static base::NoDestructor<base::win::IATPatchFunction> patch_start_service;
+  patched = patch_start_service->Patch(
       L"dwrite.dll", service_provider_dll, "StartServiceW",
       reinterpret_cast<void*>(StartServiceWPatch));
   DCHECK(patched == 0);
 
-  patched = g_iat_patch_nt_connect_port.Patch(
+  static base::NoDestructor<base::win::IATPatchFunction> patch_nt_connect_port;
+  patched = patch_nt_connect_port->Patch(
       L"dwrite.dll", "ntdll.dll", "NtAlpcConnectPort",
       reinterpret_cast<void*>(NtALpcConnectPortPatch));
   DCHECK(patched == 0);
diff --git a/content/child/runtime_features.cc b/content/child/runtime_features.cc
index c88c69ef18..64dd9f8 100644
--- a/content/child/runtime_features.cc
+++ b/content/child/runtime_features.cc
@@ -259,9 +259,6 @@
   if (command_line.HasSwitch(switches::kEnableSlimmingPaintV2))
     WebRuntimeFeatures::EnableSlimmingPaintV2(true);
 
-  if (base::FeatureList::IsEnabled(features::kLazyParseCSS))
-    WebRuntimeFeatures::EnableLazyParseCSS(true);
-
   WebRuntimeFeatures::EnablePassiveDocumentEventListeners(
       base::FeatureList::IsEnabled(features::kPassiveDocumentEventListeners));
 
diff --git a/content/common/throttling_url_loader.cc b/content/common/throttling_url_loader.cc
index 4387f6e..87be6ec 100644
--- a/content/common/throttling_url_loader.cc
+++ b/content/common/throttling_url_loader.cc
@@ -7,7 +7,6 @@
 #include "base/single_thread_task_runner.h"
 #include "base/strings/stringprintf.h"
 #include "base/threading/thread_task_runner_handle.h"
-#include "content/public/common/browser_side_navigation_policy.h"
 #include "net/http/http_status_code.h"
 #include "net/http/http_util.h"
 
@@ -289,9 +288,6 @@
   DCHECK_EQ(DEFERRED_NONE, deferred_stage_);
   DCHECK(!loader_completed_);
 
-  if (options & network::mojom::kURLLoadOptionSynchronous)
-    is_synchronous_ = true;
-
   bool deferred = false;
   DCHECK(deferring_throttles_.empty());
   if (!throttles_.empty()) {
@@ -545,11 +541,7 @@
 }
 
 void ThrottlingURLLoader::OnClientConnectionError() {
-  // TODO(reillyg): Temporary workaround for crbug.com/756751 where without
-  // browser-side navigation this error on async loads will confuse the loading
-  // of cross-origin iframes.
-  if (is_synchronous_ || content::IsBrowserSideNavigationEnabled())
-    CancelWithError(net::ERR_ABORTED, nullptr);
+  CancelWithError(net::ERR_ABORTED, nullptr);
 }
 
 void ThrottlingURLLoader::CancelWithError(int error_code,
diff --git a/content/common/throttling_url_loader.h b/content/common/throttling_url_loader.h
index 7c8a602..ddf11cb 100644
--- a/content/common/throttling_url_loader.h
+++ b/content/common/throttling_url_loader.h
@@ -151,7 +151,6 @@
   };
   DeferredStage deferred_stage_ = DEFERRED_NONE;
   bool loader_completed_ = false;
-  bool is_synchronous_ = false;
 
   struct ThrottleEntry {
     ThrottleEntry(ThrottlingURLLoader* loader,
diff --git a/content/common/throttling_url_loader_unittest.cc b/content/common/throttling_url_loader_unittest.cc
index 31bcfc4..b30c4b2 100644
--- a/content/common/throttling_url_loader_unittest.cc
+++ b/content/common/throttling_url_loader_unittest.cc
@@ -8,7 +8,6 @@
 #include "base/run_loop.h"
 #include "base/test/bind_test_util.h"
 #include "base/test/scoped_task_environment.h"
-#include "content/public/common/browser_side_navigation_policy.h"
 #include "content/public/common/url_loader_throttle.h"
 #include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
 #include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
@@ -690,33 +689,7 @@
   EXPECT_EQ(1u, client_.on_complete_called());
 }
 
-TEST_F(ThrottlingURLLoaderTest, PipeClosureBeforeSyncResponse) {
-  base::RunLoop run_loop;
-  client_.set_on_complete_callback(base::Bind(
-      [](const base::Closure& quit_closure, int error) {
-        EXPECT_EQ(net::ERR_ABORTED, error);
-        quit_closure.Run();
-      },
-      run_loop.QuitClosure()));
-
-  CreateLoaderAndStart(true);
-
-  factory_.CloseClientPipe();
-
-  run_loop.Run();
-
-  EXPECT_EQ(1u, throttle_->will_start_request_called());
-  EXPECT_EQ(0u, throttle_->will_redirect_request_called());
-  EXPECT_EQ(0u, throttle_->will_process_response_called());
-
-  EXPECT_EQ(0u, client_.on_received_response_called());
-  EXPECT_EQ(0u, client_.on_received_redirect_called());
-  EXPECT_EQ(1u, client_.on_complete_called());
-}
-
-// Once browser-side navigation is the only option these two tests should be
-// merged as the sync and async cases will be identical.
-TEST_F(ThrottlingURLLoaderTest, PipeClosureBeforeAsyncResponse) {
+TEST_F(ThrottlingURLLoaderTest, PipeClosure) {
   base::RunLoop run_loop;
   client_.set_on_complete_callback(base::Bind(
       [](const base::Closure& quit_closure, int error) {
diff --git a/content/public/common/content_features.cc b/content/public/common/content_features.cc
index 35d7f90..8d8f9856 100644
--- a/content/public/common/content_features.cc
+++ b/content/public/common/content_features.cc
@@ -223,10 +223,6 @@
 const base::Feature kLazyInitializeMediaControls{
     "LazyInitializeMediaControls", base::FEATURE_ENABLED_BY_DEFAULT};
 
-// Enables lazily parsing css properties for performance.
-const base::Feature kLazyParseCSS{"LazyParseCSS",
-                                  base::FEATURE_ENABLED_BY_DEFAULT};
-
 // Enables lowering the priority of the resources in iframes.
 const base::Feature kLowPriorityIframes{"LowPriorityIframes",
                                         base::FEATURE_DISABLED_BY_DEFAULT};
diff --git a/content/public/common/content_features.h b/content/public/common/content_features.h
index 1be9c8241..764b2d3 100644
--- a/content/public/common/content_features.h
+++ b/content/public/common/content_features.h
@@ -60,7 +60,6 @@
 CONTENT_EXPORT extern const base::Feature kLazyImageLoading;
 CONTENT_EXPORT extern const base::Feature kLazyImageVisibleLoadTimeMetrics;
 CONTENT_EXPORT extern const base::Feature kLazyInitializeMediaControls;
-CONTENT_EXPORT extern const base::Feature kLazyParseCSS;
 CONTENT_EXPORT extern const base::Feature kLowPriorityIframes;
 CONTENT_EXPORT extern const base::Feature kMediaDevicesSystemMonitorCache;
 CONTENT_EXPORT extern const base::Feature kMemoryCoordinator;
diff --git a/content/renderer/media/stream/webmediaplayer_ms.cc b/content/renderer/media/stream/webmediaplayer_ms.cc
index 82429d9..8f6decf 100644
--- a/content/renderer/media/stream/webmediaplayer_ms.cc
+++ b/content/renderer/media/stream/webmediaplayer_ms.cc
@@ -380,8 +380,7 @@
 
   if (frame) {
     // Report UMA and RAPPOR metrics.
-    media::ReportMetrics(load_type, url, frame_->GetSecurityOrigin(),
-                         media_log_.get());
+    media::ReportMetrics(load_type, url, *frame_, media_log_.get());
     routing_id = frame->GetRoutingID();
   }
 
diff --git a/content/renderer/media/video_capture_impl.cc b/content/renderer/media/video_capture_impl.cc
index 77ed36bf..aedecff2 100644
--- a/content/renderer/media/video_capture_impl.cc
+++ b/content/renderer/media/video_capture_impl.cc
@@ -357,18 +357,43 @@
   scoped_refptr<media::VideoFrame> frame;
   switch (buffer_context->buffer_type()) {
     case VideoFrameBufferHandleType::SHARED_BUFFER_HANDLE:
-      frame = media::VideoFrame::WrapExternalSharedMemory(
-          static_cast<media::VideoPixelFormat>(info->pixel_format),
-          info->coded_size, info->visible_rect, info->visible_rect.size(),
-          static_cast<uint8_t*>(buffer_context->shared_memory()->memory()),
-          buffer_context->shared_memory_size(),
-          buffer_context->shared_memory()->handle(),
-          0 /* shared_memory_offset */, info->timestamp);
+      if (info->strides) {
+        CHECK(IsYuvPlanar(info->pixel_format) &&
+              (media::VideoFrame::NumPlanes(info->pixel_format) == 3))
+            << "Currently, only YUV formats support custom strides.";
+        uint8_t* y_data =
+            static_cast<uint8_t*>(buffer_context->shared_memory()->memory());
+        uint8_t* u_data =
+            y_data + (media::VideoFrame::Rows(media::VideoFrame::kYPlane,
+                                              info->pixel_format,
+                                              info->coded_size.height()) *
+                      info->strides->stride_by_plane[0]);
+        uint8_t* v_data =
+            u_data + (media::VideoFrame::Rows(media::VideoFrame::kUPlane,
+                                              info->pixel_format,
+                                              info->coded_size.height()) *
+                      info->strides->stride_by_plane[1]);
+        frame = media::VideoFrame::WrapExternalYuvData(
+            info->pixel_format, info->coded_size, info->visible_rect,
+            info->visible_rect.size(), info->strides->stride_by_plane[0],
+            info->strides->stride_by_plane[1],
+            info->strides->stride_by_plane[2], y_data, u_data, v_data,
+            info->timestamp);
+        frame->AddSharedMemoryHandle(buffer_context->shared_memory()->handle());
+      } else {
+        frame = media::VideoFrame::WrapExternalSharedMemory(
+            info->pixel_format, info->coded_size, info->visible_rect,
+            info->visible_rect.size(),
+            static_cast<uint8_t*>(buffer_context->shared_memory()->memory()),
+            buffer_context->shared_memory_size(),
+            buffer_context->shared_memory()->handle(),
+            0 /* shared_memory_offset */, info->timestamp);
+      }
       break;
     case VideoFrameBufferHandleType::READ_ONLY_SHMEM_REGION:
       frame = media::VideoFrame::WrapExternalData(
-          static_cast<media::VideoPixelFormat>(info->pixel_format),
-          info->coded_size, info->visible_rect, info->visible_rect.size(),
+          info->pixel_format, info->coded_size, info->visible_rect,
+          info->visible_rect.size(),
           const_cast<uint8_t*>(
               static_cast<const uint8_t*>(buffer_context->read_only_shmem())),
           buffer_context->read_only_shmem_size(), info->timestamp);
@@ -384,10 +409,9 @@
         mailbox_holder_array[i] = buffer_context->mailbox_holders()[i];
       }
       frame = media::VideoFrame::WrapNativeTextures(
-          static_cast<media::VideoPixelFormat>(info->pixel_format),
-          mailbox_holder_array, media::VideoFrame::ReleaseMailboxCB(),
-          info->coded_size, info->visible_rect, info->visible_rect.size(),
-          info->timestamp);
+          info->pixel_format, mailbox_holder_array,
+          media::VideoFrame::ReleaseMailboxCB(), info->coded_size,
+          info->visible_rect, info->visible_rect.size(), info->timestamp);
       break;
   }
   if (!frame) {
diff --git a/content/renderer/media_capture_from_element/canvas_capture_handler_unittest.cc b/content/renderer/media_capture_from_element/canvas_capture_handler_unittest.cc
index 94b495d..2f1e5c29 100644
--- a/content/renderer/media_capture_from_element/canvas_capture_handler_unittest.cc
+++ b/content/renderer/media_capture_from_element/canvas_capture_handler_unittest.cc
@@ -83,7 +83,7 @@
   static sk_sp<SkImage> GenerateTestImage(bool opaque, int width, int height) {
     SkBitmap testBitmap;
     testBitmap.allocN32Pixels(width, height, opaque);
-    testBitmap.eraseARGB(kTestAlphaValue, 30, 60, 200);
+    testBitmap.eraseARGB(opaque ? 255 : kTestAlphaValue, 30, 60, 200);
     return SkImage::MakeFromBitmap(testBitmap);
   }
 
diff --git a/content/renderer/render_widget.cc b/content/renderer/render_widget.cc
index 4073dfe..7920bb1f 100644
--- a/content/renderer/render_widget.cc
+++ b/content/renderer/render_widget.cc
@@ -594,16 +594,29 @@
 
   RenderThreadImpl* render_thread_impl = RenderThreadImpl::current();
 
+  blink::scheduler::WebThreadScheduler* main_thread_scheduler = nullptr;
+  if (render_thread_impl)
+    main_thread_scheduler = render_thread_impl->GetWebMainThreadScheduler();
   blink::scheduler::WebThreadScheduler* compositor_thread_scheduler =
       blink::scheduler::WebThreadScheduler::CompositorThreadScheduler();
+  scoped_refptr<base::SingleThreadTaskRunner> compositor_input_task_runner;
+  // The |compositor_thread_scheduler| can be null in tests without a compositor
+  // thread.
+  if (compositor_thread_scheduler) {
+    // When the RenderWidget is for a frame (ie for a local root) then it uses
+    // the compositor thread task runner. When it is for a popup, it does not.
+    // When |owner_delegate_| is true, the RenderWidget is attached to the main
+    // Frame (which makes it a local root). Otherwise, if it is |for_oopif_|
+    // then it is a local root (a local Frame) sitting below a remote Frame.
+    if (owner_delegate_ || for_oopif_) {
+      compositor_input_task_runner =
+          compositor_thread_scheduler->InputTaskRunner();
+    }
+  }
 
   widget_input_handler_manager_ = WidgetInputHandlerManager::Create(
-      weak_ptr_factory_.GetWeakPtr(),
-      compositor_thread_scheduler && layer_tree_view_
-          ? compositor_thread_scheduler->InputTaskRunner()
-          : nullptr,
-      render_thread_impl ? render_thread_impl->GetWebMainThreadScheduler()
-                         : nullptr);
+      weak_ptr_factory_.GetWeakPtr(), std::move(compositor_input_task_runner),
+      main_thread_scheduler);
 
   show_callback_ = std::move(show_callback);
 
diff --git a/content/shell/app/blink_test_platform_support_win.cc b/content/shell/app/blink_test_platform_support_win.cc
index e4da4b75..0252431 100644
--- a/content/shell/app/blink_test_platform_support_win.cc
+++ b/content/shell/app/blink_test_platform_support_win.cc
@@ -54,7 +54,7 @@
   metrics.cbSize = sizeof(NONCLIENTMETRICS);
   bool success = !!::SystemParametersInfo(
       SPI_GETNONCLIENTMETRICS, metrics.cbSize, &metrics, 0);
-  CHECK(success);
+  PCHECK(success);
   LOGFONTW* system_fonts[] =
       {&metrics.lfStatusFont, &metrics.lfMenuFont, &metrics.lfSmCaptionFont};
   const wchar_t required_font[] = L"Segoe UI";
diff --git a/content/shell/app/shell_main_delegate.cc b/content/shell/app/shell_main_delegate.cc
index 446c256..416c1be 100644
--- a/content/shell/app/shell_main_delegate.cc
+++ b/content/shell/app/shell_main_delegate.cc
@@ -181,16 +181,6 @@
 
   InitLogging(command_line);
 
-  if (command_line.HasSwitch(switches::kCheckLayoutTestSysDeps)) {
-    // If CheckLayoutSystemDeps succeeds, we don't exit early. Instead we
-    // continue and try to load the fonts in BlinkTestPlatformInitialize
-    // below, and then try to bring up the rest of the content module.
-    if (!CheckLayoutSystemDeps()) {
-      *exit_code = 1;
-      return true;
-    }
-  }
-
   if (command_line.HasSwitch("run-layout-test")) {
     std::cerr << std::string(79, '*') << "\n"
               << "* The flag --run-layout-test is obsolete. Please use --"
@@ -200,6 +190,16 @@
   }
 
   if (command_line.HasSwitch(switches::kRunWebTests)) {
+    // Only run CheckLayoutSystemDeps on the browser process.
+    if (!command_line.HasSwitch(switches::kProcessType)) {
+      // If CheckLayoutSystemDeps fails, we early exit as there's no point in
+      // running the tests.
+      if (!CheckLayoutSystemDeps()) {
+        *exit_code = 1;
+        return true;
+      }
+    }
+
     EnableBrowserLayoutTestMode();
 
 #if BUILDFLAG(ENABLE_PLUGINS)
@@ -357,8 +357,7 @@
 
   browser_runner_.reset(BrowserMainRunner::Create());
   base::CommandLine& command_line = *base::CommandLine::ForCurrentProcess();
-  return command_line.HasSwitch(switches::kRunWebTests) ||
-                 command_line.HasSwitch(switches::kCheckLayoutTestSysDeps)
+  return command_line.HasSwitch(switches::kRunWebTests)
              ? LayoutTestBrowserMain(main_function_params, browser_runner_)
              : ShellBrowserMain(main_function_params, browser_runner_);
 }
@@ -420,8 +419,7 @@
 void ShellMainDelegate::PreCreateMainMessageLoop() {
 #if defined(OS_ANDROID)
   base::CommandLine& command_line = *base::CommandLine::ForCurrentProcess();
-  if (command_line.HasSwitch(switches::kRunWebTests) ||
-      command_line.HasSwitch(switches::kCheckLayoutTestSysDeps)) {
+  if (command_line.HasSwitch(switches::kRunWebTests)) {
     bool success =
         base::MessageLoop::InitMessagePumpForUIFactory(&CreateMessagePumpForUI);
     CHECK(success) << "Unable to initialize the message pump for Android";
diff --git a/content/shell/browser/layout_test/layout_test_browser_main.cc b/content/shell/browser/layout_test/layout_test_browser_main.cc
index ded9341..a047f677 100644
--- a/content/shell/browser/layout_test/layout_test_browser_main.cc
+++ b/content/shell/browser/layout_test/layout_test_browser_main.cc
@@ -155,15 +155,6 @@
   android_configuration.RedirectStreams();
 #endif
 
-  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
-          switches::kCheckLayoutTestSysDeps)) {
-    base::ThreadTaskRunnerHandle::Get()->PostTask(
-        FROM_HERE, base::BindOnce(&content::Shell::CloseAllWindows));
-    main_runner->Run();
-    main_runner->Shutdown();
-    return 0;
-  }
-
   exit_code = RunTests(main_runner);
   base::RunLoop().RunUntilIdle();
 
diff --git a/content/shell/common/layout_test/layout_test_switches.cc b/content/shell/common/layout_test/layout_test_switches.cc
index 8aa83ee..f1260fe 100644
--- a/content/shell/common/layout_test/layout_test_switches.cc
+++ b/content/shell/common/layout_test/layout_test_switches.cc
@@ -20,10 +20,6 @@
 const char kAndroidStdoutPort[] = "android-stdout-port";
 #endif // defined(OS_ANDROID)
 
-// Check whether all system dependencies for running layout tests are met.
-// TODO(tkent): Rename this to "check-web-test-sys-deps".
-const char kCheckLayoutTestSysDeps[] = "check-layout-test-sys-deps";
-
 // When specified to "enable-leak-detection" command-line option,
 // causes the leak detector to cause immediate crash when found leak.
 const char kCrashOnFailure[] = "crash-on-failure";
diff --git a/content/shell/common/layout_test/layout_test_switches.h b/content/shell/common/layout_test/layout_test_switches.h
index 6fb6947..f0dedd32 100644
--- a/content/shell/common/layout_test/layout_test_switches.h
+++ b/content/shell/common/layout_test/layout_test_switches.h
@@ -20,7 +20,6 @@
 extern const char kAndroidStdinPort[];
 extern const char kAndroidStdoutPort[];
 #endif // defined(OS_ANDROID)
-extern const char kCheckLayoutTestSysDeps[];
 extern const char kCrashOnFailure[];
 extern const char kCustomDevToolsFrontend[];
 extern const char kDebugDevTools[];
diff --git a/content/shell/test_runner/web_widget_test_client.cc b/content/shell/test_runner/web_widget_test_client.cc
index 9e24724..5fd30f4 100644
--- a/content/shell/test_runner/web_widget_test_client.cc
+++ b/content/shell/test_runner/web_widget_test_client.cc
@@ -99,6 +99,18 @@
   web_widget_test_proxy_base_->event_sender()->DoDragDrop(data, mask);
 }
 
+blink::WebLayerTreeView* WebWidgetTestClient::InitializeLayerTreeView() {
+  // This call should go to the production client, not here.
+  NOTREACHED();
+  return nullptr;
+}
+
+bool WebWidgetTestClient::AllowsBrokenNullLayerTreeView() const {
+  // This call should go to the production client, not here.
+  NOTREACHED();
+  return false;
+}
+
 TestRunnerForSpecificView* WebWidgetTestClient::view_test_runner() {
   return web_widget_test_proxy_base_->web_view_test_proxy_base()
       ->view_test_runner();
diff --git a/content/shell/test_runner/web_widget_test_client.h b/content/shell/test_runner/web_widget_test_client.h
index 54e103b..d589721 100644
--- a/content/shell/test_runner/web_widget_test_client.h
+++ b/content/shell/test_runner/web_widget_test_client.h
@@ -43,6 +43,10 @@
                      const SkBitmap& drag_image,
                      const blink::WebPoint& image_offset) override;
 
+  // WebWidgetClient overrides that are not used.
+  blink::WebLayerTreeView* InitializeLayerTreeView() override;
+  bool AllowsBrokenNullLayerTreeView() const override;
+
  private:
   void AnimateNow();
 
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index 14dfb8ce..3601ada 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -423,7 +423,10 @@
   if (use_atk) {
     sources +=
         [ "../browser/accessibility/accessibility_event_recorder_auralinux.cc" ]
-    configs += [ "//build/config/linux/atk" ]
+    configs += [
+      "//build/config/linux/atk",
+      "//build/config/linux:atspi2",
+    ]
   }
 
   if (use_glib) {
diff --git a/content/test/data/media/video_capture_test.html b/content/test/data/media/video_capture_test.html
index b05b193c..9eab6e3c 100644
--- a/content/test/data/media/video_capture_test.html
+++ b/content/test/data/media/video_capture_test.html
@@ -6,11 +6,9 @@
 <body>
   <table border="0">
     <tr>
-      <td><video id="local-view" width="96" height="96" autoplay
-          style="display:none"></video></td>
+      <td><video id="local-view" autoplay style="display:none"></video></td>
       <!-- The canvas is used to detect when video starts and stops. -->
-      <td><canvas id="local-view-canvas" width="96" height="96"
-          style="display:none"></canvas></td>
+      <td><canvas id="local-view-canvas" style="display:none"></canvas></td>
     </tr>
   </table>
 </body>
diff --git a/content/test/data/media/video_capture_test.js b/content/test/data/media/video_capture_test.js
index 96ebe0d5..13b30dc 100644
--- a/content/test/data/media/video_capture_test.js
+++ b/content/test/data/media/video_capture_test.js
@@ -6,18 +6,26 @@
   return document.getElementById(id);
 };
 
-const WIDTH = 320;
-var CONSTRAINTS = {video: {width: {exact: WIDTH}}};
 var hasReceivedTrackEndedEvent = false;
 
-function startVideoCaptureAndVerifySize() {
+function startVideoCaptureAndVerifySize(video_width, video_height) {
   console.log('Calling getUserMediaAndWaitForVideoRendering.');
-  navigator.mediaDevices.getUserMedia(CONSTRAINTS)
-      .then(gotStreamCallback)
+  var constraints = {
+    video: {
+      width: {exact: video_width},
+      height: {exact: video_height},
+    }
+  };
+  navigator.mediaDevices.getUserMedia(constraints)
+      .then(function(stream) {
+        waitForVideoStreamToSatisfyRequirementFunction(
+            stream, detectVideoWithDimensionPlaying, video_width, video_height);
+      })
       .catch(failedCallback);
 }
 
-function startVideoCaptureFromDeviceNamedVirtualDeviceAndVerifySize() {
+function startVideoCaptureFromVirtualDeviceAndVerifyUniformColorVideoWithSize(
+    video_width, video_height) {
   console.log('Trying to find device named "Virtual Device".');
   navigator.mediaDevices.enumerateDevices().then(function(devices) {
     var target_device;
@@ -36,10 +44,18 @@
       return;
     }
     var device_specific_constraints = {
-      video: {width: {exact: WIDTH}, deviceId: {exact: target_device.deviceId}}
+      video: {
+        width: {exact: video_width},
+        height: {exact: video_height},
+        deviceId: {exact: target_device.deviceId}
+      }
     };
     navigator.mediaDevices.getUserMedia(device_specific_constraints)
-        .then(gotStreamCallback)
+        .then(function(stream) {
+          waitForVideoStreamToSatisfyRequirementFunction(
+              stream, detectUniformColorVideoWithDimensionPlaying, video_width,
+              video_height);
+        })
         .catch(failedCallback);
   });
 }
@@ -68,10 +84,17 @@
   failTest('GetUserMedia call failed with code ' + error.code);
 }
 
-function gotStreamCallback(stream) {
+function waitForVideoStreamToSatisfyRequirementFunction(
+    stream, requirementFunction, video_width, video_height) {
   var localView = $('local-view');
+  localView.width = video_width;
+  localView.height = video_height;
   localView.srcObject = stream;
 
+  var canvas = $('local-view-canvas');
+  canvas.width = video_width;
+  canvas.height = video_height;
+
   var videoTracks = stream.getVideoTracks();
   if (videoTracks.length == 0) {
     failTest('Did not receive any video tracks');
@@ -81,8 +104,8 @@
     hasReceivedTrackEndedEvent = true;
   };
 
-  detectVideoPlaying('local-view').then(() => {
-    if (localView.videoWidth == WIDTH) {
+  requirementFunction('local-view', video_width, video_height).then(() => {
+    if (localView.videoWidth == video_width) {
       reportTestSuccess();
     } else {
       failTest('Video has unexpected width.');
diff --git a/content/test/data/media/webrtc_test_utilities.js b/content/test/data/media/webrtc_test_utilities.js
index fe52640..997da3c 100644
--- a/content/test/data/media/webrtc_test_utilities.js
+++ b/content/test/data/media/webrtc_test_utilities.js
@@ -50,60 +50,82 @@
   return detectVideo(videoElementName, isVideoPlaying);
 }
 
+function detectVideoWithDimensionPlaying(
+    videoElementName, video_width, video_height) {
+  return detectVideoWithDimension(
+      videoElementName, isVideoPlaying, video_width, video_height);
+}
+
 function detectVideoStopped(videoElementName) {
-  return detectVideo(videoElementName,
-                     function (pixels, previous_pixels) {
-                       return !isVideoPlaying(pixels, previous_pixels);
-                     });
+  return detectVideo(videoElementName, function(pixels, previous_pixels) {
+    return !isVideoPlaying(pixels, previous_pixels);
+  });
 }
 
 function detectBlackVideo(videoElementName) {
-  return detectVideo(videoElementName,
-                     function (pixels, previous_pixels) {
-                       return isVideoBlack(pixels);
-                     });
+  return detectVideo(videoElementName, function(pixels, previous_pixels) {
+    return isVideoBlack(pixels);
+  });
+}
+
+function detectUniformColorVideoWithDimensionPlaying(
+    videoElementName, video_width, video_height) {
+  return detectVideoWithDimension(
+      videoElementName, function(pixels, previous_pixels) {
+        return isVideoPlaying(pixels, previous_pixels) &&
+            arePixelsUniformColor(pixels) &&
+            arePixelsUniformColor(previous_pixels);
+      }, video_width, video_height);
 }
 
 function detectVideo(videoElementName, predicate) {
+  return detectVideoWithDimension(
+      videoElementName, predicate, VIDEO_TAG_WIDTH, VIDEO_TAG_HEIGHT);
+}
+
+function detectVideoWithDimension(
+    videoElementName, predicate, video_width, video_height) {
   console.log('Looking at video in element ' + videoElementName);
 
   return new Promise((resolve, reject) => {
-    var width = VIDEO_TAG_WIDTH;
-    var height = VIDEO_TAG_HEIGHT;
+    var width = video_width;
+    var height = video_height;
     var videoElement = $(videoElementName);
     var oldPixels = [];
     var startTimeMs = new Date().getTime();
     var waitVideo = setInterval(function() {
       var canvas = $(videoElementName + '-canvas');
       if (canvas == null) {
-        console.log('Waiting for ' + videoElementName + '-canvas' +
-                    ' to appear');
+        console.log(
+            'Waiting for ' + videoElementName + '-canvas' +
+            ' to appear');
         return;
       }
       var context = canvas.getContext('2d');
-      context.drawImage(videoElement, 0, 0, width, height);
-      var pixels = context.getImageData(0, 0 , width, height / 3).data;
+      context.drawImage(videoElement, 0, 0);
+      var pixels = context.getImageData(0, 0, width, height / 3).data;
 
       // Check that there is an old and a new picture with the same size to
       // compare and use the function |predicate| to detect the video state in
       // that case.
       // There's a failure(?) mode here where the video generated claims to
       // have size 2x2. Don't consider that a valid video.
-      if (oldPixels.length == pixels.length &&
-          predicate(pixels, oldPixels)) {
+      if (oldPixels.length == pixels.length && predicate(pixels, oldPixels)) {
         console.log('Done looking at video in element ' + videoElementName);
         console.log('DEBUG: video.width = ' + videoElement.videoWidth);
         console.log('DEBUG: video.height = ' + videoElement.videoHeight);
         clearInterval(waitVideo);
-        resolve({'width': videoElement.videoWidth,
-                 'height': videoElement.videoHeight});
+        resolve({
+          'width': videoElement.videoWidth,
+          'height': videoElement.videoHeight
+        });
       }
       oldPixels = pixels;
       var elapsedTime = new Date().getTime() - startTimeMs;
       if (elapsedTime > 3000) {
         startTimeMs = new Date().getTime();
-        console.log('Still waiting for video to satisfy ' +
-                    predicate.toString());
+        console.log(
+            'Still waiting for video to satisfy ' + predicate.toString());
         console.log('DEBUG: video.width = ' + videoElement.videoWidth);
         console.log('DEBUG: video.height = ' + videoElement.videoHeight);
       }
@@ -113,12 +135,13 @@
 
 function waitForConnectionToStabilize(peerConnection) {
   return new Promise((resolve, reject) => {
-    peerConnection.onsignalingstatechange = function(event) {
-      if (peerConnection.signalingState == 'stable') {
-        peerConnection.onsignalingstatechange = null;
-        resolve();
-      }
-    }
+    peerConnection.onsignalingstatechange =
+        function(event) {
+          if (peerConnection.signalingState == 'stable') {
+            peerConnection.onsignalingstatechange = null;
+            resolve();
+          }
+        }
   });
 }
 
@@ -147,6 +170,36 @@
   return true;
 }
 
+// |pixels| is in RGBA (i.e. pixels[0] is the R value for the first pixel).
+function arePixelsUniformColor(pixels) {
+  if (pixels.length < 4) {
+    failTest('expected at least one pixel');
+  }
+  var reference_r = pixels[0];
+  var reference_g = pixels[1];
+  var reference_b = pixels[2];
+  var reference_a = pixels[3];
+  for (var i = 4; i < pixels.length; i += 4) {
+    if (pixels[i + 0] != reference_r) {
+      console.log('red value at pixel ' + i + ' does not match reference');
+      return false;
+    }
+    if (pixels[i + 1] != reference_g) {
+      console.log('green value at pixel ' + i + ' does not match reference');
+      return false;
+    }
+    if (pixels[i + 2] != reference_b) {
+      console.log('blue value at pixel ' + i + ' does not match reference');
+      return false;
+    }
+    if (pixels[i + 3] != reference_a) {
+      console.log('alpha value at pixel ' + i + ' does not match reference');
+      return false;
+    }
+  }
+  return true;
+}
+
 // Checks if the given color is within 1 value away from COLOR_BACKGROUND_GREEN.
 function isAlmostBackgroundGreen(color) {
   if (Math.abs(color - COLOR_BACKGROUND_GREEN) > 1)
@@ -165,13 +218,13 @@
 // types of the operands aren't checked).
 function assertEquals(expected, actual) {
   if (actual != expected) {
-    failTest("expected '" + expected + "', got '" + actual + "'.");
+    failTest('expected \'' + expected + '\', got \'' + actual + '\'.');
   }
 }
 
 function assertNotEquals(expected, actual) {
   if (actual === expected) {
-    failTest("expected '" + expected + "', got '" + actual + "'.");
+    failTest('expected \'' + expected + '\', got \'' + actual + '\'.');
   }
 }
 
diff --git a/docs/gpu/gpu_testing_bot_details.md b/docs/gpu/gpu_testing_bot_details.md
index 23473225..d36881f1 100644
--- a/docs/gpu/gpu_testing_bot_details.md
+++ b/docs/gpu/gpu_testing_bot_details.md
@@ -583,7 +583,7 @@
 1.  When it is, update [waterfalls.pyl] to use the
     "gpu trigger script" functionality to select *either* the stable *or* the
     new driver version on the stable version of the bot. See [this
-    CL](https://chromium-review.googlesource.com/882344) for an example, though
+    CL](https://chromium-review.googlesource.com/1189059) for an example, though
     that CL was targeting a different OS version rather than driver version.
 1.  After that lands, ask the Chrome Infrastructure Labs team to roll out the
     driver update across all of the similarly configured bots in the swarming
@@ -591,7 +591,9 @@
 1.  If necessary, update pixel test expectations and remove the suppressions
     added above.
 1.  Remove the alternate swarming dimensions for the stable bot from
-    [waterfalls.pyl], locking it to the new driver version.
+    [waterfalls.pyl], locking it to the new driver version. See [this
+    CL](https://chromium-review.googlesource.com/1197329) for an example, though
+    that CL was targeting a different OS version rather than driver version.
 
 Note that we leave the experimental bot in place. We could reclaim it, but it
 seems worthwhile to continuously test the "next" version of graphics drivers as
diff --git a/extensions/browser/extension_function_histogram_value.h b/extensions/browser/extension_function_histogram_value.h
index 97bbd933..b7303a1 100644
--- a/extensions/browser/extension_function_histogram_value.h
+++ b/extensions/browser/extension_function_histogram_value.h
@@ -1338,6 +1338,7 @@
   TABCAPTURE_GETMEDIASTREAMID = 1275,
   WEBVIEWINTERNAL_SETSPATIALNAVIGATIONENABLED = 1276,
   WEBVIEWINTERNAL_ISSPATIALNAVIGATIONENABLED = 1277,
+  FILEMANAGERPRIVATEINTERNAL_GETTHUMBNAIL = 1278,
   // Last entry: Add new entries above, then run:
   // python tools/metrics/histograms/update_extension_histograms.py
   ENUM_BOUNDARY
diff --git a/extensions/renderer/guest_view/mime_handler_view/mime_handler_view_container_base.cc b/extensions/renderer/guest_view/mime_handler_view/mime_handler_view_container_base.cc
index 19b69e9..3b5841d 100644
--- a/extensions/renderer/guest_view/mime_handler_view/mime_handler_view_container_base.cc
+++ b/extensions/renderer/guest_view/mime_handler_view/mime_handler_view_container_base.cc
@@ -125,7 +125,8 @@
 class MimeHandlerViewContainerBase::PluginResourceThrottle
     : public content::URLLoaderThrottle {
  public:
-  explicit PluginResourceThrottle(MimeHandlerViewContainerBase* container)
+  explicit PluginResourceThrottle(
+      base::WeakPtr<MimeHandlerViewContainerBase> container)
       : container_(container) {}
   ~PluginResourceThrottle() override {}
 
@@ -134,6 +135,14 @@
   void WillProcessResponse(const GURL& response_url,
                            network::ResourceResponseHead* response_head,
                            bool* defer) override {
+    if (!container_) {
+      // In the embedder case if the plugin element is removed right after an
+      // ongoing request is made, MimeHandlerViewContainerBase is destroyed
+      // synchronously but the WebURLLoaderImpl corresponding to this throttle
+      // goes away asynchronously when ResourceLoader::CancelTimerFired() is
+      // called (see https://crbug.com/878359).
+      return;
+    }
     network::mojom::URLLoaderPtr dummy_new_loader;
     mojo::MakeRequest(&dummy_new_loader);
     network::mojom::URLLoaderClientPtr new_client;
@@ -158,7 +167,7 @@
     container_->SetEmbeddedLoader(std::move(transferrable_loader));
   }
 
-  MimeHandlerViewContainerBase* container_;
+  base::WeakPtr<MimeHandlerViewContainerBase> container_;
 
   DISALLOW_COPY_AND_ASSIGN(PluginResourceThrottle);
 };
@@ -226,7 +235,7 @@
     return nullptr;
 
   waiting_to_create_throttle_ = false;
-  return std::make_unique<PluginResourceThrottle>(this);
+  return std::make_unique<PluginResourceThrottle>(weak_factory_.GetWeakPtr());
 }
 
 void MimeHandlerViewContainerBase::PostJavaScriptMessage(
diff --git a/headless/app/headless_shell.cc b/headless/app/headless_shell.cc
index 244636b..4159f2d 100644
--- a/headless/app/headless_shell.cc
+++ b/headless/app/headless_shell.cc
@@ -409,7 +409,15 @@
   std::stringstream expression;
   while (true) {
     int c = fgetc(stdin);
-    if (c == EOF || c == '\n') {
+    if (c == '\n')
+      break;
+    if (c == EOF) {
+      // If there's no expression, then quit.
+      if (expression.str().size() == 0) {
+        printf("\n");
+        Shutdown();
+        return;
+      }
       break;
     }
     expression << static_cast<char>(c);
diff --git a/infra/config/branch/cq.cfg b/infra/config/branch/cq.cfg
index 80de542..029ad3b 100644
--- a/infra/config/branch/cq.cfg
+++ b/infra/config/branch/cq.cfg
@@ -76,11 +76,7 @@
         experiment_percentage: 10
       }
       builders { name: "linux-jumbo-rel" }
-      # TODO(crbug.com/646404): Scale this up to 100% mandatory.
-      builders {
-        name: "linux-libfuzzer-asan-rel"
-        experiment_percentage: 100
-      }
+      builders { name: "linux-libfuzzer-asan-rel" }
       builders { name: "linux-ozone-rel" }
       builders { name: "linux_chromium_compile_dbg_ng" }
       builders { name: "linux_chromium_asan_rel_ng" }
diff --git a/ios/chrome/app/BUILD.gn b/ios/chrome/app/BUILD.gn
index 06d7ea3..61c81cc4 100644
--- a/ios/chrome/app/BUILD.gn
+++ b/ios/chrome/app/BUILD.gn
@@ -215,6 +215,7 @@
     "//ios/chrome/browser/ui/history",
     "//ios/chrome/browser/ui/main",
     "//ios/chrome/browser/ui/main:feature_flags",
+    "//ios/chrome/browser/ui/main:tab_switcher",
     "//ios/chrome/browser/ui/promos",
     "//ios/chrome/browser/ui/settings",
     "//ios/chrome/browser/ui/signin_interaction",
@@ -222,7 +223,6 @@
     "//ios/chrome/browser/ui/tab_grid",
     "//ios/chrome/browser/ui/tab_grid:tab_grid_ui",
     "//ios/chrome/browser/ui/tab_switcher",
-    "//ios/chrome/browser/ui/tab_switcher:modes",
     "//ios/chrome/browser/ui/tabs",
     "//ios/chrome/browser/ui/toolbar/clean:toolbar_ui",
     "//ios/chrome/browser/ui/toolbar/public",
diff --git a/ios/chrome/app/main_controller.mm b/ios/chrome/app/main_controller.mm
index 45bfea07..0cb5689 100644
--- a/ios/chrome/app/main_controller.mm
+++ b/ios/chrome/app/main_controller.mm
@@ -123,6 +123,7 @@
 #import "ios/chrome/browser/ui/main/browser_view_wrangler.h"
 #import "ios/chrome/browser/ui/main/main_coordinator.h"
 #import "ios/chrome/browser/ui/main/main_feature_flags.h"
+#import "ios/chrome/browser/ui/main/tab_switcher_mode.h"
 #import "ios/chrome/browser/ui/main/view_controller_swapping.h"
 #import "ios/chrome/browser/ui/orientation_limiting_navigation_controller.h"
 #import "ios/chrome/browser/ui/promos/signin_promo_view_controller.h"
@@ -132,7 +133,7 @@
 #import "ios/chrome/browser/ui/stack_view/stack_view_controller.h"
 #include "ios/chrome/browser/ui/tab_grid/tab_grid_coordinator.h"
 #import "ios/chrome/browser/ui/tab_switcher/tab_switcher_controller.h"
-#import "ios/chrome/browser/ui/tab_switcher/tab_switcher_mode.h"
+#import "ios/chrome/browser/ui/tab_switcher/tab_switcher_transition_context.h"
 #import "ios/chrome/browser/ui/toolbar/public/omnibox_focuser.h"
 #include "ios/chrome/browser/ui/ui_util.h"
 #import "ios/chrome/browser/ui/uikit_ui_util.h"
diff --git a/ios/chrome/app/startup_tasks.mm b/ios/chrome/app/startup_tasks.mm
index b5558d6..5070c7a 100644
--- a/ios/chrome/app/startup_tasks.mm
+++ b/ios/chrome/app/startup_tasks.mm
@@ -19,6 +19,7 @@
 #include "ios/chrome/browser/reading_list/reading_list_download_service_factory.h"
 #import "ios/chrome/browser/ui/main/browser_view_information.h"
 #import "ios/chrome/browser/upgrade/upgrade_center.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
@@ -63,9 +64,7 @@
     return;
   // Start omaha service. We only do this on official builds.
   OmahaService::Start(
-      GetApplicationContext()
-          ->GetIOSChromeIOThread()
-          ->system_url_request_context_getter(),
+      GetApplicationContext()->GetSharedURLLoaderFactory()->Clone(),
       base::BindRepeating(^(const UpgradeRecommendedDetails& details) {
         [[UpgradeCenter sharedInstance] upgradeNotificationDidOccur:details];
       }));
diff --git a/ios/chrome/app/strings/ios_strings.grd b/ios/chrome/app/strings/ios_strings.grd
index 75b467ab..581981e5 100644
--- a/ios/chrome/app/strings/ios_strings.grd
+++ b/ios/chrome/app/strings/ios_strings.grd
@@ -193,7 +193,7 @@
         YES, I'M IN
       </message>
       <message name="IDS_IOS_ACCOUNT_UNIFIED_CONSENT_PERSONALIZED" desc="Description on the sign-in consent dialog to explain the benefit to sign-in. [Length: unlimited] [iOS only]" translateable="false">
-      Personalized Google services like Google Pay
+      More personal Google services, like better page suggestions
       </message>
       <message name="IDS_IOS_ACCOUNT_UNIFIED_CONSENT_SYNC_DATA" desc="Description on the sign-in consent dialog to explain the benefit to sign-in. [Length: unlimited] [iOS only]" translateable="false">
       Your passwords, history &amp; more on all devices
@@ -894,15 +894,6 @@
       <message name="IDS_IOS_NEW_INCOGNITO_TAB_IPH_PROMOTION_TEXT" desc="Text for the New Incognito Tab Tip in-product help promotion, explaining that incognito tabs can be used to browse privately. [iOS only]" meaning="The user has never created an incognito tab before. A bubble is displayed pointing to the tools menu button and informing users about how to use incognito mode.">
         To browse privately, open an incognito tab
       </message>
-      <message name="IDS_IOS_NEW_TAB_BOOKMARKS_PAGE_TITLE_MOBILE" desc="The 'Bookmarks' title on the new tab page [Length: 10em]">
-        Bookmarks
-      </message>
-      <message name="IDS_IOS_NEW_TAB_HOME" desc="Title for the Home panel of the new tab page. [Length: 10em] [iOS only]">
-        Home
-      </message>
-      <message name="IDS_IOS_NEW_TAB_INCOGNITO" desc="Title for the Incognito panel of the new tab page.">
-        Incognito
-      </message>
       <message name="IDS_IOS_NEW_TAB_IPH_PROMOTION_TEXT" desc="Text for the New Tab Tip in-product help promotion, explaining that new tabs can be added and switched between. [iOS only]" meaning="The user has never created a new tab, and a bubble is displayed pointing to the tab switcher to inform users how to create tabs and switch between them.">
         Add tabs and switch between pages
       </message>
diff --git a/ios/chrome/app/tests_fake_hook.mm b/ios/chrome/app/tests_fake_hook.mm
index f7af309d..b6224951 100644
--- a/ios/chrome/app/tests_fake_hook.mm
+++ b/ios/chrome/app/tests_fake_hook.mm
@@ -34,6 +34,10 @@
 bool ForceUIRefreshPhase1() {
   return false;
 }
+// TODO(crbug.com/885003) : Remove this hook.
+bool ForceWKWebViewSnapshots() {
+  return false;
+}
 void SetUpTestsIfPresent() {}
 void RunTestsIfPresent() {}
 
diff --git a/ios/chrome/app/tests_hook.h b/ios/chrome/app/tests_hook.h
index 18f5518..bb2a854 100644
--- a/ios/chrome/app/tests_hook.h
+++ b/ios/chrome/app/tests_hook.h
@@ -41,6 +41,11 @@
 // overriding the flag value.
 bool ForceUIRefreshPhase1();
 
+// TODO(crbug.com/885003) : Remove this hook.
+// Returns true if the WKWebView snapshotting API will be used, overriding the
+// flag value.
+bool ForceWKWebViewSnapshots();
+
 // Global integration tests setup.  This is not used by EarlGrey-based
 // integration tests.
 void SetUpTestsIfPresent();
diff --git a/ios/chrome/browser/about_flags.mm b/ios/chrome/browser/about_flags.mm
index dbbb584..d2f7a0b 100644
--- a/ios/chrome/browser/about_flags.mm
+++ b/ios/chrome/browser/about_flags.mm
@@ -387,6 +387,9 @@
     {"sso-with-wkwebview", flag_descriptions::kSSOWithWKWebViewName,
      flag_descriptions::kSSOWithWKWebViewDescription, flags_ui::kOsIos,
      FEATURE_VALUE_TYPE(kSSOWithWKWebView)},
+    {"wk-web-view-snapshots", flag_descriptions::kWKWebViewSnapshotsName,
+     flag_descriptions::kWKWebViewSnapshotsDescription, flags_ui::kOsIos,
+     FEATURE_VALUE_TYPE(kWKWebViewSnapshots)},
 };
 
 // Add all switches from experimental flags to |command_line|.
diff --git a/ios/chrome/browser/autofill/autofill_controller.h b/ios/chrome/browser/autofill/autofill_controller.h
index d69dc441..ef1ad49 100644
--- a/ios/chrome/browser/autofill/autofill_controller.h
+++ b/ios/chrome/browser/autofill/autofill_controller.h
@@ -21,6 +21,7 @@
 }
 
 namespace web {
+class WebFrame;
 class WebState;
 }
 
@@ -59,7 +60,8 @@
 // Sends the field type predictions specified in |forms| to the renderer. This
 // method is a no-op if the appropriate experiment is not set.
 - (void)sendAutofillTypePredictionsToRenderer:
-    (const std::vector<autofill::FormDataPredictions>&)forms;
+            (const std::vector<autofill::FormDataPredictions>&)forms
+                                      toFrame:(web::WebFrame*)frame;
 
 // Sets a weak reference to the view controller used to present UI.
 - (void)setBaseViewController:(UIViewController*)baseViewController;
diff --git a/ios/chrome/browser/autofill/autofill_controller.mm b/ios/chrome/browser/autofill/autofill_controller.mm
index ee7ac70..d1406709 100644
--- a/ios/chrome/browser/autofill/autofill_controller.mm
+++ b/ios/chrome/browser/autofill/autofill_controller.mm
@@ -17,6 +17,7 @@
 #import "components/autofill/ios/browser/autofill_client_ios_bridge.h"
 #include "components/autofill/ios/browser/autofill_driver_ios.h"
 #include "components/autofill/ios/browser/autofill_driver_ios_bridge.h"
+#include "components/autofill/ios/browser/autofill_switches.h"
 #import "components/autofill/ios/browser/form_suggestion.h"
 #import "components/autofill/ios/browser/form_suggestion_provider.h"
 #include "components/infobars/core/infobar_manager.h"
@@ -27,6 +28,9 @@
 #include "ios/chrome/browser/infobars/infobar_manager_impl.h"
 #include "ios/chrome/browser/pref_names.h"
 #import "ios/chrome/browser/ui/autofill/chrome_autofill_client_ios.h"
+#include "ios/web/public/web_state/web_frame.h"
+#include "ios/web/public/web_state/web_frame_util.h"
+#import "ios/web/public/web_state/web_frames_manager.h"
 #import "ios/web/public/web_state/web_state.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
@@ -39,7 +43,7 @@
                                  AutofillDriverIOSBridge> {
   AutofillAgent* _autofillAgent;
   std::unique_ptr<autofill::ChromeAutofillClientIOS> _autofillClient;
-  autofill::AutofillManager* _autofillManager;  // weak
+  web::WebState* _webState;
 }
 
 @end
@@ -60,21 +64,20 @@
   self = [super init];
   if (self) {
     _browserState = browserState;
+    _webState = webState;
     infobars::InfoBarManager* infobarManager =
         InfoBarManagerImpl::FromWebState(webState);
     DCHECK(infobarManager);
     _autofillClient.reset(new autofill::ChromeAutofillClientIOS(
         browserState, webState, infobarManager, self,
         passwordGenerationManager));
-    autofill::AutofillDriverIOS::CreateForWebStateAndDelegate(
+    autofill::AutofillDriverIOS::PrepareForWebStateWebFrameAndDelegate(
         webState, _autofillClient.get(), self,
         GetApplicationContext()->GetApplicationLocale(),
         downloadEnabled
             ? autofill::AutofillManager::ENABLE_AUTOFILL_DOWNLOAD_MANAGER
             : autofill::AutofillManager::DISABLE_AUTOFILL_DOWNLOAD_MANAGER);
     _autofillAgent = autofillAgent;
-    _autofillManager =
-        autofill::AutofillDriverIOS::FromWebState(webState)->autofill_manager();
   }
   return self;
 }
@@ -103,15 +106,29 @@
 }
 
 - (void)detachFromWebState {
-  _autofillManager = nullptr;
   [_autofillAgent detachFromWebState];
   _autofillAgent = nil;
+  _webState = nullptr;
 }
 
 - (void)setBaseViewController:(UIViewController*)baseViewController {
   _autofillClient->SetBaseViewController(baseViewController);
 }
 
+// Return the AutofillManager associated to |frame|.
+// If autofill in iframes is disabled, ignore the frame parameter and return the
+// AutofillManager associated with |_webState|.
+- (autofill::AutofillManager*)autofillManagerForFrame:(web::WebFrame*)frame {
+  if (!_webState) {
+    return nil;
+  }
+  if (autofill::switches::IsAutofillIFrameMessagingEnabled() && !frame) {
+    return nil;
+  }
+  return autofill::AutofillDriverIOS::FromWebStateAndWebFrame(_webState, frame)
+      ->autofill_manager();
+}
+
 #pragma mark - AutofillClientIOSBridge
 
 - (void)
@@ -172,14 +189,17 @@
 #pragma mark - AutofillDriverIOSBridge
 
 - (void)onFormDataFilled:(uint16_t)query_id
+                 inFrame:(web::WebFrame*)frame
                   result:(const autofill::FormData&)result {
   [_autofillAgent onFormDataFilled:result];
-  if (_autofillManager)
-    _autofillManager->OnDidFillAutofillFormData(result, base::TimeTicks::Now());
+  autofill::AutofillManager* manager = [self autofillManagerForFrame:frame];
+  if (manager)
+    manager->OnDidFillAutofillFormData(result, base::TimeTicks::Now());
 }
 
 - (void)sendAutofillTypePredictionsToRenderer:
-    (const std::vector<autofill::FormDataPredictions>&)forms {
+            (const std::vector<autofill::FormDataPredictions>&)forms
+                                      toFrame:(web::WebFrame*)frame {
   [_autofillAgent renderAutofillTypePredictions:forms];
 }
 
diff --git a/ios/chrome/browser/autofill/autofill_controller_unittest.mm b/ios/chrome/browser/autofill/autofill_controller_unittest.mm
index 0c34b28b..7fe0240 100644
--- a/ios/chrome/browser/autofill/autofill_controller_unittest.mm
+++ b/ios/chrome/browser/autofill/autofill_controller_unittest.mm
@@ -27,6 +27,7 @@
 #include "components/keyed_service/core/service_access_type.h"
 #include "components/security_state/ios/ssl_status_input_event_data.h"
 #import "ios/chrome/browser/autofill/form_suggestion_controller.h"
+#include "ios/chrome/browser/autofill/personal_data_manager_factory.h"
 #include "ios/chrome/browser/browser_state/test_chrome_browser_state.h"
 #include "ios/chrome/browser/infobars/infobar_manager_impl.h"
 #include "ios/chrome/browser/ssl/ios_security_state_tab_helper.h"
@@ -40,6 +41,9 @@
 #import "ios/web/public/navigation_manager.h"
 #include "ios/web/public/ssl_status.h"
 #import "ios/web/public/web_state/js/crw_js_injection_receiver.h"
+#include "ios/web/public/web_state/web_frame.h"
+#include "ios/web/public/web_state/web_frame_util.h"
+#import "ios/web/public/web_state/web_frames_manager.h"
 #import "ios/web/public/web_state/web_state.h"
 #import "testing/gtest_mac.h"
 #include "ui/base/test/ios/ui_view_test_utils.h"
@@ -274,11 +278,11 @@
 // Checks that viewing an HTML page containing a form results in the form being
 // registered as a FormStructure by the AutofillManager.
 TEST_F(AutofillControllerTest, ReadForm) {
-  AutofillManager* autofill_manager =
-      AutofillDriverIOS::FromWebState(web_state())->autofill_manager();
-  EXPECT_TRUE(autofill_manager->form_structures().empty())
-      << "Forms are registered at beginning";
   LoadHtml(kProfileFormHtml);
+  web::WebFrame* main_frame = web::GetMainWebFrame(web_state());
+  AutofillManager* autofill_manager =
+      AutofillDriverIOS::FromWebStateAndWebFrame(web_state(), main_frame)
+          ->autofill_manager();
   const auto& forms = autofill_manager->form_structures();
   ASSERT_EQ(1U, forms.size());
   const auto& form = *(forms.begin()->second);
@@ -295,9 +299,11 @@
 // the form being registered as a FormStructure by the AutofillManager, and the
 // name is correctly set.
 TEST_F(AutofillControllerTest, ReadFormName) {
-  AutofillManager* autofill_manager =
-      AutofillDriverIOS::FromWebState(web_state())->autofill_manager();
   LoadHtml(kMinimalFormWithNameHtml);
+  web::WebFrame* main_frame = web::GetMainWebFrame(web_state());
+  AutofillManager* autofill_manager =
+      AutofillDriverIOS::FromWebStateAndWebFrame(web_state(), main_frame)
+          ->autofill_manager();
   const auto& forms = autofill_manager->form_structures();
   ASSERT_EQ(1U, forms.size());
   const auto& form = *(forms.begin()->second);
@@ -308,10 +314,9 @@
 // with scripts (simulating user form submission) results in a profile being
 // successfully imported into the PersonalDataManager.
 TEST_F(AutofillControllerTest, ProfileImport) {
-  AutofillManager* autofill_manager =
-      AutofillDriverIOS::FromWebState(web_state())->autofill_manager();
   PersonalDataManager* personal_data_manager =
-      autofill_manager->client()->GetPersonalDataManager();
+      PersonalDataManagerFactory::GetForBrowserState(
+          ios::ChromeBrowserState::FromBrowserState(GetBrowserState()));
   // Check there are no registered profiles already.
   EXPECT_EQ(0U, personal_data_manager->GetProfiles().size());
   LoadHtml(kProfileFormHtml);
@@ -342,10 +347,9 @@
 };
 
 void AutofillControllerTest::SetUpForSuggestions(NSString* data) {
-  AutofillManager* autofill_manager =
-      AutofillDriverIOS::FromWebState(web_state())->autofill_manager();
   PersonalDataManager* personal_data_manager =
-      autofill_manager->client()->GetPersonalDataManager();
+      PersonalDataManagerFactory::GetForBrowserState(
+          ios::ChromeBrowserState::FromBrowserState(GetBrowserState()));
   AutofillProfile profile(base::GenerateGUID(), "https://www.example.com/");
   profile.SetRawInfo(NAME_FULL, base::UTF8ToUTF16("Homer Simpson"));
   profile.SetRawInfo(ADDRESS_HOME_LINE1, base::UTF8ToUTF16("123 Main Street"));
@@ -407,10 +411,9 @@
 
 // Checks that multiple profiles will offer a matching number of suggestions.
 TEST_F(AutofillControllerTest, MultipleProfileSuggestions) {
-  AutofillManager* autofill_manager =
-      AutofillDriverIOS::FromWebState(web_state())->autofill_manager();
   PersonalDataManager* personal_data_manager =
-      autofill_manager->client()->GetPersonalDataManager();
+      PersonalDataManagerFactory::GetForBrowserState(
+          ios::ChromeBrowserState::FromBrowserState(GetBrowserState()));
   AutofillProfile profile(base::GenerateGUID(), "https://www.example.com/");
   profile.SetRawInfo(NAME_FULL, base::UTF8ToUTF16("Homer Simpson"));
   profile.SetRawInfo(ADDRESS_HOME_LINE1, base::UTF8ToUTF16("123 Main Street"));
@@ -577,10 +580,10 @@
 // card being successfully imported into the PersonalDataManager.
 TEST_F(AutofillControllerTest, CreditCardImport) {
   InfoBarManagerImpl::CreateForWebState(web_state());
-  AutofillManager* autofill_manager =
-      AutofillDriverIOS::FromWebState(web_state())->autofill_manager();
   PersonalDataManager* personal_data_manager =
-      autofill_manager->client()->GetPersonalDataManager();
+      PersonalDataManagerFactory::GetForBrowserState(
+          ios::ChromeBrowserState::FromBrowserState(GetBrowserState()));
+
   // Check there are no registered profiles already.
   EXPECT_EQ(0U, personal_data_manager->GetCreditCards().size());
   LoadHtml(kCreditCardFormHtml);
diff --git a/ios/chrome/browser/autofill/automation/automation_action.mm b/ios/chrome/browser/autofill/automation/automation_action.mm
index f07a07f..3b35cff 100644
--- a/ios/chrome/browser/autofill/automation/automation_action.mm
+++ b/ios/chrome/browser/autofill/automation/automation_action.mm
@@ -21,6 +21,8 @@
 #import "ios/web/public/test/earl_grey/web_view_matchers.h"
 #include "ios/web/public/test/element_selector.h"
 #import "ios/web/public/test/js_test_util.h"
+#include "ios/web/public/web_state/web_frame_util.h"
+#import "ios/web/public/web_state/web_frames_manager.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
@@ -256,8 +258,11 @@
 // Loads the predefined autofill profile into the personal data manager, so that
 // autofill actions will be suggested when tapping on an autofillable form.
 - (void)prepareAutofillProfileWithWebState:(web::WebState*)web_state {
+  web::WebFrame* main_frame = web::GetMainWebFrame(web_state);
   autofill::AutofillManager* autofill_manager =
-      autofill::AutofillDriverIOS::FromWebState(web_state)->autofill_manager();
+      autofill::AutofillDriverIOS::FromWebStateAndWebFrame(web_state,
+                                                           main_frame)
+          ->autofill_manager();
   autofill::PersonalDataManager* personal_data_manager =
       autofill_manager->client()->GetPersonalDataManager();
   autofill::AutofillProfile profile(base::GenerateGUID(),
diff --git a/ios/chrome/browser/autofill/form_structure_browsertest.mm b/ios/chrome/browser/autofill/form_structure_browsertest.mm
index 73eef725..81e57932 100644
--- a/ios/chrome/browser/autofill/form_structure_browsertest.mm
+++ b/ios/chrome/browser/autofill/form_structure_browsertest.mm
@@ -27,6 +27,9 @@
 #include "ios/chrome/browser/ssl/ios_security_state_tab_helper.h"
 #include "ios/chrome/browser/web/chrome_web_client.h"
 #import "ios/chrome/browser/web/chrome_web_test.h"
+#include "ios/web/public/web_state/web_frame.h"
+#include "ios/web/public/web_state/web_frame_util.h"
+#import "ios/web/public/web_state/web_frames_manager.h"
 #import "ios/web/public/web_state/web_state.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
@@ -164,8 +167,10 @@
                                                std::string* output) {
   ASSERT_TRUE(LoadHtml(input));
   base::TaskScheduler::GetInstance()->FlushForTesting();
+  web::WebFrame* frame = web::GetMainWebFrame(web_state());
   AutofillManager* autofill_manager =
-      AutofillDriverIOS::FromWebState(web_state())->autofill_manager();
+      AutofillDriverIOS::FromWebStateAndWebFrame(web_state(), frame)
+          ->autofill_manager();
   ASSERT_NE(nullptr, autofill_manager);
   *output = FormStructuresToString(autofill_manager->form_structures());
 }
diff --git a/ios/chrome/browser/autofill/form_suggestion_controller.mm b/ios/chrome/browser/autofill/form_suggestion_controller.mm
index 2c038d99..fd30584 100644
--- a/ios/chrome/browser/autofill/form_suggestion_controller.mm
+++ b/ios/chrome/browser/autofill/form_suggestion_controller.mm
@@ -36,6 +36,7 @@
   AutofillSuggestionState(const std::string& form_name,
                           const std::string& field_name,
                           const std::string& field_identifier,
+                          const std::string& frame_identifier,
                           const std::string& typed_value);
   // The name of the form for autofill.
   std::string form_name;
@@ -43,6 +44,8 @@
   std::string field_name;
   // The identifier of the field for autofill.
   std::string field_identifier;
+  // The identifier of the frame for autofill.
+  std::string frame_identifier;
   // The user-typed value in the field.
   std::string typed_value;
   // The suggestions for the form field. An array of |FormSuggestion|.
@@ -53,10 +56,12 @@
     const std::string& form_name,
     const std::string& field_name,
     const std::string& field_identifier,
+    const std::string& frame_identifier,
     const std::string& typed_value)
     : form_name(form_name),
       field_name(field_name),
       field_identifier(field_identifier),
+      frame_identifier(frame_identifier),
       typed_value(typed_value) {}
 
 }  // namespace
@@ -188,6 +193,7 @@
   NSString* strongFieldName = base::SysUTF8ToNSString(params.field_name);
   NSString* strongFieldIdentifier =
       base::SysUTF8ToNSString(params.field_identifier);
+  NSString* strongFrameId = base::SysUTF8ToNSString(params.frame_id);
   NSString* strongFieldType = base::SysUTF8ToNSString(params.field_type);
   NSString* strongType = base::SysUTF8ToNSString(params.type);
   NSString* strongValue =
@@ -217,6 +223,7 @@
                                              fieldType:strongFieldType
                                                   type:strongType
                                             typedValue:strongValue
+                                               frameID:strongFrameId
                                            isMainFrame:is_main_frame
                                         hasUserGesture:has_user_gesture
                                               webState:webState
@@ -249,6 +256,7 @@
                                fieldType:strongFieldType
                                     type:strongType
                               typedValue:strongValue
+                                 frameID:strongFrameId
                                 webState:webState
                        completionHandler:readyCompletion];
   };
@@ -342,6 +350,8 @@
           fieldIdentifier:base::SysUTF8ToNSString(
                               _suggestionState->field_identifier)
                      form:base::SysUTF8ToNSString(_suggestionState->form_name)
+                  frameID:base::SysUTF8ToNSString(
+                              _suggestionState->frame_identifier)
         completionHandler:^{
           [[weakSelf accessoryViewDelegate] closeKeyboardWithoutButtonPress];
         }];
@@ -366,9 +376,9 @@
             accessoryViewUpdateBlock:
                 (AccessoryViewReadyCompletion)accessoryViewUpdateBlock {
   [self processPage:webState];
-  _suggestionState.reset(
-      new AutofillSuggestionState(params.form_name, params.field_name,
-                                  params.field_identifier, params.value));
+  _suggestionState.reset(new AutofillSuggestionState(
+      params.form_name, params.field_name, params.field_identifier,
+      params.frame_id, params.value));
   accessoryViewUpdateBlock_ = [accessoryViewUpdateBlock copy];
   [self retrieveSuggestionsForForm:params webState:webState];
 }
diff --git a/ios/chrome/browser/autofill/form_suggestion_controller_unittest.mm b/ios/chrome/browser/autofill/form_suggestion_controller_unittest.mm
index f550a4c..743ac7f3 100644
--- a/ios/chrome/browser/autofill/form_suggestion_controller_unittest.mm
+++ b/ios/chrome/browser/autofill/form_suggestion_controller_unittest.mm
@@ -38,6 +38,7 @@
 @property(weak, nonatomic, readonly) FormSuggestion* suggestion;
 @property(weak, nonatomic, readonly) NSString* formName;
 @property(weak, nonatomic, readonly) NSString* fieldName;
+@property(weak, nonatomic, readonly) NSString* frameID;
 @property(nonatomic, assign) BOOL selected;
 @property(nonatomic, assign) BOOL askedIfSuggestionsAvailable;
 @property(nonatomic, assign) BOOL askedForSuggestions;
@@ -51,6 +52,7 @@
   NSString* _formName;
   NSString* _fieldName;
   NSString* _fieldIdentifier;
+  NSString* _frameID;
   FormSuggestion* _suggestion;
 }
 
@@ -73,6 +75,10 @@
   return _fieldName;
 }
 
+- (NSString*)frameID {
+  return _frameID;
+}
+
 - (FormSuggestion*)suggestion {
   return _suggestion;
 }
@@ -83,6 +89,7 @@
                                  fieldType:(NSString*)fieldType
                                       type:(NSString*)type
                                 typedValue:(NSString*)typedValue
+                                   frameID:(NSString*)frameID
                                isMainFrame:(BOOL)isMainFrame
                             hasUserGesture:(BOOL)hasUserGesture
                                   webState:(web::WebState*)webState
@@ -98,6 +105,7 @@
                          fieldType:(NSString*)fieldType
                               type:(NSString*)type
                         typedValue:(NSString*)typedValue
+                           frameID:(NSString*)frameID
                           webState:(web::WebState*)webState
                  completionHandler:(SuggestionsReadyCompletion)completion {
   self.askedForSuggestions = YES;
@@ -108,11 +116,13 @@
                   fieldName:(NSString*)fieldName
             fieldIdentifier:(NSString*)fieldIdentifier
                        form:(NSString*)formName
+                    frameID:(NSString*)frameID
           completionHandler:(SuggestionHandledCompletion)completion {
   self.selected = YES;
   _suggestion = suggestion;
   _formName = [formName copy];
   _fieldName = [fieldName copy];
+  _frameID = [frameID copy];
   _fieldIdentifier = [fieldIdentifier copy];
   completion();
 }
@@ -428,6 +438,7 @@
   params.field_type = "text";
   params.type = "type";
   params.value = "value";
+  params.frame_id = "frame_id";
   params.input_missing = false;
   test_form_activity_tab_helper_.FormActivityRegistered(
       /*sender_frame*/ nullptr, params);
@@ -437,6 +448,7 @@
   EXPECT_TRUE([provider selected]);
   EXPECT_NSEQ(@"form", [provider formName]);
   EXPECT_NSEQ(@"field", [provider fieldName]);
+  EXPECT_NSEQ(@"frame_id", [provider frameID]);
   EXPECT_NSEQ(suggestions[0], [provider suggestion]);
 }
 
diff --git a/ios/chrome/browser/browsing_data/browsing_data_remover_impl.mm b/ios/chrome/browser/browsing_data/browsing_data_remover_impl.mm
index 25b4c0d..ff9537af5 100644
--- a/ios/chrome/browser/browsing_data/browsing_data_remover_impl.mm
+++ b/ios/chrome/browser/browsing_data/browsing_data_remover_impl.mm
@@ -312,10 +312,13 @@
     ClearIOSSnapshots(CreatePendingTaskCompletionClosure());
   }
 
+  constexpr base::TaskTraits task_traits = {
+      web::WebThread::IO, base::TaskShutdownBehavior::BLOCK_SHUTDOWN};
+
   if (IsRemoveDataMaskSet(mask, BrowsingDataRemoveMask::REMOVE_COOKIES)) {
     base::RecordAction(base::UserMetricsAction("ClearBrowsingData_Cookies"));
     base::PostTaskWithTraits(
-        FROM_HERE, {web::WebThread::IO},
+        FROM_HERE, task_traits,
         base::BindOnce(
             &ClearCookies, context_getter_,
             net::CookieDeletionInfo::TimeRange(delete_begin, delete_end),
@@ -327,7 +330,7 @@
   if (IsRemoveDataMaskSet(mask, BrowsingDataRemoveMask::REMOVE_CHANNEL_IDS)) {
     base::RecordAction(base::UserMetricsAction("ClearBrowsingData_ChannelIDs"));
     base::PostTaskWithTraits(
-        FROM_HERE, {web::WebThread::IO},
+        FROM_HERE, task_traits,
         base::BindOnce(
             &ClearChannelIDs, context_getter_, delete_begin, delete_end,
             base::BindOnce(base::IgnoreResult(&base::TaskRunner::PostTask),
@@ -370,7 +373,7 @@
         GetApplicationContext()->GetIOSChromeIOThread();
     if (ios_chrome_io_thread) {
       base::PostTaskWithTraitsAndReply(
-          FROM_HERE, {web::WebThread::IO},
+          FROM_HERE, task_traits,
           base::BindOnce(&IOSChromeIOThread::ClearHostCache,
                          base::Unretained(ios_chrome_io_thread)),
           CreatePendingTaskCompletionClosure());
@@ -477,13 +480,12 @@
 
   if (IsRemoveDataMaskSet(mask, BrowsingDataRemoveMask::REMOVE_CACHE)) {
     base::RecordAction(base::UserMetricsAction("ClearBrowsingData_Cache"));
-    ClearHttpCache(
-        context_getter_,
-        base::CreateSingleThreadTaskRunnerWithTraits({web::WebThread::IO}),
-        delete_begin, delete_end,
-        AdaptCallbackForRepeating(
-            base::BindOnce(&NetCompletionCallbackAdapter,
-                           CreatePendingTaskCompletionClosure())));
+    ClearHttpCache(context_getter_,
+                   base::CreateSingleThreadTaskRunnerWithTraits(task_traits),
+                   delete_begin, delete_end,
+                   AdaptCallbackForRepeating(
+                       base::BindOnce(&NetCompletionCallbackAdapter,
+                                      CreatePendingTaskCompletionClosure())));
   }
 
   // Remove omnibox zero-suggest cache results.
diff --git a/ios/chrome/browser/download/browser_download_service.h b/ios/chrome/browser/download/browser_download_service.h
index 34aae209..ad8069c 100644
--- a/ios/chrome/browser/download/browser_download_service.h
+++ b/ios/chrome/browser/download/browser_download_service.h
@@ -28,7 +28,13 @@
   PkPass = 1,
   // application/x-apple-aspen-config MIME type.
   iOSMobileConfig = 2,
-  kMaxValue = iOSMobileConfig,
+  // application/zip MIME type.
+  ZipArchive = 3,
+  // application/x-msdownload MIME type (.exe file).
+  MicrosoftApplication = 4,
+  // application/vnd.android.package-archive MIME type (.apk file).
+  AndroidPackageArchive = 5,
+  kMaxValue = AndroidPackageArchive,
 };
 
 // Keyed Service which acts as web::DownloadController delegate and routes
diff --git a/ios/chrome/browser/download/browser_download_service.mm b/ios/chrome/browser/download/browser_download_service.mm
index 7e11a2d..f2e27ee 100644
--- a/ios/chrome/browser/download/browser_download_service.mm
+++ b/ios/chrome/browser/download/browser_download_service.mm
@@ -21,9 +21,18 @@
   if (mime_type == kPkPassMimeType)
     return DownloadMimeTypeResult::PkPass;
 
+  if (mime_type == "application/zip")
+    return DownloadMimeTypeResult::ZipArchive;
+
   if (mime_type == "application/x-apple-aspen-config")
     return DownloadMimeTypeResult::iOSMobileConfig;
 
+  if (mime_type == "application/x-msdownload")
+    return DownloadMimeTypeResult::MicrosoftApplication;
+
+  if (mime_type == "application/vnd.android.package-archive")
+    return DownloadMimeTypeResult::AndroidPackageArchive;
+
   return DownloadMimeTypeResult::Other;
 }
 }  // namespace
diff --git a/ios/chrome/browser/download/browser_download_service_unittest.mm b/ios/chrome/browser/download/browser_download_service_unittest.mm
index 64ea4a0..71b4a7e 100644
--- a/ios/chrome/browser/download/browser_download_service_unittest.mm
+++ b/ios/chrome/browser/download/browser_download_service_unittest.mm
@@ -157,3 +157,59 @@
           DownloadMimeTypeResult::iOSMobileConfig),
       1);
 }
+
+// Tests that BrowserDownloadService uses DownloadManagerTabHelper for Zip Mime
+// Type.
+TEST_F(BrowserDownloadServiceTest, ZipArchiveMimeType) {
+  ASSERT_TRUE(download_controller()->GetDelegate());
+  auto task =
+      std::make_unique<web::FakeDownloadTask>(GURL(kUrl), "application/zip");
+  web::DownloadTask* task_ptr = task.get();
+  download_controller()->GetDelegate()->OnDownloadCreated(
+      download_controller(), &web_state_, std::move(task));
+  ASSERT_TRUE(pass_kit_tab_helper()->tasks().empty());
+  ASSERT_EQ(1U, download_manager_tab_helper()->tasks().size());
+  EXPECT_EQ(task_ptr, download_manager_tab_helper()->tasks()[0].get());
+  histogram_tester_.ExpectUniqueSample("Download.IOSDownloadMimeType",
+                                       static_cast<base::HistogramBase::Sample>(
+                                           DownloadMimeTypeResult::ZipArchive),
+                                       1);
+}
+
+// Tests that BrowserDownloadService uses DownloadManagerTabHelper for .exe Mime
+// Type.
+TEST_F(BrowserDownloadServiceTest, ExeMimeType) {
+  ASSERT_TRUE(download_controller()->GetDelegate());
+  auto task = std::make_unique<web::FakeDownloadTask>(
+      GURL(kUrl), "application/x-msdownload");
+  web::DownloadTask* task_ptr = task.get();
+  download_controller()->GetDelegate()->OnDownloadCreated(
+      download_controller(), &web_state_, std::move(task));
+  ASSERT_TRUE(pass_kit_tab_helper()->tasks().empty());
+  ASSERT_EQ(1U, download_manager_tab_helper()->tasks().size());
+  EXPECT_EQ(task_ptr, download_manager_tab_helper()->tasks()[0].get());
+  histogram_tester_.ExpectUniqueSample(
+      "Download.IOSDownloadMimeType",
+      static_cast<base::HistogramBase::Sample>(
+          DownloadMimeTypeResult::MicrosoftApplication),
+      1);
+}
+
+// Tests that BrowserDownloadService uses DownloadManagerTabHelper for .apk Mime
+// Type.
+TEST_F(BrowserDownloadServiceTest, ApkMimeType) {
+  ASSERT_TRUE(download_controller()->GetDelegate());
+  auto task = std::make_unique<web::FakeDownloadTask>(
+      GURL(kUrl), "application/vnd.android.package-archive");
+  web::DownloadTask* task_ptr = task.get();
+  download_controller()->GetDelegate()->OnDownloadCreated(
+      download_controller(), &web_state_, std::move(task));
+  ASSERT_TRUE(pass_kit_tab_helper()->tasks().empty());
+  ASSERT_EQ(1U, download_manager_tab_helper()->tasks().size());
+  EXPECT_EQ(task_ptr, download_manager_tab_helper()->tasks()[0].get());
+  histogram_tester_.ExpectUniqueSample(
+      "Download.IOSDownloadMimeType",
+      static_cast<base::HistogramBase::Sample>(
+          DownloadMimeTypeResult::AndroidPackageArchive),
+      1);
+}
diff --git a/ios/chrome/browser/feature_engagement/BUILD.gn b/ios/chrome/browser/feature_engagement/BUILD.gn
index bd858e6..4dde6c1 100644
--- a/ios/chrome/browser/feature_engagement/BUILD.gn
+++ b/ios/chrome/browser/feature_engagement/BUILD.gn
@@ -42,10 +42,10 @@
     "//ios/chrome/browser",
     "//ios/chrome/browser/browser_state",
     "//ios/chrome/browser/ui:ui_util",
+    "//ios/chrome/browser/ui/main:tab_switcher",
     "//ios/chrome/browser/ui/popup_menu:constants",
     "//ios/chrome/browser/ui/tab_grid:egtest_support",
     "//ios/chrome/browser/ui/tab_switcher:egtest_support",
-    "//ios/chrome/browser/ui/tab_switcher:modes",
     "//ios/chrome/browser/ui/table_view",
     "//ios/chrome/browser/ui/tools_menu/public",
     "//ios/chrome/test/app:test_support",
diff --git a/ios/chrome/browser/feature_engagement/feature_engagement_egtest.mm b/ios/chrome/browser/feature_engagement/feature_engagement_egtest.mm
index 3831f340..edc55c9 100644
--- a/ios/chrome/browser/feature_engagement/feature_engagement_egtest.mm
+++ b/ios/chrome/browser/feature_engagement/feature_engagement_egtest.mm
@@ -15,10 +15,10 @@
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
 #include "ios/chrome/browser/experimental_flags.h"
 #include "ios/chrome/browser/feature_engagement/tracker_factory.h"
+#import "ios/chrome/browser/ui/main/tab_switcher_mode.h"
 #import "ios/chrome/browser/ui/popup_menu/popup_menu_constants.h"
 #import "ios/chrome/browser/ui/tab_grid/tab_grid_egtest_util.h"
 #import "ios/chrome/browser/ui/tab_switcher/tab_switcher_egtest_util.h"
-#import "ios/chrome/browser/ui/tab_switcher/tab_switcher_mode.h"
 #import "ios/chrome/browser/ui/table_view/table_view_navigation_controller_constants.h"
 #include "ios/chrome/browser/ui/tools_menu/public/tools_menu_constants.h"
 #include "ios/chrome/browser/ui/ui_util.h"
diff --git a/ios/chrome/browser/infobars/confirm_infobar_controller.h b/ios/chrome/browser/infobars/confirm_infobar_controller.h
index 410a4c720..c705f615 100644
--- a/ios/chrome/browser/infobars/confirm_infobar_controller.h
+++ b/ios/chrome/browser/infobars/confirm_infobar_controller.h
@@ -11,9 +11,8 @@
 
 @interface ConfirmInfoBarController : InfoBarController
 
-- (instancetype)init NS_UNAVAILABLE;
-
-- (instancetype)initWithInfoBarDelegate:(ConfirmInfoBarDelegate*)delegate;
+- (instancetype)initWithInfoBarDelegate:(ConfirmInfoBarDelegate*)infoBarDelegate
+    NS_DESIGNATED_INITIALIZER;
 
 @end
 
diff --git a/ios/chrome/browser/infobars/confirm_infobar_controller.mm b/ios/chrome/browser/infobars/confirm_infobar_controller.mm
index e939c29..7d3288e 100644
--- a/ios/chrome/browser/infobars/confirm_infobar_controller.mm
+++ b/ios/chrome/browser/infobars/confirm_infobar_controller.mm
@@ -33,24 +33,26 @@
 
 #pragma mark - ConfirmInfoBarController
 
-@interface ConfirmInfoBarController () {
-  ConfirmInfoBarDelegate* _confirmInfobarDelegate;
-  __weak ConfirmInfoBarView* _infoBarView;
-}
+@interface ConfirmInfoBarController ()
+
+// Overrides superclass property.
+@property(nonatomic, readonly) ConfirmInfoBarDelegate* infoBarDelegate;
+
+@property(nonatomic, weak) ConfirmInfoBarView* infoBarView;
 
 @end
 
 @implementation ConfirmInfoBarController
 
+@dynamic infoBarDelegate;
+@synthesize infoBarView = _infoBarView;
+
 #pragma mark -
 #pragma mark InfoBarController
 
-- (instancetype)initWithInfoBarDelegate:(ConfirmInfoBarDelegate*)delegate {
-  self = [super init];
-  if (self) {
-    _confirmInfobarDelegate = delegate;
-  }
-  return self;
+- (instancetype)initWithInfoBarDelegate:
+    (ConfirmInfoBarDelegate*)infoBarDelegate {
+  return [super initWithInfoBarDelegate:infoBarDelegate];
 }
 
 - (UIView<InfoBarViewSizing>*)viewForFrame:(CGRect)frame {
@@ -58,17 +60,17 @@
       [[ConfirmInfoBarView alloc] initWithFrame:frame];
   _infoBarView = infoBarView;
   // Model data.
-  gfx::Image modelIcon = _confirmInfobarDelegate->GetIcon();
-  int buttons = _confirmInfobarDelegate->GetButtons();
+  gfx::Image modelIcon = self.infoBarDelegate->GetIcon();
+  int buttons = self.infoBarDelegate->GetButtons();
   NSString* buttonOK = nil;
   if (buttons & ConfirmInfoBarDelegate::BUTTON_OK) {
-    buttonOK = base::SysUTF16ToNSString(_confirmInfobarDelegate->GetButtonLabel(
+    buttonOK = base::SysUTF16ToNSString(self.infoBarDelegate->GetButtonLabel(
         ConfirmInfoBarDelegate::BUTTON_OK));
   }
   NSString* buttonCancel = nil;
   if (buttons & ConfirmInfoBarDelegate::BUTTON_CANCEL) {
     buttonCancel =
-        base::SysUTF16ToNSString(_confirmInfobarDelegate->GetButtonLabel(
+        base::SysUTF16ToNSString(self.infoBarDelegate->GetButtonLabel(
             ConfirmInfoBarDelegate::BUTTON_CANCEL));
   }
 
@@ -97,22 +99,22 @@
                     action:@selector(infoBarButtonDidPress:)];
   } else {
     // No buttons, only message.
-    DCHECK(!_confirmInfobarDelegate->GetMessageText().empty() && !buttonCancel);
+    DCHECK(!self.infoBarDelegate->GetMessageText().empty() && !buttonCancel);
   }
   return infoBarView;
 }
 
 - (void)updateInfobarLabel:(ConfirmInfoBarView*)view {
-  if (!_confirmInfobarDelegate->GetMessageText().length())
+  if (!self.infoBarDelegate->GetMessageText().length())
     return;
-  if (_confirmInfobarDelegate->GetLinkText().length()) {
+  if (self.infoBarDelegate->GetLinkText().length()) {
     base::string16 msgLink = base::SysNSStringToUTF16([[view class]
         stringAsLink:base::SysUTF16ToNSString(
-                         _confirmInfobarDelegate->GetLinkText())
+                         self.infoBarDelegate->GetLinkText())
                  tag:ConfirmInfoBarUITags::TITLE_LINK]);
-    base::string16 messageText = _confirmInfobarDelegate->GetMessageText();
+    base::string16 messageText = self.infoBarDelegate->GetMessageText();
     base::ReplaceFirstSubstringAfterOffset(
-        &messageText, 0, _confirmInfobarDelegate->GetLinkText(), msgLink);
+        &messageText, 0, self.infoBarDelegate->GetLinkText(), msgLink);
 
     __weak ConfirmInfoBarController* weakSelf = self;
     [view addLabel:base::SysUTF16ToNSString(messageText)
@@ -121,7 +123,7 @@
             }];
   } else {
     NSString* label =
-        base::SysUTF16ToNSString(_confirmInfobarDelegate->GetMessageText());
+        base::SysUTF16ToNSString(self.infoBarDelegate->GetMessageText());
     [view addLabel:label];
   }
 }
@@ -133,27 +135,23 @@
 #pragma mark - Handling of User Events
 
 - (void)infoBarButtonDidPress:(id)sender {
-  // This press might have occurred after the user has already pressed a button,
-  // in which case the view has been detached from the delegate and this press
-  // should be ignored.
-  if (!self.delegate) {
+  if ([self shouldIgnoreUserInteraction])
     return;
-  }
 
   NSUInteger buttonId = base::mac::ObjCCastStrict<UIButton>(sender).tag;
   switch (buttonId) {
     case ConfirmInfoBarUITags::OK:
-      if (_confirmInfobarDelegate->Accept()) {
+      if (self.infoBarDelegate->Accept()) {
         self.delegate->RemoveInfoBar();
       }
       break;
     case ConfirmInfoBarUITags::CANCEL:
-      if (_confirmInfobarDelegate->Cancel()) {
+      if (self.infoBarDelegate->Cancel()) {
         self.delegate->RemoveInfoBar();
       }
       break;
     case ConfirmInfoBarUITags::CLOSE:
-      _confirmInfobarDelegate->InfoBarDismissed();
+      self.infoBarDelegate->InfoBarDismissed();
       self.delegate->RemoveInfoBar();
       break;
     default:
@@ -164,13 +162,11 @@
 
 // Title link was clicked.
 - (void)infobarLinkDidPress:(NSUInteger)tag {
-  if (!self.delegate) {
+  if ([self shouldIgnoreUserInteraction])
     return;
-  }
 
   DCHECK(tag == ConfirmInfoBarUITags::TITLE_LINK);
-  _confirmInfobarDelegate->LinkClicked(
-      WindowOpenDisposition::NEW_FOREGROUND_TAB);
+  self.infoBarDelegate->LinkClicked(WindowOpenDisposition::NEW_FOREGROUND_TAB);
 }
 
 @end
diff --git a/ios/chrome/browser/infobars/infobar.h b/ios/chrome/browser/infobars/infobar.h
index 8e01e56..ec3648b 100644
--- a/ios/chrome/browser/infobars/infobar.h
+++ b/ios/chrome/browser/infobars/infobar.h
@@ -39,6 +39,7 @@
 
   // InfoBarControllerDelegate overrides:
   void SetInfoBarTargetHeight(int height) override;
+  bool IsOwned() override;
   void RemoveInfoBar() override;
 
   InfoBarController* controller_;
diff --git a/ios/chrome/browser/infobars/infobar.mm b/ios/chrome/browser/infobars/infobar.mm
index b363de4..6f9e324 100644
--- a/ios/chrome/browser/infobars/infobar.mm
+++ b/ios/chrome/browser/infobars/infobar.mm
@@ -31,7 +31,6 @@
 InfoBarIOS::~InfoBarIOS() {
   DCHECK(controller_);
   [controller_ detachView];
-  [controller_ setDelegate:nullptr];
   controller_ = nil;
 }
 
@@ -62,6 +61,10 @@
   SetTargetHeight(height);
 }
 
+bool InfoBarIOS::IsOwned() {
+  return owner() != nullptr;
+}
+
 void InfoBarIOS::RemoveInfoBar() {
   RemoveSelf();
 }
diff --git a/ios/chrome/browser/infobars/infobar_controller+protected.h b/ios/chrome/browser/infobars/infobar_controller+protected.h
index f3f9ca8f..9dd1a4f 100644
--- a/ios/chrome/browser/infobars/infobar_controller+protected.h
+++ b/ios/chrome/browser/infobars/infobar_controller+protected.h
@@ -13,6 +13,9 @@
 // not add it as a subview yet. This method must be overriden in subclasses.
 - (UIView<InfoBarViewSizing>*)viewForFrame:(CGRect)bounds;
 
+// Returns whether user interaction with the infobar should be ignored.
+- (BOOL)shouldIgnoreUserInteraction;
+
 @end
 
 #endif  // IOS_CHROME_BROWSER_INFOBARS_INFOBAR_CONTROLLER_PROTECTED_H_
diff --git a/ios/chrome/browser/infobars/infobar_controller.h b/ios/chrome/browser/infobars/infobar_controller.h
index 9264cd6..361b8027c 100644
--- a/ios/chrome/browser/infobars/infobar_controller.h
+++ b/ios/chrome/browser/infobars/infobar_controller.h
@@ -9,6 +9,10 @@
 
 #import "ios/chrome/browser/ui/infobars/infobar_view_sizing_delegate.h"
 
+namespace infobars {
+class InfoBarDelegate;
+}  // namespace infobars
+
 class InfoBarControllerDelegate;
 @protocol InfoBarViewSizing;
 
@@ -37,6 +41,14 @@
 
 @property(nonatomic, assign) InfoBarControllerDelegate* delegate;  // weak
 
+@property(nonatomic, readonly)
+    infobars::InfoBarDelegate* infoBarDelegate;  // weak
+
+- (instancetype)init NS_UNAVAILABLE;
+
+- (instancetype)initWithInfoBarDelegate:
+    (infobars::InfoBarDelegate*)infoBarDelegate NS_DESIGNATED_INITIALIZER;
+
 @end
 
 #endif  // IOS_CHROME_BROWSER_INFOBARS_INFOBAR_CONTROLLER_H_
diff --git a/ios/chrome/browser/infobars/infobar_controller.mm b/ios/chrome/browser/infobars/infobar_controller.mm
index 2f7963f1..bc1d107 100644
--- a/ios/chrome/browser/infobars/infobar_controller.mm
+++ b/ios/chrome/browser/infobars/infobar_controller.mm
@@ -22,6 +22,16 @@
 @implementation InfoBarController
 
 @synthesize delegate = _delegate;
+@synthesize infoBarDelegate = _infoBarDelegate;
+
+- (instancetype)initWithInfoBarDelegate:
+    (infobars::InfoBarDelegate*)infoBarDelegate {
+  self = [super init];
+  if (self) {
+    _infoBarDelegate = infoBarDelegate;
+  }
+  return self;
+}
 
 - (void)dealloc {
   [_infoBarView removeFromSuperview];
@@ -60,6 +70,12 @@
 - (void)detachView {
   [_infoBarView setSizingDelegate:nil];
   _delegate = nullptr;
+  _infoBarDelegate = nullptr;
+}
+
+- (BOOL)shouldIgnoreUserInteraction {
+  // Ignore user interaction if view is already detached or is about to.
+  return !_delegate || !_delegate->IsOwned() || !_infoBarDelegate;
 }
 
 #pragma mark - InfoBarViewDelegate
diff --git a/ios/chrome/browser/infobars/infobar_controller_delegate.h b/ios/chrome/browser/infobars/infobar_controller_delegate.h
index 3c9c4ee..21dc758 100644
--- a/ios/chrome/browser/infobars/infobar_controller_delegate.h
+++ b/ios/chrome/browser/infobars/infobar_controller_delegate.h
@@ -13,6 +13,9 @@
   // Notifies that the target size has been changed (e.g. after rotation).
   virtual void SetInfoBarTargetHeight(int height) = 0;
 
+  // Returns whether the infobar is owned.
+  virtual bool IsOwned() = 0;
+
   // Notifies that the infobar must be removed.
   virtual void RemoveInfoBar() = 0;
 
diff --git a/ios/chrome/browser/ios_chrome_flag_descriptions.cc b/ios/chrome/browser/ios_chrome_flag_descriptions.cc
index 23f3633..41a9323 100644
--- a/ios/chrome/browser/ios_chrome_flag_descriptions.cc
+++ b/ios/chrome/browser/ios_chrome_flag_descriptions.cc
@@ -295,6 +295,10 @@
 const char kWKHTTPSystemCookieStoreDescription[] =
     "Use WKHTTPCookieStore backed store for main context URL requests.";
 
+const char kWKWebViewSnapshotsName[] = "WKWebView Snapshots";
+const char kWKWebViewSnapshotsDescription[] =
+    "When enabled, the WKWebView snapshotting API is used for iOS 11+.";
+
 // Please insert your name/description above in alphabetical order.
 
 }  // namespace flag_descriptions
diff --git a/ios/chrome/browser/ios_chrome_flag_descriptions.h b/ios/chrome/browser/ios_chrome_flag_descriptions.h
index 32c335e..4c81ce9 100644
--- a/ios/chrome/browser/ios_chrome_flag_descriptions.h
+++ b/ios/chrome/browser/ios_chrome_flag_descriptions.h
@@ -246,6 +246,10 @@
 extern const char kWKHTTPSystemCookieStoreName[];
 extern const char kWKHTTPSystemCookieStoreDescription[];
 
+// Title and description for the flag to use the WKWebView snapshotting API.
+extern const char kWKWebViewSnapshotsName[];
+extern const char kWKWebViewSnapshotsDescription[];
+
 // Please insert your name/description above in alphabetical order.
 
 }  // namespace flag_descriptions
diff --git a/ios/chrome/browser/metrics/BUILD.gn b/ios/chrome/browser/metrics/BUILD.gn
index 2100a63..113e34f 100644
--- a/ios/chrome/browser/metrics/BUILD.gn
+++ b/ios/chrome/browser/metrics/BUILD.gn
@@ -173,10 +173,10 @@
     "//ios/chrome/browser/ui/authentication",
     "//ios/chrome/browser/ui/authentication:authentication_ui",
     "//ios/chrome/browser/ui/authentication:eg_test_support",
+    "//ios/chrome/browser/ui/main:tab_switcher",
     "//ios/chrome/browser/ui/settings",
     "//ios/chrome/browser/ui/tab_grid:egtest_support",
     "//ios/chrome/browser/ui/tab_switcher:egtest_support",
-    "//ios/chrome/browser/ui/tab_switcher:modes",
     "//ios/chrome/browser/ui/toolbar/buttons",
     "//ios/chrome/browser/ui/toolbar/legacy",
     "//ios/chrome/browser/ui/toolbar/public",
@@ -206,9 +206,9 @@
     "//ios/chrome/browser/tabs",
     "//ios/chrome/browser/ui",
     "//ios/chrome/browser/ui/main",
+    "//ios/chrome/browser/ui/main:tab_switcher",
     "//ios/chrome/browser/ui/tab_grid:egtest_support",
     "//ios/chrome/browser/ui/tab_switcher:egtest_support",
-    "//ios/chrome/browser/ui/tab_switcher:modes",
     "//ios/chrome/browser/ui/tools_menu/public",
     "//ios/chrome/browser/web_state_list",
     "//ios/chrome/test/app:test_support",
diff --git a/ios/chrome/browser/metrics/tab_usage_recorder_test_util.mm b/ios/chrome/browser/metrics/tab_usage_recorder_test_util.mm
index efa6b30b..226d8ec 100644
--- a/ios/chrome/browser/metrics/tab_usage_recorder_test_util.mm
+++ b/ios/chrome/browser/metrics/tab_usage_recorder_test_util.mm
@@ -11,9 +11,9 @@
 #import "ios/chrome/app/main_controller.h"
 #import "ios/chrome/browser/tabs/tab_model.h"
 #import "ios/chrome/browser/ui/main/browser_view_information.h"
+#import "ios/chrome/browser/ui/main/tab_switcher_mode.h"
 #include "ios/chrome/browser/ui/tab_grid/tab_grid_egtest_util.h"
 #include "ios/chrome/browser/ui/tab_switcher/tab_switcher_egtest_util.h"
-#import "ios/chrome/browser/ui/tab_switcher/tab_switcher_mode.h"
 #include "ios/chrome/browser/ui/tools_menu/public/tools_menu_constants.h"
 #include "ios/chrome/browser/ui/ui_util.h"
 #include "ios/chrome/browser/web_state_list/web_state_list.h"
diff --git a/ios/chrome/browser/metrics/ukm_egtest.mm b/ios/chrome/browser/metrics/ukm_egtest.mm
index 3a8fab0..9a12219 100644
--- a/ios/chrome/browser/metrics/ukm_egtest.mm
+++ b/ios/chrome/browser/metrics/ukm_egtest.mm
@@ -16,9 +16,9 @@
 #import "ios/chrome/browser/ui/authentication/signin_earl_grey_ui.h"
 #import "ios/chrome/browser/ui/authentication/signin_earlgrey_utils.h"
 #import "ios/chrome/browser/ui/authentication/signin_promo_view.h"
+#import "ios/chrome/browser/ui/main/tab_switcher_mode.h"
 #import "ios/chrome/browser/ui/tab_grid/tab_grid_egtest_util.h"
 #import "ios/chrome/browser/ui/tab_switcher/tab_switcher_egtest_util.h"
-#import "ios/chrome/browser/ui/tab_switcher/tab_switcher_mode.h"
 #include "ios/chrome/browser/ui/ui_util.h"
 #include "ios/chrome/grit/ios_strings.h"
 #import "ios/chrome/test/app/chrome_test_util.h"
diff --git a/ios/chrome/browser/omaha/BUILD.gn b/ios/chrome/browser/omaha/BUILD.gn
index 956c5f0..becd3ec 100644
--- a/ios/chrome/browser/omaha/BUILD.gn
+++ b/ios/chrome/browser/omaha/BUILD.gn
@@ -51,6 +51,7 @@
     "//ios/web",
     "//ios/web/public/test",
     "//net:test_support",
+    "//services/network:test_support",
     "//testing/gtest",
   ]
 }
diff --git a/ios/chrome/browser/omaha/omaha_service.h b/ios/chrome/browser/omaha/omaha_service.h
index 82c3fb6..ea79fb9a0 100644
--- a/ios/chrome/browser/omaha/omaha_service.h
+++ b/ios/chrome/browser/omaha/omaha_service.h
@@ -14,16 +14,16 @@
 #include "base/scoped_observer.h"
 #include "base/timer/timer.h"
 #include "base/version.h"
-#include "net/url_request/url_fetcher_delegate.h"
 
 namespace base {
 class DictionaryValue;
 }
 
-namespace net {
-class URLFetcher;
-class URLRequestContextGetter;
-}
+namespace network {
+class SharedURLLoaderFactory;
+class SharedURLLoaderFactoryInfo;
+class SimpleURLLoader;
+}  // namespace network
 
 struct UpgradeRecommendedDetails;
 
@@ -31,15 +31,16 @@
 // handles all the scheduling necessary to contact the server regularly.
 // All methods, but the constructor, |GetInstance| and |Start| methods, must be
 // called from the IO thread.
-class OmahaService : public net::URLFetcherDelegate {
+class OmahaService {
  public:
   // Called when an upgrade is recommended.
   using UpgradeRecommendedCallback =
       base::Callback<void(const UpgradeRecommendedDetails&)>;
 
-  // Starts the service. Also set the |URLRequestContextGetter| necessary to
+  // Starts the service. Also set the |URLLoaderFactory| necessary to
   // access the Omaha server. This method should only be called once.
-  static void Start(net::URLRequestContextGetter* request_context_getter,
+  static void Start(std::unique_ptr<network::SharedURLLoaderFactoryInfo>
+                        url_loader_factory_info,
                     const UpgradeRecommendedCallback& callback);
 
   // Returns debug information about the omaha service.
@@ -78,8 +79,7 @@
   // Initialize the timer. Used on startup.
   void Initialize();
 
-  // net::URLFetcherDelegate
-  void OnURLFetchComplete(const net::URLFetcher* fetcher) override;
+  void OnURLLoadComplete(std::unique_ptr<std::string> response_body);
 
   // Raw GetInstance method. Necessary for using singletons.
   static OmahaService* GetInstance();
@@ -88,7 +88,7 @@
   OmahaService();
   // Private constructor, only used for tests.
   explicit OmahaService(bool schedule);
-  ~OmahaService() override;
+  ~OmahaService();
 
   // Returns the time to wait before next attempt.
   static base::TimeDelta GetBackOff(uint8_t number_of_tries);
@@ -146,12 +146,17 @@
   // called after a successful installation/update ping.
   void ClearInstallRetryRequestId();
 
+  // Initialize the URLLoaderFactory instance (mostly needed for tests).
+  void InitializeURLLoaderFactory(
+      scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory);
+
   // Clears the all persistent state. Should only be used for testing.
   static void ClearPersistentStateForTests();
 
   // To communicate with the Omaha server.
-  std::unique_ptr<net::URLFetcher> fetcher_;
-  net::URLRequestContextGetter* request_context_getter_;
+  std::unique_ptr<network::SimpleURLLoader> url_loader_;
+  std::unique_ptr<network::SharedURLLoaderFactoryInfo> url_loader_factory_info_;
+  scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
 
   // The timer that call this object back when needed.
   base::OneShotTimer timer_;
diff --git a/ios/chrome/browser/omaha/omaha_service.mm b/ios/chrome/browser/omaha/omaha_service.mm
index 4da5643..2ba10f92 100644
--- a/ios/chrome/browser/omaha/omaha_service.mm
+++ b/ios/chrome/browser/omaha/omaha_service.mm
@@ -42,6 +42,8 @@
 #include "net/base/backoff_entry.h"
 #include "net/base/load_flags.h"
 #include "net/url_request/url_fetcher.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
+#include "services/network/public/cpp/simple_url_loader.h"
 #include "url/gurl.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
@@ -294,15 +296,16 @@
 }
 
 // static
-void OmahaService::Start(net::URLRequestContextGetter* request_context_getter,
+void OmahaService::Start(std::unique_ptr<network::SharedURLLoaderFactoryInfo>
+                             url_loader_factory_info,
                          const UpgradeRecommendedCallback& callback) {
-  DCHECK(request_context_getter);
+  DCHECK(url_loader_factory_info);
   DCHECK(!callback.is_null());
   OmahaService* result = GetInstance();
   result->set_upgrade_recommended_callback(callback);
   // This should only be called once.
-  DCHECK(!result->request_context_getter_);
-  result->request_context_getter_ = request_context_getter;
+  DCHECK(!result->url_loader_factory_info_ || !result->url_loader_factory_);
+  result->url_loader_factory_info_ = std::move(url_loader_factory_info);
   result->locale_lang_ = GetApplicationContext()->GetApplicationLocale();
   base::PostTaskWithTraits(FROM_HERE, {web::WebThread::IO},
                            base::Bind(&OmahaService::SendOrScheduleNextPing,
@@ -310,16 +313,14 @@
 }
 
 OmahaService::OmahaService()
-    : request_context_getter_(NULL),
-      schedule_(true),
+    : schedule_(true),
       application_install_date_(0),
       sending_install_event_(false) {
   Initialize();
 }
 
 OmahaService::OmahaService(bool schedule)
-    : request_context_getter_(NULL),
-      schedule_(schedule),
+    : schedule_(schedule),
       application_install_date_(0),
       sending_install_event_(false) {
   Initialize();
@@ -524,7 +525,7 @@
 
 void OmahaService::SendPing() {
   // Check that no request is in progress.
-  DCHECK(!fetcher_);
+  DCHECK(!url_loader_);
 
   GURL url(ios::GetChromeBrowserProvider()
                ->GetOmahaServiceProvider()
@@ -533,17 +534,30 @@
     return;
   }
 
-  fetcher_ = net::URLFetcher::Create(0, url, net::URLFetcher::POST, this);
-  fetcher_->SetRequestContext(request_context_getter_);
-  fetcher_->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES |
-                         net::LOAD_DO_NOT_SAVE_COOKIES);
-  fetcher_->SetUploadData("text/xml", GetCurrentPingContent());
+  // There are 2 situations here:
+  // 1) production code, where |url_loader_factory_info_| is used.
+  // 2) testing code, where the |url_loader_factory_| creation is triggered by
+  // the test.
+  if (url_loader_factory_info_) {
+    DCHECK(!url_loader_factory_);
+    url_loader_factory_ = network::SharedURLLoaderFactory::Create(
+        std::move(url_loader_factory_info_));
+  }
+
+  DCHECK(url_loader_factory_);
+
+  auto resource_request = std::make_unique<network::ResourceRequest>();
+  resource_request->url = url;
+  resource_request->method = "POST";
+  resource_request->load_flags =
+      net::LOAD_DO_NOT_SEND_COOKIES | net::LOAD_DO_NOT_SAVE_COOKIES;
 
   // If this is not the first try, notify the omaha server.
   if (number_of_tries_ && IsNextPingInstallRetry()) {
-    fetcher_->SetExtraRequestHeaders(base::StringPrintf(
-        "X-RequestAge: %lld",
-        (base::Time::Now() - current_ping_time_).InSeconds()));
+    resource_request->headers.SetHeader(
+        "X-RequestAge",
+        base::StringPrintf(
+            "%lld", (base::Time::Now() - current_ping_time_).InSeconds()));
   }
 
   // Update last fail time and number of tries, so that if anything fails
@@ -553,7 +567,12 @@
   next_tries_time_ = base::Time::Now() + GetBackOff(number_of_tries_);
   PersistStates();
 
-  fetcher_->Start();
+  url_loader_ = network::SimpleURLLoader::Create(std::move(resource_request),
+                                                 NO_TRAFFIC_ANNOTATION_YET);
+  url_loader_->AttachStringForUpload(GetCurrentPingContent(), "text/xml");
+  url_loader_->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
+      url_loader_factory_.get(),
+      base::BindOnce(&OmahaService::OnURLLoadComplete, base::Unretained(this)));
 }
 
 void OmahaService::SendOrScheduleNextPing() {
@@ -563,8 +582,9 @@
     return;
   }
   if (schedule_) {
-    timer_.Start(FROM_HERE, next_tries_time_ - now, this,
-                 &OmahaService::SendPing);
+    timer_.Start(
+        FROM_HERE, next_tries_time_ - now,
+        base::BindOnce(&OmahaService::SendPing, base::Unretained(this)));
   }
 }
 
@@ -585,21 +605,19 @@
   [defaults synchronize];
 }
 
-void OmahaService::OnURLFetchComplete(const net::URLFetcher* fetcher) {
-  DCHECK(fetcher_.get() == fetcher);
-  // Transfer the ownership of fetcher_ to this method.
-  std::unique_ptr<net::URLFetcher> local_fetcher = std::move(fetcher_);
+void OmahaService::OnURLLoadComplete(
+    std::unique_ptr<std::string> response_body) {
+  // Reset the loader.
+  url_loader_.reset();
 
-  if (fetcher->GetResponseCode() != 200) {
+  if (!response_body) {
     DLOG(WARNING) << "Error contacting the Omaha server";
     SendOrScheduleNextPing();
     return;
   }
 
-  std::string response;
-  bool result = fetcher->GetResponseAsString(&response);
-  DCHECK(result);
-  NSData* xml = [NSData dataWithBytes:response.data() length:response.length()];
+  NSData* xml = [NSData dataWithBytes:response_body->data()
+                               length:response_body->length()];
   NSXMLParser* parser = [[NSXMLParser alloc] initWithData:xml];
   const std::string application_id = ios::GetChromeBrowserProvider()
                                          ->GetOmahaServiceProvider()
@@ -701,6 +719,11 @@
   [defaults synchronize];
 }
 
+void OmahaService::InitializeURLLoaderFactory(
+    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory) {
+  url_loader_factory_ = url_loader_factory;
+}
+
 void OmahaService::ClearPersistentStateForTests() {
   NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
   [defaults removeObjectForKey:kNextTriesTimesKey];
diff --git a/ios/chrome/browser/omaha/omaha_service_unittest.mm b/ios/chrome/browser/omaha/omaha_service_unittest.mm
index 51c080a..3faa2082 100644
--- a/ios/chrome/browser/omaha/omaha_service_unittest.mm
+++ b/ios/chrome/browser/omaha/omaha_service_unittest.mm
@@ -23,7 +23,10 @@
 #include "ios/public/provider/chrome/browser/omaha/omaha_service_provider.h"
 #include "ios/web/public/test/test_web_thread_bundle.h"
 #include "ios/web/public/web_thread.h"
-#include "net/url_request/test_url_fetcher_factory.h"
+#include "net/http/http_status_code.h"
+#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
+#include "services/network/test/test_url_loader_factory.h"
+#include "services/network/test/test_utils.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/gtest_mac.h"
 #include "testing/platform_test.h"
@@ -41,7 +44,10 @@
 class OmahaServiceTest : public PlatformTest {
  public:
   OmahaServiceTest()
-      : need_update_(false),
+      : test_shared_url_loader_factory_(
+            base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
+                &test_url_loader_factory_)),
+        need_update_(false),
         scoped_browser_state_manager_(
             std::make_unique<TestChromeBrowserStateManager>(
                 base::FilePath(kUserDataDir))) {
@@ -81,6 +87,21 @@
         ->GetApplicationID();
   }
 
+  network::TestURLLoaderFactory::PendingRequest* GetPendingRequest(
+      size_t index = 0) {
+    if (index >= test_url_loader_factory_.pending_requests()->size())
+      return nullptr;
+    network::TestURLLoaderFactory::PendingRequest* request =
+        &(*test_url_loader_factory_.pending_requests())[index];
+    DCHECK(request);
+    return request;
+  }
+
+ protected:
+  network::TestURLLoaderFactory test_url_loader_factory_;
+  scoped_refptr<network::SharedURLLoaderFactory>
+      test_shared_url_loader_factory_;
+
  private:
   bool need_update_;
   IOSChromeScopedTestingChromeBrowserStateManager scoped_browser_state_manager_;
@@ -196,20 +217,16 @@
   OmahaService service(false);
   service.set_upgrade_recommended_callback(
       base::Bind(&OmahaServiceTest::OnNeedUpdate, base::Unretained(this)));
+  service.InitializeURLLoaderFactory(test_shared_url_loader_factory_);
   CleanService(&service, version_info::GetVersionNumber());
-  net::TestURLFetcherFactory factory_;
 
   service.SendPing();
+
   EXPECT_EQ(1, service.number_of_tries_);
   EXPECT_TRUE(service.current_ping_time_.is_null());
   EXPECT_GE(service.next_tries_time_, now + base::TimeDelta::FromMinutes(54));
   EXPECT_LE(service.next_tries_time_, now + base::TimeDelta::FromHours(7));
 
-  net::TestURLFetcher* fetcher = factory_.GetFetcherByID(0);
-  DCHECK(fetcher);
-  DCHECK(fetcher->delegate());
-  fetcher->set_status(net::URLRequestStatus());
-  fetcher->set_response_code(200);
   std::string response =
       std::string(
           "<?xml version=\"1.0\"?><response protocol=\"3.0\" server=\"prod\">"
@@ -218,8 +235,8 @@
       "\" status=\"ok\">"
       "<updatecheck status=\"noupdate\"/><ping status=\"ok\"/>"
       "</app></response>";
-  fetcher->SetResponseString(response);
-  fetcher->delegate()->OnURLFetchComplete(fetcher);
+  test_url_loader_factory_.SimulateResponseForPendingRequest(
+      GetPendingRequest()->request.url.spec(), response);
 
   EXPECT_EQ(0, service.number_of_tries_);
   EXPECT_FALSE(service.current_ping_time_.is_null());
@@ -233,20 +250,16 @@
   OmahaService service(false);
   service.set_upgrade_recommended_callback(
       base::Bind(&OmahaServiceTest::OnNeedUpdate, base::Unretained(this)));
+  service.InitializeURLLoaderFactory(test_shared_url_loader_factory_);
   CleanService(&service, "");
-  net::TestURLFetcherFactory factory_;
 
   service.SendPing();
+
   EXPECT_EQ(1, service.number_of_tries_);
   EXPECT_TRUE(service.current_ping_time_.is_null());
   EXPECT_GE(service.next_tries_time_, now + base::TimeDelta::FromMinutes(54));
   EXPECT_LE(service.next_tries_time_, now + base::TimeDelta::FromHours(7));
 
-  net::TestURLFetcher* fetcher = factory_.GetFetcherByID(0);
-  DCHECK(fetcher);
-  DCHECK(fetcher->delegate());
-  fetcher->set_status(net::URLRequestStatus());
-  fetcher->set_response_code(200);
   std::string response =
       std::string(
           "<?xml version=\"1.0\"?><response protocol=\"3.0\" server=\"prod\">"
@@ -255,8 +268,8 @@
       "\" status=\"ok\">"
       "<event status=\"ok\"/>"
       "</app></response>";
-  fetcher->SetResponseString(response);
-  fetcher->delegate()->OnURLFetchComplete(fetcher);
+  test_url_loader_factory_.SimulateResponseForPendingRequest(
+      GetPendingRequest()->request.url.spec(), response);
 
   EXPECT_FALSE(service.current_ping_time_.is_null());
   EXPECT_GT(service.last_sent_time_, now);
@@ -268,20 +281,16 @@
   OmahaService service(false);
   service.set_upgrade_recommended_callback(
       base::Bind(&OmahaServiceTest::OnNeedUpdate, base::Unretained(this)));
+  service.InitializeURLLoaderFactory(test_shared_url_loader_factory_);
   CleanService(&service, version_info::GetVersionNumber());
-  net::TestURLFetcherFactory factory_;
 
   service.SendPing();
+
   EXPECT_EQ(1, service.number_of_tries_);
   EXPECT_TRUE(service.current_ping_time_.is_null());
   EXPECT_GE(service.next_tries_time_, now + base::TimeDelta::FromMinutes(54));
   EXPECT_LE(service.next_tries_time_, now + base::TimeDelta::FromHours(7));
 
-  net::TestURLFetcher* fetcher = factory_.GetFetcherByID(0);
-  DCHECK(fetcher);
-  DCHECK(fetcher->delegate());
-  fetcher->set_status(net::URLRequestStatus());
-  fetcher->set_response_code(200);
   std::string response =
       std::string(
           "<?xml version=\"1.0\"?><response protocol=\"3.0\" server=\"prod\">"
@@ -301,8 +310,8 @@
       "</manifest>"
       "</updatecheck><ping status=\"ok\"/>"
       "</app></response>";
-  fetcher->SetResponseString(response);
-  fetcher->delegate()->OnURLFetchComplete(fetcher);
+  test_url_loader_factory_.SimulateResponseForPendingRequest(
+      GetPendingRequest()->request.url.spec(), response);
 
   EXPECT_EQ(0, service.number_of_tries_);
   EXPECT_FALSE(service.current_ping_time_.is_null());
@@ -316,24 +325,24 @@
   OmahaService service(false);
   service.set_upgrade_recommended_callback(
       base::Bind(&OmahaServiceTest::OnNeedUpdate, base::Unretained(this)));
+  service.InitializeURLLoaderFactory(test_shared_url_loader_factory_);
   CleanService(&service, version_info::GetVersionNumber());
-  net::TestURLFetcherFactory factory_;
+
+  service.SendPing();
 
   // Tries with a non 200 result.
-  service.SendPing();
   EXPECT_EQ(1, service.number_of_tries_);
   EXPECT_TRUE(service.current_ping_time_.is_null());
   EXPECT_GE(service.next_tries_time_, now + base::TimeDelta::FromMinutes(54));
   EXPECT_LE(service.next_tries_time_, now + base::TimeDelta::FromHours(7));
   base::Time next_tries_time = service.next_tries_time_;
 
-  net::TestURLFetcher* fetcher = factory_.GetFetcherByID(0);
-  DCHECK(fetcher);
-  DCHECK(fetcher->delegate());
-  fetcher->set_status(net::URLRequestStatus());
-  fetcher->set_response_code(400);
-  fetcher->SetResponseString("");
-  fetcher->delegate()->OnURLFetchComplete(fetcher);
+  auto resource_response_head =
+      network::CreateResourceResponseHead(net::HTTP_BAD_REQUEST);
+  test_url_loader_factory_.SimulateResponseForPendingRequest(
+      GetPendingRequest()->request.url,
+      network::URLLoaderCompletionStatus(net::OK), resource_response_head,
+      std::string());
 
   EXPECT_EQ(1, service.number_of_tries_);
   EXPECT_TRUE(service.current_ping_time_.is_null());
@@ -343,19 +352,15 @@
 
   // Tries with an incorrect xml message.
   service.SendPing();
+
   EXPECT_EQ(2, service.number_of_tries_);
   EXPECT_TRUE(service.current_ping_time_.is_null());
   EXPECT_GE(service.next_tries_time_, now + base::TimeDelta::FromMinutes(54));
   EXPECT_LE(service.next_tries_time_, now + base::TimeDelta::FromHours(7));
   next_tries_time = service.next_tries_time_;
 
-  fetcher = factory_.GetFetcherByID(0);
-  DCHECK(fetcher);
-  DCHECK(fetcher->delegate());
-  fetcher->set_status(net::URLRequestStatus());
-  fetcher->set_response_code(200);
-  fetcher->SetResponseString("Incorrect Message");
-  fetcher->delegate()->OnURLFetchComplete(fetcher);
+  test_url_loader_factory_.SimulateResponseForPendingRequest(
+      GetPendingRequest()->request.url.spec(), "Incorrect Message");
 
   EXPECT_EQ(2, service.number_of_tries_);
   EXPECT_TRUE(service.current_ping_time_.is_null());
@@ -403,20 +408,16 @@
   OmahaService service(false);
   service.set_upgrade_recommended_callback(
       base::Bind(&OmahaServiceTest::OnNeedUpdate, base::Unretained(this)));
+  service.InitializeURLLoaderFactory(test_shared_url_loader_factory_);
   CleanService(&service, "");
-  net::TestURLFetcherFactory factory_;
 
   service.SendPing();
+
   EXPECT_EQ(1, service.number_of_tries_);
   EXPECT_TRUE(service.current_ping_time_.is_null());
   EXPECT_GE(service.next_tries_time_, now + base::TimeDelta::FromMinutes(54));
   EXPECT_LE(service.next_tries_time_, now + base::TimeDelta::FromHours(7));
 
-  net::TestURLFetcher* fetcher = factory_.GetFetcherByID(0);
-  DCHECK(fetcher);
-  DCHECK(fetcher->delegate());
-  fetcher->set_status(net::URLRequestStatus());
-  fetcher->set_response_code(200);
   std::string response =
       std::string(
           "<?xml version=\"1.0\"?><response protocol=\"3.0\" server=\"prod\">"
@@ -425,8 +426,8 @@
       "\" status=\"ok\">"
       "<event status=\"ok\"/>"
       "</app></response>";
-  fetcher->SetResponseString(response);
-  fetcher->delegate()->OnURLFetchComplete(fetcher);
+  test_url_loader_factory_.SimulateResponseForPendingRequest(
+      GetPendingRequest()->request.url.spec(), response);
 
   EXPECT_EQ(1, service.number_of_tries_);
   EXPECT_LT(service.current_ping_time_ - now, base::TimeDelta::FromMinutes(1));
@@ -440,20 +441,16 @@
   OmahaService service(false);
   service.set_upgrade_recommended_callback(
       base::Bind(&OmahaServiceTest::OnNeedUpdate, base::Unretained(this)));
+  service.InitializeURLLoaderFactory(test_shared_url_loader_factory_);
   CleanService(&service, version_info::GetVersionNumber());
-  net::TestURLFetcherFactory factory_;
 
   service.SendPing();
+
   EXPECT_EQ(1, service.number_of_tries_);
   EXPECT_TRUE(service.current_ping_time_.is_null());
   EXPECT_GE(service.next_tries_time_, now + base::TimeDelta::FromMinutes(54));
   EXPECT_LE(service.next_tries_time_, now + base::TimeDelta::FromHours(7));
 
-  net::TestURLFetcher* fetcher = factory_.GetFetcherByID(0);
-  DCHECK(fetcher);
-  DCHECK(fetcher->delegate());
-  fetcher->set_status(net::URLRequestStatus());
-  fetcher->set_response_code(200);
   std::string response =
       std::string(
           "<?xml version=\"1.0\"?><response protocol=\"3.0\" server=\"prod\">"
@@ -462,8 +459,8 @@
       "\" status=\"ok\">"
       "<updatecheck status=\"noupdate\"/><ping status=\"ok\"/>"
       "</app></response>";
-  fetcher->SetResponseString(response);
-  fetcher->delegate()->OnURLFetchComplete(fetcher);
+  test_url_loader_factory_.SimulateResponseForPendingRequest(
+      GetPendingRequest()->request.url.spec(), response);
 
   EXPECT_EQ(0, service.number_of_tries_);
   EXPECT_FALSE(service.current_ping_time_.is_null());
@@ -476,8 +473,8 @@
   OmahaService service(false);
   service.set_upgrade_recommended_callback(
       base::Bind(&OmahaServiceTest::OnNeedUpdate, base::Unretained(this)));
+  service.InitializeURLLoaderFactory(test_shared_url_loader_factory_);
   CleanService(&service, "");
-  net::TestURLFetcherFactory factory_;
 
   EXPECT_FALSE(service.IsNextPingInstallRetry());
   std::string id1 = service.GetNextPingRequestId(OmahaService::INSTALL_EVENT);
@@ -485,11 +482,7 @@
   ASSERT_EQ(id1, service.GetNextPingRequestId(OmahaService::INSTALL_EVENT));
 
   service.SendPing();
-  net::TestURLFetcher* fetcher = factory_.GetFetcherByID(0);
-  DCHECK(fetcher);
-  DCHECK(fetcher->delegate());
-  fetcher->set_status(net::URLRequestStatus());
-  fetcher->set_response_code(200);
+
   std::string response =
       std::string(
           "<?xml version=\"1.0\"?><response protocol=\"3.0\" server=\"prod\">"
@@ -498,8 +491,8 @@
       "\" status=\"ok\">"
       "<updatecheck status=\"noupdate\"/><ping status=\"ok\"/>"
       "</app></response>";
-  fetcher->SetResponseString(response);
-  fetcher->delegate()->OnURLFetchComplete(fetcher);
+  test_url_loader_factory_.SimulateResponseForPendingRequest(
+      GetPendingRequest()->request.url.spec(), response);
 
   EXPECT_FALSE(service.IsNextPingInstallRetry());
   id1 = service.GetNextPingRequestId(OmahaService::USAGE_PING);
diff --git a/ios/chrome/browser/passwords/password_controller.mm b/ios/chrome/browser/passwords/password_controller.mm
index b34a633..dd567ba 100644
--- a/ios/chrome/browser/passwords/password_controller.mm
+++ b/ios/chrome/browser/passwords/password_controller.mm
@@ -371,6 +371,7 @@
                                  fieldType:(NSString*)fieldType
                                       type:(NSString*)type
                                 typedValue:(NSString*)typedValue
+                                   frameID:(NSString*)frameID
                                isMainFrame:(BOOL)isMainFrame
                             hasUserGesture:(BOOL)hasUserGesture
                                   webState:(web::WebState*)webState
@@ -416,6 +417,7 @@
                          fieldType:(NSString*)fieldType
                               type:(NSString*)type
                         typedValue:(NSString*)typedValue
+                           frameID:(NSString*)frameID
                           webState:(web::WebState*)webState
                  completionHandler:(SuggestionsReadyCompletion)completion {
   DCHECK(GetPageURLAndCheckTrustLevel(webState, nullptr));
@@ -427,6 +429,7 @@
                   fieldName:(NSString*)fieldName2
             fieldIdentifier:(NSString*)fieldIdentifier
                        form:(NSString*)formName
+                    frameID:(NSString*)frameID
           completionHandler:(SuggestionHandledCompletion)completion {
   if (suggestion.identifier == 1) {
     // Navigate to the settings list.
diff --git a/ios/chrome/browser/passwords/password_controller_unittest.mm b/ios/chrome/browser/passwords/password_controller_unittest.mm
index e8a0433d..7de9876 100644
--- a/ios/chrome/browser/passwords/password_controller_unittest.mm
+++ b/ios/chrome/browser/passwords/password_controller_unittest.mm
@@ -39,6 +39,9 @@
 #include "ios/web/public/ssl_status.h"
 #import "ios/web/public/test/fakes/test_web_state.h"
 #import "ios/web/public/test/web_js_test.h"
+#include "ios/web/public/web_state/web_frame.h"
+#include "ios/web/public/web_state/web_frame_util.h"
+#import "ios/web/public/web_state/web_frames_manager.h"
 #import "ios/web/public/web_state/web_state.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -1071,6 +1074,7 @@
     EXPECT_NSEQ(@"[]=, onkeyup=false, onchange=false",
                 ExecuteJavaScript(kUsernamePasswordVerificationScript));
 
+    std::string mainFrameID = web::GetMainWebFrameId(web_state());
     // Emulate that the user clicks on the username field in the first form.
     // That's required in order that PasswordController can identify which form
     // should be filled.
@@ -1082,6 +1086,7 @@
                          fieldType:@"text"
                               type:@"focus"
                         typedValue:@""
+                           frameID:base::SysUTF8ToNSString(mainFrameID)
                           webState:web_state()
                  completionHandler:^(NSArray* suggestions,
                                      id<FormSuggestionProvider> provider) {
@@ -1116,6 +1121,7 @@
                   fieldName:@"u"
             fieldIdentifier:@"u"
                        form:base::SysUTF8ToNSString(FormName(0))
+                    frameID:base::SysUTF8ToNSString(mainFrameID)
           completionHandler:completion];
     EXPECT_TRUE(
         WaitUntilConditionOrTimeout(kWaitForJSCompletionTimeout, ^bool() {
@@ -1370,20 +1376,22 @@
       EXPECT_CALL(*store_, GetLogins(_, _))
           .WillRepeatedly(WithArg<1>(InvokeEmptyConsumerWithForms()));
     }
-    [passwordController_ checkIfSuggestionsAvailableForForm:@"dynamic_form"
-                                                  fieldName:@"username"
-                                            fieldIdentifier:@"username"
-                                                  fieldType:@"text"
-                                                       type:@"focus"
-                                                 typedValue:@""
-                                                isMainFrame:YES
-                                             hasUserGesture:YES
-                                                   webState:web_state()
-                                          completionHandler:^(BOOL success) {
-                                            completion_handler_success =
-                                                success;
-                                            completion_handler_called = YES;
-                                          }];
+    std::string mainFrameID = web::GetMainWebFrameId(web_state());
+    [passwordController_
+        checkIfSuggestionsAvailableForForm:@"dynamic_form"
+                                 fieldName:@"username"
+                           fieldIdentifier:@"username"
+                                 fieldType:@"text"
+                                      type:@"focus"
+                                typedValue:@""
+                                   frameID:base::SysUTF8ToNSString(mainFrameID)
+                               isMainFrame:YES
+                            hasUserGesture:YES
+                                  webState:web_state()
+                         completionHandler:^(BOOL success) {
+                           completion_handler_success = success;
+                           completion_handler_called = YES;
+                         }];
     // Wait until the expected handler is called.
     EXPECT_TRUE(WaitUntilConditionOrTimeout(kWaitForActionTimeout, ^bool() {
       return completion_handler_called;
@@ -1407,19 +1415,22 @@
   PasswordForm form(CreatePasswordForm(BaseUrl().c_str(), "user", "pw"));
   EXPECT_CALL(*store_, GetLogins(_, _))
       .WillOnce(WithArg<1>(InvokeConsumer(form)));
-  [passwordController_ checkIfSuggestionsAvailableForForm:@"dynamic_form"
-                                                fieldName:@"address"
-                                          fieldIdentifier:@"address"
-                                                fieldType:@"text"
-                                                     type:@"focus"
-                                               typedValue:@""
-                                              isMainFrame:YES
-                                           hasUserGesture:YES
-                                                 webState:web_state()
-                                        completionHandler:^(BOOL success) {
-                                          completion_handler_success = success;
-                                          completion_handler_called = YES;
-                                        }];
+  std::string mainFrameID = web::GetMainWebFrameId(web_state());
+  [passwordController_
+      checkIfSuggestionsAvailableForForm:@"dynamic_form"
+                               fieldName:@"address"
+                         fieldIdentifier:@"address"
+                               fieldType:@"text"
+                                    type:@"focus"
+                              typedValue:@""
+                                 frameID:base::SysUTF8ToNSString(mainFrameID)
+                             isMainFrame:YES
+                          hasUserGesture:YES
+                                webState:web_state()
+                       completionHandler:^(BOOL success) {
+                         completion_handler_success = success;
+                         completion_handler_called = YES;
+                       }];
   // Wait until the expected handler is called.
   EXPECT_TRUE(WaitUntilConditionOrTimeout(kWaitForActionTimeout, ^bool() {
     return completion_handler_called;
@@ -1437,19 +1448,22 @@
   __block BOOL completion_handler_called = NO;
 
   EXPECT_CALL(*store_, GetLogins(_, _)).Times(0);
-  [passwordController_ checkIfSuggestionsAvailableForForm:@"form"
-                                                fieldName:@"address"
-                                          fieldIdentifier:@"address"
-                                                fieldType:@"text"
-                                                     type:@"focus"
-                                               typedValue:@""
-                                              isMainFrame:YES
-                                           hasUserGesture:YES
-                                                 webState:web_state()
-                                        completionHandler:^(BOOL success) {
-                                          completion_handler_success = success;
-                                          completion_handler_called = YES;
-                                        }];
+  std::string mainFrameID = web::GetMainWebFrameId(web_state());
+  [passwordController_
+      checkIfSuggestionsAvailableForForm:@"form"
+                               fieldName:@"address"
+                         fieldIdentifier:@"address"
+                               fieldType:@"text"
+                                    type:@"focus"
+                              typedValue:@""
+                                 frameID:base::SysUTF8ToNSString(mainFrameID)
+                             isMainFrame:YES
+                          hasUserGesture:YES
+                                webState:web_state()
+                       completionHandler:^(BOOL success) {
+                         completion_handler_success = success;
+                         completion_handler_called = YES;
+                       }];
   // Wait until the expected handler is called.
   EXPECT_TRUE(WaitUntilConditionOrTimeout(kWaitForActionTimeout, ^bool() {
     return completion_handler_called;
diff --git a/ios/chrome/browser/translate/after_translate_infobar_controller.h b/ios/chrome/browser/translate/after_translate_infobar_controller.h
index 589ac35..57ee824 100644
--- a/ios/chrome/browser/translate/after_translate_infobar_controller.h
+++ b/ios/chrome/browser/translate/after_translate_infobar_controller.h
@@ -13,10 +13,9 @@
 
 @interface AfterTranslateInfoBarController : InfoBarController
 
-- (instancetype)init NS_UNAVAILABLE;
-
 - (instancetype)initWithInfoBarDelegate:
-    (translate::TranslateInfoBarDelegate*)delegate;
+    (translate::TranslateInfoBarDelegate*)infoBarDelegate
+    NS_DESIGNATED_INITIALIZER;
 
 @end
 
diff --git a/ios/chrome/browser/translate/after_translate_infobar_controller.mm b/ios/chrome/browser/translate/after_translate_infobar_controller.mm
index 9543d3d..256fd42 100644
--- a/ios/chrome/browser/translate/after_translate_infobar_controller.mm
+++ b/ios/chrome/browser/translate/after_translate_infobar_controller.mm
@@ -9,6 +9,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "components/strings/grit/components_strings.h"
 #include "components/translate/core/browser/translate_infobar_delegate.h"
+#import "ios/chrome/browser/infobars/infobar_controller+protected.h"
 #include "ios/chrome/browser/infobars/infobar_controller_delegate.h"
 #include "ios/chrome/browser/translate/translate_infobar_tags.h"
 #import "ios/chrome/browser/ui/infobars/confirm_infobar_view.h"
@@ -28,36 +29,37 @@
 }  // namespace
 
 @interface AfterTranslateInfoBarController () {
-  translate::TranslateInfoBarDelegate* _translateInfoBarDelegate;  // weak
   AlwaysTranslateSwitchState _alwaysTranslateSwitchState;
 }
 
+// Overrides superclass property.
+@property(nonatomic, readonly)
+    translate::TranslateInfoBarDelegate* infoBarDelegate;
+
 @end
 
 @implementation AfterTranslateInfoBarController
 
+@dynamic infoBarDelegate;
+
 #pragma mark -
 #pragma mark InfoBarControllerProtocol
 
 - (instancetype)initWithInfoBarDelegate:
-    (translate::TranslateInfoBarDelegate*)delegate {
-  self = [super init];
-  if (self) {
-    _translateInfoBarDelegate = delegate;
-  }
-  return self;
+    (translate::TranslateInfoBarDelegate*)infoBarDelegate {
+  return [super initWithInfoBarDelegate:infoBarDelegate];
 }
 
 - (UIView<InfoBarViewSizing>*)viewForFrame:(CGRect)frame {
   ConfirmInfoBarView* infoBarView =
       [[ConfirmInfoBarView alloc] initWithFrame:frame];
   // Icon
-  gfx::Image icon = _translateInfoBarDelegate->GetIcon();
+  gfx::Image icon = self.infoBarDelegate->GetIcon();
   if (!icon.IsEmpty())
     [infoBarView addLeftIcon:icon.ToUIImage()];
   // Main text.
   const bool autodeterminedSourceLanguage =
-      _translateInfoBarDelegate->original_language_code() ==
+      self.infoBarDelegate->original_language_code() ==
       translate::kUnknownLanguageCode;
   bool swappedLanguageButtons;
   std::vector<base::string16> strings;
@@ -69,11 +71,10 @@
   NSString* label3 = autodeterminedSourceLanguage
                          ? @""
                          : base::SysUTF16ToNSString(strings[2]);
-  base::string16 stdOriginal =
-      _translateInfoBarDelegate->original_language_name();
+  base::string16 stdOriginal = self.infoBarDelegate->original_language_name();
   NSString* original = base::SysUTF16ToNSString(stdOriginal);
-  NSString* target = base::SysUTF16ToNSString(
-      _translateInfoBarDelegate->target_language_name());
+  NSString* target =
+      base::SysUTF16ToNSString(self.infoBarDelegate->target_language_name());
   NSString* label =
       [[NSString alloc] initWithFormat:@"%@ %@ %@%@ %@.", label1, original,
                                        label2, label3, target];
@@ -93,10 +94,10 @@
                    action:@selector(infoBarButtonDidPress:)];
   // Always translate switch.
   _alwaysTranslateSwitchState = ALWAYS_TRANSLATE_SWITCH_NOT_CHANGED;
-  if (_translateInfoBarDelegate->ShouldShowAlwaysTranslateShortcut()) {
+  if (self.infoBarDelegate->ShouldShowAlwaysTranslateShortcut()) {
     base::string16 alwaysTranslate = l10n_util::GetStringFUTF16(
         IDS_TRANSLATE_INFOBAR_ALWAYS_TRANSLATE, stdOriginal);
-    const BOOL switchValue = _translateInfoBarDelegate->ShouldAlwaysTranslate();
+    const BOOL switchValue = self.infoBarDelegate->ShouldAlwaysTranslate();
     [infoBarView
         addSwitchWithLabel:base::SysUTF16ToNSString(alwaysTranslate)
                       isOn:switchValue
@@ -110,26 +111,22 @@
 #pragma mark - Handling of User Events
 
 - (void)infoBarButtonDidPress:(id)sender {
-  // This press might have occurred after the user has already pressed a button,
-  // in which case the view has been detached from the delegate and this press
-  // should be ignored.
-  if (!self.delegate) {
+  if ([self shouldIgnoreUserInteraction])
     return;
-  }
 
   NSUInteger buttonId = base::mac::ObjCCastStrict<UIButton>(sender).tag;
   switch (buttonId) {
     case TranslateInfoBarIOSTag::CLOSE:
-      _translateInfoBarDelegate->InfoBarDismissed();
+      self.infoBarDelegate->InfoBarDismissed();
       self.delegate->RemoveInfoBar();
       break;
     case TranslateInfoBarIOSTag::AFTER_DONE:
       [self saveAlwaysTranslateState];
-      _translateInfoBarDelegate->InfoBarDismissed();
+      self.infoBarDelegate->InfoBarDismissed();
       self.delegate->RemoveInfoBar();
       break;
     case TranslateInfoBarIOSTag::AFTER_REVERT:
-      _translateInfoBarDelegate->RevertTranslation();
+      self.infoBarDelegate->RevertTranslation();
       break;
     default:
       NOTREACHED() << "Unexpected Translate button label";
@@ -153,10 +150,10 @@
 
   const bool alwaysTranslate =
       _alwaysTranslateSwitchState == ALWAYS_TRANSLATE_SWITCH_SET_TO_ENABLED;
-  if (alwaysTranslate == _translateInfoBarDelegate->ShouldAlwaysTranslate())
+  if (alwaysTranslate == self.infoBarDelegate->ShouldAlwaysTranslate())
     return;
 
-  _translateInfoBarDelegate->ToggleAlwaysTranslate();
+  self.infoBarDelegate->ToggleAlwaysTranslate();
 }
 
 @end
diff --git a/ios/chrome/browser/translate/before_translate_infobar_controller.h b/ios/chrome/browser/translate/before_translate_infobar_controller.h
index e74cb9e..73ac4a1 100644
--- a/ios/chrome/browser/translate/before_translate_infobar_controller.h
+++ b/ios/chrome/browser/translate/before_translate_infobar_controller.h
@@ -14,10 +14,9 @@
 
 @interface BeforeTranslateInfoBarController : InfoBarController
 
-- (instancetype)init NS_UNAVAILABLE;
-
 - (instancetype)initWithInfoBarDelegate:
-    (translate::TranslateInfoBarDelegate*)delegate;
+    (translate::TranslateInfoBarDelegate*)infoBarDelegate
+    NS_DESIGNATED_INITIALIZER;
 
 @property(nonatomic, weak) id<LanguageSelectionHandler>
     languageSelectionHandler;
diff --git a/ios/chrome/browser/translate/before_translate_infobar_controller.mm b/ios/chrome/browser/translate/before_translate_infobar_controller.mm
index 9f18d72..f8651dfd 100644
--- a/ios/chrome/browser/translate/before_translate_infobar_controller.mm
+++ b/ios/chrome/browser/translate/before_translate_infobar_controller.mm
@@ -13,6 +13,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "components/strings/grit/components_strings.h"
 #include "components/translate/core/browser/translate_infobar_delegate.h"
+#import "ios/chrome/browser/infobars/infobar_controller+protected.h"
 #include "ios/chrome/browser/infobars/infobar_controller_delegate.h"
 #include "ios/chrome/browser/translate/language_selection_context.h"
 #include "ios/chrome/browser/translate/language_selection_delegate.h"
@@ -29,10 +30,13 @@
 
 @interface BeforeTranslateInfoBarController ()<LanguageSelectionDelegate>
 
+// Overrides superclass property.
+@property(nonatomic, readonly)
+    translate::TranslateInfoBarDelegate* infoBarDelegate;
+
 @end
 
 @implementation BeforeTranslateInfoBarController {
-  translate::TranslateInfoBarDelegate* _translateInfoBarDelegate;  // weak
   // Stores whether the user is currently choosing in the UIPickerView the
   // original language, or the target language.
   TranslateInfoBarIOSTag::Tag _languageSelectionType;
@@ -40,17 +44,14 @@
 }
 
 @synthesize languageSelectionHandler = _languageSelectionHandler;
+@dynamic infoBarDelegate;
 
 #pragma mark -
 #pragma mark InfoBarControllerProtocol
 
 - (instancetype)initWithInfoBarDelegate:
-    (translate::TranslateInfoBarDelegate*)delegate {
-  self = [super init];
-  if (self) {
-    _translateInfoBarDelegate = delegate;
-  }
-  return self;
+    (translate::TranslateInfoBarDelegate*)infoBarDelegate {
+  return [super initWithInfoBarDelegate:infoBarDelegate];
 }
 
 - (UIView<InfoBarViewSizing>*)viewForFrame:(CGRect)frame {
@@ -58,7 +59,7 @@
       [[ConfirmInfoBarView alloc] initWithFrame:frame];
   _infoBarView = infoBarView;
   // Icon
-  gfx::Image icon = _translateInfoBarDelegate->GetIcon();
+  gfx::Image icon = self.infoBarDelegate->GetIcon();
   if (!icon.IsEmpty())
     [infoBarView addLeftIcon:icon.ToUIImage()];
 
@@ -82,10 +83,10 @@
 }
 
 - (void)updateInfobarLabelOnView:(ConfirmInfoBarView*)view {
-  NSString* originalLanguage = base::SysUTF16ToNSString(
-      _translateInfoBarDelegate->original_language_name());
-  NSString* targetLanguage = base::SysUTF16ToNSString(
-      _translateInfoBarDelegate->target_language_name());
+  NSString* originalLanguage =
+      base::SysUTF16ToNSString(self.infoBarDelegate->original_language_name());
+  NSString* targetLanguage =
+      base::SysUTF16ToNSString(self.infoBarDelegate->target_language_name());
   base::string16 originalLanguageWithLink =
       base::SysNSStringToUTF16([[view class]
           stringAsLink:originalLanguage
@@ -107,22 +108,18 @@
 #pragma mark - Handling of User Events
 
 - (void)infoBarButtonDidPress:(id)sender {
-  // This press might have occurred after the user has already pressed a button,
-  // in which case the view has been detached from the delegate and this press
-  // should be ignored.
-  if (!self.delegate) {
+  if ([self shouldIgnoreUserInteraction])
     return;
-  }
 
   NSUInteger buttonId = base::mac::ObjCCastStrict<UIButton>(sender).tag;
   switch (buttonId) {
     case TranslateInfoBarIOSTag::BEFORE_ACCEPT:
-      _translateInfoBarDelegate->Translate();
+      self.infoBarDelegate->Translate();
       break;
     case TranslateInfoBarIOSTag::BEFORE_DENY:
-      _translateInfoBarDelegate->TranslationDeclined();
-      if (_translateInfoBarDelegate->ShouldShowNeverTranslateShortcut())
-        _translateInfoBarDelegate->ShowNeverTranslateInfobar();
+      self.infoBarDelegate->TranslationDeclined();
+      if (self.infoBarDelegate->ShouldShowNeverTranslateShortcut())
+        self.infoBarDelegate->ShowNeverTranslateInfobar();
       else
         self.delegate->RemoveInfoBar();
       break;
@@ -133,6 +130,9 @@
 }
 
 - (void)infobarLinkDidPress:(NSUInteger)tag {
+  if ([self shouldIgnoreUserInteraction])
+    return;
+
   _languageSelectionType = static_cast<TranslateInfoBarIOSTag::Tag>(tag);
   DCHECK(_languageSelectionType ==
              TranslateInfoBarIOSTag::BEFORE_SOURCE_LANGUAGE ||
@@ -144,13 +144,13 @@
   int originalLanguageIndex = -1;
   int targetLanguageIndex = -1;
 
-  for (size_t i = 0; i < _translateInfoBarDelegate->num_languages(); ++i) {
-    if (_translateInfoBarDelegate->language_code_at(i) ==
-        _translateInfoBarDelegate->original_language_code()) {
+  for (size_t i = 0; i < self.infoBarDelegate->num_languages(); ++i) {
+    if (self.infoBarDelegate->language_code_at(i) ==
+        self.infoBarDelegate->original_language_code()) {
       originalLanguageIndex = i;
     }
-    if (_translateInfoBarDelegate->language_code_at(i) ==
-        _translateInfoBarDelegate->target_language_code()) {
+    if (self.infoBarDelegate->language_code_at(i) ==
+        self.infoBarDelegate->target_language_code()) {
       targetLanguageIndex = i;
     }
   }
@@ -165,10 +165,10 @@
     selectedRow = targetLanguageIndex;
     disabledRow = originalLanguageIndex;
   }
-  LanguageSelectionContext* context = [LanguageSelectionContext
-      contextWithLanguageData:_translateInfoBarDelegate
-                 initialIndex:selectedRow
-             unavailableIndex:disabledRow];
+  LanguageSelectionContext* context =
+      [LanguageSelectionContext contextWithLanguageData:self.infoBarDelegate
+                                           initialIndex:selectedRow
+                                       unavailableIndex:disabledRow];
   [self.languageSelectionHandler showLanguageSelectorWithContext:context
                                                         delegate:self];
 }
@@ -176,15 +176,18 @@
 #pragma mark - LanguageSelectionDelegate
 
 - (void)languageSelectorSelectedLanguage:(std::string)languageCode {
+  if ([self shouldIgnoreUserInteraction])
+    return;
+
   if (_languageSelectionType ==
           TranslateInfoBarIOSTag::BEFORE_SOURCE_LANGUAGE &&
-      languageCode != _translateInfoBarDelegate->target_language_code()) {
-    _translateInfoBarDelegate->UpdateOriginalLanguage(languageCode);
+      languageCode != self.infoBarDelegate->target_language_code()) {
+    self.infoBarDelegate->UpdateOriginalLanguage(languageCode);
   }
   if (_languageSelectionType ==
           TranslateInfoBarIOSTag::BEFORE_TARGET_LANGUAGE &&
-      languageCode != _translateInfoBarDelegate->original_language_code()) {
-    _translateInfoBarDelegate->UpdateTargetLanguage(languageCode);
+      languageCode != self.infoBarDelegate->original_language_code()) {
+    self.infoBarDelegate->UpdateTargetLanguage(languageCode);
   }
   [self updateInfobarLabelOnView:_infoBarView];
 }
diff --git a/ios/chrome/browser/translate/never_translate_infobar_controller.h b/ios/chrome/browser/translate/never_translate_infobar_controller.h
index d823b1d8..0bf8367 100644
--- a/ios/chrome/browser/translate/never_translate_infobar_controller.h
+++ b/ios/chrome/browser/translate/never_translate_infobar_controller.h
@@ -13,10 +13,9 @@
 
 @interface NeverTranslateInfoBarController : InfoBarController
 
-- (instancetype)init NS_UNAVAILABLE;
-
 - (instancetype)initWithInfoBarDelegate:
-    (translate::TranslateInfoBarDelegate*)delegate;
+    (translate::TranslateInfoBarDelegate*)infoBarDelegate
+    NS_DESIGNATED_INITIALIZER;
 
 @end
 
diff --git a/ios/chrome/browser/translate/never_translate_infobar_controller.mm b/ios/chrome/browser/translate/never_translate_infobar_controller.mm
index 91a312bf..1217499a 100644
--- a/ios/chrome/browser/translate/never_translate_infobar_controller.mm
+++ b/ios/chrome/browser/translate/never_translate_infobar_controller.mm
@@ -9,6 +9,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "components/strings/grit/components_strings.h"
 #include "components/translate/core/browser/translate_infobar_delegate.h"
+#import "ios/chrome/browser/infobars/infobar_controller+protected.h"
 #include "ios/chrome/browser/infobars/infobar_controller_delegate.h"
 #include "ios/chrome/browser/translate/translate_infobar_tags.h"
 #import "ios/chrome/browser/ui/infobars/confirm_infobar_view.h"
@@ -20,9 +21,11 @@
 #error "This file requires ARC support."
 #endif
 
-@interface NeverTranslateInfoBarController () {
-  translate::TranslateInfoBarDelegate* _translateInfoBarDelegate;
-}
+@interface NeverTranslateInfoBarController ()
+
+// Overrides superclass property.
+@property(nonatomic, readonly)
+    translate::TranslateInfoBarDelegate* infoBarDelegate;
 
 // Action for any of the user defined buttons.
 - (void)infoBarButtonDidPress:(id)sender;
@@ -31,28 +34,26 @@
 
 @implementation NeverTranslateInfoBarController
 
+@dynamic infoBarDelegate;
+
 #pragma mark -
 #pragma mark InfoBarControllerProtocol
 
 - (instancetype)initWithInfoBarDelegate:
-    (translate::TranslateInfoBarDelegate*)delegate {
-  self = [super init];
-  if (self) {
-    _translateInfoBarDelegate = delegate;
-  }
-  return self;
+    (translate::TranslateInfoBarDelegate*)infoBarDelegate {
+  return [super initWithInfoBarDelegate:infoBarDelegate];
 }
 
 - (UIView<InfoBarViewSizing>*)viewForFrame:(CGRect)frame {
   ConfirmInfoBarView* infoBarView =
       [[ConfirmInfoBarView alloc] initWithFrame:frame];
   // Icon
-  gfx::Image icon = _translateInfoBarDelegate->GetIcon();
+  gfx::Image icon = self.infoBarDelegate->GetIcon();
   if (!icon.IsEmpty())
     [infoBarView addLeftIcon:icon.ToUIImage()];
   // Main text.
   base::string16 originalLanguage =
-      _translateInfoBarDelegate->original_language_name();
+      self.infoBarDelegate->original_language_name();
   [infoBarView
       addLabel:l10n_util::GetNSStringF(IDS_IOS_TRANSLATE_INFOBAR_NEVER_MESSAGE,
                                        originalLanguage)];
@@ -77,26 +78,22 @@
 #pragma mark - Handling of User Events
 
 - (void)infoBarButtonDidPress:(id)sender {
-  // This press might have occurred after the user has already pressed a button,
-  // in which case the view has been detached from the delegate and this press
-  // should be ignored.
-  if (!self.delegate) {
+  if ([self shouldIgnoreUserInteraction])
     return;
-  }
 
   NSUInteger buttonId = base::mac::ObjCCastStrict<UIButton>(sender).tag;
   switch (buttonId) {
     case TranslateInfoBarIOSTag::CLOSE:
-      _translateInfoBarDelegate->InfoBarDismissed();
+      self.infoBarDelegate->InfoBarDismissed();
       self.delegate->RemoveInfoBar();
       break;
     case TranslateInfoBarIOSTag::DENY_LANGUAGE:
-      _translateInfoBarDelegate->NeverTranslatePageLanguage();
+      self.infoBarDelegate->NeverTranslatePageLanguage();
       self.delegate->RemoveInfoBar();
       break;
     case TranslateInfoBarIOSTag::DENY_WEBSITE:
-      if (!_translateInfoBarDelegate->IsSiteBlacklisted())
-        _translateInfoBarDelegate->ToggleSiteBlacklist();
+      if (!self.infoBarDelegate->IsSiteBlacklisted())
+        self.infoBarDelegate->ToggleSiteBlacklist();
       self.delegate->RemoveInfoBar();
       break;
     default:
diff --git a/ios/chrome/browser/translate/translate_message_infobar_controller.h b/ios/chrome/browser/translate/translate_message_infobar_controller.h
index 95ecf73..efdf830f 100644
--- a/ios/chrome/browser/translate/translate_message_infobar_controller.h
+++ b/ios/chrome/browser/translate/translate_message_infobar_controller.h
@@ -13,10 +13,9 @@
 
 @interface TranslateMessageInfoBarController : InfoBarController
 
-- (instancetype)init NS_UNAVAILABLE;
-
 - (instancetype)initWithInfoBarDelegate:
-    (translate::TranslateInfoBarDelegate*)delegate;
+    (translate::TranslateInfoBarDelegate*)infoBarDelegate
+    NS_DESIGNATED_INITIALIZER;
 
 @end
 
diff --git a/ios/chrome/browser/translate/translate_message_infobar_controller.mm b/ios/chrome/browser/translate/translate_message_infobar_controller.mm
index aff14325..31b4ad4e 100644
--- a/ios/chrome/browser/translate/translate_message_infobar_controller.mm
+++ b/ios/chrome/browser/translate/translate_message_infobar_controller.mm
@@ -7,6 +7,7 @@
 #include "base/mac/foundation_util.h"
 #include "base/strings/sys_string_conversions.h"
 #include "components/translate/core/browser/translate_infobar_delegate.h"
+#import "ios/chrome/browser/infobars/infobar_controller+protected.h"
 #include "ios/chrome/browser/infobars/infobar_controller_delegate.h"
 #include "ios/chrome/browser/translate/translate_infobar_tags.h"
 #import "ios/chrome/browser/ui/infobars/confirm_infobar_view.h"
@@ -16,41 +17,40 @@
 #error "This file requires ARC support."
 #endif
 
-@interface TranslateMessageInfoBarController () {
-  translate::TranslateInfoBarDelegate* _translateInfoBarDelegate;
-}
+@interface TranslateMessageInfoBarController ()
+
+// Overrides superclass property.
+@property(nonatomic, readonly)
+    translate::TranslateInfoBarDelegate* infoBarDelegate;
 
 @end
 
 @implementation TranslateMessageInfoBarController
 
+@dynamic infoBarDelegate;
+
 - (instancetype)initWithInfoBarDelegate:
-    (translate::TranslateInfoBarDelegate*)delegate {
-  self = [super init];
-  if (self) {
-    _translateInfoBarDelegate = delegate;
-  }
-  return self;
+    (translate::TranslateInfoBarDelegate*)infoBarDelegate {
+  return [super initWithInfoBarDelegate:infoBarDelegate];
 }
 
 - (UIView<InfoBarViewSizing>*)viewForFrame:(CGRect)frame {
   ConfirmInfoBarView* infoBarView =
       [[ConfirmInfoBarView alloc] initWithFrame:frame];
   // Icon
-  gfx::Image icon = _translateInfoBarDelegate->GetIcon();
+  gfx::Image icon = self.infoBarDelegate->GetIcon();
   if (!icon.IsEmpty())
     [infoBarView addLeftIcon:icon.ToUIImage()];
   // Text.
-  [infoBarView
-      addLabel:base::SysUTF16ToNSString(
-                   _translateInfoBarDelegate->GetMessageInfoBarText())];
+  [infoBarView addLabel:base::SysUTF16ToNSString(
+                            self.infoBarDelegate->GetMessageInfoBarText())];
   // Close button.
   [infoBarView addCloseButtonWithTag:TranslateInfoBarIOSTag::CLOSE
                               target:self
                               action:@selector(infoBarButtonDidPress:)];
   // Other button.
   base::string16 buttonText(
-      _translateInfoBarDelegate->GetMessageInfoBarButtonText());
+      self.infoBarDelegate->GetMessageInfoBarButtonText());
   if (!buttonText.empty()) {
     [infoBarView addButton:base::SysUTF16ToNSString(buttonText)
                        tag:TranslateInfoBarIOSTag::MESSAGE
@@ -63,21 +63,17 @@
 #pragma mark - Handling of User Events
 
 - (void)infoBarButtonDidPress:(id)sender {
-  // This press might have occurred after the user has already pressed a button,
-  // in which case the view has been detached from the delegate and this press
-  // should be ignored.
-  if (!self.delegate) {
+  if ([self shouldIgnoreUserInteraction])
     return;
-  }
 
   NSUInteger buttonId = base::mac::ObjCCastStrict<UIButton>(sender).tag;
   switch (buttonId) {
     case TranslateInfoBarIOSTag::CLOSE:
-      _translateInfoBarDelegate->InfoBarDismissed();
+      self.infoBarDelegate->InfoBarDismissed();
       self.delegate->RemoveInfoBar();
       break;
     case TranslateInfoBarIOSTag::MESSAGE:
-      _translateInfoBarDelegate->MessageInfoBarButtonPressed();
+      self.infoBarDelegate->MessageInfoBarButtonPressed();
       break;
     default:
       NOTREACHED() << "Unexpected Translate button label";
diff --git a/ios/chrome/browser/ui/BUILD.gn b/ios/chrome/browser/ui/BUILD.gn
index 134f070..2b698573 100644
--- a/ios/chrome/browser/ui/BUILD.gn
+++ b/ios/chrome/browser/ui/BUILD.gn
@@ -372,6 +372,7 @@
     "//ios/chrome/browser/ui/image_util",
     "//ios/chrome/browser/ui/keyboard",
     "//ios/chrome/browser/ui/main:feature_flags",
+    "//ios/chrome/browser/ui/main:tab_switcher",
     "//ios/chrome/browser/ui/main_content:main_content_ui",
     "//ios/chrome/browser/ui/main_content:main_content_ui_broadcasting_util",
     "//ios/chrome/browser/ui/ntp",
@@ -396,7 +397,6 @@
     "//ios/chrome/browser/ui/snackbar",
     "//ios/chrome/browser/ui/stack_view",
     "//ios/chrome/browser/ui/static_content",
-    "//ios/chrome/browser/ui/tab_switcher",
     "//ios/chrome/browser/ui/tabs",
     "//ios/chrome/browser/ui/tabs:coordinator",
     "//ios/chrome/browser/ui/tabs/requirements",
@@ -452,7 +452,6 @@
     "//ios/chrome/browser/ui/overscroll_actions",
     "//ios/chrome/browser/ui/settings",
     "//ios/chrome/browser/ui/stack_view",
-    "//ios/chrome/browser/ui/tab_switcher",
     "//ios/chrome/browser/ui/tabs:coordinator",
     "//ios/chrome/browser/ui/toolbar",
     "//ios/chrome/browser/web:web_internal",
diff --git a/ios/chrome/browser/ui/autofill/BUILD.gn b/ios/chrome/browser/ui/autofill/BUILD.gn
index a412546..cc8b809 100644
--- a/ios/chrome/browser/ui/autofill/BUILD.gn
+++ b/ios/chrome/browser/ui/autofill/BUILD.gn
@@ -106,6 +106,8 @@
     ":autofill_ui",
     "//components/autofill/core/browser",
     "//components/autofill/ios/browser:test_support",
+    "//components/strings:components_strings_grit",
+    "//ios/chrome/browser/autofill",
     "//ios/chrome/test/app:test_support",
     "//ios/chrome/test/earl_grey:test_support",
     "//ios/web/public/test/http_server",
diff --git a/ios/chrome/browser/ui/autofill/save_card_infobar_controller.h b/ios/chrome/browser/ui/autofill/save_card_infobar_controller.h
index 68171ed..1427a56d 100644
--- a/ios/chrome/browser/ui/autofill/save_card_infobar_controller.h
+++ b/ios/chrome/browser/ui/autofill/save_card_infobar_controller.h
@@ -19,11 +19,9 @@
 @interface SaveCardInfoBarController : InfoBarController
 
 - (instancetype)initWithInfoBarDelegate:
-    (autofill::AutofillSaveCardInfoBarDelegateMobile*)delegate
+    (autofill::AutofillSaveCardInfoBarDelegateMobile*)infoBarDelegate
     NS_DESIGNATED_INITIALIZER;
 
-- (instancetype)init NS_UNAVAILABLE;
-
 @end
 
 #endif  // IOS_CHROME_BROWSER_UI_AUTOFILL_SAVE_CARD_INFOBAR_CONTROLLER_H_
diff --git a/ios/chrome/browser/ui/autofill/save_card_infobar_controller.mm b/ios/chrome/browser/ui/autofill/save_card_infobar_controller.mm
index 7afb565..f5c519e 100644
--- a/ios/chrome/browser/ui/autofill/save_card_infobar_controller.mm
+++ b/ios/chrome/browser/ui/autofill/save_card_infobar_controller.mm
@@ -57,8 +57,9 @@
 
 @interface SaveCardInfoBarController ()<SaveCardInfoBarViewDelegate>
 
+// Overrides superclass property.
 @property(nonatomic, assign)
-    autofill::AutofillSaveCardInfoBarDelegateMobile* saveCardInfobarDelegate;
+    autofill::AutofillSaveCardInfoBarDelegateMobile* infoBarDelegate;
 
 @property(nonatomic, weak) SaveCardInfoBarView* infoBarView;
 
@@ -66,16 +67,12 @@
 
 @implementation SaveCardInfoBarController
 
-@synthesize saveCardInfobarDelegate = _saveCardInfobarDelegate;
+@dynamic infoBarDelegate;
 @synthesize infoBarView = _infoBarView;
 
 - (instancetype)initWithInfoBarDelegate:
-    (autofill::AutofillSaveCardInfoBarDelegateMobile*)delegate {
-  self = [super init];
-  if (self) {
-    self.saveCardInfobarDelegate = delegate;
-  }
-  return self;
+    (autofill::AutofillSaveCardInfoBarDelegateMobile*)infoBarDelegate {
+  return [super initWithInfoBarDelegate:infoBarDelegate];
 }
 
 - (UIView<InfoBarViewSizing>*)viewForFrame:(CGRect)frame {
@@ -83,28 +80,27 @@
       [[SaveCardInfoBarView alloc] initWithFrame:frame];
   self.infoBarView = infoBarView;
   self.infoBarView.accessibilityIdentifier =
-      self.saveCardInfobarDelegate->upload()
-          ? kSaveCardInfobarViewUploadAccessibilityID
-          : kSaveCardInfobarViewLocalAccessibilityID;
+      self.infoBarDelegate->upload() ? kSaveCardInfobarViewUploadAccessibilityID
+                                     : kSaveCardInfobarViewLocalAccessibilityID;
   self.infoBarView.delegate = self;
 
   // Close button.
   [self.infoBarView setCloseButtonImage:InfoBarCloseImage()];
 
   // Icon.
-  gfx::Image icon = self.saveCardInfobarDelegate->GetIcon();
+  gfx::Image icon = self.infoBarDelegate->GetIcon();
   DCHECK(!icon.IsEmpty());
-  if (self.saveCardInfobarDelegate->IsGooglePayBrandingEnabled())
+  if (self.infoBarDelegate->IsGooglePayBrandingEnabled())
     [self.infoBarView setGooglePayIcon:icon.ToUIImage()];
   else
     [self.infoBarView setIcon:icon.ToUIImage()];
 
   // Message, if any.
-  base::string16 messageText = self.saveCardInfobarDelegate->GetMessageText();
+  base::string16 messageText = self.infoBarDelegate->GetMessageText();
   if (!messageText.empty()) {
     MessageWithLinks* message = [[MessageWithLinks alloc] init];
-    const base::string16 linkText = self.saveCardInfobarDelegate->GetLinkText();
-    GURL linkURL = self.saveCardInfobarDelegate->GetLinkURL();
+    const base::string16 linkText = self.infoBarDelegate->GetLinkText();
+    GURL linkURL = self.infoBarDelegate->GetLinkURL();
 
     if (!linkText.empty() && !linkURL.is_empty()) {
       std::vector<GURL> linkURLs;
@@ -123,27 +119,24 @@
   }
 
   // Description, if any.
-  const base::string16 description =
-      self.saveCardInfobarDelegate->GetDescriptionText();
+  const base::string16 description = self.infoBarDelegate->GetDescriptionText();
   if (!description.empty()) {
     [self.infoBarView setDescription:base::SysUTF16ToNSString(description)];
   }
 
   // Card details.
   [self.infoBarView
-      setCardIssuerIcon:NativeImage(
-                            self.saveCardInfobarDelegate->issuer_icon_id())];
-  [self.infoBarView
-      setCardLabel:base::SysUTF16ToNSString(
-                       self.saveCardInfobarDelegate->card_label())];
+      setCardIssuerIcon:NativeImage(self.infoBarDelegate->issuer_icon_id())];
+  [self.infoBarView setCardLabel:base::SysUTF16ToNSString(
+                                     self.infoBarDelegate->card_label())];
   [self.infoBarView
       setCardSublabel:base::SysUTF16ToNSString(
-                          self.saveCardInfobarDelegate->card_sub_label())];
+                          self.infoBarDelegate->card_sub_label())];
 
   // Legal messages, if any.
-  if (!self.saveCardInfobarDelegate->legal_messages().empty()) {
+  if (!self.infoBarDelegate->legal_messages().empty()) {
     NSMutableArray* legalMessages = [[NSMutableArray alloc] init];
-    for (const auto& line : self.saveCardInfobarDelegate->legal_messages()) {
+    for (const auto& line : self.infoBarDelegate->legal_messages()) {
       MessageWithLinks* message = [[MessageWithLinks alloc] init];
       message.messageText = base::SysUTF16ToNSString(line.text());
       NSMutableArray* linkRanges = [[NSMutableArray alloc] init];
@@ -161,13 +154,13 @@
 
   // Cancel button.
   const base::string16 cancelButtonTitle = GetTitleForButton(
-      self.saveCardInfobarDelegate, ConfirmInfoBarDelegate::BUTTON_CANCEL);
+      self.infoBarDelegate, ConfirmInfoBarDelegate::BUTTON_CANCEL);
   [self.infoBarView
       setCancelButtonTitle:base::SysUTF16ToNSString(cancelButtonTitle)];
 
   // Confirm button.
   const base::string16 confirmButtonTitle = GetTitleForButton(
-      self.saveCardInfobarDelegate, ConfirmInfoBarDelegate::BUTTON_OK);
+      self.infoBarDelegate, ConfirmInfoBarDelegate::BUTTON_OK);
   [self.infoBarView
       setConfirmButtonTitle:base::SysUTF16ToNSString(confirmButtonTitle)];
 
@@ -177,53 +170,42 @@
 #pragma mark - SaveCardInfoBarViewDelegate
 
 - (void)saveCardInfoBarViewDidTapLink:(SaveCardInfoBarView*)sender {
-  // Ignore this tap if the view has been detached from the delegate.
-  if (!self.delegate) {
+  if ([self shouldIgnoreUserInteraction])
     return;
-  }
 
-  self.saveCardInfobarDelegate->LinkClicked(
-      WindowOpenDisposition::NEW_FOREGROUND_TAB);
+  self.infoBarDelegate->LinkClicked(WindowOpenDisposition::NEW_FOREGROUND_TAB);
 }
 
 - (void)saveCardInfoBarView:(SaveCardInfoBarView*)sender
          didTapLegalLinkURL:(const GURL&)linkURL {
-  // Ignore this tap if the view has been detached from the delegate.
-  if (!self.delegate) {
+  if ([self shouldIgnoreUserInteraction])
     return;
-  }
 
-  self.saveCardInfobarDelegate->OnLegalMessageLinkClicked(linkURL);
+  self.infoBarDelegate->OnLegalMessageLinkClicked(linkURL);
 }
 
 - (void)saveCardInfoBarViewDidTapClose:(SaveCardInfoBarView*)sender {
-  // Ignore this tap if the view has been detached from the delegate.
-  if (!self.delegate) {
+  if ([self shouldIgnoreUserInteraction])
     return;
-  }
 
-  self.saveCardInfobarDelegate->InfoBarDismissed();
+  self.infoBarDelegate->InfoBarDismissed();
   self.delegate->RemoveInfoBar();
 }
 
 - (void)saveCardInfoBarViewDidTapCancel:(SaveCardInfoBarView*)sender {
-  // Ignore this tap if the view has been detached from the delegate.
-  if (!self.delegate) {
+  if ([self shouldIgnoreUserInteraction])
     return;
-  }
 
-  if (self.saveCardInfobarDelegate->Cancel()) {
+  if (self.infoBarDelegate->Cancel()) {
     self.delegate->RemoveInfoBar();
   }
 }
 
 - (void)saveCardInfoBarViewDidTapConfirm:(SaveCardInfoBarView*)sender {
-  // Ignore this tap if the view has been detached from the delegate.
-  if (!self.delegate) {
+  if ([self shouldIgnoreUserInteraction])
     return;
-  }
 
-  if (self.saveCardInfobarDelegate->Accept()) {
+  if (self.infoBarDelegate->Accept()) {
     self.delegate->RemoveInfoBar();
   }
 }
diff --git a/ios/chrome/browser/ui/autofill/save_card_infobar_egtest.mm b/ios/chrome/browser/ui/autofill/save_card_infobar_egtest.mm
index d38aa9d..bdeccd2 100644
--- a/ios/chrome/browser/ui/autofill/save_card_infobar_egtest.mm
+++ b/ios/chrome/browser/ui/autofill/save_card_infobar_egtest.mm
@@ -6,16 +6,24 @@
 
 #include "base/logging.h"
 #import "base/test/ios/wait_util.h"
+#include "base/test/metrics/histogram_tester.h"
 #include "components/autofill/core/browser/autofill_manager.h"
+#include "components/autofill/core/browser/autofill_metrics.h"
 #include "components/autofill/core/browser/form_data_importer.h"
+#include "components/autofill/core/browser/personal_data_manager.h"
 #include "components/autofill/ios/browser/autofill_driver_ios.h"
 #import "components/autofill/ios/browser/credit_card_save_manager_test_observer_bridge.h"
 #include "components/autofill/ios/browser/ios_test_event_waiter.h"
+#include "components/strings/grit/components_strings.h"
+#include "ios/chrome/browser/autofill/personal_data_manager_factory.h"
 #import "ios/chrome/browser/ui/autofill/save_card_infobar_controller.h"
 #import "ios/chrome/test/app/chrome_test_util.h"
 #import "ios/chrome/test/earl_grey/chrome_earl_grey.h"
+#import "ios/chrome/test/earl_grey/chrome_matchers.h"
 #import "ios/chrome/test/earl_grey/chrome_test_case.h"
 #import "ios/web/public/test/http_server/http_server.h"
+#include "ios/web/public/web_state/web_frame_util.h"
+#import "ios/web/public/web_state/web_frames_manager.h"
 #import "ios/web/public/web_state/web_state.h"
 #include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
 #include "services/network/test/test_url_loader_factory.h"
@@ -57,6 +65,15 @@
   SENT_UPLOAD_CARD_REQUEST,
 };
 
+id<GREYMatcher> closeButtonMatcher() {
+  return chrome_test_util::ButtonWithAccessibilityLabelId(IDS_CLOSE);
+}
+
+id<GREYMatcher> saveButtonMatcher() {
+  return chrome_test_util::ButtonWithAccessibilityLabelId(
+      IDS_AUTOFILL_SAVE_CARD_PROMPT_ACCEPT);
+}
+
 }  // namepsace
 
 namespace autofill {
@@ -70,8 +87,9 @@
 
   static CreditCardSaveManager* credit_card_save_manager() {
     web::WebState* web_state = chrome_test_util::GetCurrentWebState();
+    web::WebFrame* main_frame = web::GetMainWebFrame(web_state);
     DCHECK(web_state);
-    return AutofillDriverIOS::FromWebState(web_state)
+    return AutofillDriverIOS::FromWebStateAndWebFrame(web_state, main_frame)
         ->autofill_manager()
         ->form_data_importer_.get()
         ->credit_card_save_manager_.get();
@@ -79,8 +97,9 @@
 
   static payments::PaymentsClient* payments_client() {
     web::WebState* web_state = chrome_test_util::GetCurrentWebState();
+    web::WebFrame* main_frame = web::GetMainWebFrame(web_state);
     DCHECK(web_state);
-    return AutofillDriverIOS::FromWebState(web_state)
+    return AutofillDriverIOS::FromWebStateAndWebFrame(web_state, main_frame)
         ->autofill_manager()
         ->payments_client();
   }
@@ -98,6 +117,7 @@
   std::unique_ptr<autofill::CreditCardSaveManagerTestObserverBridge>
       credit_card_save_manager_observer_;
   std::unique_ptr<autofill::IOSTestEventWaiter<InfobarEvent>> event_waiter_;
+  autofill::PersonalDataManager* personal_data_manager_;
 }
 
 @end
@@ -107,6 +127,10 @@
 - (void)setUp {
   [super setUp];
 
+  personal_data_manager_ =
+      autofill::PersonalDataManagerFactory::GetForBrowserState(
+          chrome_test_util::GetOriginalBrowserState());
+
   // Set up the URL loader factory for the PaymentsClient so we can intercept
   // those network requests.
   shared_url_loader_factory_ =
@@ -125,6 +149,15 @@
           credit_card_save_manager, self);
 }
 
+- (void)tearDown {
+  // Clear existing credit cards.
+  for (const auto* creditCard : personal_data_manager_->GetCreditCards()) {
+    personal_data_manager_->RemoveByGUID(creditCard->guid());
+  }
+
+  [super tearDown];
+}
+
 #pragma mark - autofill::IOSTestEventWaiter helper methods
 
 - (void)resetEventWaiterForEvents:(std::list<InfobarEvent>)events
@@ -193,7 +226,7 @@
 
 #pragma mark - Helper methods
 
-- (BOOL)waitForSaveCardInfobarOrTimeout:(NSString*)accessibilityIdentifier {
+- (BOOL)waitForUIElementToAppearOrTimeout:(NSString*)accessibilityIdentifier {
   ConditionBlock condition = ^{
     NSError* error = nil;
     [[EarlGrey
@@ -205,11 +238,24 @@
   return WaitUntilConditionOrTimeout(kWaitForUIElementTimeout, condition);
 }
 
+- (BOOL)waitForUIElementToDisappearOrTimeout:
+    (NSString*)accessibilityIdentifier {
+  ConditionBlock condition = ^{
+    NSError* error = nil;
+    [[EarlGrey
+        selectElementWithMatcher:grey_accessibilityID(accessibilityIdentifier)]
+        assertWithMatcher:grey_nil()
+                    error:&error];
+    return error == nil;
+  };
+  return WaitUntilConditionOrTimeout(kWaitForUIElementTimeout, condition);
+}
+
 #pragma mark - Tests
 
 // Ensures that submitting the form should query Google Payments; and the
-// fallback local save infobar appears if the request unexpectedly fails but the
-// form data is complete.
+// fallback local save infobar becomes visible if the request unexpectedly fails
+// but the form data is complete.
 - (void)testOfferLocalSave_FullData_RequestFails {
   [ChromeEarlGrey
       loadURL:web::test::HttpServer::MakeUrl(kCreditCardUploadForm)];
@@ -228,14 +274,14 @@
   [self waitForEvents];
 
   // Wait until the save card infobar becomes visible.
-  GREYAssert([self waitForSaveCardInfobarOrTimeout:
+  GREYAssert([self waitForUIElementToAppearOrTimeout:
                        kSaveCardInfobarViewLocalAccessibilityID],
              @"Save card infobar failed to show.");
 }
 
 // Ensures that submitting the form should query Google Payments; and the
-// fallback local save infobar appears if the request is declined but the form
-// data is complete.
+// fallback local save infobar becomes visible if the request is declined but
+// the form data is complete.
 - (void)testOfferLocalSave_FullData_PaymentsDeclines {
   [ChromeEarlGrey
       loadURL:web::test::HttpServer::MakeUrl(kCreditCardUploadForm)];
@@ -253,7 +299,7 @@
   [self waitForEvents];
 
   // Wait until the save card infobar becomes visible.
-  GREYAssert([self waitForSaveCardInfobarOrTimeout:
+  GREYAssert([self waitForUIElementToAppearOrTimeout:
                        kSaveCardInfobarViewLocalAccessibilityID],
              @"Save card infobar failed to show.");
 }
@@ -261,7 +307,7 @@
 // Ensures that submitting the form, even with only card number and expiration
 // date, should query Google Payments; but the fallback local save infobar
 // should not appear if the request is declined and the form data is incomplete.
-- (void)testNoLocalSave_PartialData_PaymentsDeclines {
+- (void)testNotOfferLocalSave_PartialData_PaymentsDeclines {
   [ChromeEarlGrey
       loadURL:web::test::HttpServer::MakeUrl(kCreditCardUploadForm)];
 
@@ -277,7 +323,7 @@
   [self waitForEvents];
 
   // Make sure the save card infobar does not become visible.
-  GREYAssertFalse([self waitForSaveCardInfobarOrTimeout:
+  GREYAssertFalse([self waitForUIElementToAppearOrTimeout:
                             kSaveCardInfobarViewLocalAccessibilityID],
                   @"Save card infobar should not show.");
 }
@@ -300,7 +346,7 @@
   [self waitForEvents];
 
   // Wait until the save card infobar becomes visible.
-  GREYAssert([self waitForSaveCardInfobarOrTimeout:
+  GREYAssert([self waitForUIElementToAppearOrTimeout:
                        kSaveCardInfobarViewUploadAccessibilityID],
              @"Save card infobar failed to show.");
 }
@@ -324,9 +370,179 @@
   [self waitForEvents];
 
   // Wait until the save card infobar becomes visible.
-  GREYAssert([self waitForSaveCardInfobarOrTimeout:
+  GREYAssert([self waitForUIElementToAppearOrTimeout:
                        kSaveCardInfobarViewUploadAccessibilityID],
              @"Save card infobar failed to show.");
 }
 
+// Ensures that the infobar goes away and UMA metrics are correctly logged if
+// the user declines upload.
+- (void)testUMA_Upstream_UserDeclines {
+  base::HistogramTester histogram_tester;
+
+  [ChromeEarlGrey
+      loadURL:web::test::HttpServer::MakeUrl(kCreditCardUploadForm)];
+
+  // Set up the Google Payments server response.
+  test_url_loader_factory_.AddResponse(kURLGetUploadDetailsRequest,
+                                       kResponseGetUploadDetailsSuccess);
+
+  [self resetEventWaiterForEvents:
+            {InfobarEvent::REQUESTED_UPLOAD_SAVE,
+             InfobarEvent::RECEIVED_GET_UPLOAD_DETAILS_RESPONSE}
+                          timeout:kWaitForDownloadTimeout];
+  [self fillAndSubmitForm];
+  [self waitForEvents];
+
+  // Wait until the save card infobar becomes visible.
+  GREYAssert([self waitForUIElementToAppearOrTimeout:
+                       kSaveCardInfobarViewUploadAccessibilityID],
+             @"Save card infobar failed to show.");
+
+  // Tap the X button.
+  [[EarlGrey selectElementWithMatcher:closeButtonMatcher()]
+      performAction:grey_tap()];
+
+  // Wait until the save card infobar disappears.
+  GREYAssert([self waitForUIElementToDisappearOrTimeout:
+                       kSaveCardInfobarViewUploadAccessibilityID],
+             @"Save card infobar failed to disappear.");
+
+  // Ensure that UMA was logged correctly.
+  histogram_tester.ExpectUniqueSample(
+      "Autofill.UploadOfferedCardOrigin",
+      autofill::AutofillMetrics::OFFERING_UPLOAD_OF_NEW_CARD, 1);
+  histogram_tester.ExpectTotalCount("Autofill.UploadAcceptedCardOrigin", 0);
+}
+
+// Ensures that the infobar goes away, an UploadCardRequest RPC is sent to
+// Google Payments, and UMA metrics are correctly logged if the user accepts
+// upload.
+- (void)testUMA_Upstream_UserAccepts {
+  base::HistogramTester histogram_tester;
+
+  [ChromeEarlGrey
+      loadURL:web::test::HttpServer::MakeUrl(kCreditCardUploadForm)];
+
+  // Set up the Google Payments server response.
+  test_url_loader_factory_.AddResponse(kURLGetUploadDetailsRequest,
+                                       kResponseGetUploadDetailsSuccess);
+
+  [self resetEventWaiterForEvents:
+            {InfobarEvent::REQUESTED_UPLOAD_SAVE,
+             InfobarEvent::RECEIVED_GET_UPLOAD_DETAILS_RESPONSE}
+                          timeout:kWaitForDownloadTimeout];
+  [self fillAndSubmitForm];
+  [self waitForEvents];
+
+  // Wait until the save card infobar becomes visible.
+  GREYAssert([self waitForUIElementToAppearOrTimeout:
+                       kSaveCardInfobarViewUploadAccessibilityID],
+             @"Save card infobar failed to show.");
+
+  // Disable EarlGrey's synchronization since it's blocked by infobar animation.
+  [[GREYConfiguration sharedInstance]
+          setValue:@NO
+      forConfigKey:kGREYConfigKeySynchronizationEnabled];
+
+  [self resetEventWaiterForEvents:{InfobarEvent::SENT_UPLOAD_CARD_REQUEST}
+                          timeout:kWaitForDownloadTimeout];
+  // Tap the save button.
+  [[EarlGrey selectElementWithMatcher:saveButtonMatcher()]
+      performAction:grey_tap()];
+  [self waitForEvents];
+
+  // Reenable synchronization now that the infobar animation is over.
+  [[GREYConfiguration sharedInstance]
+          setValue:@YES
+      forConfigKey:kGREYConfigKeySynchronizationEnabled];
+
+  // Wait until the save card infobar disappears.
+  GREYAssert([self waitForUIElementToDisappearOrTimeout:
+                       kSaveCardInfobarViewUploadAccessibilityID],
+             @"Save card infobar failed to disappear.");
+
+  // Ensure that UMA was logged correctly.
+  histogram_tester.ExpectUniqueSample(
+      "Autofill.UploadOfferedCardOrigin",
+      autofill::AutofillMetrics::OFFERING_UPLOAD_OF_NEW_CARD, 1);
+  histogram_tester.ExpectUniqueSample(
+      "Autofill.UploadAcceptedCardOrigin",
+      autofill::AutofillMetrics::USER_ACCEPTED_UPLOAD_OF_NEW_CARD, 1);
+}
+
+// Ensures that the infobar goes away and no credit card is saved to Chrome if
+// the user declines local save.
+- (void)testUserData_LocalSave_UserDeclines {
+  [ChromeEarlGrey
+      loadURL:web::test::HttpServer::MakeUrl(kCreditCardUploadForm)];
+
+  // Set up the Google Payments server response.
+  test_url_loader_factory_.AddResponse(kURLGetUploadDetailsRequest,
+                                       kResponseGetUploadDetailsFailure);
+
+  [self resetEventWaiterForEvents:
+            {InfobarEvent::REQUESTED_UPLOAD_SAVE,
+             InfobarEvent::RECEIVED_GET_UPLOAD_DETAILS_RESPONSE,
+             InfobarEvent::OFFERED_LOCAL_SAVE}
+                          timeout:kWaitForDownloadTimeout];
+  [self fillAndSubmitForm];
+  [self waitForEvents];
+
+  // Wait until the save card infobar becomes visible.
+  GREYAssert([self waitForUIElementToAppearOrTimeout:
+                       kSaveCardInfobarViewLocalAccessibilityID],
+             @"Save card infobar failed to show.");
+
+  // Tap the X button.
+  [[EarlGrey selectElementWithMatcher:closeButtonMatcher()]
+      performAction:grey_tap()];
+
+  // Wait until the save card infobar disappears.
+  GREYAssert([self waitForUIElementToDisappearOrTimeout:
+                       kSaveCardInfobarViewLocalAccessibilityID],
+             @"Save card infobar failed to disappear.");
+
+  // Ensure credit card is not saved locally.
+  GREYAssertEqual(0U, personal_data_manager_->GetCreditCards().size(),
+                  @"No credit card should have been saved.");
+}
+
+// Ensures that the infobar goes away and the credit card is saved to Chrome if
+// the user accepts local save.
+- (void)testUserData_LocalSave_UserAccepts {
+  [ChromeEarlGrey
+      loadURL:web::test::HttpServer::MakeUrl(kCreditCardUploadForm)];
+
+  // Set up the Google Payments server response.
+  test_url_loader_factory_.AddResponse(kURLGetUploadDetailsRequest,
+                                       kResponseGetUploadDetailsFailure);
+
+  [self resetEventWaiterForEvents:
+            {InfobarEvent::REQUESTED_UPLOAD_SAVE,
+             InfobarEvent::RECEIVED_GET_UPLOAD_DETAILS_RESPONSE,
+             InfobarEvent::OFFERED_LOCAL_SAVE}
+                          timeout:kWaitForDownloadTimeout];
+  [self fillAndSubmitForm];
+  [self waitForEvents];
+
+  // Wait until the save card infobar becomes visible.
+  GREYAssert([self waitForUIElementToAppearOrTimeout:
+                       kSaveCardInfobarViewLocalAccessibilityID],
+             @"Save card infobar failed to show.");
+
+  // Tap the save button.
+  [[EarlGrey selectElementWithMatcher:saveButtonMatcher()]
+      performAction:grey_tap()];
+
+  // Wait until the save card infobar disappears.
+  GREYAssert([self waitForUIElementToDisappearOrTimeout:
+                       kSaveCardInfobarViewLocalAccessibilityID],
+             @"Save card infobar failed to disappear.");
+
+  // Ensure credit card is saved locally.
+  GREYAssertEqual(1U, personal_data_manager_->GetCreditCards().size(),
+                  @"Credit card should have been saved.");
+}
+
 @end
diff --git a/ios/chrome/browser/ui/browser_view_controller.mm b/ios/chrome/browser/ui/browser_view_controller.mm
index d91a50f..c0479beb0 100644
--- a/ios/chrome/browser/ui/browser_view_controller.mm
+++ b/ios/chrome/browser/ui/browser_view_controller.mm
@@ -195,7 +195,6 @@
 #import "ios/chrome/browser/ui/stack_view/card_view.h"
 #import "ios/chrome/browser/ui/stack_view/page_animation_util.h"
 #import "ios/chrome/browser/ui/static_content/static_html_native_content.h"
-#import "ios/chrome/browser/ui/tab_switcher/tab_switcher_controller.h"
 #import "ios/chrome/browser/ui/tabs/background_tab_animation_view.h"
 #import "ios/chrome/browser/ui/tabs/foreground_tab_animation_view.h"
 #import "ios/chrome/browser/ui/tabs/requirements/tab_strip_constants.h"
diff --git a/ios/chrome/browser/ui/content_suggestions/BUILD.gn b/ios/chrome/browser/ui/content_suggestions/BUILD.gn
index fd5089c..33daaec 100644
--- a/ios/chrome/browser/ui/content_suggestions/BUILD.gn
+++ b/ios/chrome/browser/ui/content_suggestions/BUILD.gn
@@ -59,7 +59,6 @@
     "//ios/chrome/browser/ui/coordinators:chrome_coordinators",
     "//ios/chrome/browser/ui/favicon",
     "//ios/chrome/browser/ui/ntp",
-    "//ios/chrome/browser/ui/ntp:ntp_header",
     "//ios/chrome/browser/ui/ntp:ntp_internal",
     "//ios/chrome/browser/ui/overscroll_actions",
     "//ios/chrome/browser/ui/reading_list",
@@ -125,7 +124,6 @@
     "//ios/chrome/browser/ui/content_suggestions/identifier",
     "//ios/chrome/browser/ui/list_model",
     "//ios/chrome/browser/ui/ntp",
-    "//ios/chrome/browser/ui/ntp:ntp_header",
     "//ios/chrome/browser/ui/overscroll_actions",
     "//ios/chrome/browser/ui/toolbar:toolbar_ui",
     "//ios/chrome/browser/ui/toolbar/buttons:buttons",
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_coordinator.h b/ios/chrome/browser/ui/content_suggestions/content_suggestions_coordinator.h
index 5765be5..acc4be5 100644
--- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_coordinator.h
+++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_coordinator.h
@@ -6,7 +6,7 @@
 #define IOS_CHROME_BROWSER_UI_CONTENT_SUGGESTIONS_CONTENT_SUGGESTIONS_COORDINATOR_H_
 
 #import "ios/chrome/browser/ui/coordinators/chrome_coordinator.h"
-#import "ios/chrome/browser/ui/ntp/new_tab_page_panel_protocol.h"
+#import "ios/web/public/web_state/ui/crw_native_content.h"
 
 namespace ios {
 class ChromeBrowserState;
@@ -24,8 +24,7 @@
 
 // Coordinator to manage the Suggestions UI via a
 // ContentSuggestionsViewController.
-@interface ContentSuggestionsCoordinator
-    : ChromeCoordinator<NewTabPagePanelProtocol>
+@interface ContentSuggestionsCoordinator : ChromeCoordinator<CRWNativeContent>
 
 // BrowserState used to create the ContentSuggestionFactory.
 @property(nonatomic, assign) ios::ChromeBrowserState* browserState;
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_coordinator.mm b/ios/chrome/browser/ui/content_suggestions/content_suggestions_coordinator.mm
index de90a0d..a511cdde 100644
--- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_coordinator.mm
+++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_coordinator.mm
@@ -81,7 +81,6 @@
 @synthesize webStateList = _webStateList;
 @synthesize toolbarDelegate = _toolbarDelegate;
 @synthesize dispatcher = _dispatcher;
-@synthesize delegate = _delegate;
 @synthesize metricsRecorder = _metricsRecorder;
 @synthesize NTPMediator = _NTPMediator;
 
@@ -214,10 +213,6 @@
 
 #pragma mark - ContentSuggestionsViewControllerAudience
 
-- (void)contentOffsetDidChange {
-  [self.delegate updateNtpBarShadowForPanelController:self];
-}
-
 - (void)promoShown {
   NotificationPromoWhatsNew* notificationPromo =
       [self.contentSuggestionsMediator notificationPromo];
@@ -278,39 +273,7 @@
   return height + topInset;
 }
 
-#pragma mark - NewTabPagePanelProtocol
-
-- (CGFloat)alphaForBottomShadow {
-  UICollectionView* collection = self.suggestionsViewController.collectionView;
-
-  NSInteger numberOfSection =
-      [collection.dataSource numberOfSectionsInCollectionView:collection];
-
-  NSInteger lastNonEmptySection = 0;
-  NSInteger lastItemIndex = 0;
-  for (NSInteger i = 0; i < numberOfSection; i++) {
-    NSInteger itemsInSection = [collection.dataSource collectionView:collection
-                                              numberOfItemsInSection:i];
-    if (itemsInSection > 0) {
-      // Some sections might be empty. Only consider the last non-empty one.
-      lastNonEmptySection = i;
-      lastItemIndex = itemsInSection - 1;
-    }
-  }
-  if (lastNonEmptySection == 0)
-    return 0;
-
-  NSIndexPath* lastCellIndexPath =
-      [NSIndexPath indexPathForItem:lastItemIndex
-                          inSection:lastNonEmptySection];
-  UICollectionViewLayoutAttributes* attributes =
-      [collection layoutAttributesForItemAtIndexPath:lastCellIndexPath];
-  CGRect lastCellFrame = attributes.frame;
-  CGFloat pixelsBelowFrame =
-      CGRectGetMaxY(lastCellFrame) - CGRectGetMaxY(collection.bounds);
-  CGFloat alpha = pixelsBelowFrame / kNewTabPageDistanceToFadeShadow;
-  return MIN(MAX(alpha, 0), 1);
-}
+#pragma mark - CRWNativeContent
 
 - (UIView*)view {
   return self.suggestionsViewController.view;
@@ -324,7 +287,6 @@
   self.headerController.isShowing = YES;
   [self.suggestionsViewController.collectionView
           .collectionViewLayout invalidateLayout];
-  [self.delegate updateNtpBarShadowForPanelController:self];
 }
 
 - (void)wasHidden {
@@ -347,4 +309,16 @@
   [self.suggestionsViewController clearOverscroll];
 }
 
+- (NSString*)title {
+  return nil;
+}
+
+- (const GURL&)url {
+  return GURL::EmptyGURL();
+}
+
+- (BOOL)isViewAlive {
+  return YES;
+}
+
 @end
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_header_view.h b/ios/chrome/browser/ui/content_suggestions/content_suggestions_header_view.h
index a28607b..cf00b87 100644
--- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_header_view.h
+++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_header_view.h
@@ -5,12 +5,46 @@
 #ifndef IOS_CHROME_BROWSER_UI_CONTENT_SUGGESTIONS_CONTENT_SUGGESTIONS_HEADER_VIEW_H_
 #define IOS_CHROME_BROWSER_UI_CONTENT_SUGGESTIONS_CONTENT_SUGGESTIONS_HEADER_VIEW_H_
 
-#import "ios/chrome/browser/ui/ntp/ntp_header_view_adapter.h"
+#import <UIKit/UIKit.h>
+
+#import "ios/chrome/browser/ui/toolbar/toolbar_owner.h"
 
 // Header view for the NTP. The header view contains all views that are
 // displayed above the list of most visited sites, which includes the
 // primary toolbar, doodle, and fake omnibox.
-@interface ContentSuggestionsHeaderView : UIView<NTPHeaderViewAdapter>
+@interface ContentSuggestionsHeaderView : UIView<ToolbarOwner>
+
+// Returns the toolbar view.
+@property(nonatomic, readonly) UIView* toolBarView;
+
+// Adds the |toolbarView| to the view implementing this protocol.
+// Can only be added once.
+- (void)addToolbarView:(UIView*)toolbarView;
+
+// Returns the progress of the search field position along
+// |ntp_header::kAnimationDistance| as the offset changes.
+- (CGFloat)searchFieldProgressForOffset:(CGFloat)offset
+                         safeAreaInsets:(UIEdgeInsets)safeAreaInsets;
+
+// Changes the constraints of searchField based on its initialFrame and the
+// scroll view's y |offset|. Also adjust the alpha values for |_searchBoxBorder|
+// and |_shadow| and the constant values for the |constraints|.|screenWidth| is
+// the width of the screen, including the space outside the safe area. The
+// |safeAreaInsets| is relative to the view used to calculate the |width|.
+- (void)updateSearchFieldWidth:(NSLayoutConstraint*)widthConstraint
+                        height:(NSLayoutConstraint*)heightConstraint
+                     topMargin:(NSLayoutConstraint*)topMarginConstraint
+                     hintLabel:(UILabel*)hintLabel
+            subviewConstraints:(NSArray*)constraints
+                     forOffset:(CGFloat)offset
+                   screenWidth:(CGFloat)screenWidth
+                safeAreaInsets:(UIEdgeInsets)safeAreaInsets;
+
+// Adds views necessary to customize the NTP search box.
+- (void)addViewsToSearchField:(UIView*)searchField;
+
+// Highlights the fake omnibox.
+- (void)setFakeboxHighlighted:(BOOL)highlighted;
 
 @end
 
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_header_view.mm b/ios/chrome/browser/ui/content_suggestions/content_suggestions_header_view.mm
index dcd397d..9f76b9d 100644
--- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_header_view.mm
+++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_header_view.mm
@@ -68,17 +68,6 @@
   return self;
 }
 
-#pragma mark - Private
-
-// Scale the the hint label down to at most content_suggestions::kHintTextScale.
-- (void)scaleHintLabel:(UIView*)hintLabel percent:(CGFloat)percent {
-  CGFloat scaleValue =
-      1 + (content_suggestions::kHintTextScale * (1 - percent));
-  hintLabel.transform = CGAffineTransformMakeScale(scaleValue, scaleValue);
-}
-
-#pragma mark - NTPHeaderViewAdapter
-
 - (void)addToolbarView:(UIView*)toolbarView {
   _toolBarView = toolbarView;
   [self addSubview:toolbarView];
@@ -267,24 +256,13 @@
                    completion:nil];
 }
 
-- (void)fadeOutShadow {
-  // No-op.
-}
+#pragma mark - Private
 
-- (void)hideToolbarViewsForNewTabPage {
-  // No-op.
-}
-
-- (void)setToolbarTabCount:(int)tabCount {
-  // No-op.
-}
-
-- (void)setCanGoForward:(BOOL)canGoForward {
-  // No-op.
-}
-
-- (void)setCanGoBack:(BOOL)canGoBack {
-  // No-op.
+// Scale the the hint label down to at most content_suggestions::kHintTextScale.
+- (void)scaleHintLabel:(UIView*)hintLabel percent:(CGFloat)percent {
+  CGFloat scaleValue =
+      1 + (content_suggestions::kHintTextScale * (1 - percent));
+  hintLabel.transform = CGAffineTransformMakeScale(scaleValue, scaleValue);
 }
 
 #pragma mark - ToolbarOwner
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_header_view_controller.mm b/ios/chrome/browser/ui/content_suggestions/content_suggestions_header_view_controller.mm
index 9cecd82e..4dfd07f 100644
--- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_header_view_controller.mm
+++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_header_view_controller.mm
@@ -19,7 +19,6 @@
 #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_header_view_controller_delegate.h"
 #import "ios/chrome/browser/ui/content_suggestions/ntp_home_constant.h"
 #import "ios/chrome/browser/ui/ntp/new_tab_page_header_constants.h"
-#import "ios/chrome/browser/ui/ntp/new_tab_page_header_view.h"
 #import "ios/chrome/browser/ui/toolbar/adaptive/primary_toolbar_view.h"
 #import "ios/chrome/browser/ui/toolbar/adaptive/primary_toolbar_view_controller.h"
 #import "ios/chrome/browser/ui/toolbar/public/fakebox_focuser.h"
@@ -64,7 +63,7 @@
 // The number of tabs to show in the google landing fake toolbar.
 @property(nonatomic, assign) int tabCount;
 
-@property(nonatomic, strong) UIView<NTPHeaderViewAdapter>* headerView;
+@property(nonatomic, strong) ContentSuggestionsHeaderView* headerView;
 @property(nonatomic, strong) UIButton* fakeOmnibox;
 @property(nonatomic, strong) UIButton* accessibilityButton;
 @property(nonatomic, strong) UILabel* searchHintLabel;
@@ -148,14 +147,6 @@
   [self.accessibilityButton removeObserver:self forKeyPath:@"highlighted"];
 }
 
-#pragma mark - Property
-
-- (void)setIsShowing:(BOOL)isShowing {
-  _isShowing = isShowing;
-  if (isShowing)
-    [self.headerView hideToolbarViewsForNewTabPage];
-}
-
 #pragma mark - ContentSuggestionsHeaderControlling
 
 - (void)updateFakeOmniboxForOffset:(CGFloat)offset
@@ -242,12 +233,8 @@
 
 - (UIView*)headerForWidth:(CGFloat)width {
   if (!self.headerView) {
-    if (IsUIRefreshPhase1Enabled()) {
-      self.headerView = [[ContentSuggestionsHeaderView alloc] init];
-      [self addFakeTapView];
-    } else {
-      self.headerView = [[NewTabPageHeaderView alloc] init];
-    }
+    self.headerView = [[ContentSuggestionsHeaderView alloc] init];
+    [self addFakeTapView];
     [self addFakeOmnibox];
 
     [self.headerView addSubview:self.logoVendor.view];
@@ -276,16 +263,6 @@
                         fakeOmnibox:self.fakeOmnibox
                       andHeaderView:self.headerView];
 
-    // iPhone header also contains a toolbar since the normal toolbar is
-    // hidden.
-    if (!IsUIRefreshPhase1Enabled() && !IsIPadIdiom()) {
-      [_headerView addToolbarWithReadingListModel:self.readingListModel
-                                       dispatcher:self.dispatcher];
-      [_headerView setToolbarTabCount:self.tabCount];
-      [_headerView setCanGoForward:self.canGoForward];
-      [_headerView setCanGoBack:self.canGoBack];
-    }
-
     [self.headerView addViewsToSearchField:self.fakeOmnibox];
     [self.logoVendor fetchDoodle];
   }
@@ -498,7 +475,6 @@
         (!IsUIRefreshPhase1Enabled() &&
          !content_suggestions::IsRegularXRegularSizeClass(self))) {
       [self.dispatcher onFakeboxAnimationComplete];
-      [self.headerView fadeOutShadow];
     }
   };
   [self.collectionSynchronizer shiftTilesUpWithCompletionBlock:completionBlock];
@@ -536,21 +512,6 @@
   [self updateLogoAndFakeboxDisplay];
 }
 
-- (void)setTabCount:(int)tabCount {
-  _tabCount = tabCount;
-  [self.headerView setToolbarTabCount:tabCount];
-}
-
-- (void)setCanGoForward:(BOOL)canGoForward {
-  _canGoForward = canGoForward;
-  [self.headerView setCanGoForward:self.canGoForward];
-}
-
-- (void)setCanGoBack:(BOOL)canGoBack {
-  _canGoBack = canGoBack;
-  [self.headerView setCanGoBack:self.canGoBack];
-}
-
 - (void)locationBarBecomesFirstResponder {
   if (!self.isShowing)
     return;
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller.mm b/ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller.mm
index 9b854c64..f25db6f 100644
--- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller.mm
+++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller.mm
@@ -114,7 +114,6 @@
     [self addEmptySectionPlaceholderIfNeeded:indexPath.section];
   }
       completion:^(BOOL) {
-        [self.audience contentOffsetDidChange];
         // The context menu could be displayed for the deleted entry.
         [self.suggestionCommandHandler dismissModals];
       }];
@@ -133,7 +132,6 @@
     [self.collectionView deleteSections:[NSIndexSet indexSetWithIndex:section]];
   }
       completion:^(BOOL) {
-        [self.audience contentOffsetDidChange];
         // The context menu could be displayed for the deleted entries.
         [self.suggestionCommandHandler dismissModals];
       }];
@@ -146,9 +144,7 @@
         addSectionsForSectionInfoToModel:@[ sectionInfo ]];
     [self.collectionView insertSections:addedSections];
   }
-      completion:^(BOOL) {
-        [self.audience contentOffsetDidChange];
-      }];
+                                completion:nil];
 
   [self.collectionView performBatchUpdates:^{
     NSIndexPath* removedItem = [self.collectionUpdater
@@ -162,9 +158,7 @@
                                       withSectionInfo:sectionInfo];
     [self.collectionView insertItemsAtIndexPaths:addedItems];
   }
-      completion:^(BOOL) {
-        [self.audience contentOffsetDidChange];
-      }];
+                                completion:nil];
 }
 
 - (NSInteger)numberOfSuggestionsAbove:(NSInteger)section {
@@ -312,8 +306,6 @@
   // presented (e.g. rotation on stack view).
   [self correctMissingSafeArea];
   [self updateConstraints];
-  // Update the shadow bar.
-  [self.audience contentOffsetDidChange];
 }
 
 - (void)viewWillTransitionToSize:(CGSize)size
@@ -642,7 +634,6 @@
 
 - (void)scrollViewDidScroll:(UIScrollView*)scrollView {
   [super scrollViewDidScroll:scrollView];
-  [self.audience contentOffsetDidChange];
   [self.overscrollActionsController scrollViewDidScroll:scrollView];
   [self.headerSynchronizer updateFakeOmniboxOnCollectionScroll];
   self.scrolledToTop =
@@ -670,22 +661,6 @@
       scrollViewWillEndDragging:scrollView
                    withVelocity:velocity
             targetContentOffset:targetContentOffset];
-
-  CGFloat toolbarHeight = ntp_header::ToolbarHeight();
-  CGFloat targetY = targetContentOffset->y;
-
-  if (content_suggestions::IsRegularXRegularSizeClass(self) || targetY <= 0 ||
-      targetY >= toolbarHeight || !self.containsToolbar)
-    return;
-
-  CGFloat xOffset = self.collectionView.contentOffset.x;
-  // Adjust the toolbar to be all the way on or off screen.
-  if (targetY > toolbarHeight / 2) {
-    [self.collectionView setContentOffset:CGPointMake(xOffset, toolbarHeight)
-                                 animated:YES];
-  } else {
-    [self.collectionView setContentOffset:CGPointMake(xOffset, 0) animated:YES];
-  }
 }
 
 #pragma mark - UIGestureRecognizerDelegate
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller_audience.h b/ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller_audience.h
index 945a647..cb9603a 100644
--- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller_audience.h
+++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller_audience.h
@@ -8,9 +8,6 @@
 // Audience for the ContentSuggestions, getting informations from it.
 @protocol ContentSuggestionsViewControllerAudience
 
-// Notifies the audience that the content suggestions collection's content
-// offset has changed, for example after scrolling or adding/removing an item.
-- (void)contentOffsetDidChange;
 // Notifies the audience that the promo has been shown.
 - (void)promoShown;
 
diff --git a/ios/chrome/browser/ui/first_run/first_run_egtest.mm b/ios/chrome/browser/ui/first_run/first_run_egtest.mm
index 872b9d0e..a67a441f 100644
--- a/ios/chrome/browser/ui/first_run/first_run_egtest.mm
+++ b/ios/chrome/browser/ui/first_run/first_run_egtest.mm
@@ -195,7 +195,7 @@
       performAction:grey_tap()];
 
   id<GREYMatcher> newTab =
-      grey_kindOfClass(NSClassFromString(@"NewTabPageView"));
+      grey_kindOfClass(NSClassFromString(@"ContentSuggestionsHeaderView"));
   [[EarlGrey selectElementWithMatcher:newTab]
       assertWithMatcher:grey_sufficientlyVisible()];
 }
diff --git a/ios/chrome/browser/ui/image_util/image_copier.mm b/ios/chrome/browser/ui/image_util/image_copier.mm
index 3b4ccd35..bb01a563 100644
--- a/ios/chrome/browser/ui/image_util/image_copier.mm
+++ b/ios/chrome/browser/ui/image_util/image_copier.mm
@@ -4,9 +4,13 @@
 
 #import "image_copier.h"
 
+#include <MobileCoreServices/MobileCoreServices.h>
+
+#import "base/strings/sys_string_conversions.h"
 #include "base/task/post_task.h"
 #include "components/strings/grit/components_strings.h"
 #import "ios/chrome/browser/ui/alert_coordinator/alert_coordinator.h"
+#import "ios/chrome/browser/ui/image_util/image_util.h"
 #import "ios/chrome/browser/web/image_fetch_tab_helper.h"
 #include "ios/chrome/grit/ios_strings.h"
 #include "ios/web/public/web_task_traits.h"
@@ -61,7 +65,7 @@
 - (void)copyImageAtURL:(const GURL&)url
               referrer:(const web::Referrer&)referrer
               webState:(web::WebState*)webState {
-  // Dismisses current alert.
+  // Dismiss current alert.
   [self.alertCoordinator stop];
 
   __weak ImageCopier* weakSelf = self;
@@ -74,7 +78,7 @@
   [self.alertCoordinator
       addItemWithTitle:l10n_util::GetNSStringWithFixup(IDS_CANCEL)
                 action:^() {
-                  // Cancels current copy and closes the alert.
+                  // Cancel current copy and closes the alert.
                   weakSelf.activeID = kNoActiveCopy;
                   [weakSelf.alertCoordinator stop];
                 }
@@ -93,20 +97,27 @@
 
   ImageFetchTabHelper* tabHelper = ImageFetchTabHelper::FromWebState(webState);
   DCHECK(tabHelper);
+  NSString* urlStr = base::SysUTF8ToNSString(url.spec());
   tabHelper->GetImageData(url, referrer, ^(NSData* data) {
-    // Checks that the copy has not been canceled.
+    // Check that the copy has not been canceled.
     if (callbackID == weakSelf.activeID) {
-      UIImage* image = [UIImage imageWithData:data];
-      if (image) {
-        UIPasteboard.generalPasteboard.image = image;
-      }
+      NSMutableDictionary* item =
+          [NSMutableDictionary dictionaryWithCapacity:3];
+      [item setValue:urlStr forKey:(__bridge NSString*)kUTTypeText];
+      [item setValue:[NSURL URLWithString:urlStr]
+              forKey:(__bridge NSString*)kUTTypeURL];
+      NSString* uti = GetImageUTIFromData(data);
+      if (uti)
+        [item setValue:data forKey:uti];
+      UIPasteboard.generalPasteboard.items =
+          [NSMutableArray arrayWithObject:item];
       // Finishes this copy.
       weakSelf.activeID = kNoActiveCopy;
       [weakSelf.alertCoordinator stop];
     }
   });
 
-  // Delays launching alert by |kAlertDelayInMs|.
+  // Delay launching alert by |kAlertDelayInMs|.
   base::PostDelayedTaskWithTraits(
       FROM_HERE, {web::WebThread::UI}, base::BindOnce(^{
         // Checks that the copy has not finished yet.
diff --git a/ios/chrome/browser/ui/image_util/image_util.h b/ios/chrome/browser/ui/image_util/image_util.h
index 7bea162..974d1de 100644
--- a/ios/chrome/browser/ui/image_util/image_util.h
+++ b/ios/chrome/browser/ui/image_util/image_util.h
@@ -35,4 +35,8 @@
 // is nil, empty, or cannot be recognized, nil will be returned.
 NSString* GetImageExtensionFromData(NSData* data);
 
+// Returns the UTI by checking the first byte of image |data|. If |data|
+// is nil, empty, or cannot be recognized, nil will be returned.
+NSString* GetImageUTIFromData(NSData* data);
+
 #endif  // IOS_CHROME_BROWSER_UI_IMAGE_UTIL_IMAGE_UTIL_H_
diff --git a/ios/chrome/browser/ui/image_util/image_util.mm b/ios/chrome/browser/ui/image_util/image_util.mm
index 4cd321d..521421b 100644
--- a/ios/chrome/browser/ui/image_util/image_util.mm
+++ b/ios/chrome/browser/ui/image_util/image_util.mm
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#import <MobileCoreServices/MobileCoreServices.h>
 #import <UIKit/UIKit.h>
 
 #import "ios/chrome/browser/ui/image_util/image_util.h"
@@ -13,6 +14,16 @@
 #error "This file requires ARC support."
 #endif
 
+namespace {
+NSString* kImageExtensionJPG = @"jpg";
+NSString* kImageExtensionPNG = @"png";
+NSString* kImageExtensionTIF = @"tif";
+NSString* kImageExtensionBMP = @"bmp";
+NSString* kImageExtensionGIF = @"gif";
+NSString* kImageExtensionICO = @"ico";
+NSString* kImageExtensionWebP = @"webp";
+}
+
 UIColor* DominantColorForImage(const gfx::Image& image, CGFloat opacity) {
   SkColor color = color_utils::CalculateKMeanColorOfBitmap(*image.ToSkBitmap());
   UIColor* result = [UIColor colorWithRed:SkColorGetR(color) / 255.0
@@ -58,28 +69,41 @@
   const char* pdata = static_cast<const char*>(data.bytes);
   switch (pdata[0]) {
     case '\xFF':
-      return strncmp(pdata, "\xFF\xD8\xFF", 3) ? nil : @"jpg";
+      return strncmp(pdata, "\xFF\xD8\xFF", 3) ? nil : kImageExtensionJPG;
     case '\x89':
-      return strncmp(pdata, "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A", 8) ? nil
-                                                                   : @"png";
+      return strncmp(pdata, "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A", 8)
+                 ? nil
+                 : kImageExtensionPNG;
     case 'G':
       return (strncmp(pdata, "GIF87a", 6) && strncmp(pdata, "GIF89a", 6))
                  ? nil
-                 : @"gif";
+                 : kImageExtensionGIF;
     case '\x49':
-      return strncmp(pdata, "\x49\x49\x2A\x00", 4) ? nil : @"tif";
+      return strncmp(pdata, "\x49\x49\x2A\x00", 4) ? nil : kImageExtensionTIF;
     case '\x4D':
-      return strncmp(pdata, "\x4D\x4D\x00\x2A", 4) ? nil : @"tif";
+      return strncmp(pdata, "\x4D\x4D\x00\x2A", 4) ? nil : kImageExtensionTIF;
     case 'B':
-      return strncmp(pdata, "BM", 2) ? nil : @"bmp";
+      return strncmp(pdata, "BM", 2) ? nil : kImageExtensionBMP;
     case 'R':
       return (strncmp(pdata, "RIFF", 4) || strncmp(pdata + 8, "WEBP", 4))
                  ? nil
-                 : @"webp";
+                 : kImageExtensionWebP;
     case '\0':
-      return strncmp(pdata, "\x00\x00\x01\x00", 4) ? nil : @"ico";
+      return strncmp(pdata, "\x00\x00\x01\x00", 4) ? nil : kImageExtensionICO;
     default:
       return nil;
   }
   return nil;
 }
+
+NSString* GetImageUTIFromData(NSData* data) {
+  static NSDictionary* dict = @{
+    kImageExtensionJPG : (__bridge NSString*)kUTTypeJPEG,
+    kImageExtensionPNG : (__bridge NSString*)kUTTypePNG,
+    kImageExtensionGIF : (__bridge NSString*)kUTTypeGIF,
+    kImageExtensionTIF : (__bridge NSString*)kUTTypeTIFF,
+    kImageExtensionBMP : (__bridge NSString*)kUTTypeBMP,
+    kImageExtensionICO : (__bridge NSString*)kUTTypeICO
+  };
+  return dict[GetImageExtensionFromData(data)];
+}
diff --git a/ios/chrome/browser/ui/main/BUILD.gn b/ios/chrome/browser/ui/main/BUILD.gn
index 2d529a2..7e4b7650 100644
--- a/ios/chrome/browser/ui/main/BUILD.gn
+++ b/ios/chrome/browser/ui/main/BUILD.gn
@@ -20,6 +20,7 @@
   ]
   deps = [
     ":feature_flags",
+    ":tab_switcher",
     "//base",
     "//ios/chrome/app/resources:launchscreen_xib",
     "//ios/chrome/browser",
@@ -32,7 +33,6 @@
     "//ios/chrome/browser/tabs",
     "//ios/chrome/browser/tabs:tabs_internal",
     "//ios/chrome/browser/ui/main/transitions",
-    "//ios/chrome/browser/ui/tab_switcher",
     "//ios/public/provider/chrome/browser",
   ]
   public_deps = [
@@ -52,6 +52,22 @@
   ]
 }
 
+source_set("tab_switcher") {
+  configs += [ "//build/config/compiler:enable_arc" ]
+  sources = [
+    "tab_switcher.h",
+    "tab_switcher_mode.h",
+    "tab_switcher_mode.mm",
+  ]
+  deps = [
+    "//base",
+    "//ios/chrome/browser/ui",
+    "//ios/chrome/browser/ui/commands",
+    "//ui/base",
+    "//url",
+  ]
+}
+
 source_set("unit_tests") {
   configs += [ "//build/config/compiler:enable_arc" ]
   testonly = true
@@ -63,6 +79,7 @@
   ]
   deps = [
     ":main",
+    ":tab_switcher",
     ":test_support",
     "//base",
     "//base/test:test_support",
@@ -71,7 +88,6 @@
     "//ios/chrome/browser/browser_state:test_support",
     "//ios/chrome/browser/tabs",
     "//ios/chrome/browser/ui:ui_internal",
-    "//ios/chrome/browser/ui/tab_switcher",
     "//ios/chrome/test:block_cleanup_test",
     "//ios/web/public/test",
     "//testing/gtest",
@@ -86,8 +102,8 @@
     "main_view_controller_test.mm",
   ]
   deps = [
+    ":tab_switcher",
     "//base",
-    "//ios/chrome/browser/ui/tab_switcher",
     "//ios/chrome/test:test_support",
   ]
 }
diff --git a/ios/chrome/browser/ui/main/main_containing_view_controller.mm b/ios/chrome/browser/ui/main/main_containing_view_controller.mm
index f43d224a..00e825ec 100644
--- a/ios/chrome/browser/ui/main/main_containing_view_controller.mm
+++ b/ios/chrome/browser/ui/main/main_containing_view_controller.mm
@@ -5,7 +5,7 @@
 #import "ios/chrome/browser/ui/main/main_containing_view_controller.h"
 
 #import "base/logging.h"
-#import "ios/chrome/browser/ui/tab_switcher/tab_switcher.h"
+#import "ios/chrome/browser/ui/main/tab_switcher.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
diff --git a/ios/chrome/browser/ui/main/main_containing_view_controller_unittest.mm b/ios/chrome/browser/ui/main/main_containing_view_controller_unittest.mm
index c5892d49..c569b9a 100644
--- a/ios/chrome/browser/ui/main/main_containing_view_controller_unittest.mm
+++ b/ios/chrome/browser/ui/main/main_containing_view_controller_unittest.mm
@@ -9,7 +9,7 @@
 #import "base/test/ios/wait_util.h"
 #include "base/test/scoped_feature_list.h"
 #import "ios/chrome/browser/ui/main/main_view_controller_test.h"
-#import "ios/chrome/browser/ui/tab_switcher/tab_switcher.h"
+#import "ios/chrome/browser/ui/main/tab_switcher.h"
 #import "ios/chrome/test/block_cleanup_test.h"
 #include "testing/gtest_mac.h"
 
diff --git a/ios/chrome/browser/ui/main/main_presenting_view_controller.mm b/ios/chrome/browser/ui/main/main_presenting_view_controller.mm
index 62537b4..cd6214d9 100644
--- a/ios/chrome/browser/ui/main/main_presenting_view_controller.mm
+++ b/ios/chrome/browser/ui/main/main_presenting_view_controller.mm
@@ -8,9 +8,9 @@
 #include "base/mac/bundle_locations.h"
 #include "base/mac/foundation_util.h"
 #import "ios/chrome/browser/ui/main/bvc_container_view_controller.h"
+#import "ios/chrome/browser/ui/main/tab_switcher.h"
 #import "ios/chrome/browser/ui/main/transitions/bvc_container_to_tab_switcher_animator.h"
 #import "ios/chrome/browser/ui/main/transitions/tab_switcher_to_bvc_container_animator.h"
-#import "ios/chrome/browser/ui/tab_switcher/tab_switcher.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
diff --git a/ios/chrome/browser/ui/main/main_presenting_view_controller_unittest.mm b/ios/chrome/browser/ui/main/main_presenting_view_controller_unittest.mm
index 7481624..fd51858c 100644
--- a/ios/chrome/browser/ui/main/main_presenting_view_controller_unittest.mm
+++ b/ios/chrome/browser/ui/main/main_presenting_view_controller_unittest.mm
@@ -9,7 +9,7 @@
 #import "base/test/ios/wait_util.h"
 #include "base/test/scoped_feature_list.h"
 #import "ios/chrome/browser/ui/main/main_view_controller_test.h"
-#import "ios/chrome/browser/ui/tab_switcher/tab_switcher.h"
+#import "ios/chrome/browser/ui/main/tab_switcher.h"
 #import "ios/chrome/test/block_cleanup_test.h"
 #include "testing/gtest_mac.h"
 
diff --git a/ios/chrome/browser/ui/main/main_view_controller_test.mm b/ios/chrome/browser/ui/main/main_view_controller_test.mm
index ac8908c0..24c353e8 100644
--- a/ios/chrome/browser/ui/main/main_view_controller_test.mm
+++ b/ios/chrome/browser/ui/main/main_view_controller_test.mm
@@ -6,7 +6,7 @@
 
 #import <UIKit/UIKit.h>
 
-#import "ios/chrome/browser/ui/tab_switcher/tab_switcher.h"
+#import "ios/chrome/browser/ui/main/tab_switcher.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_switcher.h b/ios/chrome/browser/ui/main/tab_switcher.h
similarity index 94%
rename from ios/chrome/browser/ui/tab_switcher/tab_switcher.h
rename to ios/chrome/browser/ui/main/tab_switcher.h
index 1deaf82..0a83b96 100644
--- a/ios/chrome/browser/ui/tab_switcher/tab_switcher.h
+++ b/ios/chrome/browser/ui/main/tab_switcher.h
@@ -2,19 +2,19 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef IOS_CHROME_BROWSER_UI_TAB_SWITCHER_TAB_SWITCHER_H_
-#define IOS_CHROME_BROWSER_UI_TAB_SWITCHER_TAB_SWITCHER_H_
+#ifndef IOS_CHROME_BROWSER_UI_MAIN_TAB_SWITCHER_H_
+#define IOS_CHROME_BROWSER_UI_MAIN_TAB_SWITCHER_H_
 
 #import <UIKit/UIKit.h>
 
 #import "ios/chrome/browser/ui/commands/application_commands.h"
-#include "ios/chrome/browser/ui/tab_switcher/tab_switcher_transition_context.h"
 #include "ui/base/page_transition_types.h"
 #include "url/gurl.h"
 
 @protocol OmniboxFocuser;
 @class Tab;
 @class TabModel;
+@class TabSwitcherTransitionContext;
 @protocol TabSwitcher;
 @protocol ToolbarCommands;
 @protocol ToolbarOwner;
@@ -114,4 +114,4 @@
 
 @end
 
-#endif  // IOS_CHROME_BROWSER_UI_TAB_SWITCHER_TAB_SWITCHER_H_
+#endif  // IOS_CHROME_BROWSER_UI_MAIN_TAB_SWITCHER_H_
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_switcher_mode.h b/ios/chrome/browser/ui/main/tab_switcher_mode.h
similarity index 63%
rename from ios/chrome/browser/ui/tab_switcher/tab_switcher_mode.h
rename to ios/chrome/browser/ui/main/tab_switcher_mode.h
index 751f1c4..4f133257 100644
--- a/ios/chrome/browser/ui/tab_switcher/tab_switcher_mode.h
+++ b/ios/chrome/browser/ui/main/tab_switcher_mode.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef IOS_CHROME_BROWSER_UI_TAB_SWITCHER_TAB_SWITCHER_MODE_H_
-#define IOS_CHROME_BROWSER_UI_TAB_SWITCHER_TAB_SWITCHER_MODE_H_
+#ifndef IOS_CHROME_BROWSER_UI_MAIN_TAB_SWITCHER_MODE_H_
+#define IOS_CHROME_BROWSER_UI_MAIN_TAB_SWITCHER_MODE_H_
 
 // The style of tab switcher.
 enum class TabSwitcherMode { STACK, TABLET_SWITCHER, GRID };
@@ -11,4 +11,4 @@
 // Returns the current tab switcher mode.
 TabSwitcherMode GetTabSwitcherMode();
 
-#endif  // IOS_CHROME_BROWSER_UI_TAB_SWITCHER_TAB_SWITCHER_MODE_H_
+#endif  // IOS_CHROME_BROWSER_UI_MAIN_TAB_SWITCHER_MODE_H_
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_switcher_mode.mm b/ios/chrome/browser/ui/main/tab_switcher_mode.mm
similarity index 89%
rename from ios/chrome/browser/ui/tab_switcher/tab_switcher_mode.mm
rename to ios/chrome/browser/ui/main/tab_switcher_mode.mm
index 0198ebf..e327c97 100644
--- a/ios/chrome/browser/ui/tab_switcher/tab_switcher_mode.mm
+++ b/ios/chrome/browser/ui/main/tab_switcher_mode.mm
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "ios/chrome/browser/ui/tab_switcher/tab_switcher_mode.h"
+#import "ios/chrome/browser/ui/main/tab_switcher_mode.h"
 
 #include "ios/chrome/browser/ui/ui_util.h"
 
diff --git a/ios/chrome/browser/ui/main/transitions/BUILD.gn b/ios/chrome/browser/ui/main/transitions/BUILD.gn
index 9b3184a8..9bd9e361 100644
--- a/ios/chrome/browser/ui/main/transitions/BUILD.gn
+++ b/ios/chrome/browser/ui/main/transitions/BUILD.gn
@@ -12,7 +12,7 @@
   ]
   deps = [
     "//base",
-    "//ios/chrome/browser/ui/tab_switcher",
+    "//ios/chrome/browser/ui/main:tab_switcher",
   ]
   libs = [ "UIKit.framework" ]
 }
diff --git a/ios/chrome/browser/ui/main/transitions/bvc_container_to_tab_switcher_animator.mm b/ios/chrome/browser/ui/main/transitions/bvc_container_to_tab_switcher_animator.mm
index d5ea12f..05b69c2 100644
--- a/ios/chrome/browser/ui/main/transitions/bvc_container_to_tab_switcher_animator.mm
+++ b/ios/chrome/browser/ui/main/transitions/bvc_container_to_tab_switcher_animator.mm
@@ -5,7 +5,7 @@
 #import "ios/chrome/browser/ui/main/transitions/bvc_container_to_tab_switcher_animator.h"
 
 #import "base/mac/foundation_util.h"
-#import "ios/chrome/browser/ui/tab_switcher/tab_switcher.h"
+#import "ios/chrome/browser/ui/main/tab_switcher.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
diff --git a/ios/chrome/browser/ui/main/transitions/tab_switcher_to_bvc_container_animator.mm b/ios/chrome/browser/ui/main/transitions/tab_switcher_to_bvc_container_animator.mm
index bd5d10a3..8151f8bd 100644
--- a/ios/chrome/browser/ui/main/transitions/tab_switcher_to_bvc_container_animator.mm
+++ b/ios/chrome/browser/ui/main/transitions/tab_switcher_to_bvc_container_animator.mm
@@ -5,7 +5,7 @@
 #import "ios/chrome/browser/ui/main/transitions/tab_switcher_to_bvc_container_animator.h"
 
 #import "base/mac/foundation_util.h"
-#import "ios/chrome/browser/ui/tab_switcher/tab_switcher.h"
+#import "ios/chrome/browser/ui/main/tab_switcher.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
diff --git a/ios/chrome/browser/ui/ntp/BUILD.gn b/ios/chrome/browser/ui/ntp/BUILD.gn
index a4da2cb..ace9d67 100644
--- a/ios/chrome/browser/ui/ntp/BUILD.gn
+++ b/ios/chrome/browser/ui/ntp/BUILD.gn
@@ -7,7 +7,6 @@
     "new_tab_page_controller_delegate.h",
     "new_tab_page_header_constants.h",
     "new_tab_page_header_constants.mm",
-    "new_tab_page_panel_protocol.h",
   ]
   configs += [ "//build/config/compiler:enable_arc" ]
   deps = [
@@ -15,40 +14,6 @@
   ]
 }
 
-source_set("ntp_header") {
-  configs += [ "//build/config/compiler:enable_arc" ]
-  sources = [
-    "new_tab_page_header_view.h",
-    "new_tab_page_header_view.mm",
-    "new_tab_page_toolbar_controller.h",
-    "new_tab_page_toolbar_controller.mm",
-    "ntp_header_view_adapter.h",
-  ]
-  deps = [
-    ":ntp",
-    "//base",
-    "//components/ntp_tiles",
-    "//components/strings",
-    "//components/toolbar",
-    "//ios/chrome/app/theme",
-    "//ios/chrome/browser/tabs",
-    "//ios/chrome/browser/ui",
-    "//ios/chrome/browser/ui/commands",
-    "//ios/chrome/browser/ui/content_suggestions:content_suggestions_ui_util",
-    "//ios/chrome/browser/ui/image_util",
-    "//ios/chrome/browser/ui/toolbar",
-    "//ios/chrome/browser/ui/toolbar:resource_macros",
-    "//ios/chrome/browser/ui/toolbar:toolbar_ui",
-    "//ios/chrome/browser/ui/toolbar/clean:toolbar_ui",
-    "//ios/chrome/browser/ui/toolbar/legacy",
-    "//ios/chrome/browser/ui/toolbar/public",
-    "//ios/chrome/browser/ui/toolbar/public:feature_flags",
-    "//ios/chrome/common",
-    "//ios/chrome/common/favicon",
-    "//ui/base",
-  ]
-}
-
 source_set("ntp_controller") {
   configs += [ "//build/config/compiler:enable_arc" ]
   sources = [
@@ -105,50 +70,14 @@
     "incognito_view_controller.mm",
     "metrics.h",
     "metrics.mm",
-    "new_tab_page_bar.h",
-    "new_tab_page_bar.mm",
-    "new_tab_page_bar_button.h",
-    "new_tab_page_bar_button.mm",
-    "new_tab_page_bar_item.h",
-    "new_tab_page_bar_item.mm",
-    "new_tab_page_panel_protocol.mm",
-    "new_tab_page_view.h",
-    "new_tab_page_view.mm",
     "notification_promo_whats_new.h",
     "notification_promo_whats_new.mm",
     "ntp_tile_saver.h",
     "ntp_tile_saver.mm",
   ]
-  public_deps = [
-    ":ntp_header",
-  ]
   deps = [
     ":ntp",
-    "resources:bookmarks_bar_bg",
-    "resources:bookmarks_bar_bg_pressed",
-    "resources:bookmarks_bar_breadcrumb",
-    "resources:bookmarks_bar_edit",
-    "resources:bookmarks_bar_edit_moreshadow",
-    "resources:bookmarks_bar_edit_pressed",
-    "resources:bookmarks_bar_edit_pressed_moreshadow",
-    "resources:bookmarks_bar_left",
-    "resources:bookmarks_button_bg",
-    "resources:bookmarks_button_border",
-    "resources:disclosure_open",
     "resources:incognito_icon",
-    "resources:incognito_legacy_icon",
-    "resources:ntp_bookmarks",
-    "resources:ntp_bottom_bar_shadow",
-    "resources:ntp_delete_button",
-    "resources:ntp_google_search_box",
-    "resources:ntp_incognito",
-    "resources:ntp_mv_placeholder_bg",
-    "resources:ntp_mv_recentbar",
-    "resources:ntp_mv_recentpress",
-    "resources:ntp_mv_search",
-    "resources:ntp_mv_thumbnail_container",
-    "resources:ntp_mv_thumbnail_empty",
-    "resources:ntp_mv_welcome_favicon",
     "resources:ntp_opentabs",
     "resources:ntp_opentabs_clock",
     "resources:ntp_opentabs_header",
@@ -225,8 +154,6 @@
   configs += [ "//build/config/compiler:enable_arc" ]
   testonly = true
   sources = [
-    "new_tab_page_bar_unittest.mm",
-    "new_tab_page_controller_unittest.mm",
     "notification_promo_whats_new_unittest.mm",
     "ntp_tile_saver_unittest.mm",
   ]
diff --git a/ios/chrome/browser/ui/ntp/incognito_view.mm b/ios/chrome/browser/ui/ntp/incognito_view.mm
index 4c88896b..787a752 100644
--- a/ios/chrome/browser/ui/ntp/incognito_view.mm
+++ b/ios/chrome/browser/ui/ntp/incognito_view.mm
@@ -30,17 +30,13 @@
 namespace {
 
 const CGFloat kStackViewHorizontalMargin = 20.0;
-const CGFloat kStackViewHorizontalMarginLegacy = 24.0;
 const CGFloat kStackViewMaxWidth = 416.0;
 const CGFloat kStackViewDefaultSpacing = 20.0;
-const CGFloat kStackViewDefaultSpacingLegacy = 32.0;
 const CGFloat kStackViewImageSpacing = 22.0;
-const CGFloat kStackViewImageSpacingLegacy = 24.0;
 const CGFloat kLayoutGuideVerticalMargin = 8.0;
 const CGFloat kLayoutGuideMinHeight = 12.0;
 
 const int kLinkColor = 0x3A8FFF;
-const int kLinkColorLegacy = 0x03A9F4;
 
 // The URL for the the Learn More page shown on incognito new tab.
 // Taken from ntp_resource_cache.cc.
@@ -168,43 +164,25 @@
     _containerView = [[UIView alloc] initWithFrame:frame];
     [_containerView setTranslatesAutoresizingMaskIntoConstraints:NO];
 
-    // The following stackview constants depend on the state of the UIRefresh
-    // experiment.
-    BOOL refreshEnabled = IsUIRefreshPhase1Enabled();
-    const CGFloat stackViewHorizontalMargin =
-        refreshEnabled ? kStackViewHorizontalMargin
-                       : kStackViewHorizontalMarginLegacy;
-    const CGFloat stackViewDefaultSpacing =
-        refreshEnabled ? kStackViewDefaultSpacing
-                       : kStackViewDefaultSpacingLegacy;
-    const CGFloat stackViewImageSpacing =
-        refreshEnabled ? kStackViewImageSpacing : kStackViewImageSpacingLegacy;
-
     // Stackview in which all the subviews (image, labels, button) are added.
     _stackView = [[UIStackView alloc] init];
     [_stackView setTranslatesAutoresizingMaskIntoConstraints:NO];
     _stackView.axis = UILayoutConstraintAxisVertical;
-    _stackView.spacing = stackViewDefaultSpacing;
+    _stackView.spacing = kStackViewDefaultSpacing;
     _stackView.distribution = UIStackViewDistributionFill;
     _stackView.alignment = UIStackViewAlignmentCenter;
     [_containerView addSubview:_stackView];
 
     // Incognito image.
-    NSString* incognitoImageName =
-        refreshEnabled ? @"incognito_icon" : @"incognito_legacy_icon";
     UIImageView* incognitoImage = [[UIImageView alloc]
-        initWithImage:[UIImage imageNamed:incognitoImageName]];
+        initWithImage:[UIImage imageNamed:@"incognito_icon"]];
     [_stackView addArrangedSubview:incognitoImage];
     if (@available(iOS 11.0, *)) {
-      [_stackView setCustomSpacing:stackViewImageSpacing
+      [_stackView setCustomSpacing:kStackViewImageSpacing
                          afterView:incognitoImage];
     }
 
-    if (refreshEnabled) {
-      [self addUIRefreshTextSections];
-    } else {
-      [self addLegacyTextSections];
-    }
+    [self addTextSections];
 
     // |topGuide| and |bottomGuide| exist to vertically position the stackview
     // inside the container scrollview.
@@ -253,10 +231,10 @@
       // Center the stackview horizontally with a minimum margin.
       [_stackView.leadingAnchor
           constraintGreaterThanOrEqualToAnchor:_containerView.leadingAnchor
-                                      constant:stackViewHorizontalMargin],
+                                      constant:kStackViewHorizontalMargin],
       [_stackView.trailingAnchor
           constraintLessThanOrEqualToAnchor:_containerView.trailingAnchor
-                                   constant:-stackViewHorizontalMargin],
+                                   constant:-kStackViewHorizontalMargin],
       [_stackView.centerXAnchor
           constraintEqualToAnchor:_containerView.centerXAnchor],
 
@@ -359,9 +337,6 @@
 
 // Updates the height of the margins for the top and bottom toolbars.
 - (void)updateToolbarMargins {
-  if (!IsUIRefreshPhase1Enabled())
-    return;
-
   if (IsRegularXRegularSizeClass(self)) {
     _topToolbarMarginHeight.constant = 0;
   } else {
@@ -384,7 +359,7 @@
 }
 
 // Adds views containing the text of the incognito page to |_stackView|.
-- (void)addUIRefreshTextSections {
+- (void)addTextSections {
   UIColor* titleTextColor = [UIColor whiteColor];
   UIColor* bodyTextColor = BodyTextColor();
   UIColor* linkTextColor = UIColorFromRGB(kLinkColor);
@@ -468,74 +443,4 @@
            object:nil];
 }
 
-#pragma mark - Legacy UI
-
-// Returns an autoreleased label that is styled for the legacy UI.
-- (UILabel*)legacyLabelWithMessageID:(int)messageID
-                                font:(UIFont*)font
-                               alpha:(CGFloat)alpha {
-  NSString* string = l10n_util::GetNSString(messageID);
-  NSMutableAttributedString* attributedString =
-      [[NSMutableAttributedString alloc] initWithString:string];
-  NSMutableParagraphStyle* paragraphStyle =
-      [[NSMutableParagraphStyle alloc] init];
-  [paragraphStyle setLineSpacing:4];
-  [paragraphStyle setAlignment:NSTextAlignmentJustified];
-  [attributedString addAttribute:NSParagraphStyleAttributeName
-                           value:paragraphStyle
-                           range:NSMakeRange(0, string.length)];
-  UILabel* label = [[UILabel alloc] initWithFrame:CGRectZero];
-  [label setTranslatesAutoresizingMaskIntoConstraints:NO];
-  [label setNumberOfLines:0];
-  [label setFont:font];
-  [label setAttributedText:attributedString];
-  [label setTextColor:[UIColor colorWithWhite:1.0 alpha:alpha]];
-  return label;
-}
-
-// Adds views containing the text of the incognito page to |_stackView|.
-- (void)addLegacyTextSections {
-  // Title.
-  UIFont* titleFont = [[MDCTypography fontLoader] lightFontOfSize:24];
-  UILabel* incognitoTabHeading =
-      [self legacyLabelWithMessageID:IDS_NEW_TAB_OTR_HEADING
-                                font:titleFont
-                               alpha:0.8];
-  [_stackView addArrangedSubview:incognitoTabHeading];
-
-  // Description paragraph.
-  UIFont* regularFont = [[MDCTypography fontLoader] regularFontOfSize:14];
-  UILabel* incognitoTabDescription =
-      [self legacyLabelWithMessageID:IDS_NEW_TAB_OTR_DESCRIPTION
-                                font:regularFont
-                               alpha:0.7];
-  [_stackView addArrangedSubview:incognitoTabDescription];
-
-  // Warning paragraph.
-  UILabel* incognitoTabWarning =
-      [self legacyLabelWithMessageID:IDS_NEW_TAB_OTR_MESSAGE_WARNING
-                                font:regularFont
-                               alpha:0.7];
-  [_stackView addArrangedSubview:incognitoTabWarning];
-
-  // Learn more button.
-  MDCButton* learnMore = [[MDCFlatButton alloc] init];
-  [learnMore setBackgroundColor:[UIColor clearColor]
-                       forState:UIControlStateNormal];
-  UIColor* inkColor =
-      [[[MDCPalette greyPalette] tint300] colorWithAlphaComponent:0.25];
-  [learnMore setInkColor:inkColor];
-  [learnMore setTranslatesAutoresizingMaskIntoConstraints:NO];
-  [learnMore setTitle:l10n_util::GetNSString(IDS_NEW_TAB_OTR_LEARN_MORE_LINK)
-             forState:UIControlStateNormal];
-  [learnMore setTitleColor:UIColorFromRGB(kLinkColorLegacy)
-                  forState:UIControlStateNormal];
-  UIFont* buttonFont = [[MDCTypography fontLoader] boldFontOfSize:14];
-  [[learnMore titleLabel] setFont:buttonFont];
-  [learnMore addTarget:self
-                action:@selector(learnMoreButtonPressed)
-      forControlEvents:UIControlEventTouchUpInside];
-  [_stackView addArrangedSubview:learnMore];
-}
-
 @end
diff --git a/ios/chrome/browser/ui/ntp/incognito_view_controller.h b/ios/chrome/browser/ui/ntp/incognito_view_controller.h
index bc0c878..6320751 100644
--- a/ios/chrome/browser/ui/ntp/incognito_view_controller.h
+++ b/ios/chrome/browser/ui/ntp/incognito_view_controller.h
@@ -7,18 +7,17 @@
 
 #import <UIKit/UIKit.h>
 
-#import "ios/chrome/browser/ui/ntp/new_tab_page_panel_protocol.h"
+#import "ios/web/public/web_state/ui/crw_native_content.h"
 
 @protocol NewTabPageControllerDelegate;
 @protocol UrlLoader;
 
-@interface IncognitoViewController : UIViewController<NewTabPagePanelProtocol>
+@interface IncognitoViewController : UIViewController<CRWNativeContent>
 
 // Init with the given loader object. |loader| may be nil, but isn't
 // retained so it must outlive this controller.
 // |toolbarDelegate| is used to fade the toolbar views on page scroll.
-- (id)initWithLoader:(id<UrlLoader>)loader
-     toolbarDelegate:(id<NewTabPageControllerDelegate>)toolbarDelegate;
+- (id)initWithLoader:(id<UrlLoader>)loader;
 
 @end
 
diff --git a/ios/chrome/browser/ui/ntp/incognito_view_controller.mm b/ios/chrome/browser/ui/ntp/incognito_view_controller.mm
index b24dea1..56ca627 100644
--- a/ios/chrome/browser/ui/ntp/incognito_view_controller.mm
+++ b/ios/chrome/browser/ui/ntp/incognito_view_controller.mm
@@ -9,7 +9,6 @@
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
 #import "ios/chrome/browser/ui/commands/browser_commands.h"
 #import "ios/chrome/browser/ui/ntp/incognito_view.h"
-#import "ios/chrome/browser/ui/ntp/new_tab_page_controller_delegate.h"
 #import "ios/chrome/browser/ui/uikit_ui_util.h"
 #import "ios/chrome/browser/ui/url_loader.h"
 
@@ -17,36 +16,21 @@
 #error "This file requires ARC support."
 #endif
 
-namespace {
-const CGFloat kDistanceToFadeToolbar = 50.0;
-}  // namespace
-
-@interface IncognitoViewController ()<UIScrollViewDelegate>
+@interface IncognitoViewController ()
 // The scrollview containing the actual views.
 @property(nonatomic, strong) IncognitoView* incognitoView;
-
-@property(nonatomic, weak) id<NewTabPageControllerDelegate> toolbarDelegate;
 @property(nonatomic, weak) id<UrlLoader> loader;
 @end
 
 @implementation IncognitoViewController
 
 @synthesize incognitoView = _incognitoView;
-@synthesize toolbarDelegate = _toolbarDelegate;
 @synthesize loader = _loader;
 
-// Property declared in NewTabPagePanelProtocol.
-@synthesize delegate = _delegate;
-
-- (id)initWithLoader:(id<UrlLoader>)loader
-     toolbarDelegate:(id<NewTabPageControllerDelegate>)toolbarDelegate {
+- (id)initWithLoader:(id<UrlLoader>)loader {
   self = [super init];
   if (self) {
     _loader = loader;
-    if (!IsIPadIdiom()) {
-      _toolbarDelegate = toolbarDelegate;
-      [_toolbarDelegate setToolbarBackgroundToIncognitoNTPColorWithAlpha:1];
-    }
   }
   return self;
 }
@@ -61,62 +45,40 @@
   [self.incognitoView
       setBackgroundColor:[UIColor colorWithWhite:34 / 255.0 alpha:1.0]];
 
-  if (!IsIPadIdiom()) {
-    [self.incognitoView setDelegate:self];
-  }
-
   [self.view addSubview:self.incognitoView];
 }
 
 - (void)dealloc {
-  [_toolbarDelegate setToolbarBackgroundToIncognitoNTPColorWithAlpha:0];
   [_incognitoView setDelegate:nil];
 }
 
-#pragma mark - NewTabPagePanelProtocol
+#pragma mark - CRWNativeContent
+
+- (void)wasShown {
+}
 
 - (void)reload {
 }
 
-- (void)wasShown {
-  CGFloat alpha =
-      [self incognitoBackgroundAlphaForScrollView:self.incognitoView];
-  [self.toolbarDelegate setToolbarBackgroundToIncognitoNTPColorWithAlpha:alpha];
-}
-
 - (void)wasHidden {
-  [self.toolbarDelegate setToolbarBackgroundToIncognitoNTPColorWithAlpha:0];
-}
-
-- (void)dismissModals {
-}
-
-- (CGFloat)alphaForBottomShadow {
-  return 0;
 }
 
 - (CGPoint)scrollOffset {
   return CGPointZero;
 }
 
+- (void)dismissModals {
+}
+
 - (void)willUpdateSnapshot {
 }
 
-#pragma mark - UIScrollViewDelegate methods
-
-- (void)scrollViewDidScroll:(UIScrollView*)scrollView {
-  CGFloat alpha =
-      [self incognitoBackgroundAlphaForScrollView:self.incognitoView];
-  [self.toolbarDelegate setToolbarBackgroundToIncognitoNTPColorWithAlpha:alpha];
+- (const GURL&)url {
+  return GURL::EmptyGURL();
 }
 
-#pragma mark - Private
-
-// Calculate the alpha for the toolbar background color of the NTP's color.
-- (CGFloat)incognitoBackgroundAlphaForScrollView:(UIScrollView*)scrollView {
-  CGFloat alpha = (kDistanceToFadeToolbar - scrollView.contentOffset.y) /
-                  kDistanceToFadeToolbar;
-  return MAX(alpha, 0);
+- (BOOL)isViewAlive {
+  return YES;
 }
 
 @end
diff --git a/ios/chrome/browser/ui/ntp/new_tab_page_bar.h b/ios/chrome/browser/ui/ntp/new_tab_page_bar.h
deleted file mode 100644
index fab928d..0000000
--- a/ios/chrome/browser/ui/ntp/new_tab_page_bar.h
+++ /dev/null
@@ -1,40 +0,0 @@
-// Copyright 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef IOS_CHROME_BROWSER_UI_NTP_NEW_TAB_PAGE_BAR_H_
-#define IOS_CHROME_BROWSER_UI_NTP_NEW_TAB_PAGE_BAR_H_
-
-#import <UIKit/UIKit.h>
-
-@class NewTabPageBarItem;
-
-@protocol NewTabPageBarDelegate
-// Called when new tab page bar item is selected and the selection is changed.
-- (void)newTabBarItemDidChange:(NewTabPageBarItem*)selectedItem;
-@end
-
-// The bar in the new tab page that switches between the provided choices.
-// It also draws a notch pointing to the center of the selected choice to
-// create a "speech bubble" effect.
-@interface NewTabPageBar : UIView<UIGestureRecognizerDelegate>
-
-@property(nonatomic, strong) NSArray* items;
-// Which button is currently selected.
-@property(nonatomic, assign) NSUInteger selectedIndex;
-// Percentage of the overlay that sits over the tab bar buttons.
-@property(nonatomic, assign) CGFloat overlayPercentage;
-@property(nonatomic, readonly, strong) NSArray* buttons;
-@property(nonatomic, weak) id<NewTabPageBarDelegate> delegate;
-
-// Safe area set by the NTP view. This is used as the safeAreaInsets of this
-// view as it needs to be used before the safeAreaInsets is set up.
-@property(nonatomic, assign) UIEdgeInsets safeAreaInsetFromNTPView;
-
-// Updates the alpha of the shadow image. When the alpha changes from 0 to 1 or
-// 1 to 0, the alpha change is animated.
-- (void)setShadowAlpha:(CGFloat)alpha;
-
-@end
-
-#endif  // IOS_CHROME_BROWSER_UI_NTP_NEW_TAB_PAGE_BAR_H_
diff --git a/ios/chrome/browser/ui/ntp/new_tab_page_bar.mm b/ios/chrome/browser/ui/ntp/new_tab_page_bar.mm
deleted file mode 100644
index c12d3cec..0000000
--- a/ios/chrome/browser/ui/ntp/new_tab_page_bar.mm
+++ /dev/null
@@ -1,226 +0,0 @@
-// Copyright 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import "ios/chrome/browser/ui/ntp/new_tab_page_bar.h"
-
-#import <QuartzCore/QuartzCore.h>
-#include <cmath>
-
-#include "base/logging.h"
-
-#import "ios/chrome/browser/ui/bookmarks/bookmark_utils_ios.h"
-#import "ios/chrome/browser/ui/ntp/new_tab_page_bar_button.h"
-#import "ios/chrome/browser/ui/ntp/new_tab_page_bar_item.h"
-#import "ios/chrome/browser/ui/rtl_geometry.h"
-#include "ios/chrome/browser/ui/ui_util.h"
-#import "ios/chrome/browser/ui/uikit_ui_util.h"
-#import "ui/gfx/ios/NSString+CrStringDrawing.h"
-#include "ui/gfx/scoped_ui_graphics_push_context_ios.h"
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
-namespace {
-
-const CGFloat kBarHeight = 48.0f;
-
-const CGFloat kRegularLayoutButtonWidth = 168;
-
-}  // anonymous namespace
-
-@interface NewTabPageBar () {
-  UIImageView* shadow_;
-}
-
-@property(nonatomic, readwrite, strong) NSArray* buttons;
-
-// View containing the content and respecting the safe area.
-@property(nonatomic, strong) UIView* contentView;
-
-- (void)setup;
-- (void)calculateButtonWidth;
-- (void)setupButton:(UIButton*)button;
-@end
-
-@implementation NewTabPageBar {
-  // Don't allow tabbar animations on startup, only after first tap.
-  BOOL canAnimate_;
-  // Overlay view, used to highlight the selected button.
-  UIImageView* overlayView_;
-  // Overlay view, used to highlight the selected button.
-  UIView* overlayColorView_;
-  // Width of a button.
-  CGFloat buttonWidth_;
-}
-
-@synthesize items = items_;
-@synthesize selectedIndex = selectedIndex_;
-@synthesize buttons = buttons_;
-@synthesize delegate = delegate_;
-@synthesize overlayPercentage = overlayPercentage_;
-@synthesize contentView = _contentView;
-@synthesize safeAreaInsetFromNTPView = _safeAreaInsetFromNTPView;
-
-- (id)initWithFrame:(CGRect)frame {
-  self = [super initWithFrame:frame];
-  if (self) {
-    [self setup];
-  }
-  return self;
-}
-
-- (id)initWithCoder:(NSCoder*)aDecoder {
-  self = [super initWithCoder:aDecoder];
-  if (self) {
-    [self setup];
-  }
-  return self;
-}
-
-- (void)setup {
-  self.selectedIndex = NSNotFound;
-  canAnimate_ = NO;
-  self.backgroundColor = [UIColor whiteColor];
-
-  _contentView = [[UIView alloc] initWithFrame:CGRectZero];
-  [self addSubview:_contentView];
-
-  // Make the drop shadow.
-  UIImage* shadowImage = [UIImage imageNamed:@"ntp_bottom_bar_shadow"];
-  shadow_ = [[UIImageView alloc] initWithImage:shadowImage];
-  // Shadow is positioned directly above the new tab page bar.
-  [shadow_
-      setFrame:CGRectMake(0, -shadowImage.size.height, self.bounds.size.width,
-                          shadowImage.size.height)];
-  [shadow_ setAutoresizingMask:UIViewAutoresizingFlexibleWidth];
-  [self addSubview:shadow_];
-
-  self.contentMode = UIViewContentModeRedraw;
-}
-
-- (void)layoutSubviews {
-  [super layoutSubviews];
-
-  CGRect contentViewFrame = self.frame;
-  contentViewFrame.size.height -= self.safeAreaInsetFromNTPView.bottom;
-  contentViewFrame.origin.y = 0;
-  self.contentView.frame = contentViewFrame;
-
-  // |buttonWidth_| changes with the screen orientation when the NTP button bar
-  // is enabled.
-  [self calculateButtonWidth];
-
-  CGFloat buttonPadding = floor((CGRectGetWidth(self.contentView.bounds) -
-                                 buttonWidth_ * self.buttons.count) /
-                                2);
-
-  for (NSUInteger i = 0; i < self.buttons.count; ++i) {
-    NewTabPageBarButton* button = [self.buttons objectAtIndex:i];
-    LayoutRect layout =
-        LayoutRectMake(buttonPadding + (i * buttonWidth_),
-                       CGRectGetWidth(self.contentView.bounds), 0, buttonWidth_,
-                       CGRectGetHeight(self.contentView.bounds));
-    button.frame = LayoutRectGetRect(layout);
-    [button setContentToDisplay:new_tab_page_bar_button::ContentType::IMAGE];
-  }
-
-  // Position overlay image over percentage of tab bar button(s).
-  CGRect frame = [overlayView_ frame];
-  frame.origin.x = floor(
-      buttonWidth_ * self.buttons.count * overlayPercentage_ + buttonPadding);
-  frame.size.width = buttonWidth_;
-  DCHECK(!std::isnan(frame.origin.x));
-  DCHECK(!std::isnan(frame.size.width));
-  [overlayView_ setFrame:frame];
-}
-
-- (CGSize)sizeThatFits:(CGSize)size {
-  return CGSizeMake(size.width,
-                    kBarHeight + self.safeAreaInsetFromNTPView.bottom);
-}
-
-- (void)calculateButtonWidth {
-  if (IsCompactWidth()) {
-    if ([items_ count] > 0) {
-      buttonWidth_ = self.contentView.bounds.size.width / [items_ count];
-    } else {
-      // In incognito on phones, there are no items shown.
-      buttonWidth_ = 0;
-    }
-    return;
-  }
-
-  buttonWidth_ = kRegularLayoutButtonWidth;
-}
-
-// When setting a new set of items on the tab bar, the buttons need to be
-// regenerated and the old buttons need to be removed.
-- (void)setItems:(NSArray*)newItems {
-  if (newItems == items_)
-    return;
-
-  items_ = newItems;
-  // Remove all the existing buttons from the view.
-  for (UIButton* button in self.buttons) {
-    [button removeFromSuperview];
-  }
-
-  // Create a set of new buttons.
-  [self calculateButtonWidth];
-  if (newItems.count) {
-    NSMutableArray* newButtons = [NSMutableArray array];
-    for (NSUInteger i = 0; i < newItems.count; ++i) {
-      NewTabPageBarItem* item = [newItems objectAtIndex:i];
-      NewTabPageBarButton* button = [NewTabPageBarButton buttonWithItem:item];
-      button.frame = CGRectIntegral(
-          CGRectMake(i * buttonWidth_, 0, buttonWidth_, kBarHeight));
-      [self setupButton:button];
-      [self.contentView addSubview:button];
-      [newButtons addObject:button];
-    }
-    self.buttons = newButtons;
-  } else {
-    self.buttons = nil;
-  }
-  [self setNeedsLayout];
-}
-
-- (void)setOverlayPercentage:(CGFloat)overlayPercentage {
-  DCHECK(!std::isnan(overlayPercentage));
-  overlayPercentage_ = overlayPercentage;
-  [self setNeedsLayout];
-}
-
-- (void)setupButton:(UIButton*)button {
-  // Treat the NTP buttons as normal buttons, where the standard is to fire an
-  // action on touch up inside.
-  [button addTarget:self
-                action:@selector(buttonDidTap:)
-      forControlEvents:UIControlEventTouchUpInside];
-}
-
-- (void)buttonDidTap:(UIButton*)button {
-  canAnimate_ = YES;
-  NSUInteger buttonIndex = [self.buttons indexOfObject:button];
-  if (buttonIndex != NSNotFound) {
-    self.selectedIndex = buttonIndex;
-    [delegate_ newTabBarItemDidChange:[self.items objectAtIndex:buttonIndex]];
-  }
-}
-
-- (void)setShadowAlpha:(CGFloat)alpha {
-  CGFloat currentAlpha = [shadow_ alpha];
-  CGFloat diff = std::abs(alpha - currentAlpha);
-  if (diff >= 1) {
-    [UIView animateWithDuration:0.3
-                     animations:^{
-                       [shadow_ setAlpha:alpha];
-                     }];
-  } else {
-    [shadow_ setAlpha:alpha];
-  }
-}
-
-@end
diff --git a/ios/chrome/browser/ui/ntp/new_tab_page_bar_button.h b/ios/chrome/browser/ui/ntp/new_tab_page_bar_button.h
deleted file mode 100644
index 8313c02..0000000
--- a/ios/chrome/browser/ui/ntp/new_tab_page_bar_button.h
+++ /dev/null
@@ -1,42 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef IOS_CHROME_BROWSER_UI_NTP_NEW_TAB_PAGE_BAR_BUTTON_H_
-#define IOS_CHROME_BROWSER_UI_NTP_NEW_TAB_PAGE_BAR_BUTTON_H_
-
-#import <Foundation/Foundation.h>
-#import <UIKit/UIKit.h>
-
-@class NewTabPageBarItem;
-
-namespace new_tab_page_bar_button {
-
-enum class ContentType {
-  IMAGE,
-  TEXT,
-};
-
-}  // namespace new_tab_page_bar_button
-
-// Represents a button in the new tab page bar.
-@interface NewTabPageBarButton : UIButton
-
-// Returns an autoreleased button based on |item|'s |title| and |image|. By
-// defaults, the button shows the title instead of the image, and with a non
-// incognito color scheme.
-+ (instancetype)buttonWithItem:(NewTabPageBarItem*)item;
-
-// Selects which color scheme to use for the buttons. If |percentage| is 1.0,
-// the color scheme is for incognito. If |percentage| is 0.0, the color scheme
-// is for non incognito. If |percentage| is in-between, the color scheme is an
-// interpolation between the two.
-// Percentage must be in the interval [0, 1].
-- (void)useIncognitoColorScheme:(CGFloat)percentage;
-
-// Selects which kind of content the button should display.
-- (void)setContentToDisplay:(new_tab_page_bar_button::ContentType)contentType;
-
-@end
-
-#endif  // IOS_CHROME_BROWSER_UI_NTP_NEW_TAB_PAGE_BAR_BUTTON_H_
diff --git a/ios/chrome/browser/ui/ntp/new_tab_page_bar_button.mm b/ios/chrome/browser/ui/ntp/new_tab_page_bar_button.mm
deleted file mode 100644
index 73104bf..0000000
--- a/ios/chrome/browser/ui/ntp/new_tab_page_bar_button.mm
+++ /dev/null
@@ -1,129 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import "ios/chrome/browser/ui/ntp/new_tab_page_bar_button.h"
-
-#include "base/logging.h"
-
-#import "ios/chrome/browser/ui/ntp/new_tab_page_bar_item.h"
-#import "ios/chrome/browser/ui/uikit_ui_util.h"
-#import "ios/third_party/material_components_ios/src/components/Typography/src/MaterialTypography.h"
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
-namespace {
-
-const int kButtonColor = 0x333333;
-const int kButtonSelectedColor = 0x4285F4;
-
-}  // anonymous namespace
-
-@interface NewTabPageBarButton ()
-
-@property(nonatomic, strong) UIColor* color;
-@property(nonatomic, strong) UIColor* selectedColor;
-@property(nonatomic, strong) UIColor* incognitoColor;
-@property(nonatomic, strong) UIColor* incognitoSelectedColor;
-@property(nonatomic, strong) UIColor* interpolatedColor;
-@property(nonatomic, strong) UIColor* interpolatedSelectedColor;
-@property(nonatomic, strong) UIImage* image;
-@property(nonatomic, copy) NSString* title;
-
-// Sets the tint color of the button to |interpolatedColor| or
-// |interpolatedSelectedColor|, depending on the state of the button.
-- (void)refreshTintColor;
-
-@end
-
-@implementation NewTabPageBarButton
-
-@synthesize color = _color;
-@synthesize selectedColor = _selectedColor;
-@synthesize incognitoColor = _incognitoColor;
-@synthesize incognitoSelectedColor = _incognitoSelectedColor;
-@synthesize interpolatedColor = _interpolatedColor;
-@synthesize interpolatedSelectedColor = _interpolatedSelectedColor;
-@synthesize image = _image;
-@synthesize title = _title;
-
-+ (instancetype)buttonWithItem:(NewTabPageBarItem*)item {
-  DCHECK(item);
-  DCHECK(item.title);
-  DCHECK(item.image);
-  NewTabPageBarButton* button =
-      [[self class] buttonWithType:UIButtonTypeCustom];
-
-  button.title = item.title;
-  button.image =
-      [item.image imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
-  button.color = UIColorFromRGB(kButtonColor, 1.0);
-  button.selectedColor = UIColorFromRGB(kButtonSelectedColor, 1.0);
-  button.incognitoColor = [UIColor colorWithWhite:1 alpha:0.5];
-  button.incognitoSelectedColor = [UIColor whiteColor];
-
-  button.autoresizingMask = UIViewAutoresizingFlexibleWidth;
-  button.adjustsImageWhenHighlighted = NO;
-  button.accessibilityLabel = item.title;
-  button.titleLabel.font = [MDCTypography body2Font];
-  button.titleLabel.adjustsFontSizeToFitWidth = YES;
-  button.titleLabel.minimumScaleFactor = 0.6;
-
-  [button useIncognitoColorScheme:0];
-  [button setContentToDisplay:new_tab_page_bar_button::ContentType::TEXT];
-  return button;
-}
-
-- (void)useIncognitoColorScheme:(CGFloat)percentage {
-  DCHECK(percentage >= 0 && percentage <= 1);
-  self.interpolatedColor =
-      InterpolateFromColorToColor(_color, _incognitoColor, percentage);
-  self.interpolatedSelectedColor = InterpolateFromColorToColor(
-      _selectedColor, _incognitoSelectedColor, percentage);
-
-  [self setTitleColor:_interpolatedColor forState:UIControlStateNormal];
-  [self setTitleColor:_interpolatedSelectedColor
-             forState:UIControlStateSelected];
-  [self setTitleColor:_interpolatedSelectedColor
-             forState:UIControlStateHighlighted];
-
-  [self refreshTintColor];
-}
-
-- (void)setContentToDisplay:(new_tab_page_bar_button::ContentType)contentType {
-  switch (contentType) {
-    case new_tab_page_bar_button::ContentType::IMAGE:
-      [self setImage:_image forState:UIControlStateNormal];
-      [self setTitle:nil forState:UIControlStateNormal];
-      break;
-    case new_tab_page_bar_button::ContentType::TEXT:
-      [self setImage:nil forState:UIControlStateNormal];
-      [self setTitle:_title forState:UIControlStateNormal];
-      break;
-  }
-}
-
-- (void)refreshTintColor {
-  if (self.selected || self.highlighted) {
-    [self setTintColor:_interpolatedSelectedColor];
-  } else {
-    [self setTintColor:_interpolatedColor];
-  }
-}
-
-#pragma mark -
-#pragma mark UIButton overrides
-
-- (void)setSelected:(BOOL)selected {
-  [super setSelected:selected];
-  [self refreshTintColor];
-}
-
-- (void)setHighlighted:(BOOL)highlighted {
-  [super setHighlighted:highlighted];
-  [self refreshTintColor];
-}
-
-@end
diff --git a/ios/chrome/browser/ui/ntp/new_tab_page_bar_item.h b/ios/chrome/browser/ui/ntp/new_tab_page_bar_item.h
deleted file mode 100644
index 8608dd0..0000000
--- a/ios/chrome/browser/ui/ntp/new_tab_page_bar_item.h
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef IOS_CHROME_BROWSER_UI_NTP_NEW_TAB_PAGE_BAR_ITEM_H_
-#define IOS_CHROME_BROWSER_UI_NTP_NEW_TAB_PAGE_BAR_ITEM_H_
-
-#import <Foundation/Foundation.h>
-#import <UIKit/UIKit.h>
-
-#import "ios/chrome/browser/ui/content_suggestions/ntp_home_constant.h"
-
-// Represents an item on the new tab page bar, similar to a UITabBarItem.
-@interface NewTabPageBarItem : NSObject
-
-// Convenience method for creating a tab bar choice.
-+ (NewTabPageBarItem*)newTabPageBarItemWithTitle:(NSString*)title
-                                      identifier:
-                                          (ntp_home::PanelIdentifier)identifier
-                                           image:(UIImage*)imageName
-    NS_RETURNS_NOT_RETAINED;
-
-@property(nonatomic, copy) NSString* title;
-@property(nonatomic, assign) ntp_home::PanelIdentifier identifier;
-@property(nonatomic, strong) UIImage* image;
-@property(nonatomic, weak) UIView* view;
-@end
-
-#endif  // IOS_CHROME_BROWSER_UI_NTP_NEW_TAB_PAGE_BAR_ITEM_H_
diff --git a/ios/chrome/browser/ui/ntp/new_tab_page_bar_item.mm b/ios/chrome/browser/ui/ntp/new_tab_page_bar_item.mm
deleted file mode 100644
index ec9ae55..0000000
--- a/ios/chrome/browser/ui/ntp/new_tab_page_bar_item.mm
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import "ios/chrome/browser/ui/ntp/new_tab_page_bar_item.h"
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
-@implementation NewTabPageBarItem
-@synthesize title = title_;
-@synthesize identifier = identifier_;
-@synthesize image = image_;
-@synthesize view = view_;
-
-+ (NewTabPageBarItem*)newTabPageBarItemWithTitle:(NSString*)title
-                                      identifier:
-                                          (ntp_home::PanelIdentifier)identifier
-                                           image:(UIImage*)image {
-  NewTabPageBarItem* item = [[NewTabPageBarItem alloc] init];
-  if (item) {
-    item.title = title;
-    item.identifier = identifier;
-    item.image = image;
-  }
-  return item;
-}
-
-@end
diff --git a/ios/chrome/browser/ui/ntp/new_tab_page_bar_unittest.mm b/ios/chrome/browser/ui/ntp/new_tab_page_bar_unittest.mm
deleted file mode 100644
index 71c0c26..0000000
--- a/ios/chrome/browser/ui/ntp/new_tab_page_bar_unittest.mm
+++ /dev/null
@@ -1,62 +0,0 @@
-// Copyright 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import "ios/chrome/browser/ui/ntp/new_tab_page_bar.h"
-#import "ios/chrome/browser/ui/content_suggestions/ntp_home_constant.h"
-#import "ios/chrome/browser/ui/ntp/new_tab_page_bar_item.h"
-#include "ios/chrome/browser/ui/ui_util.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "testing/gtest_mac.h"
-#include "testing/platform_test.h"
-#import "third_party/ocmock/OCMock/OCMock.h"
-#import "third_party/ocmock/gtest_support.h"
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
-@interface NewTabPageBar (Testing)
-- (void)buttonDidTap:(UIButton*)button;
-@end
-
-namespace {
-
-class NewTabPageBarTest : public PlatformTest {
- protected:
-  void SetUp() override {
-    CGRect frame = CGRectMake(0, 0, 320, 44);
-    bar_ = [[NewTabPageBar alloc] initWithFrame:frame];
-  };
-  NewTabPageBar* bar_;
-};
-
-TEST_F(NewTabPageBarTest, SetItems) {
-  NewTabPageBarItem* firstItem = [NewTabPageBarItem
-      newTabPageBarItemWithTitle:@"Home"
-                      identifier:ntp_home::HOME_PANEL
-                           image:[UIImage imageNamed:@"ntp_bookmarks"]];
-  // Tests that identifier test function can return both true and false.
-  EXPECT_TRUE(firstItem.identifier == ntp_home::HOME_PANEL);
-
-  NewTabPageBarItem* secondItem = [NewTabPageBarItem
-      newTabPageBarItemWithTitle:@"Bookmarks"
-                      identifier:ntp_home::BOOKMARKS_PANEL
-                           image:[UIImage imageNamed:@"ntp_bookmarks"]];
-  NewTabPageBarItem* thirdItem = [NewTabPageBarItem
-      newTabPageBarItemWithTitle:@"RecentTabs"
-                      identifier:ntp_home::RECENT_TABS_PANEL
-                           image:[UIImage imageNamed:@"ntp_bookmarks"]];
-
-  [bar_ setItems:[NSArray arrayWithObject:firstItem]];
-  EXPECT_EQ(bar_.buttons.count, 1U);
-  [bar_ setItems:[NSArray arrayWithObjects:firstItem, secondItem, nil]];
-  EXPECT_EQ(bar_.buttons.count, 2U);
-  [bar_ setItems:[NSArray
-                     arrayWithObjects:firstItem, secondItem, thirdItem, nil]];
-  EXPECT_EQ(bar_.buttons.count, 3U);
-  [bar_ setItems:[NSArray arrayWithObject:firstItem]];
-  EXPECT_EQ(bar_.buttons.count, 1U);
-}
-
-}  // anonymous namespace
diff --git a/ios/chrome/browser/ui/ntp/new_tab_page_controller.h b/ios/chrome/browser/ui/ntp/new_tab_page_controller.h
index 8345639..ac752d1 100644
--- a/ios/chrome/browser/ui/ntp/new_tab_page_controller.h
+++ b/ios/chrome/browser/ui/ntp/new_tab_page_controller.h
@@ -10,8 +10,6 @@
 
 #import "ios/chrome/browser/ui/content_suggestions/ntp_home_constant.h"
 #import "ios/chrome/browser/ui/native_content_controller.h"
-#import "ios/chrome/browser/ui/ntp/new_tab_page_bar.h"
-#import "ios/chrome/browser/ui/ntp/new_tab_page_panel_protocol.h"
 #import "ios/chrome/browser/ui/toolbar/toolbar_owner.h"
 #import "ios/public/provider/chrome/browser/voice/logo_animation_controller.h"
 
@@ -24,7 +22,6 @@
 @protocol CRWSwipeRecognizerProvider;
 @class GoogleLandingViewController;
 @protocol NewTabPageControllerDelegate;
-@protocol NewTabPagePanelProtocol;
 @protocol OmniboxFocuser;
 @protocol FakeboxFocuser;
 @protocol SnackbarCommands;
@@ -35,22 +32,15 @@
 // each with its own controller. The panels are created lazily.
 //
 // The strongly retained instance variables |*Controller_| are instances of
-// subclasses of NewTabPagePanelProtocol that are created lazily.
+// subclasses of CRWNativeContent that are created lazily.
 // Each Panel is its own controller with the accessible views are added to the
 // |ntpView_|.
 //
-// newTabPageView_ is a horizontally scrollable view that contains the
-// *PanelController instances available to the user at the moment. A tab-page
-// bar inside |ntpView| provides direct access to the *PanelControllers on the
-// scrollable view.
-//
-// The currently visible *PanelController is accessible through
+// The currently visible CRWNativeContent is accessible through
 // |currentController_|.
 //
 @interface NewTabPageController
     : NativeContentController<LogoAnimationControllerOwnerOwner,
-                              NewTabPageBarDelegate,
-                              NewTabPagePanelControllerDelegate,
                               ToolbarOwner,
                               UIGestureRecognizerDelegate,
                               UIScrollViewDelegate>
@@ -79,10 +69,6 @@
                              UrlLoader>)dispatcher
            safeAreaInset:(UIEdgeInsets)safeAreaInset;
 
-// Returns |YES| if the current visible controller allows showing the location
-// bar hint text.
-- (BOOL)wantsLocationBarHintText;
-
 // Animates the NTP fakebox to the focused position and focuses the real
 // omnibox.
 - (void)focusFakebox;
@@ -94,8 +80,8 @@
 @class NewTabPageView;
 
 @interface NewTabPageController (TestSupport)
-- (id<NewTabPagePanelProtocol>)currentController;
-- (id<NewTabPagePanelProtocol>)incognitoController;
+- (id<CRWNativeContent>)currentController;
+- (id<CRWNativeContent>)incognitoController;
 @end
 
 #endif  // IOS_CHROME_BROWSER_UI_NTP_NEW_TAB_PAGE_CONTROLLER_H_
diff --git a/ios/chrome/browser/ui/ntp/new_tab_page_controller.mm b/ios/chrome/browser/ui/ntp/new_tab_page_controller.mm
index 6cfc401e..fe0e59d 100644
--- a/ios/chrome/browser/ui/ntp/new_tab_page_controller.mm
+++ b/ios/chrome/browser/ui/ntp/new_tab_page_controller.mm
@@ -10,28 +10,17 @@
 
 #include "base/metrics/user_metrics.h"
 #include "base/metrics/user_metrics_action.h"
-#include "components/prefs/pref_service.h"
-#include "components/search_engines/template_url_service.h"
 #include "components/strings/grit/components_strings.h"
-#include "components/sync_sessions/synced_session.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
-#include "ios/chrome/browser/experimental_flags.h"
-#include "ios/chrome/browser/pref_names.h"
-#include "ios/chrome/browser/search_engines/template_url_service_factory.h"
-#include "ios/chrome/browser/sync/sync_setup_service.h"
-#include "ios/chrome/browser/sync/sync_setup_service_factory.h"
 #import "ios/chrome/browser/tabs/tab_model.h"
-#import "ios/chrome/browser/ui/commands/browser_commands.h"
 #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_utils.h"
 #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_coordinator.h"
 #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_header_view_controller.h"
 #import "ios/chrome/browser/ui/content_suggestions/ntp_home_constant.h"
 #import "ios/chrome/browser/ui/ntp/incognito_view_controller.h"
-#import "ios/chrome/browser/ui/ntp/new_tab_page_bar_item.h"
 #import "ios/chrome/browser/ui/ntp/new_tab_page_header_constants.h"
-#import "ios/chrome/browser/ui/ntp/new_tab_page_view.h"
-#import "ios/chrome/browser/ui/rtl_geometry.h"
 #include "ios/chrome/browser/ui/ui_util.h"
+#import "ios/chrome/common/ui_util/constraints_ui_util.h"
 #include "ios/chrome/grit/ios_strings.h"
 #import "ios/web/web_state/ui/crw_swipe_recognizer_provider.h"
 #include "ui/base/l10n/l10n_util.h"
@@ -48,7 +37,7 @@
   __weak id<UrlLoader> _loader;
   IncognitoViewController* _incognitoController;
   // The currently visible controller, one of the above.
-  __weak id<NewTabPagePanelProtocol> _currentController;
+  __weak id<CRWNativeContent> _currentController;
 
   // Delegate to focus and blur the omnibox.
   __weak id<OmniboxFocuser> _focuser;
@@ -59,10 +48,7 @@
   TabModel* _tabModel;
 }
 
-// Load panel on demand.
-- (BOOL)loadPanel:(NewTabPageBarItem*)item;
-
-@property(nonatomic, strong) NewTabPageView* view;
+@property(nonatomic, strong) UIView* view;
 
 // To ease modernizing the NTP only the internal panels are being converted
 // to UIViewControllers.  This means all the plumbing between the
@@ -82,9 +68,6 @@
                               UrlLoader>
     dispatcher;
 
-// Panel displaying the "Home" view, with the logo and the fake omnibox.
-@property(nonatomic, strong) id<NewTabPagePanelProtocol> homePanel;
-
 // Coordinator for the ContentSuggestions.
 @property(nonatomic, strong)
     ContentSuggestionsCoordinator* contentSuggestionsCoordinator;
@@ -101,7 +84,6 @@
 @synthesize swipeRecognizerProvider = _swipeRecognizerProvider;
 @synthesize parentViewController = _parentViewController;
 @synthesize dispatcher = _dispatcher;
-@synthesize homePanel = _homePanel;
 @synthesize contentSuggestionsCoordinator = _contentSuggestionsCoordinator;
 @synthesize headerController = _headerController;
 
@@ -131,62 +113,56 @@
     _tabModel = tabModel;
     self.title = l10n_util::GetNSString(IDS_NEW_TAB_TITLE);
 
-    NewTabPageBar* tabBar =
-        [[NewTabPageBar alloc] initWithFrame:CGRectMake(0, 412, 320, 48)];
-    _view = [[NewTabPageView alloc] initWithFrame:CGRectMake(0, 0, 320, 460)
-                                        andTabBar:tabBar];
-    _view.safeAreaInsetForToolbar = safeAreaInset;
-    [tabBar setDelegate:self];
+    _view = [[UIView alloc] initWithFrame:CGRectZero];
 
     bool isIncognito = _browserState->IsOffTheRecord();
 
-    NSString* incognito = l10n_util::GetNSString(IDS_IOS_NEW_TAB_INCOGNITO);
-    NSString* home = l10n_util::GetNSString(IDS_IOS_NEW_TAB_HOME);
-    NSString* bookmarks =
-        l10n_util::GetNSString(IDS_IOS_NEW_TAB_BOOKMARKS_PAGE_TITLE_MOBILE);
-    NSString* openTabs = l10n_util::GetNSString(IDS_IOS_NEW_TAB_RECENT_TABS);
-
-    NSMutableArray* tabBarItems = [NSMutableArray array];
-    NewTabPageBarItem* itemToDisplay = nil;
+    UIViewController* panelController = nil;
     if (isIncognito) {
-      NewTabPageBarItem* incognitoItem = [NewTabPageBarItem
-          newTabPageBarItemWithTitle:incognito
-                          identifier:ntp_home::INCOGNITO_PANEL
-                               image:[UIImage imageNamed:@"ntp_incognito"]];
-      itemToDisplay = incognitoItem;
-    } else {
-      NewTabPageBarItem* homeItem = [NewTabPageBarItem
-          newTabPageBarItemWithTitle:home
-                          identifier:ntp_home::HOME_PANEL
-                               image:[UIImage imageNamed:@"ntp_mv_search"]];
-      NewTabPageBarItem* bookmarksItem = [NewTabPageBarItem
-          newTabPageBarItemWithTitle:bookmarks
-                          identifier:ntp_home::BOOKMARKS_PANEL
-                               image:[UIImage imageNamed:@"ntp_bookmarks"]];
-      [tabBarItems addObject:bookmarksItem];
-      NewTabPageBarItem* openTabsItem = [NewTabPageBarItem
-          newTabPageBarItemWithTitle:openTabs
-                          identifier:ntp_home::RECENT_TABS_PANEL
-                               image:[UIImage imageNamed:@"ntp_opentabs"]];
-      [tabBarItems addObject:openTabsItem];
-      self.view.tabBar.items = tabBarItems;
-      itemToDisplay = homeItem;
-      base::RecordAction(UserMetricsAction("MobileNTPShowMostVisited"));
-    }
-    DCHECK(itemToDisplay);
-    [self loadPanel:itemToDisplay];
-    if (isIncognito) {
+      _incognitoController =
+          [[IncognitoViewController alloc] initWithLoader:_loader];
+      panelController = _incognitoController;
       _currentController = self.incognitoController;
     } else {
-      _currentController = self.homePanel;
+      self.contentSuggestionsCoordinator = [
+          [ContentSuggestionsCoordinator alloc] initWithBaseViewController:nil];
+      self.contentSuggestionsCoordinator.URLLoader = _loader;
+      self.contentSuggestionsCoordinator.browserState = _browserState;
+      self.contentSuggestionsCoordinator.dispatcher = self.dispatcher;
+      self.contentSuggestionsCoordinator.webStateList =
+          [_tabModel webStateList];
+      self.contentSuggestionsCoordinator.toolbarDelegate = _toolbarDelegate;
+      [self.contentSuggestionsCoordinator start];
+      self.headerController =
+          self.contentSuggestionsCoordinator.headerController;
+      panelController = [self.contentSuggestionsCoordinator viewController];
+      _currentController = self.contentSuggestionsCoordinator;
+      base::RecordAction(UserMetricsAction("MobileNTPShowMostVisited"));
     }
+
+    // To ease modernizing the NTP only the internal panels are
+    // UIViewControllers.  This means all the plumbing between the
+    // BrowserViewController and the internal NTP panels (WebController, NTP)
+    // hierarchy is skipped.  While normally the logic to push and pop a view
+    // controller would be owned by a coordinator, in this case the old NTP
+    // controller adds and removes child view controllers itself when a load
+    // is initiated, and when WebController calls -willBeDismissed.
+    // TODO(crbug.com/826369): This will be cleaned up when removing the NTP
+    // from CRWNativeContent.
+    DCHECK(panelController);
+    [self.parentViewController addChildViewController:panelController];
+    [self.view addSubview:panelController.view];
+    [panelController didMoveToParentViewController:self.parentViewController];
+
+    panelController.view.translatesAutoresizingMaskIntoConstraints = NO;
+    AddSameConstraints(self.view, panelController.view);
+
     [_currentController wasShown];
   }
   return self;
 }
 
 - (void)focusFakebox {
-  DCHECK(IsUIRefreshPhase1Enabled());
   [self.contentSuggestionsCoordinator.headerController focusFakebox];
 }
 
@@ -199,8 +175,6 @@
       removeFromParentViewController];
 
   [self.contentSuggestionsCoordinator stop];
-
-  [self.homePanel setDelegate:nil];
 }
 
 #pragma mark - Properties
@@ -240,41 +214,17 @@
 
 - (void)wasShown {
   [_currentController wasShown];
-  if (_currentController != self.homePanel) {
+  if (_currentController != self.contentSuggestionsCoordinator) {
     // Ensure that the NTP has the latest data when it is shown, except for
     // Home.
     [self reload];
   }
-  [self.view.tabBar setShadowAlpha:[_currentController alphaForBottomShadow]];
 }
 
 - (void)wasHidden {
   [_currentController wasHidden];
 }
 
-- (BOOL)wantsLocationBarHintText {
-  // Always show hint text on iPhone.
-  if (!IsIPadIdiom())
-    return YES;
-  // Always show the location bar hint text if the search engine is not Google.
-  TemplateURLService* service =
-      ios::TemplateURLServiceFactory::GetForBrowserState(_browserState);
-  if (service) {
-    const TemplateURL* defaultURL = service->GetDefaultSearchProvider();
-    if (defaultURL &&
-        defaultURL->GetEngineType(service->search_terms_data()) !=
-            SEARCH_ENGINE_GOOGLE) {
-      return YES;
-    }
-  }
-
-  // Always return true when incognito.
-  if (_browserState->IsOffTheRecord())
-    return YES;
-
-  return NO;
-}
-
 - (void)dismissModals {
   [_currentController dismissModals];
 }
@@ -287,94 +237,6 @@
   return [_currentController scrollOffset];
 }
 
-#pragma mark -
-
-// Called when the user presses a segment that's not currently selected.
-// Pressing a segment that's already selected does not trigger this action.
-- (void)newTabBarItemDidChange:(NewTabPageBarItem*)selectedItem {
-  if (selectedItem.identifier == ntp_home::BOOKMARKS_PANEL) {
-    [self.dispatcher showBookmarksManager];
-  } else if (selectedItem.identifier == ntp_home::RECENT_TABS_PANEL) {
-    [self.dispatcher showRecentTabs];
-  }
-
-  if (_browserState->IsOffTheRecord())
-    return;
-
-  // Update metrics. Intentionally omitting a metric for Incognito panel.
-  if (selectedItem.identifier == ntp_home::HOME_PANEL) {
-    base::RecordAction(UserMetricsAction("MobileNTPSwitchToMostVisited"));
-  } else if (selectedItem.identifier == ntp_home::RECENT_TABS_PANEL) {
-    base::RecordAction(UserMetricsAction("MobileNTPSwitchToOpenTabs"));
-  } else if (selectedItem.identifier == ntp_home::BOOKMARKS_PANEL) {
-    base::RecordAction(UserMetricsAction("MobileNTPSwitchToBookmarks"));
-  }
-}
-
-- (BOOL)loadPanel:(NewTabPageBarItem*)item {
-  DCHECK(self.parentViewController);
-  UIViewController* panelController = nil;
-  UICollectionView* collectionView = nil;
-  // Only load the controllers once.
-  if (item.identifier == ntp_home::HOME_PANEL) {
-    if (!self.contentSuggestionsCoordinator) {
-      self.contentSuggestionsCoordinator = [
-          [ContentSuggestionsCoordinator alloc] initWithBaseViewController:nil];
-      self.contentSuggestionsCoordinator.URLLoader = _loader;
-      self.contentSuggestionsCoordinator.browserState = _browserState;
-      self.contentSuggestionsCoordinator.dispatcher = self.dispatcher;
-      self.contentSuggestionsCoordinator.webStateList =
-          [_tabModel webStateList];
-      self.contentSuggestionsCoordinator.toolbarDelegate = _toolbarDelegate;
-      [self.contentSuggestionsCoordinator start];
-      self.headerController =
-          self.contentSuggestionsCoordinator.headerController;
-    }
-    panelController = [self.contentSuggestionsCoordinator viewController];
-    collectionView =
-        self.contentSuggestionsCoordinator.viewController.collectionView;
-    self.homePanel = self.contentSuggestionsCoordinator;
-    [self.homePanel setDelegate:self];
-  } else if (item.identifier == ntp_home::INCOGNITO_PANEL) {
-    if (!_incognitoController)
-      _incognitoController =
-          [[IncognitoViewController alloc] initWithLoader:_loader
-                                          toolbarDelegate:_toolbarDelegate];
-    panelController = _incognitoController;
-  } else {
-    NOTREACHED();
-    return NO;
-  }
-
-  UIView* view = panelController.view;
-  if (item.identifier == ntp_home::HOME_PANEL) {
-    // Update the shadow for the toolbar after the view creation.
-    [self.view.tabBar setShadowAlpha:[self.homePanel alphaForBottomShadow]];
-  }
-
-  BOOL created = NO;
-  if (view.superview == nil) {
-    created = YES;
-    item.view = view;
-
-    // To ease modernizing the NTP only the internal panels are being converted
-    // to UIViewControllers.  This means all the plumbing between the
-    // BrowserViewController and the internal NTP panels (WebController, NTP)
-    // hierarchy is skipped.  While normally the logic to push and pop a view
-    // controller would be owned by a coordinator, in this case the old NTP
-    // controller adds and removes child view controllers itself when a load
-    // is initiated, and when WebController calls -willBeDismissed.
-    DCHECK(panelController);
-    [self.parentViewController addChildViewController:panelController];
-    [self.view insertSubview:view belowSubview:self.view.tabBar];
-    self.view.contentView = view;
-    self.view.contentCollectionView = collectionView;
-    [panelController didMoveToParentViewController:self.parentViewController];
-  }
-  return created;
-}
-
-
 #pragma mark - LogoAnimationControllerOwnerOwner
 
 - (id<LogoAnimationControllerOwner>)logoAnimationControllerOwner {
@@ -402,24 +264,15 @@
              : 0.0;
 }
 
-#pragma mark - NewTabPagePanelControllerDelegate
-
-- (void)updateNtpBarShadowForPanelController:
-    (id<NewTabPagePanelProtocol>)ntpPanelController {
-  if (_currentController != ntpPanelController)
-    return;
-  [self.view.tabBar setShadowAlpha:[ntpPanelController alphaForBottomShadow]];
-}
-
 @end
 
 @implementation NewTabPageController (TestSupport)
 
-- (id<NewTabPagePanelProtocol>)currentController {
+- (id<CRWNativeContent>)currentController {
   return _currentController;
 }
 
-- (id<NewTabPagePanelProtocol>)incognitoController {
+- (id<CRWNativeContent>)incognitoController {
   return _incognitoController;
 }
 
diff --git a/ios/chrome/browser/ui/ntp/new_tab_page_controller_delegate.h b/ios/chrome/browser/ui/ntp/new_tab_page_controller_delegate.h
index 46e2b9e..e2ac4d9 100644
--- a/ios/chrome/browser/ui/ntp/new_tab_page_controller_delegate.h
+++ b/ios/chrome/browser/ui/ntp/new_tab_page_controller_delegate.h
@@ -7,10 +7,6 @@
 
 // Delete for NTP and it's subclasses to communicate with the toolbar.
 @protocol NewTabPageControllerDelegate
-// Sets the background color of the toolbar to the color of the incognito NTP,
-// with an |alpha|.
-// TODO(crbug.com/807330) Remove post UI refresh.
-- (void)setToolbarBackgroundToIncognitoNTPColorWithAlpha:(CGFloat)alpha;
 // Sets the toolbar location bar alpha and vertical offset based on |progress|.
 - (void)setScrollProgressForTabletOmnibox:(CGFloat)progress;
 @end
diff --git a/ios/chrome/browser/ui/ntp/new_tab_page_controller_unittest.mm b/ios/chrome/browser/ui/ntp/new_tab_page_controller_unittest.mm
deleted file mode 100644
index 2d4be39..0000000
--- a/ios/chrome/browser/ui/ntp/new_tab_page_controller_unittest.mm
+++ /dev/null
@@ -1,134 +0,0 @@
-// Copyright 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import "ios/chrome/browser/ui/ntp/new_tab_page_controller.h"
-
-#include <memory>
-
-#include "base/mac/foundation_util.h"
-#include "base/memory/ptr_util.h"
-#include "components/bookmarks/test/bookmark_test_helpers.h"
-#include "components/prefs/testing_pref_service.h"
-#include "components/search_engines/template_url_service.h"
-#include "components/sessions/core/tab_restore_service.h"
-#include "ios/chrome/browser/bookmarks/bookmark_model_factory.h"
-#include "ios/chrome/browser/browser_state/test_chrome_browser_state.h"
-#include "ios/chrome/browser/chrome_url_constants.h"
-#include "ios/chrome/browser/favicon/ios_chrome_large_icon_service_factory.h"
-#include "ios/chrome/browser/search_engines/template_url_service_factory.h"
-#include "ios/chrome/browser/sessions/ios_chrome_tab_restore_service_factory.h"
-#import "ios/chrome/browser/sessions/test_session_service.h"
-#import "ios/chrome/browser/tabs/tab_model.h"
-#import "ios/chrome/browser/ui/ntp/new_tab_page_view.h"
-#include "ios/chrome/browser/ui/ui_util.h"
-#include "ios/chrome/test/block_cleanup_test.h"
-#include "ios/chrome/test/ios_chrome_scoped_testing_local_state.h"
-#include "ios/chrome/test/testing_application_context.h"
-#include "ios/web/public/test/test_web_thread_bundle.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "testing/gtest_mac.h"
-#import "third_party/ocmock/OCMock/OCMock.h"
-#import "third_party/ocmock/gtest_support.h"
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
-
-namespace {
-
-class NewTabPageControllerTest : public BlockCleanupTest {
- protected:
-  void SetUp() override {
-    BlockCleanupTest::SetUp();
-
-    // Set up a test ChromeBrowserState instance.
-    TestChromeBrowserState::Builder test_cbs_builder;
-    test_cbs_builder.AddTestingFactory(
-        IOSChromeTabRestoreServiceFactory::GetInstance(),
-        IOSChromeTabRestoreServiceFactory::GetDefaultFactory());
-    test_cbs_builder.AddTestingFactory(
-        ios::TemplateURLServiceFactory::GetInstance(),
-        ios::TemplateURLServiceFactory::GetDefaultFactory());
-    test_cbs_builder.AddTestingFactory(
-        IOSChromeLargeIconServiceFactory::GetInstance(),
-        IOSChromeLargeIconServiceFactory::GetDefaultFactory());
-    chrome_browser_state_ = test_cbs_builder.Build();
-
-    // Load TemplateURLService.
-    TemplateURLService* template_url_service =
-        ios::TemplateURLServiceFactory::GetForBrowserState(
-            chrome_browser_state_.get());
-    template_url_service->Load();
-
-    chrome_browser_state_->CreateBookmarkModel(true);
-    bookmarks::test::WaitForBookmarkModelToLoad(
-        ios::BookmarkModelFactory::GetForBrowserState(
-            chrome_browser_state_.get()));
-    GURL url(kChromeUINewTabURL);
-    parentViewController_ = [[UIViewController alloc] init];
-    tabModel_ = [[TabModel alloc]
-        initWithSessionWindow:nil
-               sessionService:[[TestSessionService alloc] init]
-                 browserState:chrome_browser_state_.get()];
-    controller_ =
-        [[NewTabPageController alloc] initWithUrl:url
-                                           loader:nil
-                                          focuser:nil
-                                     browserState:chrome_browser_state_.get()
-                                  toolbarDelegate:nil
-                                         tabModel:tabModel_
-                             parentViewController:parentViewController_
-                                       dispatcher:nil
-                                    safeAreaInset:UIEdgeInsetsZero];
-
-    incognitoController_ = [[NewTabPageController alloc]
-                 initWithUrl:url
-                      loader:nil
-                     focuser:nil
-                browserState:chrome_browser_state_
-                                 ->GetOffTheRecordChromeBrowserState()
-             toolbarDelegate:nil
-                    tabModel:nil
-        parentViewController:parentViewController_
-                  dispatcher:nil
-               safeAreaInset:UIEdgeInsetsZero];
-  };
-
-  void TearDown() override {
-    incognitoController_ = nil;
-    controller_ = nil;
-    parentViewController_ = nil;
-    [tabModel_ browserStateDestroyed];
-    tabModel_ = nil;
-
-    // There may be blocks released below that have weak references to |profile|
-    // owned by chrome_browser_state_.  Ensure BlockCleanupTest::TearDown() is
-    // called before |chrome_browser_state_| is reset.
-    BlockCleanupTest::TearDown();
-    chrome_browser_state_.reset();
-  }
-
-  web::TestWebThreadBundle thread_bundle_;
-  IOSChromeScopedTestingLocalState local_state_;
-  std::unique_ptr<TestChromeBrowserState> chrome_browser_state_;
-  TabModel* tabModel_;
-  UIViewController* parentViewController_;
-  NewTabPageController* controller_;
-  NewTabPageController* incognitoController_;
-};
-
-TEST_F(NewTabPageControllerTest, TestWantsLocationBarHintText) {
-  // Default NTP doesn't show location bar hint text on iPad, and it does on
-  // iPhone.
-  if (IsIPadIdiom())
-    EXPECT_EQ(NO, [controller_ wantsLocationBarHintText]);
-  else
-    EXPECT_EQ(YES, [controller_ wantsLocationBarHintText]);
-
-  // Default incognito always does.
-  EXPECT_EQ(YES, [incognitoController_ wantsLocationBarHintText]);
-}
-
-}  // anonymous namespace
diff --git a/ios/chrome/browser/ui/ntp/new_tab_page_header_constants.mm b/ios/chrome/browser/ui/ntp/new_tab_page_header_constants.mm
index 034a4ea..1df1f36 100644
--- a/ios/chrome/browser/ui/ntp/new_tab_page_header_constants.mm
+++ b/ios/chrome/browser/ui/ntp/new_tab_page_header_constants.mm
@@ -15,7 +15,6 @@
 
 const CGFloat kMinHeaderHeight = 62;
 const CGFloat kAnimationDistance = 42;
-const CGFloat kToolbarHeightLegacy = 56;
 const CGFloat kToolbarHeight = 48;
 const CGFloat kScrolledToTopOmniboxBottomMargin = 4;
 const CGFloat kHintLabelSidePadding = 37;
@@ -24,10 +23,7 @@
 const CGFloat kMaxTopMarginDiff = 4;
 
 CGFloat ToolbarHeight() {
-  if (IsUIRefreshPhase1Enabled()) {
-    return kToolbarHeight;
-  }
-  return kToolbarHeightLegacy;
+  return kToolbarHeight;
 }
 
 }  // ntp_header
diff --git a/ios/chrome/browser/ui/ntp/new_tab_page_header_view.h b/ios/chrome/browser/ui/ntp/new_tab_page_header_view.h
deleted file mode 100644
index 666217e..0000000
--- a/ios/chrome/browser/ui/ntp/new_tab_page_header_view.h
+++ /dev/null
@@ -1,22 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef IOS_CHROME_BROWSER_UI_NTP_NEW_TAB_PAGE_HEADER_VIEW_H_
-#define IOS_CHROME_BROWSER_UI_NTP_NEW_TAB_PAGE_HEADER_VIEW_H_
-
-#import <UIKit/UIKit.h>
-
-#import "ios/chrome/browser/ui/ntp/ntp_header_view_adapter.h"
-#import "ios/chrome/browser/ui/toolbar/toolbar_owner.h"
-
-class ReadingListModel;
-
-// Header view for the Material Design NTP. The header view contains all views
-// that are displayed above the list of most visited sites, which includes the
-// toolbar buttons, Google doodle, and fake omnibox.
-@interface NewTabPageHeaderView : UIView<NTPHeaderViewAdapter>
-
-@end
-
-#endif  // IOS_CHROME_BROWSER_UI_NTP_NEW_TAB_PAGE_HEADER_VIEW_H_
diff --git a/ios/chrome/browser/ui/ntp/new_tab_page_header_view.mm b/ios/chrome/browser/ui/ntp/new_tab_page_header_view.mm
deleted file mode 100644
index 9a29cd1..0000000
--- a/ios/chrome/browser/ui/ntp/new_tab_page_header_view.mm
+++ /dev/null
@@ -1,247 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import "ios/chrome/browser/ui/ntp/new_tab_page_header_view.h"
-
-#include "base/logging.h"
-#import "ios/chrome/browser/tabs/tab_model.h"
-#import "ios/chrome/browser/tabs/tab_model_observer.h"
-#import "ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_utils.h"
-#import "ios/chrome/browser/ui/image_util/image_util.h"
-#import "ios/chrome/browser/ui/ntp/new_tab_page_header_constants.h"
-#import "ios/chrome/browser/ui/ntp/new_tab_page_toolbar_controller.h"
-#import "ios/chrome/browser/ui/toolbar/legacy/toolbar_utils.h"
-#import "ios/chrome/browser/ui/toolbar/toolbar_snapshot_providing.h"
-#import "ios/chrome/browser/ui/uikit_ui_util.h"
-#import "ios/chrome/common/material_timing.h"
-#include "ios/chrome/grit/ios_theme_resources.h"
-#import "ui/gfx/ios/uikit_util.h"
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
-@interface NewTabPageHeaderView ()<ToolbarSnapshotProviding> {
-  NewTabPageToolbarController* _toolbarController;
-  UIImageView* _searchBoxBorder;
-  UIImageView* _shadow;
-}
-
-@end
-
-@implementation NewTabPageHeaderView
-
-#pragma mark - Public
-
-- (instancetype)initWithFrame:(CGRect)frame {
-  self = [super initWithFrame:frame];
-  if (self) {
-    self.clipsToBounds = YES;
-  }
-  return self;
-}
-
-
-- (UIView*)toolBarView {
-  return [_toolbarController view];
-}
-
-- (void)addToolbarWithReadingListModel:(ReadingListModel*)readingListModel
-                            dispatcher:(id)dispatcher {
-  DCHECK(!_toolbarController);
-  DCHECK(readingListModel);
-
-  _toolbarController =
-      [[NewTabPageToolbarController alloc] initWithDispatcher:dispatcher];
-  _toolbarController.readingListModel = readingListModel;
-
-  [self addSubview:[_toolbarController view]];
-
-  [self addConstraintsToToolbar];
-}
-
-- (void)setCanGoForward:(BOOL)canGoForward {
-  [_toolbarController setCanGoForward:canGoForward];
-  [self hideToolbarViewsForNewTabPage];
-}
-
-- (void)setCanGoBack:(BOOL)canGoBack {
-  [_toolbarController setCanGoBack:canGoBack];
-  [self hideToolbarViewsForNewTabPage];
-}
-
-- (void)hideToolbarViewsForNewTabPage {
-  [_toolbarController hideViewsForNewTabPage:YES];
-};
-
-- (void)setToolbarTabCount:(int)tabCount {
-  [_toolbarController setTabCount:tabCount];
-}
-
-- (void)addViewsToSearchField:(UIView*)searchField {
-  [searchField setBackgroundColor:[UIColor whiteColor]];
-  UIImage* searchBorderImage =
-      StretchableImageNamed(@"ntp_google_search_box", 12, 12);
-  _searchBoxBorder = [[UIImageView alloc] initWithImage:searchBorderImage];
-  [_searchBoxBorder setFrame:[searchField bounds]];
-  [_searchBoxBorder setAutoresizingMask:UIViewAutoresizingFlexibleWidth |
-                                        UIViewAutoresizingFlexibleHeight];
-  [searchField insertSubview:_searchBoxBorder atIndex:0];
-
-  UIImage* fullBleedShadow = NativeImage(IDR_IOS_TOOLBAR_SHADOW_FULL_BLEED);
-  _shadow = [[UIImageView alloc] initWithImage:fullBleedShadow];
-  CGRect shadowFrame = [searchField bounds];
-  shadowFrame.origin.y = searchField.bounds.size.height;
-  shadowFrame.size.height = fullBleedShadow.size.height;
-  [_shadow setFrame:shadowFrame];
-  [_shadow setUserInteractionEnabled:NO];
-  [_shadow setAutoresizingMask:UIViewAutoresizingFlexibleWidth |
-                               UIViewAutoresizingFlexibleTopMargin];
-  [searchField addSubview:_shadow];
-  [_shadow setAlpha:0];
-}
-
-- (CGFloat)searchFieldProgressForOffset:(CGFloat)offset
-                         safeAreaInsets:(UIEdgeInsets)safeAreaInsets {
-  NOTREACHED();
-  return 0;
-}
-
-- (void)setFakeboxHighlighted:(BOOL)highlighted {
-  // Not implemented pre-ui-refresh.
-}
-
-- (void)updateSearchFieldWidth:(NSLayoutConstraint*)widthConstraint
-                        height:(NSLayoutConstraint*)heightConstraint
-                     topMargin:(NSLayoutConstraint*)topMarginConstraint
-                     hintLabel:(UILabel*)hintLabel
-            subviewConstraints:(NSArray*)constraints
-                     forOffset:(CGFloat)offset
-                   screenWidth:(CGFloat)screenWidth
-                safeAreaInsets:(UIEdgeInsets)safeAreaInsets {
-  CGFloat contentWidth = std::max<CGFloat>(
-      0, screenWidth - safeAreaInsets.left - safeAreaInsets.right);
-  // The scroll offset at which point searchField's frame should stop growing.
-  CGFloat maxScaleOffset = self.frame.size.height -
-                           ntp_header::kMinHeaderHeight - safeAreaInsets.top;
-  // The scroll offset at which point searchField's frame should start
-  // growing.
-  CGFloat startScaleOffset = maxScaleOffset - ntp_header::kAnimationDistance;
-  CGFloat percent = 0;
-  if (offset > startScaleOffset) {
-    CGFloat animatingOffset = offset - startScaleOffset;
-    percent = MIN(1, MAX(0, animatingOffset / ntp_header::kAnimationDistance));
-  }
-
-  if (screenWidth == 0 || contentWidth == 0)
-    return;
-
-  CGFloat searchFieldNormalWidth =
-      content_suggestions::searchFieldWidth(contentWidth);
-
-  // Calculate the amount to grow the width and height of searchField so that
-  // its frame covers the entire toolbar area.
-  CGFloat maxXInset = ui::AlignValueToUpperPixel(
-      (searchFieldNormalWidth - screenWidth) / 2 - 1);
-  CGFloat maxHeightDiff =
-      ntp_header::ToolbarHeight() - content_suggestions::kSearchFieldHeight;
-
-  widthConstraint.constant = searchFieldNormalWidth - 2 * maxXInset * percent;
-  topMarginConstraint.constant = -content_suggestions::searchFieldTopMargin() -
-                                 ntp_header::kMaxTopMarginDiff * percent;
-  heightConstraint.constant =
-      content_suggestions::kSearchFieldHeight + maxHeightDiff * percent;
-
-  [_searchBoxBorder setAlpha:(1 - percent)];
-  [_shadow setAlpha:percent];
-
-  // Adjust the position of the search field's subviews by adjusting their
-  // constraint constant value.
-  CGFloat constantDiff =
-      percent * (ntp_header::kMaxHorizontalMarginDiff + safeAreaInsets.left);
-  for (NSLayoutConstraint* constraint in constraints) {
-    if (constraint.constant > 0)
-      constraint.constant =
-          constantDiff + ntp_header::kHintLabelSidePaddingLegacy;
-    else
-      constraint.constant = -constantDiff;
-  }
-}
-
-- (void)safeAreaInsetsDidChange {
-  [super safeAreaInsetsDidChange];
-  _toolbarController.heightConstraint.constant =
-      ToolbarHeightWithTopOfScreenOffset([_toolbarController statusBarOffset]);
-}
-
-- (void)fadeOutShadow {
-  [UIView animateWithDuration:ios::material::kDuration1
-                   animations:^{
-                     [_shadow setAlpha:0];
-                   }];
-}
-
-#pragma mark - ToolbarOwner
-
-- (CGRect)toolbarFrame {
-  return _toolbarController.view.frame;
-}
-
-- (id<ToolbarSnapshotProviding>)toolbarSnapshotProvider {
-  return self;
-}
-
-#pragma mark - ToolbarSnapshotProviding
-
-- (UIView*)snapshotForTabSwitcher {
-  return nil;
-}
-
-- (UIView*)snapshotForStackViewWithWidth:(CGFloat)width
-                          safeAreaInsets:(UIEdgeInsets)safeAreaInsets {
-  UIView* toolbar = _toolbarController.view;
-  CGRect oldFrame = toolbar.frame;
-  CGRect newFrame = oldFrame;
-  newFrame.size.width = width;
-
-  toolbar.frame = newFrame;
-  [_toolbarController activateFakeSafeAreaInsets:safeAreaInsets];
-
-  UIView* toolbarSnapshotView;
-  if ([toolbar window]) {
-    // Take a snapshot only if it has been added to the view hierarchy.
-    toolbarSnapshotView = [toolbar snapshotViewAfterScreenUpdates:NO];
-  } else {
-    toolbarSnapshotView = [[UIView alloc] initWithFrame:toolbar.frame];
-    [toolbarSnapshotView layer].contents = static_cast<id>(
-        CaptureViewWithOption(toolbar, 0, kClientSideRendering).CGImage);
-  }
-
-  toolbar.frame = oldFrame;
-  [_toolbarController deactivateFakeSafeAreaInsets];
-
-  return toolbarSnapshotView;
-}
-
-- (UIColor*)toolbarBackgroundColor {
-  return [UIColor whiteColor];
-}
-
-#pragma mark - Private
-
-- (void)addConstraintsToToolbar {
-  _toolbarController.heightConstraint.constant =
-      ToolbarHeightWithTopOfScreenOffset([_toolbarController statusBarOffset]);
-  _toolbarController.heightConstraint.active = YES;
-  [NSLayoutConstraint activateConstraints:@[
-    [[_toolbarController view].leadingAnchor
-        constraintEqualToAnchor:self.leadingAnchor],
-    [[_toolbarController view].topAnchor
-        constraintEqualToAnchor:self.topAnchor],
-    [[_toolbarController view].trailingAnchor
-        constraintEqualToAnchor:self.trailingAnchor],
-  ]];
-}
-
-@end
diff --git a/ios/chrome/browser/ui/ntp/new_tab_page_panel_protocol.h b/ios/chrome/browser/ui/ntp/new_tab_page_panel_protocol.h
deleted file mode 100644
index f89f8e6e5..0000000
--- a/ios/chrome/browser/ui/ntp/new_tab_page_panel_protocol.h
+++ /dev/null
@@ -1,53 +0,0 @@
-// Copyright 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef IOS_CHROME_BROWSER_UI_NTP_NEW_TAB_PAGE_PANEL_PROTOCOL_H_
-#define IOS_CHROME_BROWSER_UI_NTP_NEW_TAB_PAGE_PANEL_PROTOCOL_H_
-
-#import <UIKit/UIKit.h>
-
-@protocol NewTabPagePanelProtocol;
-
-extern const int kNewTabPageShadowHeight;
-extern const int kNewTabPageDistanceToFadeShadow;
-
-@protocol NewTabPagePanelControllerDelegate<NSObject>
-
-// Updates the NTP bar shadow alpha for the given NewTabPagePanelProtocol.
-- (void)updateNtpBarShadowForPanelController:
-    (id<NewTabPagePanelProtocol>)ntpPanelController;
-
-@end
-
-// Base class of a controller for the panels in the New Tab Page. This should
-// not be instantiated, but instead one of its sub-classes.
-@protocol NewTabPagePanelProtocol
-
-// NewTabPagePanelController delegate, may be nil.
-@property(nonatomic, assign) id<NewTabPagePanelControllerDelegate> delegate;
-
-// Alpha value to use for the NewTabPageBar shadow.
-@property(nonatomic, readonly) CGFloat alphaForBottomShadow;
-
-// Reload any displayed data to ensure the view is up to date.
-- (void)reload;
-
-// Notifies the NewTabPagePanelProtocol that it has been shown.
-- (void)wasShown;
-
-// Notifies the NewTabPagePanelProtocol that it has been hidden.
-- (void)wasHidden;
-
-// Dismisses any modal interaction elements.
-- (void)dismissModals;
-
-// Returns the scroll offset associated with this panel.
-- (CGPoint)scrollOffset;
-
-// Called when a snapshot of the content will be taken.
-- (void)willUpdateSnapshot;
-
-@end
-
-#endif  // IOS_CHROME_BROWSER_UI_NTP_NEW_TAB_PAGE_PANEL_PROTOCOL_H_
diff --git a/ios/chrome/browser/ui/ntp/new_tab_page_panel_protocol.mm b/ios/chrome/browser/ui/ntp/new_tab_page_panel_protocol.mm
deleted file mode 100644
index 006dcae..0000000
--- a/ios/chrome/browser/ui/ntp/new_tab_page_panel_protocol.mm
+++ /dev/null
@@ -1,12 +0,0 @@
-// Copyright 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import "ios/chrome/browser/ui/ntp/new_tab_page_panel_protocol.h"
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
-const int kNewTabPageShadowHeight = 2;
-const int kNewTabPageDistanceToFadeShadow = 20;
diff --git a/ios/chrome/browser/ui/ntp/new_tab_page_toolbar_controller.h b/ios/chrome/browser/ui/ntp/new_tab_page_toolbar_controller.h
deleted file mode 100644
index aebff38..0000000
--- a/ios/chrome/browser/ui/ntp/new_tab_page_toolbar_controller.h
+++ /dev/null
@@ -1,48 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef IOS_CHROME_BROWSER_UI_NTP_NEW_TAB_PAGE_TOOLBAR_CONTROLLER_H_
-#define IOS_CHROME_BROWSER_UI_NTP_NEW_TAB_PAGE_TOOLBAR_CONTROLLER_H_
-
-#import "ios/chrome/browser/ui/toolbar/legacy/toolbar_controller.h"
-
-@protocol ApplicationCommands;
-@protocol BrowserCommands;
-@protocol GoogleLandingDataSource;
-@protocol OmniboxFocuser;
-@protocol FakeboxFocuser;
-@protocol UrlLoader;
-
-// New tab page specific toolbar. The background view is hidden and the
-// navigation buttons are also hidden if there is no forward history. Does not
-// contain an omnibox but tapping in the center will focus the main toolbar's
-// omnibox.
-@interface NewTabPageToolbarController : ToolbarController
-
-// Designated initializer. The underlying ToolbarController is initialized with
-// ToolbarControllerStyleLightMode.
-- (instancetype)initWithDispatcher:(id<ApplicationCommands,
-                                       BrowserCommands,
-                                       OmniboxFocuser,
-                                       FakeboxFocuser,
-                                       ToolbarCommands,
-                                       UrlLoader>)dispatcher;
-
-@property(nonatomic, readonly, weak) id<ApplicationCommands,
-                                        BrowserCommands,
-                                        OmniboxFocuser,
-                                        FakeboxFocuser,
-                                        ToolbarCommands,
-                                        UrlLoader>
-    dispatcher;
-
-// |YES| if the toolbar can show the forward arrow.
-- (void)setCanGoForward:(BOOL)canGoForward;
-
-// |YES| if the toolbar can show the back arrow.
-- (void)setCanGoBack:(BOOL)canGoBack;
-
-@end
-
-#endif  // IOS_CHROME_BROWSER_UI_NTP_NEW_TAB_PAGE_TOOLBAR_CONTROLLER_H_
diff --git a/ios/chrome/browser/ui/ntp/new_tab_page_toolbar_controller.mm b/ios/chrome/browser/ui/ntp/new_tab_page_toolbar_controller.mm
deleted file mode 100644
index 64c71884..0000000
--- a/ios/chrome/browser/ui/ntp/new_tab_page_toolbar_controller.mm
+++ /dev/null
@@ -1,249 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import "ios/chrome/browser/ui/ntp/new_tab_page_toolbar_controller.h"
-
-#include "base/logging.h"
-#include "base/metrics/user_metrics.h"
-#include "base/metrics/user_metrics_action.h"
-#include "components/strings/grit/components_strings.h"
-#include "components/toolbar/toolbar_model.h"
-#import "ios/chrome/browser/ui/commands/application_commands.h"
-#import "ios/chrome/browser/ui/commands/browser_commands.h"
-#import "ios/chrome/browser/ui/rtl_geometry.h"
-#import "ios/chrome/browser/ui/toolbar/legacy/toolbar_controller+protected.h"
-#import "ios/chrome/browser/ui/toolbar/legacy/toolbar_controller_constants.h"
-#import "ios/chrome/browser/ui/toolbar/public/fakebox_focuser.h"
-#import "ios/chrome/browser/ui/toolbar/public/omnibox_focuser.h"
-#include "ios/chrome/browser/ui/toolbar/toolbar_resource_macros.h"
-#import "ios/chrome/browser/ui/uikit_ui_util.h"
-#include "ui/base/l10n/l10n_util.h"
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
-using base::UserMetricsAction;
-
-namespace {
-
-const CGFloat kButtonYOffset = 4.0;
-const CGFloat kBackButtonLeading = 0;
-const CGFloat kForwardButtonLeading = 48;
-const CGSize kBackButtonSize = {48, 48};
-const CGSize kForwardButtonSize = {48, 48};
-const CGFloat kOmniboxFocuserTrailing = 96;
-
-enum {
-  NTPToolbarButtonNameBack = NumberOfToolbarButtonNames,
-  NTPToolbarButtonNameForward,
-  NumberOfNTPToolbarButtonNames,
-};
-
-}  // namespace
-
-@interface NewTabPageToolbarController () {
-  UIButton* _backButton;
-  UIButton* _forwardButton;
-  UIButton* _omniboxFocuser;
-}
-
-// |YES| if the google landing toolbar can show the forward arrow.
-@property(nonatomic, assign) BOOL canGoForward;
-
-// |YES| if the google landing toolbar can show the back arrow.
-@property(nonatomic, assign) BOOL canGoBack;
-
-@end
-
-@implementation NewTabPageToolbarController
-
-@synthesize canGoForward = _canGoForward;
-@synthesize canGoBack = _canGoBack;
-@dynamic dispatcher;
-
-- (instancetype)initWithDispatcher:(id<ApplicationCommands,
-                                       BrowserCommands,
-                                       OmniboxFocuser,
-                                       FakeboxFocuser,
-                                       ToolbarCommands,
-                                       UrlLoader>)dispatcher {
-  self = [super initWithStyle:ToolbarControllerStyleLightMode
-                   dispatcher:dispatcher];
-  if (self) {
-    [self.backgroundView setHidden:YES];
-
-    CGFloat boundingWidth = self.view.bounds.size.width;
-    LayoutRect backButtonLayout =
-        LayoutRectMake(kBackButtonLeading, boundingWidth, kButtonYOffset,
-                       kBackButtonSize.width, kBackButtonSize.height);
-    _backButton =
-        [[UIButton alloc] initWithFrame:LayoutRectGetRect(backButtonLayout)];
-    [_backButton
-        setAutoresizingMask:UIViewAutoresizingFlexibleTrailingMargin() |
-                            UIViewAutoresizingFlexibleBottomMargin];
-    LayoutRect forwardButtonLayout =
-        LayoutRectMake(kForwardButtonLeading, boundingWidth, kButtonYOffset,
-                       kForwardButtonSize.width, kForwardButtonSize.height);
-    _forwardButton =
-        [[UIButton alloc] initWithFrame:LayoutRectGetRect(forwardButtonLayout)];
-    [_forwardButton
-        setAutoresizingMask:UIViewAutoresizingFlexibleTrailingMargin() |
-                            UIViewAutoresizingFlexibleBottomMargin];
-    _omniboxFocuser = [[UIButton alloc] init];
-    [_omniboxFocuser
-        setAccessibilityLabel:l10n_util::GetNSString(IDS_ACCNAME_LOCATION)];
-
-    _omniboxFocuser.translatesAutoresizingMaskIntoConstraints = NO;
-
-    [self.contentView addSubview:_backButton];
-    [self.contentView addSubview:_forwardButton];
-    [self.contentView addSubview:_omniboxFocuser];
-    [NSLayoutConstraint activateConstraints:@[
-      [_omniboxFocuser.leadingAnchor
-          constraintEqualToAnchor:_forwardButton.trailingAnchor],
-      [_omniboxFocuser.trailingAnchor
-          constraintEqualToAnchor:self.contentView.trailingAnchor
-                         constant:-kOmniboxFocuserTrailing],
-      [_omniboxFocuser.topAnchor
-          constraintEqualToAnchor:_forwardButton.topAnchor],
-      [_omniboxFocuser.bottomAnchor
-          constraintEqualToAnchor:_forwardButton.bottomAnchor]
-    ]];
-
-    [_backButton setImageEdgeInsets:UIEdgeInsetsMakeDirected(0, 0, 0, -10)];
-    [_forwardButton setImageEdgeInsets:UIEdgeInsetsMakeDirected(0, -7, 0, 0)];
-
-    // Set up the button images.
-    [self setUpButton:_backButton
-           withImageEnum:NTPToolbarButtonNameBack
-         forInitialState:UIControlStateDisabled
-        hasDisabledImage:YES
-           synchronously:NO];
-    [self setUpButton:_forwardButton
-           withImageEnum:NTPToolbarButtonNameForward
-         forInitialState:UIControlStateDisabled
-        hasDisabledImage:YES
-           synchronously:NO];
-
-    UILongPressGestureRecognizer* backLongPress =
-        [[UILongPressGestureRecognizer alloc]
-            initWithTarget:self
-                    action:@selector(handleLongPress:)];
-    [_backButton addGestureRecognizer:backLongPress];
-    [_backButton addTarget:self.dispatcher
-                    action:@selector(goBack)
-          forControlEvents:UIControlEventTouchUpInside];
-
-    UILongPressGestureRecognizer* forwardLongPress =
-        [[UILongPressGestureRecognizer alloc]
-            initWithTarget:self
-                    action:@selector(handleLongPress:)];
-    [_forwardButton addGestureRecognizer:forwardLongPress];
-    [_forwardButton addTarget:self.dispatcher
-                       action:@selector(goForward)
-             forControlEvents:UIControlEventTouchUpInside];
-
-    [_omniboxFocuser addTarget:self
-                        action:@selector(focusOmnibox:)
-              forControlEvents:UIControlEventTouchUpInside];
-
-    SetA11yLabelAndUiAutomationName(_backButton, IDS_ACCNAME_BACK, @"Back");
-    SetA11yLabelAndUiAutomationName(_forwardButton, IDS_ACCNAME_FORWARD,
-                                    @"Forward");
-
-    [[self stackButton] addTarget:dispatcher
-                           action:@selector(displayTabSwitcher)
-                 forControlEvents:UIControlEventTouchUpInside];
-  }
-  return self;
-}
-
-#pragma mark - Overridden superclass public methods.
-
-- (BOOL)imageShouldFlipForRightToLeftLayoutDirection:(int)imageEnum {
-  DCHECK(imageEnum < NumberOfNTPToolbarButtonNames);
-  if (imageEnum < NumberOfToolbarButtonNames)
-    return [super imageShouldFlipForRightToLeftLayoutDirection:imageEnum];
-  if (imageEnum == NTPToolbarButtonNameBack ||
-      imageEnum == NTPToolbarButtonNameForward) {
-    return YES;
-  }
-  return NO;
-}
-
-- (void)hideViewsForNewTabPage:(BOOL)hide {
-  [super hideViewsForNewTabPage:hide];
-  // Show the back/forward buttons if there is forward history.
-  BOOL forwardEnabled = self.canGoForward;
-  [_backButton setHidden:!forwardEnabled && hide];
-  [_backButton setEnabled:self.canGoBack];
-  [_forwardButton setHidden:!forwardEnabled && hide];
-}
-
-#pragma mark - Overridden superclass protected methods.
-
-- (CGFloat)statusBarOffset {
-  return 0;
-}
-
-- (int)imageEnumForButton:(UIButton*)button {
-  if (button == _backButton)
-    return NTPToolbarButtonNameBack;
-  if (button == _forwardButton)
-    return NTPToolbarButtonNameForward;
-  return [super imageEnumForButton:button];
-}
-
-- (int)imageIdForImageEnum:(int)index
-                     style:(ToolbarControllerStyle)style
-                  forState:(ToolbarButtonUIState)state {
-  DCHECK(style < ToolbarControllerStyleMaxStyles);
-  DCHECK(state < NumberOfToolbarButtonUIStates);
-
-  if (index >= NumberOfNTPToolbarButtonNames)
-    NOTREACHED();
-  if (index < NumberOfToolbarButtonNames)
-    return [super imageIdForImageEnum:index style:style forState:state];
-
-  index -= NumberOfToolbarButtonNames;
-
-  const int numberOfAddedNames =
-      NumberOfNTPToolbarButtonNames - NumberOfToolbarButtonNames;
-  // Name, style [light, dark], UIControlState [normal, pressed, disabled]
-  static int
-      buttonImageIds[numberOfAddedNames][2][NumberOfToolbarButtonUIStates] = {
-          TOOLBAR_IDR_THREE_STATE(BACK), TOOLBAR_IDR_THREE_STATE(FORWARD),
-      };
-  return buttonImageIds[index][style][state];
-}
-
-- (IBAction)recordUserMetrics:(id)sender {
-  if (sender == _backButton) {
-    base::RecordAction(UserMetricsAction("MobileToolbarBack"));
-  } else if (sender == _forwardButton) {
-    base::RecordAction(UserMetricsAction("MobileToolbarForward"));
-  } else {
-    [super recordUserMetrics:sender];
-  }
-}
-
-#pragma mark - Private methods.
-
-- (void)handleLongPress:(UILongPressGestureRecognizer*)gesture {
-  if (gesture.state != UIGestureRecognizerStateBegan)
-    return;
-
-  if (gesture.view == _backButton) {
-    [self.dispatcher showTabHistoryPopupForBackwardHistory];
-  } else if (gesture.view == _forwardButton) {
-    [self.dispatcher showTabHistoryPopupForForwardHistory];
-  }
-}
-
-- (void)focusOmnibox:(id)sender {
-  [self.dispatcher fakeboxFocused];
-}
-
-@end
diff --git a/ios/chrome/browser/ui/ntp/new_tab_page_view.h b/ios/chrome/browser/ui/ntp/new_tab_page_view.h
deleted file mode 100644
index a88ce62..0000000
--- a/ios/chrome/browser/ui/ntp/new_tab_page_view.h
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef IOS_CHROME_BROWSER_UI_NTP_NEW_TAB_PAGE_VIEW_H_
-#define IOS_CHROME_BROWSER_UI_NTP_NEW_TAB_PAGE_VIEW_H_
-
-#import <UIKit/UIKit.h>
-
-@class NewTabPageBar;
-
-// Container view for the new tab page so that the subviews don't get directly
-// accessed from the view controller.
-@interface NewTabPageView : UIView
-@property(nonatomic, weak, readonly) NewTabPageBar* tabBar;
-@property(nonatomic, weak) UIView* contentView;
-@property(nonatomic, weak) UICollectionView* contentCollectionView;
-// Safe area to be used for toolbar. Once the view is part of the view hierarchy
-// and has its own safe area set, this is equal to safeAreaInsets. But as a
-// snapshot of the view is taken before it is inserted in the view hierarchy,
-// this property needs to be set to what would be the safe area after being
-// inserted in the view hierarchy, before the snapshot is taken.
-@property(nonatomic, assign) UIEdgeInsets safeAreaInsetForToolbar;
-
-- (instancetype)initWithFrame:(CGRect)frame
-                    andTabBar:(NewTabPageBar*)tabBar NS_DESIGNATED_INITIALIZER;
-
-- (instancetype)initWithFrame:(CGRect)frame NS_UNAVAILABLE;
-
-// initWithCoder would only be needed for building this view through .xib files.
-- (instancetype)initWithCoder:(NSCoder*)aDecoder NS_UNAVAILABLE;
-
-@end
-
-#endif  // IOS_CHROME_BROWSER_UI_NTP_NEW_TAB_PAGE_VIEW_H_
diff --git a/ios/chrome/browser/ui/ntp/new_tab_page_view.mm b/ios/chrome/browser/ui/ntp/new_tab_page_view.mm
deleted file mode 100644
index c6a7250..0000000
--- a/ios/chrome/browser/ui/ntp/new_tab_page_view.mm
+++ /dev/null
@@ -1,100 +0,0 @@
-// Copyright 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import "ios/chrome/browser/ui/ntp/new_tab_page_view.h"
-
-#include "base/logging.h"
-#import "ios/chrome/browser/ui/ntp/new_tab_page_bar.h"
-#import "ios/chrome/browser/ui/ntp/new_tab_page_bar_item.h"
-#import "ios/chrome/browser/ui/rtl_geometry.h"
-#include "ios/chrome/browser/ui/ui_util.h"
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
-@implementation NewTabPageView
-@synthesize contentView = _contentView;
-@synthesize contentCollectionView = _contentCollectionView;
-@synthesize tabBar = tabBar_;
-@synthesize safeAreaInsetForToolbar = _safeAreaInsetForToolbar;
-
-- (instancetype)initWithFrame:(CGRect)frame
-                    andTabBar:(NewTabPageBar*)tabBar {
-  self = [super initWithFrame:frame];
-  if (self) {
-    [self addSubview:tabBar];
-    tabBar_ = tabBar;
-  }
-  return self;
-}
-
-- (void)safeAreaInsetsDidChange {
-  self.safeAreaInsetForToolbar = self.safeAreaInsets;
-  [super safeAreaInsetsDidChange];
-}
-
-- (instancetype)initWithFrame:(CGRect)frame {
-  NOTREACHED();
-  return nil;
-}
-
-- (instancetype)initWithCoder:(NSCoder*)aDecoder {
-  NOTREACHED();
-  return nil;
-}
-
-#pragma mark - Properties
-
-- (void)setSafeAreaInsetForToolbar:(UIEdgeInsets)safeAreaInsetForToolbar {
-  _safeAreaInsetForToolbar = safeAreaInsetForToolbar;
-  self.tabBar.safeAreaInsetFromNTPView = safeAreaInsetForToolbar;
-}
-
-- (void)setFrame:(CGRect)frame {
-  [super setFrame:frame];
-
-  // This should never be needed in autolayout.
-  if (self.translatesAutoresizingMaskIntoConstraints) {
-    // Trigger a layout.  The |-layoutIfNeeded| call is required because
-    // sometimes  |-layoutSubviews| is not successfully triggered when
-    // |-setNeedsLayout| is called after frame changes due to autoresizing
-    // masks.
-    [self setNeedsLayout];
-    [self layoutIfNeeded];
-  }
-}
-
-- (void)layoutSubviews {
-  [super layoutSubviews];
-
-  // TODO(crbug.com/807330) Completely remove tabbar once
-  // IsUIRefreshPhase1Enabled is defaulted on.
-  self.tabBar.hidden = !self.tabBar.items.count || IsUIRefreshPhase1Enabled();
-  if (self.tabBar.hidden) {
-    self.contentView.frame = self.bounds;
-  } else {
-    CGSize barSize = [self.tabBar sizeThatFits:self.bounds.size];
-    self.tabBar.frame = CGRectMake(CGRectGetMinX(self.bounds),
-                                   CGRectGetMaxY(self.bounds) - barSize.height,
-                                   barSize.width, barSize.height);
-    CGRect previousContentFrame = self.contentView.frame;
-    self.contentView.frame = CGRectMake(
-        CGRectGetMinX(self.bounds), CGRectGetMinY(self.bounds),
-        CGRectGetWidth(self.bounds), CGRectGetMinY(self.tabBar.frame));
-    if (!CGRectEqualToRect(previousContentFrame, self.contentView.frame)) {
-      [self.contentCollectionView.collectionViewLayout invalidateLayout];
-    }
-  }
-
-  // When using a new_tab_page_view in autolayout -setFrame is never called,
-  // which means all the logic to keep the selected scroll index set is never
-  // called.  Rather than refactor away all of this, just make sure -setFrame is
-  // called when loaded in autolayout.
-  if (!self.translatesAutoresizingMaskIntoConstraints) {
-    [self setFrame:self.frame];
-  }
-}
-
-@end
diff --git a/ios/chrome/browser/ui/ntp/ntp_header_view_adapter.h b/ios/chrome/browser/ui/ntp/ntp_header_view_adapter.h
deleted file mode 100644
index d734b725..0000000
--- a/ios/chrome/browser/ui/ntp/ntp_header_view_adapter.h
+++ /dev/null
@@ -1,78 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef IOS_CHROME_BROWSER_UI_NTP_NTP_HEADER_ADAPTER_H_
-#define IOS_CHROME_BROWSER_UI_NTP_NTP_HEADER_ADAPTER_H_
-
-#import <UIKit/UIKit.h>
-
-#import "ios/chrome/browser/ui/toolbar/toolbar_owner.h"
-
-class ReadingListModel;
-
-// Temporary protocol to simplify adding a second header view for the UI
-// Refresh. TODO(crbug.com/807330) Remove post UI refresh.
-@protocol NTPHeaderViewAdapter<NSObject, ToolbarOwner>
-
-// Return the toolbar view.
-@property(nonatomic, readonly) UIView* toolBarView;
-
-// Return the progress of the search field position along
-// |ntp_header::kAnimationDistance| as the offset changes.
-- (CGFloat)searchFieldProgressForOffset:(CGFloat)offset
-                         safeAreaInsets:(UIEdgeInsets)safeAreaInsets;
-
-// Changes the constraints of searchField based on its initialFrame and the
-// scroll view's y |offset|. Also adjust the alpha values for |_searchBoxBorder|
-// and |_shadow| and the constant values for the |constraints|.|screenWidth| is
-// the width of the screen, including the space outside the safe area. The
-// |safeAreaInsets| is relative to the view used to calculate the |width|.
-- (void)updateSearchFieldWidth:(NSLayoutConstraint*)widthConstraint
-                        height:(NSLayoutConstraint*)heightConstraint
-                     topMargin:(NSLayoutConstraint*)topMarginConstraint
-                     hintLabel:(UILabel*)hintLabel
-            subviewConstraints:(NSArray*)constraints
-                     forOffset:(CGFloat)offset
-                   screenWidth:(CGFloat)screenWidth
-                safeAreaInsets:(UIEdgeInsets)safeAreaInsets;
-
-// Adds views necessary to customize the NTP search box.
-- (void)addViewsToSearchField:(UIView*)searchField;
-
-// Highlight the fake omnibox.
-- (void)setFakeboxHighlighted:(BOOL)highlighted;
-
-// TODO(crbug.com/807330) Remove post UI refresh.
-// Animates legacy header view's |_shadow|'s alpha to 0.
-- (void)fadeOutShadow;
-
-// TODO(crbug.com/807330) Remove post UI refresh.
-// Hides legacy toolbar subviews that should not be displayed on the new tab
-// page.
-- (void)hideToolbarViewsForNewTabPage;
-
-// TODO(crbug.com/807330) Remove post UI refresh.
-// Updates the toolbar tab count;
-- (void)setToolbarTabCount:(int)tabCount;
-
-// TODO(crbug.com/807330) Remove post UI refresh.
-// |YES| if the toolbar can show the forward arrow.
-- (void)setCanGoForward:(BOOL)canGoForward;
-
-// TODO(crbug.com/807330) Remove post UI refresh.
-// |YES| if the toolbar can show the back arrow.
-- (void)setCanGoBack:(BOOL)canGoBack;
-
-@optional
-// Adds the |toolbarView| to the view implementing this protocol.
-// Can only be added once.
-- (void)addToolbarView:(UIView*)toolbarView;
-// TODO(crbug.com/807330) Remove post UI refresh.
-// Creates a NewTabPageToolbarController using the given |dispatcher|,
-// |readingListModel|, and adds the toolbar view to self.
-- (void)addToolbarWithReadingListModel:(ReadingListModel*)readingListModel
-                            dispatcher:(id)dispatcher;
-@end
-
-#endif  // IOS_CHROME_BROWSER_UI_NTP_NTP_HEADER_ADAPTER_H_
diff --git a/ios/chrome/browser/ui/ntp/recent_tabs/legacy_recent_tabs_table_coordinator.h b/ios/chrome/browser/ui/ntp/recent_tabs/legacy_recent_tabs_table_coordinator.h
index a5a88be..1648137 100644
--- a/ios/chrome/browser/ui/ntp/recent_tabs/legacy_recent_tabs_table_coordinator.h
+++ b/ios/chrome/browser/ui/ntp/recent_tabs/legacy_recent_tabs_table_coordinator.h
@@ -8,7 +8,6 @@
 #import <UIKit/UIKit.h>
 
 #import "ios/chrome/browser/ui/coordinators/chrome_coordinator.h"
-#import "ios/chrome/browser/ui/ntp/new_tab_page_panel_protocol.h"
 
 namespace ios {
 class ChromeBrowserState;
diff --git a/ios/chrome/browser/ui/ntp/resources/BUILD.gn b/ios/chrome/browser/ui/ntp/resources/BUILD.gn
index 520cc5ca..7ba5c17 100644
--- a/ios/chrome/browser/ui/ntp/resources/BUILD.gn
+++ b/ios/chrome/browser/ui/ntp/resources/BUILD.gn
@@ -4,15 +4,6 @@
 
 import("//build/config/ios/asset_catalog.gni")
 
-imageset("ntp_mv_thumbnail_container") {
-  sources = [
-    "ntp_mv_thumbnail_container.imageset/Contents.json",
-    "ntp_mv_thumbnail_container.imageset/ntp_mv_thumbnail_container.png",
-    "ntp_mv_thumbnail_container.imageset/ntp_mv_thumbnail_container@2x.png",
-    "ntp_mv_thumbnail_container.imageset/ntp_mv_thumbnail_container@3x.png",
-  ]
-}
-
 imageset("ntp_opentabs_last_row_h") {
   sources = [
     "ntp_opentabs_last_row_h.imageset/Contents.json",
@@ -55,51 +46,6 @@
   ]
 }
 
-imageset("ntp_bookmarks") {
-  sources = [
-    "ntp_bookmarks.imageset/Contents.json",
-    "ntp_bookmarks.imageset/ntp_bookmarks.png",
-    "ntp_bookmarks.imageset/ntp_bookmarks@2x.png",
-    "ntp_bookmarks.imageset/ntp_bookmarks@3x.png",
-  ]
-}
-
-imageset("bookmarks_button_border") {
-  sources = [
-    "bookmarks_button_border.imageset/Contents.json",
-    "bookmarks_button_border.imageset/bookmarks_button_border.png",
-    "bookmarks_button_border.imageset/bookmarks_button_border@2x.png",
-  ]
-}
-
-imageset("bookmarks_bar_edit_pressed_moreshadow") {
-  sources = [
-    "bookmarks_bar_edit_pressed_moreshadow.imageset/Contents.json",
-    "bookmarks_bar_edit_pressed_moreshadow.imageset/bookmarks_bar_edit_pressed_moreshadow.png",
-    "bookmarks_bar_edit_pressed_moreshadow.imageset/bookmarks_bar_edit_pressed_moreshadow@2x.png",
-  ]
-}
-
-imageset("bookmarks_bar_edit_pressed") {
-  sources = [
-    "bookmarks_bar_edit_pressed.imageset/Contents.json",
-    "bookmarks_bar_edit_pressed.imageset/bookmarks_bar_edit_pressed.png",
-    "bookmarks_bar_edit_pressed.imageset/bookmarks_bar_edit_pressed@2x.png",
-    "bookmarks_bar_edit_pressed.imageset/bookmarks_bar_edit_pressed@2x~ipad.png",
-    "bookmarks_bar_edit_pressed.imageset/bookmarks_bar_edit_pressed~ipad.png",
-  ]
-}
-
-imageset("bookmarks_bar_breadcrumb") {
-  sources = [
-    "bookmarks_bar_breadcrumb.imageset/Contents.json",
-    "bookmarks_bar_breadcrumb.imageset/bookmarks_bar_breadcrumb.png",
-    "bookmarks_bar_breadcrumb.imageset/bookmarks_bar_breadcrumb@2x.png",
-    "bookmarks_bar_breadcrumb.imageset/bookmarks_bar_breadcrumb@2x~ipad.png",
-    "bookmarks_bar_breadcrumb.imageset/bookmarks_bar_breadcrumb~ipad.png",
-  ]
-}
-
 imageset("ntp_opentabs_header") {
   sources = [
     "ntp_opentabs_header.imageset/Contents.json",
@@ -108,22 +54,6 @@
   ]
 }
 
-imageset("bookmarks_bar_edit_moreshadow") {
-  sources = [
-    "bookmarks_bar_edit_moreshadow.imageset/Contents.json",
-    "bookmarks_bar_edit_moreshadow.imageset/bookmarks_bar_edit_moreshadow.png",
-    "bookmarks_bar_edit_moreshadow.imageset/bookmarks_bar_edit_moreshadow@2x.png",
-  ]
-}
-
-imageset("ntp_delete_button") {
-  sources = [
-    "ntp_delete_button.imageset/Contents.json",
-    "ntp_delete_button.imageset/ntp_delete_button.png",
-    "ntp_delete_button.imageset/ntp_delete_button@2x.png",
-  ]
-}
-
 imageset("ntp_opentabs_phone") {
   sources = [
     "ntp_opentabs_phone.imageset/Contents.json",
@@ -133,22 +63,6 @@
   ]
 }
 
-imageset("ntp_mv_recentpress") {
-  sources = [
-    "ntp_mv_recentpress.imageset/Contents.json",
-    "ntp_mv_recentpress.imageset/ntp_mv_recentpress@2x~ipad.png",
-    "ntp_mv_recentpress.imageset/ntp_mv_recentpress~ipad.png",
-  ]
-}
-
-imageset("bookmarks_bar_left") {
-  sources = [
-    "bookmarks_bar_left.imageset/Contents.json",
-    "bookmarks_bar_left.imageset/bookmarks_bar_left@2x~ipad.png",
-    "bookmarks_bar_left.imageset/bookmarks_bar_left~ipad.png",
-  ]
-}
-
 imageset("ntp_opentabs_list_item_h") {
   sources = [
     "ntp_opentabs_list_item_h.imageset/Contents.json",
@@ -165,24 +79,6 @@
   ]
 }
 
-imageset("disclosure_open") {
-  sources = [
-    "disclosure_open.imageset/Contents.json",
-    "disclosure_open.imageset/disclosure_open.png",
-    "disclosure_open.imageset/disclosure_open@2x.png",
-  ]
-}
-
-imageset("bookmarks_bar_edit") {
-  sources = [
-    "bookmarks_bar_edit.imageset/Contents.json",
-    "bookmarks_bar_edit.imageset/bookmarks_bar_edit.png",
-    "bookmarks_bar_edit.imageset/bookmarks_bar_edit@2x.png",
-    "bookmarks_bar_edit.imageset/bookmarks_bar_edit@2x~ipad.png",
-    "bookmarks_bar_edit.imageset/bookmarks_bar_edit~ipad.png",
-  ]
-}
-
 imageset("incognito_icon") {
   sources = [
     "incognito_icon.imageset/Contents.json",
@@ -192,15 +88,6 @@
   ]
 }
 
-imageset("incognito_legacy_icon") {
-  sources = [
-    "incognito_legacy_icon.imageset/Contents.json",
-    "incognito_legacy_icon.imageset/incognito_legacy_icon.png",
-    "incognito_legacy_icon.imageset/incognito_legacy_icon@2x.png",
-    "incognito_legacy_icon.imageset/incognito_legacy_icon@3x.png",
-  ]
-}
-
 imageset("ntp_opentabs_recent_arrow") {
   sources = [
     "ntp_opentabs_recent_arrow.imageset/Contents.json",
@@ -210,33 +97,6 @@
   ]
 }
 
-imageset("bookmarks_bar_bg") {
-  sources = [
-    "bookmarks_bar_bg.imageset/Contents.json",
-    "bookmarks_bar_bg.imageset/bookmarks_bar_bg.png",
-    "bookmarks_bar_bg.imageset/bookmarks_bar_bg@2x.png",
-    "bookmarks_bar_bg.imageset/bookmarks_bar_bg@2x~ipad.png",
-    "bookmarks_bar_bg.imageset/bookmarks_bar_bg~ipad.png",
-  ]
-}
-
-imageset("ntp_google_search_box") {
-  sources = [
-    "ntp_google_search_box.imageset/Contents.json",
-    "ntp_google_search_box.imageset/ntp_google_search_box.png",
-    "ntp_google_search_box.imageset/ntp_google_search_box@2x.png",
-    "ntp_google_search_box.imageset/ntp_google_search_box@3x.png",
-  ]
-}
-
-imageset("ntp_bottom_bar_shadow") {
-  sources = [
-    "ntp_bottom_bar_shadow.imageset/Contents.json",
-    "ntp_bottom_bar_shadow.imageset/ntp_bottom_bar_shadow.png",
-    "ntp_bottom_bar_shadow.imageset/ntp_bottom_bar_shadow@2x.png",
-  ]
-}
-
 imageset("ntp_opentabs_laptop") {
   sources = [
     "ntp_opentabs_laptop.imageset/Contents.json",
@@ -246,39 +106,6 @@
   ]
 }
 
-imageset("ntp_mv_recentbar") {
-  sources = [
-    "ntp_mv_recentbar.imageset/Contents.json",
-    "ntp_mv_recentbar.imageset/ntp_mv_recentbar@2x~ipad.png",
-    "ntp_mv_recentbar.imageset/ntp_mv_recentbar~ipad.png",
-  ]
-}
-
-imageset("bookmarks_button_bg") {
-  sources = [
-    "bookmarks_button_bg.imageset/Contents.json",
-    "bookmarks_button_bg.imageset/bookmarks_button_bg.png",
-    "bookmarks_button_bg.imageset/bookmarks_button_bg@2x.png",
-  ]
-}
-
-imageset("ntp_mv_search") {
-  sources = [
-    "ntp_mv_search.imageset/Contents.json",
-    "ntp_mv_search.imageset/ntp_mv_search.png",
-    "ntp_mv_search.imageset/ntp_mv_search@2x.png",
-  ]
-}
-
-imageset("ntp_mv_placeholder_bg") {
-  sources = [
-    "ntp_mv_placeholder_bg.imageset/Contents.json",
-    "ntp_mv_placeholder_bg.imageset/ntp_mv_placeholder_bg.png",
-    "ntp_mv_placeholder_bg.imageset/ntp_mv_placeholder_bg@2x.png",
-    "ntp_mv_placeholder_bg.imageset/ntp_mv_placeholder_bg@3x.png",
-  ]
-}
-
 imageset("ntp_recently_closed") {
   sources = [
     "ntp_recently_closed.imageset/Contents.json",
@@ -287,37 +114,3 @@
     "ntp_recently_closed.imageset/ntp_recently_closed@3x.png",
   ]
 }
-
-imageset("ntp_mv_thumbnail_empty") {
-  sources = [
-    "ntp_mv_thumbnail_empty.imageset/Contents.json",
-    "ntp_mv_thumbnail_empty.imageset/ntp_mv_thumbnail_empty.png",
-    "ntp_mv_thumbnail_empty.imageset/ntp_mv_thumbnail_empty@2x.png",
-    "ntp_mv_thumbnail_empty.imageset/ntp_mv_thumbnail_empty@2x~ipad.png",
-    "ntp_mv_thumbnail_empty.imageset/ntp_mv_thumbnail_empty~ipad.png",
-  ]
-}
-
-imageset("ntp_incognito") {
-  sources = [
-    "ntp_incognito.imageset/Contents.json",
-    "ntp_incognito.imageset/ntp_incognito.png",
-    "ntp_incognito.imageset/ntp_incognito@2x.png",
-  ]
-}
-
-imageset("ntp_mv_welcome_favicon") {
-  sources = [
-    "ntp_mv_welcome_favicon.imageset/Contents.json",
-    "ntp_mv_welcome_favicon.imageset/ntp_mv_welcome_favicon.png",
-    "ntp_mv_welcome_favicon.imageset/ntp_mv_welcome_favicon@2x.png",
-  ]
-}
-
-imageset("bookmarks_bar_bg_pressed") {
-  sources = [
-    "bookmarks_bar_bg_pressed.imageset/Contents.json",
-    "bookmarks_bar_bg_pressed.imageset/bookmarks_bar_bg_pressed@2x~ipad.png",
-    "bookmarks_bar_bg_pressed.imageset/bookmarks_bar_bg_pressed~ipad.png",
-  ]
-}
diff --git a/ios/chrome/browser/ui/ntp/resources/bookmarks_bar_bg.imageset/Contents.json b/ios/chrome/browser/ui/ntp/resources/bookmarks_bar_bg.imageset/Contents.json
deleted file mode 100644
index 608e023c..0000000
--- a/ios/chrome/browser/ui/ntp/resources/bookmarks_bar_bg.imageset/Contents.json
+++ /dev/null
@@ -1,28 +0,0 @@
-{
-    "images": [
-        {
-            "idiom": "iphone",
-            "scale": "1x",
-            "filename": "bookmarks_bar_bg.png"
-        },
-        {
-            "idiom": "iphone",
-            "scale": "2x",
-            "filename": "bookmarks_bar_bg@2x.png"
-        },
-        {
-            "idiom": "ipad",
-            "scale": "2x",
-            "filename": "bookmarks_bar_bg@2x~ipad.png"
-        },
-        {
-            "idiom": "ipad",
-            "scale": "1x",
-            "filename": "bookmarks_bar_bg~ipad.png"
-        }
-    ],
-    "info": {
-        "version": 1,
-        "author": "xcode"
-    }
-}
diff --git a/ios/chrome/browser/ui/ntp/resources/bookmarks_bar_bg.imageset/bookmarks_bar_bg.png b/ios/chrome/browser/ui/ntp/resources/bookmarks_bar_bg.imageset/bookmarks_bar_bg.png
deleted file mode 100644
index 37fa987..0000000
--- a/ios/chrome/browser/ui/ntp/resources/bookmarks_bar_bg.imageset/bookmarks_bar_bg.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/browser/ui/ntp/resources/bookmarks_bar_bg.imageset/bookmarks_bar_bg@2x.png b/ios/chrome/browser/ui/ntp/resources/bookmarks_bar_bg.imageset/bookmarks_bar_bg@2x.png
deleted file mode 100644
index 1b313f30..0000000
--- a/ios/chrome/browser/ui/ntp/resources/bookmarks_bar_bg.imageset/bookmarks_bar_bg@2x.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/browser/ui/ntp/resources/bookmarks_bar_bg.imageset/bookmarks_bar_bg@2x~ipad.png b/ios/chrome/browser/ui/ntp/resources/bookmarks_bar_bg.imageset/bookmarks_bar_bg@2x~ipad.png
deleted file mode 100644
index f07cc6b..0000000
--- a/ios/chrome/browser/ui/ntp/resources/bookmarks_bar_bg.imageset/bookmarks_bar_bg@2x~ipad.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/browser/ui/ntp/resources/bookmarks_bar_bg.imageset/bookmarks_bar_bg~ipad.png b/ios/chrome/browser/ui/ntp/resources/bookmarks_bar_bg.imageset/bookmarks_bar_bg~ipad.png
deleted file mode 100644
index 54ff9f13..0000000
--- a/ios/chrome/browser/ui/ntp/resources/bookmarks_bar_bg.imageset/bookmarks_bar_bg~ipad.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/browser/ui/ntp/resources/bookmarks_bar_bg_pressed.imageset/Contents.json b/ios/chrome/browser/ui/ntp/resources/bookmarks_bar_bg_pressed.imageset/Contents.json
deleted file mode 100644
index 9ff186f..0000000
--- a/ios/chrome/browser/ui/ntp/resources/bookmarks_bar_bg_pressed.imageset/Contents.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
-    "images": [
-        {
-            "idiom": "ipad",
-            "scale": "2x",
-            "filename": "bookmarks_bar_bg_pressed@2x~ipad.png"
-        },
-        {
-            "idiom": "ipad",
-            "scale": "1x",
-            "filename": "bookmarks_bar_bg_pressed~ipad.png"
-        }
-    ],
-    "info": {
-        "version": 1,
-        "author": "xcode"
-    }
-}
diff --git a/ios/chrome/browser/ui/ntp/resources/bookmarks_bar_bg_pressed.imageset/bookmarks_bar_bg_pressed@2x~ipad.png b/ios/chrome/browser/ui/ntp/resources/bookmarks_bar_bg_pressed.imageset/bookmarks_bar_bg_pressed@2x~ipad.png
deleted file mode 100644
index 7c53a34..0000000
--- a/ios/chrome/browser/ui/ntp/resources/bookmarks_bar_bg_pressed.imageset/bookmarks_bar_bg_pressed@2x~ipad.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/browser/ui/ntp/resources/bookmarks_bar_bg_pressed.imageset/bookmarks_bar_bg_pressed~ipad.png b/ios/chrome/browser/ui/ntp/resources/bookmarks_bar_bg_pressed.imageset/bookmarks_bar_bg_pressed~ipad.png
deleted file mode 100644
index 00f846d..0000000
--- a/ios/chrome/browser/ui/ntp/resources/bookmarks_bar_bg_pressed.imageset/bookmarks_bar_bg_pressed~ipad.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/browser/ui/ntp/resources/bookmarks_bar_breadcrumb.imageset/Contents.json b/ios/chrome/browser/ui/ntp/resources/bookmarks_bar_breadcrumb.imageset/Contents.json
deleted file mode 100644
index c4724fd..0000000
--- a/ios/chrome/browser/ui/ntp/resources/bookmarks_bar_breadcrumb.imageset/Contents.json
+++ /dev/null
@@ -1,28 +0,0 @@
-{
-    "images": [
-        {
-            "idiom": "iphone",
-            "scale": "1x",
-            "filename": "bookmarks_bar_breadcrumb.png"
-        },
-        {
-            "idiom": "iphone",
-            "scale": "2x",
-            "filename": "bookmarks_bar_breadcrumb@2x.png"
-        },
-        {
-            "idiom": "ipad",
-            "scale": "2x",
-            "filename": "bookmarks_bar_breadcrumb@2x~ipad.png"
-        },
-        {
-            "idiom": "ipad",
-            "scale": "1x",
-            "filename": "bookmarks_bar_breadcrumb~ipad.png"
-        }
-    ],
-    "info": {
-        "version": 1,
-        "author": "xcode"
-    }
-}
diff --git a/ios/chrome/browser/ui/ntp/resources/bookmarks_bar_breadcrumb.imageset/bookmarks_bar_breadcrumb.png b/ios/chrome/browser/ui/ntp/resources/bookmarks_bar_breadcrumb.imageset/bookmarks_bar_breadcrumb.png
deleted file mode 100644
index 4ce5b81..0000000
--- a/ios/chrome/browser/ui/ntp/resources/bookmarks_bar_breadcrumb.imageset/bookmarks_bar_breadcrumb.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/browser/ui/ntp/resources/bookmarks_bar_breadcrumb.imageset/bookmarks_bar_breadcrumb@2x.png b/ios/chrome/browser/ui/ntp/resources/bookmarks_bar_breadcrumb.imageset/bookmarks_bar_breadcrumb@2x.png
deleted file mode 100644
index 6ef6b54..0000000
--- a/ios/chrome/browser/ui/ntp/resources/bookmarks_bar_breadcrumb.imageset/bookmarks_bar_breadcrumb@2x.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/browser/ui/ntp/resources/bookmarks_bar_breadcrumb.imageset/bookmarks_bar_breadcrumb@2x~ipad.png b/ios/chrome/browser/ui/ntp/resources/bookmarks_bar_breadcrumb.imageset/bookmarks_bar_breadcrumb@2x~ipad.png
deleted file mode 100644
index c6ef249..0000000
--- a/ios/chrome/browser/ui/ntp/resources/bookmarks_bar_breadcrumb.imageset/bookmarks_bar_breadcrumb@2x~ipad.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/browser/ui/ntp/resources/bookmarks_bar_breadcrumb.imageset/bookmarks_bar_breadcrumb~ipad.png b/ios/chrome/browser/ui/ntp/resources/bookmarks_bar_breadcrumb.imageset/bookmarks_bar_breadcrumb~ipad.png
deleted file mode 100644
index 45790bfb..0000000
--- a/ios/chrome/browser/ui/ntp/resources/bookmarks_bar_breadcrumb.imageset/bookmarks_bar_breadcrumb~ipad.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/browser/ui/ntp/resources/bookmarks_bar_edit.imageset/Contents.json b/ios/chrome/browser/ui/ntp/resources/bookmarks_bar_edit.imageset/Contents.json
deleted file mode 100644
index 31b64cb..0000000
--- a/ios/chrome/browser/ui/ntp/resources/bookmarks_bar_edit.imageset/Contents.json
+++ /dev/null
@@ -1,28 +0,0 @@
-{
-    "images": [
-        {
-            "idiom": "iphone",
-            "scale": "1x",
-            "filename": "bookmarks_bar_edit.png"
-        },
-        {
-            "idiom": "iphone",
-            "scale": "2x",
-            "filename": "bookmarks_bar_edit@2x.png"
-        },
-        {
-            "idiom": "ipad",
-            "scale": "2x",
-            "filename": "bookmarks_bar_edit@2x~ipad.png"
-        },
-        {
-            "idiom": "ipad",
-            "scale": "1x",
-            "filename": "bookmarks_bar_edit~ipad.png"
-        }
-    ],
-    "info": {
-        "version": 1,
-        "author": "xcode"
-    }
-}
diff --git a/ios/chrome/browser/ui/ntp/resources/bookmarks_bar_edit.imageset/bookmarks_bar_edit.png b/ios/chrome/browser/ui/ntp/resources/bookmarks_bar_edit.imageset/bookmarks_bar_edit.png
deleted file mode 100644
index 6c1ecc39..0000000
--- a/ios/chrome/browser/ui/ntp/resources/bookmarks_bar_edit.imageset/bookmarks_bar_edit.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/browser/ui/ntp/resources/bookmarks_bar_edit.imageset/bookmarks_bar_edit@2x.png b/ios/chrome/browser/ui/ntp/resources/bookmarks_bar_edit.imageset/bookmarks_bar_edit@2x.png
deleted file mode 100644
index b2307174..0000000
--- a/ios/chrome/browser/ui/ntp/resources/bookmarks_bar_edit.imageset/bookmarks_bar_edit@2x.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/browser/ui/ntp/resources/bookmarks_bar_edit.imageset/bookmarks_bar_edit@2x~ipad.png b/ios/chrome/browser/ui/ntp/resources/bookmarks_bar_edit.imageset/bookmarks_bar_edit@2x~ipad.png
deleted file mode 100644
index 3191d3d..0000000
--- a/ios/chrome/browser/ui/ntp/resources/bookmarks_bar_edit.imageset/bookmarks_bar_edit@2x~ipad.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/browser/ui/ntp/resources/bookmarks_bar_edit.imageset/bookmarks_bar_edit~ipad.png b/ios/chrome/browser/ui/ntp/resources/bookmarks_bar_edit.imageset/bookmarks_bar_edit~ipad.png
deleted file mode 100644
index 24baad02..0000000
--- a/ios/chrome/browser/ui/ntp/resources/bookmarks_bar_edit.imageset/bookmarks_bar_edit~ipad.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/browser/ui/ntp/resources/bookmarks_bar_edit_moreshadow.imageset/Contents.json b/ios/chrome/browser/ui/ntp/resources/bookmarks_bar_edit_moreshadow.imageset/Contents.json
deleted file mode 100644
index 8991eb9b..0000000
--- a/ios/chrome/browser/ui/ntp/resources/bookmarks_bar_edit_moreshadow.imageset/Contents.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
-    "images": [
-        {
-            "idiom": "universal",
-            "scale": "1x",
-            "filename": "bookmarks_bar_edit_moreshadow.png"
-        },
-        {
-            "idiom": "universal",
-            "scale": "2x",
-            "filename": "bookmarks_bar_edit_moreshadow@2x.png"
-        }
-    ],
-    "info": {
-        "version": 1,
-        "author": "xcode"
-    }
-}
diff --git a/ios/chrome/browser/ui/ntp/resources/bookmarks_bar_edit_moreshadow.imageset/bookmarks_bar_edit_moreshadow.png b/ios/chrome/browser/ui/ntp/resources/bookmarks_bar_edit_moreshadow.imageset/bookmarks_bar_edit_moreshadow.png
deleted file mode 100644
index 043c704..0000000
--- a/ios/chrome/browser/ui/ntp/resources/bookmarks_bar_edit_moreshadow.imageset/bookmarks_bar_edit_moreshadow.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/browser/ui/ntp/resources/bookmarks_bar_edit_moreshadow.imageset/bookmarks_bar_edit_moreshadow@2x.png b/ios/chrome/browser/ui/ntp/resources/bookmarks_bar_edit_moreshadow.imageset/bookmarks_bar_edit_moreshadow@2x.png
deleted file mode 100644
index 8d70db4..0000000
--- a/ios/chrome/browser/ui/ntp/resources/bookmarks_bar_edit_moreshadow.imageset/bookmarks_bar_edit_moreshadow@2x.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/browser/ui/ntp/resources/bookmarks_bar_edit_pressed.imageset/Contents.json b/ios/chrome/browser/ui/ntp/resources/bookmarks_bar_edit_pressed.imageset/Contents.json
deleted file mode 100644
index 8107b44..0000000
--- a/ios/chrome/browser/ui/ntp/resources/bookmarks_bar_edit_pressed.imageset/Contents.json
+++ /dev/null
@@ -1,28 +0,0 @@
-{
-    "images": [
-        {
-            "idiom": "iphone",
-            "scale": "1x",
-            "filename": "bookmarks_bar_edit_pressed.png"
-        },
-        {
-            "idiom": "iphone",
-            "scale": "2x",
-            "filename": "bookmarks_bar_edit_pressed@2x.png"
-        },
-        {
-            "idiom": "ipad",
-            "scale": "2x",
-            "filename": "bookmarks_bar_edit_pressed@2x~ipad.png"
-        },
-        {
-            "idiom": "ipad",
-            "scale": "1x",
-            "filename": "bookmarks_bar_edit_pressed~ipad.png"
-        }
-    ],
-    "info": {
-        "version": 1,
-        "author": "xcode"
-    }
-}
diff --git a/ios/chrome/browser/ui/ntp/resources/bookmarks_bar_edit_pressed.imageset/bookmarks_bar_edit_pressed.png b/ios/chrome/browser/ui/ntp/resources/bookmarks_bar_edit_pressed.imageset/bookmarks_bar_edit_pressed.png
deleted file mode 100644
index 1d6b4d59..0000000
--- a/ios/chrome/browser/ui/ntp/resources/bookmarks_bar_edit_pressed.imageset/bookmarks_bar_edit_pressed.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/browser/ui/ntp/resources/bookmarks_bar_edit_pressed.imageset/bookmarks_bar_edit_pressed@2x.png b/ios/chrome/browser/ui/ntp/resources/bookmarks_bar_edit_pressed.imageset/bookmarks_bar_edit_pressed@2x.png
deleted file mode 100644
index 90f5cca2..0000000
--- a/ios/chrome/browser/ui/ntp/resources/bookmarks_bar_edit_pressed.imageset/bookmarks_bar_edit_pressed@2x.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/browser/ui/ntp/resources/bookmarks_bar_edit_pressed.imageset/bookmarks_bar_edit_pressed@2x~ipad.png b/ios/chrome/browser/ui/ntp/resources/bookmarks_bar_edit_pressed.imageset/bookmarks_bar_edit_pressed@2x~ipad.png
deleted file mode 100644
index f546876..0000000
--- a/ios/chrome/browser/ui/ntp/resources/bookmarks_bar_edit_pressed.imageset/bookmarks_bar_edit_pressed@2x~ipad.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/browser/ui/ntp/resources/bookmarks_bar_edit_pressed.imageset/bookmarks_bar_edit_pressed~ipad.png b/ios/chrome/browser/ui/ntp/resources/bookmarks_bar_edit_pressed.imageset/bookmarks_bar_edit_pressed~ipad.png
deleted file mode 100644
index 29b83553..0000000
--- a/ios/chrome/browser/ui/ntp/resources/bookmarks_bar_edit_pressed.imageset/bookmarks_bar_edit_pressed~ipad.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/browser/ui/ntp/resources/bookmarks_bar_edit_pressed_moreshadow.imageset/Contents.json b/ios/chrome/browser/ui/ntp/resources/bookmarks_bar_edit_pressed_moreshadow.imageset/Contents.json
deleted file mode 100644
index 23d486f..0000000
--- a/ios/chrome/browser/ui/ntp/resources/bookmarks_bar_edit_pressed_moreshadow.imageset/Contents.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
-    "images": [
-        {
-            "idiom": "universal",
-            "scale": "1x",
-            "filename": "bookmarks_bar_edit_pressed_moreshadow.png"
-        },
-        {
-            "idiom": "universal",
-            "scale": "2x",
-            "filename": "bookmarks_bar_edit_pressed_moreshadow@2x.png"
-        }
-    ],
-    "info": {
-        "version": 1,
-        "author": "xcode"
-    }
-}
diff --git a/ios/chrome/browser/ui/ntp/resources/bookmarks_bar_edit_pressed_moreshadow.imageset/bookmarks_bar_edit_pressed_moreshadow.png b/ios/chrome/browser/ui/ntp/resources/bookmarks_bar_edit_pressed_moreshadow.imageset/bookmarks_bar_edit_pressed_moreshadow.png
deleted file mode 100644
index f35ee799..0000000
--- a/ios/chrome/browser/ui/ntp/resources/bookmarks_bar_edit_pressed_moreshadow.imageset/bookmarks_bar_edit_pressed_moreshadow.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/browser/ui/ntp/resources/bookmarks_bar_edit_pressed_moreshadow.imageset/bookmarks_bar_edit_pressed_moreshadow@2x.png b/ios/chrome/browser/ui/ntp/resources/bookmarks_bar_edit_pressed_moreshadow.imageset/bookmarks_bar_edit_pressed_moreshadow@2x.png
deleted file mode 100644
index 7804131..0000000
--- a/ios/chrome/browser/ui/ntp/resources/bookmarks_bar_edit_pressed_moreshadow.imageset/bookmarks_bar_edit_pressed_moreshadow@2x.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/browser/ui/ntp/resources/bookmarks_bar_left.imageset/Contents.json b/ios/chrome/browser/ui/ntp/resources/bookmarks_bar_left.imageset/Contents.json
deleted file mode 100644
index 151f807..0000000
--- a/ios/chrome/browser/ui/ntp/resources/bookmarks_bar_left.imageset/Contents.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
-    "images": [
-        {
-            "idiom": "ipad",
-            "scale": "2x",
-            "filename": "bookmarks_bar_left@2x~ipad.png"
-        },
-        {
-            "idiom": "ipad",
-            "scale": "1x",
-            "filename": "bookmarks_bar_left~ipad.png"
-        }
-    ],
-    "info": {
-        "version": 1,
-        "author": "xcode"
-    }
-}
diff --git a/ios/chrome/browser/ui/ntp/resources/bookmarks_bar_left.imageset/bookmarks_bar_left@2x~ipad.png b/ios/chrome/browser/ui/ntp/resources/bookmarks_bar_left.imageset/bookmarks_bar_left@2x~ipad.png
deleted file mode 100644
index b9c9a70..0000000
--- a/ios/chrome/browser/ui/ntp/resources/bookmarks_bar_left.imageset/bookmarks_bar_left@2x~ipad.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/browser/ui/ntp/resources/bookmarks_bar_left.imageset/bookmarks_bar_left~ipad.png b/ios/chrome/browser/ui/ntp/resources/bookmarks_bar_left.imageset/bookmarks_bar_left~ipad.png
deleted file mode 100644
index 4143435c..0000000
--- a/ios/chrome/browser/ui/ntp/resources/bookmarks_bar_left.imageset/bookmarks_bar_left~ipad.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/browser/ui/ntp/resources/bookmarks_button_bg.imageset/Contents.json b/ios/chrome/browser/ui/ntp/resources/bookmarks_button_bg.imageset/Contents.json
deleted file mode 100644
index bc53e909..0000000
--- a/ios/chrome/browser/ui/ntp/resources/bookmarks_button_bg.imageset/Contents.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
-    "images": [
-        {
-            "idiom": "universal",
-            "scale": "1x",
-            "filename": "bookmarks_button_bg.png"
-        },
-        {
-            "idiom": "universal",
-            "scale": "2x",
-            "filename": "bookmarks_button_bg@2x.png"
-        }
-    ],
-    "info": {
-        "version": 1,
-        "author": "xcode"
-    }
-}
diff --git a/ios/chrome/browser/ui/ntp/resources/bookmarks_button_bg.imageset/bookmarks_button_bg.png b/ios/chrome/browser/ui/ntp/resources/bookmarks_button_bg.imageset/bookmarks_button_bg.png
deleted file mode 100644
index fe8e21a..0000000
--- a/ios/chrome/browser/ui/ntp/resources/bookmarks_button_bg.imageset/bookmarks_button_bg.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/browser/ui/ntp/resources/bookmarks_button_bg.imageset/bookmarks_button_bg@2x.png b/ios/chrome/browser/ui/ntp/resources/bookmarks_button_bg.imageset/bookmarks_button_bg@2x.png
deleted file mode 100644
index 6e3ee279..0000000
--- a/ios/chrome/browser/ui/ntp/resources/bookmarks_button_bg.imageset/bookmarks_button_bg@2x.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/browser/ui/ntp/resources/bookmarks_button_border.imageset/Contents.json b/ios/chrome/browser/ui/ntp/resources/bookmarks_button_border.imageset/Contents.json
deleted file mode 100644
index c948d99..0000000
--- a/ios/chrome/browser/ui/ntp/resources/bookmarks_button_border.imageset/Contents.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
-    "images": [
-        {
-            "idiom": "universal",
-            "scale": "1x",
-            "filename": "bookmarks_button_border.png"
-        },
-        {
-            "idiom": "universal",
-            "scale": "2x",
-            "filename": "bookmarks_button_border@2x.png"
-        }
-    ],
-    "info": {
-        "version": 1,
-        "author": "xcode"
-    }
-}
diff --git a/ios/chrome/browser/ui/ntp/resources/bookmarks_button_border.imageset/bookmarks_button_border.png b/ios/chrome/browser/ui/ntp/resources/bookmarks_button_border.imageset/bookmarks_button_border.png
deleted file mode 100644
index ba98442..0000000
--- a/ios/chrome/browser/ui/ntp/resources/bookmarks_button_border.imageset/bookmarks_button_border.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/browser/ui/ntp/resources/bookmarks_button_border.imageset/bookmarks_button_border@2x.png b/ios/chrome/browser/ui/ntp/resources/bookmarks_button_border.imageset/bookmarks_button_border@2x.png
deleted file mode 100644
index a37c5ba..0000000
--- a/ios/chrome/browser/ui/ntp/resources/bookmarks_button_border.imageset/bookmarks_button_border@2x.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/browser/ui/ntp/resources/disclosure_open.imageset/Contents.json b/ios/chrome/browser/ui/ntp/resources/disclosure_open.imageset/Contents.json
deleted file mode 100644
index c7cb532..0000000
--- a/ios/chrome/browser/ui/ntp/resources/disclosure_open.imageset/Contents.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
-    "images": [
-        {
-            "idiom": "universal",
-            "scale": "1x",
-            "filename": "disclosure_open.png"
-        },
-        {
-            "idiom": "universal",
-            "scale": "2x",
-            "filename": "disclosure_open@2x.png"
-        }
-    ],
-    "info": {
-        "version": 1,
-        "author": "xcode"
-    }
-}
diff --git a/ios/chrome/browser/ui/ntp/resources/disclosure_open.imageset/disclosure_open.png b/ios/chrome/browser/ui/ntp/resources/disclosure_open.imageset/disclosure_open.png
deleted file mode 100644
index 9f5bca6..0000000
--- a/ios/chrome/browser/ui/ntp/resources/disclosure_open.imageset/disclosure_open.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/browser/ui/ntp/resources/disclosure_open.imageset/disclosure_open@2x.png b/ios/chrome/browser/ui/ntp/resources/disclosure_open.imageset/disclosure_open@2x.png
deleted file mode 100644
index ef7b6402..0000000
--- a/ios/chrome/browser/ui/ntp/resources/disclosure_open.imageset/disclosure_open@2x.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/browser/ui/ntp/resources/incognito_legacy_icon.imageset/Contents.json b/ios/chrome/browser/ui/ntp/resources/incognito_legacy_icon.imageset/Contents.json
deleted file mode 100644
index a8113a5d6..0000000
--- a/ios/chrome/browser/ui/ntp/resources/incognito_legacy_icon.imageset/Contents.json
+++ /dev/null
@@ -1,23 +0,0 @@
-{
-    "images": [
-        {
-            "idiom": "universal",
-            "scale": "1x",
-            "filename": "incognito_legacy_icon.png"
-        },
-        {
-            "idiom": "universal",
-            "scale": "2x",
-            "filename": "incognito_legacy_icon@2x.png"
-        },
-        {
-            "idiom": "universal",
-            "scale": "3x",
-            "filename": "incognito_legacy_icon@3x.png"
-        }
-    ],
-    "info": {
-        "version": 1,
-        "author": "xcode"
-    }
-}
diff --git a/ios/chrome/browser/ui/ntp/resources/incognito_legacy_icon.imageset/incognito_legacy_icon.png b/ios/chrome/browser/ui/ntp/resources/incognito_legacy_icon.imageset/incognito_legacy_icon.png
deleted file mode 100644
index 9925f9ba..0000000
--- a/ios/chrome/browser/ui/ntp/resources/incognito_legacy_icon.imageset/incognito_legacy_icon.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/browser/ui/ntp/resources/incognito_legacy_icon.imageset/incognito_legacy_icon@2x.png b/ios/chrome/browser/ui/ntp/resources/incognito_legacy_icon.imageset/incognito_legacy_icon@2x.png
deleted file mode 100644
index 802b709..0000000
--- a/ios/chrome/browser/ui/ntp/resources/incognito_legacy_icon.imageset/incognito_legacy_icon@2x.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/browser/ui/ntp/resources/incognito_legacy_icon.imageset/incognito_legacy_icon@3x.png b/ios/chrome/browser/ui/ntp/resources/incognito_legacy_icon.imageset/incognito_legacy_icon@3x.png
deleted file mode 100644
index 30fa024..0000000
--- a/ios/chrome/browser/ui/ntp/resources/incognito_legacy_icon.imageset/incognito_legacy_icon@3x.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/browser/ui/ntp/resources/ntp_bookmarks.imageset/Contents.json b/ios/chrome/browser/ui/ntp/resources/ntp_bookmarks.imageset/Contents.json
deleted file mode 100644
index 7c7c6aa..0000000
--- a/ios/chrome/browser/ui/ntp/resources/ntp_bookmarks.imageset/Contents.json
+++ /dev/null
@@ -1,23 +0,0 @@
-{
-    "images": [
-        {
-            "idiom": "universal",
-            "scale": "1x",
-            "filename": "ntp_bookmarks.png"
-        },
-        {
-            "idiom": "universal",
-            "scale": "2x",
-            "filename": "ntp_bookmarks@2x.png"
-        },
-        {
-            "idiom": "universal",
-            "scale": "3x",
-            "filename": "ntp_bookmarks@3x.png"
-        }
-    ],
-    "info": {
-        "version": 1,
-        "author": "xcode"
-    }
-}
diff --git a/ios/chrome/browser/ui/ntp/resources/ntp_bookmarks.imageset/ntp_bookmarks.png b/ios/chrome/browser/ui/ntp/resources/ntp_bookmarks.imageset/ntp_bookmarks.png
deleted file mode 100644
index e10c472..0000000
--- a/ios/chrome/browser/ui/ntp/resources/ntp_bookmarks.imageset/ntp_bookmarks.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/browser/ui/ntp/resources/ntp_bookmarks.imageset/ntp_bookmarks@2x.png b/ios/chrome/browser/ui/ntp/resources/ntp_bookmarks.imageset/ntp_bookmarks@2x.png
deleted file mode 100644
index 9c1c9541..0000000
--- a/ios/chrome/browser/ui/ntp/resources/ntp_bookmarks.imageset/ntp_bookmarks@2x.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/browser/ui/ntp/resources/ntp_bookmarks.imageset/ntp_bookmarks@3x.png b/ios/chrome/browser/ui/ntp/resources/ntp_bookmarks.imageset/ntp_bookmarks@3x.png
deleted file mode 100644
index d0084ffc..0000000
--- a/ios/chrome/browser/ui/ntp/resources/ntp_bookmarks.imageset/ntp_bookmarks@3x.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/browser/ui/ntp/resources/ntp_bottom_bar_shadow.imageset/Contents.json b/ios/chrome/browser/ui/ntp/resources/ntp_bottom_bar_shadow.imageset/Contents.json
deleted file mode 100644
index 74d4477..0000000
--- a/ios/chrome/browser/ui/ntp/resources/ntp_bottom_bar_shadow.imageset/Contents.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
-    "images": [
-        {
-            "idiom": "universal",
-            "scale": "1x",
-            "filename": "ntp_bottom_bar_shadow.png"
-        },
-        {
-            "idiom": "universal",
-            "scale": "2x",
-            "filename": "ntp_bottom_bar_shadow@2x.png"
-        }
-    ],
-    "info": {
-        "version": 1,
-        "author": "xcode"
-    }
-}
diff --git a/ios/chrome/browser/ui/ntp/resources/ntp_bottom_bar_shadow.imageset/ntp_bottom_bar_shadow.png b/ios/chrome/browser/ui/ntp/resources/ntp_bottom_bar_shadow.imageset/ntp_bottom_bar_shadow.png
deleted file mode 100644
index ac2c812b..0000000
--- a/ios/chrome/browser/ui/ntp/resources/ntp_bottom_bar_shadow.imageset/ntp_bottom_bar_shadow.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/browser/ui/ntp/resources/ntp_bottom_bar_shadow.imageset/ntp_bottom_bar_shadow@2x.png b/ios/chrome/browser/ui/ntp/resources/ntp_bottom_bar_shadow.imageset/ntp_bottom_bar_shadow@2x.png
deleted file mode 100644
index 3e0d7f0..0000000
--- a/ios/chrome/browser/ui/ntp/resources/ntp_bottom_bar_shadow.imageset/ntp_bottom_bar_shadow@2x.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/browser/ui/ntp/resources/ntp_delete_button.imageset/Contents.json b/ios/chrome/browser/ui/ntp/resources/ntp_delete_button.imageset/Contents.json
deleted file mode 100644
index 096916700..0000000
--- a/ios/chrome/browser/ui/ntp/resources/ntp_delete_button.imageset/Contents.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
-    "images": [
-        {
-            "idiom": "universal",
-            "scale": "1x",
-            "filename": "ntp_delete_button.png"
-        },
-        {
-            "idiom": "universal",
-            "scale": "2x",
-            "filename": "ntp_delete_button@2x.png"
-        }
-    ],
-    "info": {
-        "version": 1,
-        "author": "xcode"
-    }
-}
diff --git a/ios/chrome/browser/ui/ntp/resources/ntp_delete_button.imageset/ntp_delete_button.png b/ios/chrome/browser/ui/ntp/resources/ntp_delete_button.imageset/ntp_delete_button.png
deleted file mode 100644
index d1ce6883..0000000
--- a/ios/chrome/browser/ui/ntp/resources/ntp_delete_button.imageset/ntp_delete_button.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/browser/ui/ntp/resources/ntp_delete_button.imageset/ntp_delete_button@2x.png b/ios/chrome/browser/ui/ntp/resources/ntp_delete_button.imageset/ntp_delete_button@2x.png
deleted file mode 100644
index 597623a..0000000
--- a/ios/chrome/browser/ui/ntp/resources/ntp_delete_button.imageset/ntp_delete_button@2x.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/browser/ui/ntp/resources/ntp_google_search_box.imageset/Contents.json b/ios/chrome/browser/ui/ntp/resources/ntp_google_search_box.imageset/Contents.json
deleted file mode 100644
index 0512e147..0000000
--- a/ios/chrome/browser/ui/ntp/resources/ntp_google_search_box.imageset/Contents.json
+++ /dev/null
@@ -1,23 +0,0 @@
-{
-    "images": [
-        {
-            "idiom": "universal",
-            "scale": "1x",
-            "filename": "ntp_google_search_box.png"
-        },
-        {
-            "idiom": "universal",
-            "scale": "2x",
-            "filename": "ntp_google_search_box@2x.png"
-        },
-        {
-            "idiom": "universal",
-            "scale": "3x",
-            "filename": "ntp_google_search_box@3x.png"
-        }
-    ],
-    "info": {
-        "version": 1,
-        "author": "xcode"
-    }
-}
diff --git a/ios/chrome/browser/ui/ntp/resources/ntp_google_search_box.imageset/ntp_google_search_box.png b/ios/chrome/browser/ui/ntp/resources/ntp_google_search_box.imageset/ntp_google_search_box.png
deleted file mode 100644
index 7e7f9965..0000000
--- a/ios/chrome/browser/ui/ntp/resources/ntp_google_search_box.imageset/ntp_google_search_box.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/browser/ui/ntp/resources/ntp_google_search_box.imageset/ntp_google_search_box@2x.png b/ios/chrome/browser/ui/ntp/resources/ntp_google_search_box.imageset/ntp_google_search_box@2x.png
deleted file mode 100644
index 251b6b8..0000000
--- a/ios/chrome/browser/ui/ntp/resources/ntp_google_search_box.imageset/ntp_google_search_box@2x.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/browser/ui/ntp/resources/ntp_google_search_box.imageset/ntp_google_search_box@3x.png b/ios/chrome/browser/ui/ntp/resources/ntp_google_search_box.imageset/ntp_google_search_box@3x.png
deleted file mode 100644
index 49b461a..0000000
--- a/ios/chrome/browser/ui/ntp/resources/ntp_google_search_box.imageset/ntp_google_search_box@3x.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/browser/ui/ntp/resources/ntp_incognito.imageset/Contents.json b/ios/chrome/browser/ui/ntp/resources/ntp_incognito.imageset/Contents.json
deleted file mode 100644
index 30d3a84..0000000
--- a/ios/chrome/browser/ui/ntp/resources/ntp_incognito.imageset/Contents.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
-    "images": [
-        {
-            "idiom": "universal",
-            "scale": "1x",
-            "filename": "ntp_incognito.png"
-        },
-        {
-            "idiom": "universal",
-            "scale": "2x",
-            "filename": "ntp_incognito@2x.png"
-        }
-    ],
-    "info": {
-        "version": 1,
-        "author": "xcode"
-    }
-}
diff --git a/ios/chrome/browser/ui/ntp/resources/ntp_incognito.imageset/ntp_incognito.png b/ios/chrome/browser/ui/ntp/resources/ntp_incognito.imageset/ntp_incognito.png
deleted file mode 100644
index 4d34ede..0000000
--- a/ios/chrome/browser/ui/ntp/resources/ntp_incognito.imageset/ntp_incognito.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/browser/ui/ntp/resources/ntp_incognito.imageset/ntp_incognito@2x.png b/ios/chrome/browser/ui/ntp/resources/ntp_incognito.imageset/ntp_incognito@2x.png
deleted file mode 100644
index 26667a76..0000000
--- a/ios/chrome/browser/ui/ntp/resources/ntp_incognito.imageset/ntp_incognito@2x.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/browser/ui/ntp/resources/ntp_mv_placeholder_bg.imageset/Contents.json b/ios/chrome/browser/ui/ntp/resources/ntp_mv_placeholder_bg.imageset/Contents.json
deleted file mode 100644
index e3b9fad..0000000
--- a/ios/chrome/browser/ui/ntp/resources/ntp_mv_placeholder_bg.imageset/Contents.json
+++ /dev/null
@@ -1,23 +0,0 @@
-{
-    "images": [
-        {
-            "idiom": "universal",
-            "scale": "1x",
-            "filename": "ntp_mv_placeholder_bg.png"
-        },
-        {
-            "idiom": "universal",
-            "scale": "2x",
-            "filename": "ntp_mv_placeholder_bg@2x.png"
-        },
-        {
-            "idiom": "universal",
-            "scale": "3x",
-            "filename": "ntp_mv_placeholder_bg@3x.png"
-        }
-    ],
-    "info": {
-        "version": 1,
-        "author": "xcode"
-    }
-}
diff --git a/ios/chrome/browser/ui/ntp/resources/ntp_mv_placeholder_bg.imageset/ntp_mv_placeholder_bg.png b/ios/chrome/browser/ui/ntp/resources/ntp_mv_placeholder_bg.imageset/ntp_mv_placeholder_bg.png
deleted file mode 100644
index cd6dfc76..0000000
--- a/ios/chrome/browser/ui/ntp/resources/ntp_mv_placeholder_bg.imageset/ntp_mv_placeholder_bg.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/browser/ui/ntp/resources/ntp_mv_placeholder_bg.imageset/ntp_mv_placeholder_bg@2x.png b/ios/chrome/browser/ui/ntp/resources/ntp_mv_placeholder_bg.imageset/ntp_mv_placeholder_bg@2x.png
deleted file mode 100644
index 131df93..0000000
--- a/ios/chrome/browser/ui/ntp/resources/ntp_mv_placeholder_bg.imageset/ntp_mv_placeholder_bg@2x.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/browser/ui/ntp/resources/ntp_mv_placeholder_bg.imageset/ntp_mv_placeholder_bg@3x.png b/ios/chrome/browser/ui/ntp/resources/ntp_mv_placeholder_bg.imageset/ntp_mv_placeholder_bg@3x.png
deleted file mode 100644
index 5aa642e..0000000
--- a/ios/chrome/browser/ui/ntp/resources/ntp_mv_placeholder_bg.imageset/ntp_mv_placeholder_bg@3x.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/browser/ui/ntp/resources/ntp_mv_recentbar.imageset/Contents.json b/ios/chrome/browser/ui/ntp/resources/ntp_mv_recentbar.imageset/Contents.json
deleted file mode 100644
index 39a30a6..0000000
--- a/ios/chrome/browser/ui/ntp/resources/ntp_mv_recentbar.imageset/Contents.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
-    "images": [
-        {
-            "idiom": "ipad",
-            "scale": "2x",
-            "filename": "ntp_mv_recentbar@2x~ipad.png"
-        },
-        {
-            "idiom": "ipad",
-            "scale": "1x",
-            "filename": "ntp_mv_recentbar~ipad.png"
-        }
-    ],
-    "info": {
-        "version": 1,
-        "author": "xcode"
-    }
-}
diff --git a/ios/chrome/browser/ui/ntp/resources/ntp_mv_recentbar.imageset/ntp_mv_recentbar@2x~ipad.png b/ios/chrome/browser/ui/ntp/resources/ntp_mv_recentbar.imageset/ntp_mv_recentbar@2x~ipad.png
deleted file mode 100644
index bca13cc..0000000
--- a/ios/chrome/browser/ui/ntp/resources/ntp_mv_recentbar.imageset/ntp_mv_recentbar@2x~ipad.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/browser/ui/ntp/resources/ntp_mv_recentbar.imageset/ntp_mv_recentbar~ipad.png b/ios/chrome/browser/ui/ntp/resources/ntp_mv_recentbar.imageset/ntp_mv_recentbar~ipad.png
deleted file mode 100644
index 316f923..0000000
--- a/ios/chrome/browser/ui/ntp/resources/ntp_mv_recentbar.imageset/ntp_mv_recentbar~ipad.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/browser/ui/ntp/resources/ntp_mv_recentpress.imageset/Contents.json b/ios/chrome/browser/ui/ntp/resources/ntp_mv_recentpress.imageset/Contents.json
deleted file mode 100644
index 4ed3786..0000000
--- a/ios/chrome/browser/ui/ntp/resources/ntp_mv_recentpress.imageset/Contents.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
-    "images": [
-        {
-            "idiom": "ipad",
-            "scale": "2x",
-            "filename": "ntp_mv_recentpress@2x~ipad.png"
-        },
-        {
-            "idiom": "ipad",
-            "scale": "1x",
-            "filename": "ntp_mv_recentpress~ipad.png"
-        }
-    ],
-    "info": {
-        "version": 1,
-        "author": "xcode"
-    }
-}
diff --git a/ios/chrome/browser/ui/ntp/resources/ntp_mv_recentpress.imageset/ntp_mv_recentpress@2x~ipad.png b/ios/chrome/browser/ui/ntp/resources/ntp_mv_recentpress.imageset/ntp_mv_recentpress@2x~ipad.png
deleted file mode 100644
index 5007339..0000000
--- a/ios/chrome/browser/ui/ntp/resources/ntp_mv_recentpress.imageset/ntp_mv_recentpress@2x~ipad.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/browser/ui/ntp/resources/ntp_mv_recentpress.imageset/ntp_mv_recentpress~ipad.png b/ios/chrome/browser/ui/ntp/resources/ntp_mv_recentpress.imageset/ntp_mv_recentpress~ipad.png
deleted file mode 100644
index 3b455fa..0000000
--- a/ios/chrome/browser/ui/ntp/resources/ntp_mv_recentpress.imageset/ntp_mv_recentpress~ipad.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/browser/ui/ntp/resources/ntp_mv_search.imageset/Contents.json b/ios/chrome/browser/ui/ntp/resources/ntp_mv_search.imageset/Contents.json
deleted file mode 100644
index 593b5d0..0000000
--- a/ios/chrome/browser/ui/ntp/resources/ntp_mv_search.imageset/Contents.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
-    "images": [
-        {
-            "idiom": "universal",
-            "scale": "1x",
-            "filename": "ntp_mv_search.png"
-        },
-        {
-            "idiom": "universal",
-            "scale": "2x",
-            "filename": "ntp_mv_search@2x.png"
-        }
-    ],
-    "info": {
-        "version": 1,
-        "author": "xcode"
-    }
-}
diff --git a/ios/chrome/browser/ui/ntp/resources/ntp_mv_search.imageset/ntp_mv_search.png b/ios/chrome/browser/ui/ntp/resources/ntp_mv_search.imageset/ntp_mv_search.png
deleted file mode 100644
index 34e10b7..0000000
--- a/ios/chrome/browser/ui/ntp/resources/ntp_mv_search.imageset/ntp_mv_search.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/browser/ui/ntp/resources/ntp_mv_search.imageset/ntp_mv_search@2x.png b/ios/chrome/browser/ui/ntp/resources/ntp_mv_search.imageset/ntp_mv_search@2x.png
deleted file mode 100644
index 26e13ad..0000000
--- a/ios/chrome/browser/ui/ntp/resources/ntp_mv_search.imageset/ntp_mv_search@2x.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/browser/ui/ntp/resources/ntp_mv_thumbnail_container.imageset/Contents.json b/ios/chrome/browser/ui/ntp/resources/ntp_mv_thumbnail_container.imageset/Contents.json
deleted file mode 100644
index d00307c..0000000
--- a/ios/chrome/browser/ui/ntp/resources/ntp_mv_thumbnail_container.imageset/Contents.json
+++ /dev/null
@@ -1,23 +0,0 @@
-{
-    "images": [
-        {
-            "idiom": "universal",
-            "scale": "1x",
-            "filename": "ntp_mv_thumbnail_container.png"
-        },
-        {
-            "idiom": "universal",
-            "scale": "2x",
-            "filename": "ntp_mv_thumbnail_container@2x.png"
-        },
-        {
-            "idiom": "universal",
-            "scale": "3x",
-            "filename": "ntp_mv_thumbnail_container@3x.png"
-        }
-    ],
-    "info": {
-        "version": 1,
-        "author": "xcode"
-    }
-}
diff --git a/ios/chrome/browser/ui/ntp/resources/ntp_mv_thumbnail_container.imageset/ntp_mv_thumbnail_container.png b/ios/chrome/browser/ui/ntp/resources/ntp_mv_thumbnail_container.imageset/ntp_mv_thumbnail_container.png
deleted file mode 100644
index 1f69478..0000000
--- a/ios/chrome/browser/ui/ntp/resources/ntp_mv_thumbnail_container.imageset/ntp_mv_thumbnail_container.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/browser/ui/ntp/resources/ntp_mv_thumbnail_container.imageset/ntp_mv_thumbnail_container@2x.png b/ios/chrome/browser/ui/ntp/resources/ntp_mv_thumbnail_container.imageset/ntp_mv_thumbnail_container@2x.png
deleted file mode 100644
index d3d37ce..0000000
--- a/ios/chrome/browser/ui/ntp/resources/ntp_mv_thumbnail_container.imageset/ntp_mv_thumbnail_container@2x.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/browser/ui/ntp/resources/ntp_mv_thumbnail_container.imageset/ntp_mv_thumbnail_container@3x.png b/ios/chrome/browser/ui/ntp/resources/ntp_mv_thumbnail_container.imageset/ntp_mv_thumbnail_container@3x.png
deleted file mode 100644
index e12f8d4..0000000
--- a/ios/chrome/browser/ui/ntp/resources/ntp_mv_thumbnail_container.imageset/ntp_mv_thumbnail_container@3x.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/browser/ui/ntp/resources/ntp_mv_thumbnail_empty.imageset/Contents.json b/ios/chrome/browser/ui/ntp/resources/ntp_mv_thumbnail_empty.imageset/Contents.json
deleted file mode 100644
index f913a1f..0000000
--- a/ios/chrome/browser/ui/ntp/resources/ntp_mv_thumbnail_empty.imageset/Contents.json
+++ /dev/null
@@ -1,28 +0,0 @@
-{
-    "images": [
-        {
-            "idiom": "iphone",
-            "scale": "1x",
-            "filename": "ntp_mv_thumbnail_empty.png"
-        },
-        {
-            "idiom": "iphone",
-            "scale": "2x",
-            "filename": "ntp_mv_thumbnail_empty@2x.png"
-        },
-        {
-            "idiom": "ipad",
-            "scale": "2x",
-            "filename": "ntp_mv_thumbnail_empty@2x~ipad.png"
-        },
-        {
-            "idiom": "ipad",
-            "scale": "1x",
-            "filename": "ntp_mv_thumbnail_empty~ipad.png"
-        }
-    ],
-    "info": {
-        "version": 1,
-        "author": "xcode"
-    }
-}
diff --git a/ios/chrome/browser/ui/ntp/resources/ntp_mv_thumbnail_empty.imageset/ntp_mv_thumbnail_empty.png b/ios/chrome/browser/ui/ntp/resources/ntp_mv_thumbnail_empty.imageset/ntp_mv_thumbnail_empty.png
deleted file mode 100644
index b58242d..0000000
--- a/ios/chrome/browser/ui/ntp/resources/ntp_mv_thumbnail_empty.imageset/ntp_mv_thumbnail_empty.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/browser/ui/ntp/resources/ntp_mv_thumbnail_empty.imageset/ntp_mv_thumbnail_empty@2x.png b/ios/chrome/browser/ui/ntp/resources/ntp_mv_thumbnail_empty.imageset/ntp_mv_thumbnail_empty@2x.png
deleted file mode 100644
index c13f2c2..0000000
--- a/ios/chrome/browser/ui/ntp/resources/ntp_mv_thumbnail_empty.imageset/ntp_mv_thumbnail_empty@2x.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/browser/ui/ntp/resources/ntp_mv_thumbnail_empty.imageset/ntp_mv_thumbnail_empty@2x~ipad.png b/ios/chrome/browser/ui/ntp/resources/ntp_mv_thumbnail_empty.imageset/ntp_mv_thumbnail_empty@2x~ipad.png
deleted file mode 100644
index 1a11732..0000000
--- a/ios/chrome/browser/ui/ntp/resources/ntp_mv_thumbnail_empty.imageset/ntp_mv_thumbnail_empty@2x~ipad.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/browser/ui/ntp/resources/ntp_mv_thumbnail_empty.imageset/ntp_mv_thumbnail_empty~ipad.png b/ios/chrome/browser/ui/ntp/resources/ntp_mv_thumbnail_empty.imageset/ntp_mv_thumbnail_empty~ipad.png
deleted file mode 100644
index 49a0e2e..0000000
--- a/ios/chrome/browser/ui/ntp/resources/ntp_mv_thumbnail_empty.imageset/ntp_mv_thumbnail_empty~ipad.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/browser/ui/ntp/resources/ntp_mv_welcome_favicon.imageset/Contents.json b/ios/chrome/browser/ui/ntp/resources/ntp_mv_welcome_favicon.imageset/Contents.json
deleted file mode 100644
index af8dde26..0000000
--- a/ios/chrome/browser/ui/ntp/resources/ntp_mv_welcome_favicon.imageset/Contents.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
-    "images": [
-        {
-            "idiom": "universal",
-            "scale": "1x",
-            "filename": "ntp_mv_welcome_favicon.png"
-        },
-        {
-            "idiom": "universal",
-            "scale": "2x",
-            "filename": "ntp_mv_welcome_favicon@2x.png"
-        }
-    ],
-    "info": {
-        "version": 1,
-        "author": "xcode"
-    }
-}
diff --git a/ios/chrome/browser/ui/ntp/resources/ntp_mv_welcome_favicon.imageset/ntp_mv_welcome_favicon.png b/ios/chrome/browser/ui/ntp/resources/ntp_mv_welcome_favicon.imageset/ntp_mv_welcome_favicon.png
deleted file mode 100644
index ad246660..0000000
--- a/ios/chrome/browser/ui/ntp/resources/ntp_mv_welcome_favicon.imageset/ntp_mv_welcome_favicon.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/browser/ui/ntp/resources/ntp_mv_welcome_favicon.imageset/ntp_mv_welcome_favicon@2x.png b/ios/chrome/browser/ui/ntp/resources/ntp_mv_welcome_favicon.imageset/ntp_mv_welcome_favicon@2x.png
deleted file mode 100644
index eab9976..0000000
--- a/ios/chrome/browser/ui/ntp/resources/ntp_mv_welcome_favicon.imageset/ntp_mv_welcome_favicon@2x.png
+++ /dev/null
Binary files differ
diff --git a/ios/chrome/browser/ui/payments/full_card_requester_unittest.mm b/ios/chrome/browser/ui/payments/full_card_requester_unittest.mm
index 5728f310..e017ae8 100644
--- a/ios/chrome/browser/ui/payments/full_card_requester_unittest.mm
+++ b/ios/chrome/browser/ui/payments/full_card_requester_unittest.mm
@@ -20,6 +20,9 @@
 #include "ios/chrome/browser/ui/autofill/card_unmask_prompt_view_bridge.h"
 #import "ios/chrome/test/scoped_key_window.h"
 #import "ios/web/public/test/fakes/crw_test_js_injection_receiver.h"
+#include "ios/web/public/test/fakes/fake_web_frame.h"
+#import "ios/web/public/web_state/web_frame_util.h"
+#import "ios/web/public/web_state/web_frames_manager.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/platform_test.h"
 #include "third_party/ocmock/gtest_support.h"
@@ -70,6 +73,9 @@
         [[AutofillAgent alloc] initWithPrefService:browser_state()->GetPrefs()
                                           webState:web_state()];
     InfoBarManagerImpl::CreateForWebState(web_state());
+    web_state()->CreateWebFramesManager();
+    auto main_frame = std::make_unique<web::FakeWebFrame>("main", true, GURL());
+    web_state()->AddWebFrame(std::move(main_frame));
     autofill_controller_ =
         [[AutofillController alloc] initWithBrowserState:browser_state()
                                                 webState:web_state()
@@ -99,9 +105,10 @@
   FullCardRequester full_card_requester(base_view_controller, browser_state());
 
   EXPECT_EQ(nil, base_view_controller.presentedViewController);
-
+  web::WebFrame* main_frame = web::GetMainWebFrame(web_state());
   autofill::AutofillManager* autofill_manager =
-      autofill::AutofillDriverIOS::FromWebState(web_state())
+      autofill::AutofillDriverIOS::FromWebStateAndWebFrame(web_state(),
+                                                           main_frame)
           ->autofill_manager();
   FakeResultDelegate* fake_result_delegate = new FakeResultDelegate;
   full_card_requester.GetFullCard(*credit_cards()[0], autofill_manager,
diff --git a/ios/chrome/browser/ui/payments/payment_request_manager.mm b/ios/chrome/browser/ui/payments/payment_request_manager.mm
index e1078b98..758c971 100644
--- a/ios/chrome/browser/ui/payments/payment_request_manager.mm
+++ b/ios/chrome/browser/ui/payments/payment_request_manager.mm
@@ -66,7 +66,10 @@
 #import "ios/web/public/web_state/navigation_context.h"
 #import "ios/web/public/web_state/ui/crw_web_view_proxy.h"
 #include "ios/web/public/web_state/url_verification_constants.h"
-#include "ios/web/public/web_state/web_state.h"
+#include "ios/web/public/web_state/web_frame.h"
+#include "ios/web/public/web_state/web_frame_util.h"
+#import "ios/web/public/web_state/web_frames_manager.h"
+#import "ios/web/public/web_state/web_state.h"
 #import "ios/web/public/web_state/web_state_observer_bridge.h"
 #include "third_party/libaddressinput/chromium/chrome_metadata_source.h"
 #include "third_party/libaddressinput/chromium/chrome_storage_impl.h"
@@ -665,8 +668,11 @@
           _activeWebState->GetLastCommittedURL()));
   BOOL connectionSecure =
       _activeWebState->GetLastCommittedURL().SchemeIs(url::kHttpsScheme);
+  // Payment Request is only enabled in main frame.
+  web::WebFrame* main_frame = web::GetMainWebFrame(_activeWebState);
   autofill::AutofillManager* autofillManager =
-      autofill::AutofillDriverIOS::FromWebState(_activeWebState)
+      autofill::AutofillDriverIOS::FromWebStateAndWebFrame(_activeWebState,
+                                                           main_frame)
           ->autofill_manager();
   _paymentRequestCoordinator = [[PaymentRequestCoordinator alloc]
       initWithBaseViewController:_baseViewController];
diff --git a/ios/chrome/browser/ui/side_swipe/BUILD.gn b/ios/chrome/browser/ui/side_swipe/BUILD.gn
index 860816d..5261ca7 100644
--- a/ios/chrome/browser/ui/side_swipe/BUILD.gn
+++ b/ios/chrome/browser/ui/side_swipe/BUILD.gn
@@ -62,3 +62,25 @@
     "//third_party/ocmock",
   ]
 }
+
+source_set("eg_tests") {
+  configs += [ "//build/config/compiler:enable_arc" ]
+  testonly = true
+  sources = [
+    "side_swipe_egtest.mm",
+  ]
+
+  deps = [
+    ":side_swipe",
+    "//base",
+    "//ios/chrome/browser",
+    "//ios/chrome/browser/ui:ui_util",
+    "//ios/chrome/browser/ui/toolbar/adaptive:adaptive_ui",
+    "//ios/chrome/test/app:test_support",
+    "//ios/chrome/test/earl_grey:test_support",
+    "//ios/testing/earl_grey:earl_grey_support",
+    "//ios/third_party/earl_grey:earl_grey+link",
+    "//ios/web/public/test/http_server",
+  ]
+  libs = [ "XCTest.framework" ]
+}
diff --git a/ios/chrome/browser/ui/side_swipe/card_side_swipe_view.mm b/ios/chrome/browser/ui/side_swipe/card_side_swipe_view.mm
index 493cb9b..16a36ad 100644
--- a/ios/chrome/browser/ui/side_swipe/card_side_swipe_view.mm
+++ b/ios/chrome/browser/ui/side_swipe/card_side_swipe_view.mm
@@ -15,7 +15,6 @@
 #import "ios/chrome/browser/tabs/tab.h"
 #import "ios/chrome/browser/tabs/tab_model.h"
 #import "ios/chrome/browser/ui/background_generator.h"
-#import "ios/chrome/browser/ui/ntp/new_tab_page_panel_protocol.h"
 #include "ios/chrome/browser/ui/rtl_geometry.h"
 #import "ios/chrome/browser/ui/side_swipe/side_swipe_util.h"
 #import "ios/chrome/browser/ui/side_swipe_gesture_recognizer.h"
@@ -124,15 +123,6 @@
     ]];
 
     [NSLayoutConstraint activateConstraints:constraints];
-
-    if (!IsUIRefreshPhase1Enabled()) {
-      [NSLayoutConstraint activateConstraints:@[
-        [[_shadowView topAnchor] constraintEqualToAnchor:self.topAnchor
-                                                constant:topMargin],
-        [[_shadowView heightAnchor]
-            constraintEqualToConstant:kNewTabPageShadowHeight],
-      ]];
-    }
   }
   return self;
 }
diff --git a/ios/chrome/browser/ui/side_swipe/side_swipe_controller.mm b/ios/chrome/browser/ui/side_swipe/side_swipe_controller.mm
index 929464d..55c9e346 100644
--- a/ios/chrome/browser/ui/side_swipe/side_swipe_controller.mm
+++ b/ios/chrome/browser/ui/side_swipe/side_swipe_controller.mm
@@ -242,7 +242,8 @@
 
   // Since the toolbar and the contentView can overlap, check the toolbar frame
   // first, and confirm the right gesture recognizer is firing.
-  if ([self.toolbarInteractionHandler isInsideToolbar:location]) {
+  if ([self.toolbarInteractionHandler
+          isInsideToolbar:[gesture.view convertPoint:location toView:nil]]) {
     if (![gesture isEqual:panGestureRecognizer_]) {
       return NO;
     }
diff --git a/ios/chrome/browser/ui/side_swipe/side_swipe_egtest.mm b/ios/chrome/browser/ui/side_swipe/side_swipe_egtest.mm
new file mode 100644
index 0000000..882140e4
--- /dev/null
+++ b/ios/chrome/browser/ui/side_swipe/side_swipe_egtest.mm
@@ -0,0 +1,73 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import <EarlGrey/EarlGrey.h>
+#import <XCTest/XCTest.h>
+
+#import "ios/chrome/browser/ui/toolbar/adaptive/primary_toolbar_view.h"
+#import "ios/chrome/browser/ui/toolbar/adaptive/secondary_toolbar_view.h"
+#import "ios/chrome/browser/ui/uikit_ui_util.h"
+#import "ios/chrome/test/earl_grey/chrome_earl_grey.h"
+#import "ios/chrome/test/earl_grey/chrome_earl_grey_ui.h"
+#import "ios/chrome/test/earl_grey/chrome_matchers.h"
+#import "ios/chrome/test/earl_grey/chrome_test_case.h"
+#include "ios/testing/earl_grey/disabled_test_macros.h"
+#include "net/test/embedded_test_server/default_handlers.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+// Integration tests for side swipe.
+@interface SideSwipeTestCase : ChromeTestCase
+@end
+
+@implementation SideSwipeTestCase
+
+#pragma mark - Tests
+
+// Tests that swiping horizontally on the bottom toolbar is changing tab.
+- (void)testSideSwipeBottomToolbar {
+  if (!IsSplitToolbarMode()) {
+    EARL_GREY_TEST_SKIPPED(
+        @"This tests should only be tested if the secondary toolbar is "
+        @"present");
+  }
+  [self checkSideSwipeOnToolbarClass:[SecondaryToolbarView class]];
+}
+
+// Tests that swiping horizontally on the top toolbar is changing tab.
+- (void)testSideSwipeTopToolbar {
+  [self checkSideSwipeOnToolbarClass:[PrimaryToolbarView class]];
+}
+
+#pragma mark - Helpers
+
+// Checks that side swipe on an element of class |klass| is working to change
+// tab.
+- (void)checkSideSwipeOnToolbarClass:(Class)klass {
+  // Setup the server.
+  net::test_server::RegisterDefaultHandlers(self.testServer);
+  GREYAssertTrue(self.testServer->Start(), @"Test server failed to start.");
+
+  // Load the first page.
+  [ChromeEarlGrey loadURL:self.testServer->GetURL("/echo")];
+  [ChromeEarlGrey waitForWebViewContainingText:"Echo"];
+
+  // Open a new Tab to have a tab to switch to.
+  [ChromeEarlGreyUI openNewTab];
+
+  // Load the second page in the new tab.
+  [ChromeEarlGrey loadURL:self.testServer->GetURL("/defaultresponse")];
+  [ChromeEarlGrey waitForWebViewContainingText:"Default response"];
+
+  // Side swipe on the toolbar.
+  [[EarlGrey selectElementWithMatcher:grey_kindOfClass(klass)]
+      performAction:grey_swipeSlowInDirection(kGREYDirectionRight)];
+
+  // Check that we swiped back to our web page.
+  [ChromeEarlGrey waitForWebViewContainingText:"Echo"];
+}
+
+@end
diff --git a/ios/chrome/browser/ui/stack_view/BUILD.gn b/ios/chrome/browser/ui/stack_view/BUILD.gn
index 03a5617..1adf728 100644
--- a/ios/chrome/browser/ui/stack_view/BUILD.gn
+++ b/ios/chrome/browser/ui/stack_view/BUILD.gn
@@ -58,9 +58,9 @@
     "//ios/chrome/browser/ui/image_util",
     "//ios/chrome/browser/ui/keyboard",
     "//ios/chrome/browser/ui/main:feature_flags",
+    "//ios/chrome/browser/ui/main:tab_switcher",
     "//ios/chrome/browser/ui/ntp",
     "//ios/chrome/browser/ui/ntp:ntp_internal",
-    "//ios/chrome/browser/ui/tab_switcher",
     "//ios/chrome/browser/ui/toolbar",
     "//ios/chrome/browser/ui/toolbar:toolbar_ui",
     "//ios/chrome/browser/ui/toolbar/buttons",
@@ -129,7 +129,7 @@
     "//ios/chrome/browser/browser_state",
     "//ios/chrome/browser/tabs",
     "//ios/chrome/browser/ui:ui_internal",
-    "//ios/chrome/browser/ui/tab_switcher:modes",
+    "//ios/chrome/browser/ui/main:tab_switcher",
     "//ios/chrome/browser/ui/toolbar/buttons",
     "//ios/chrome/browser/ui/toolbar/legacy",
     "//ios/chrome/browser/ui/toolbar/public",
diff --git a/ios/chrome/browser/ui/stack_view/stack_view_controller.h b/ios/chrome/browser/ui/stack_view/stack_view_controller.h
index 934bb2f..bfceb63 100644
--- a/ios/chrome/browser/ui/stack_view/stack_view_controller.h
+++ b/ios/chrome/browser/ui/stack_view/stack_view_controller.h
@@ -7,7 +7,7 @@
 
 #import <UIKit/UIKit.h>
 
-#import "ios/chrome/browser/ui/tab_switcher/tab_switcher.h"
+#import "ios/chrome/browser/ui/main/tab_switcher.h"
 #include "ui/base/page_transition_types.h"
 
 @protocol ApplicationCommands;
diff --git a/ios/chrome/browser/ui/stack_view/stack_view_controller.mm b/ios/chrome/browser/ui/stack_view/stack_view_controller.mm
index e8146e4..d41a4121 100644
--- a/ios/chrome/browser/ui/stack_view/stack_view_controller.mm
+++ b/ios/chrome/browser/ui/stack_view/stack_view_controller.mm
@@ -38,7 +38,6 @@
 #import "ios/chrome/browser/ui/commands/open_new_tab_command.h"
 #import "ios/chrome/browser/ui/keyboard/UIKeyCommand+Chrome.h"
 #include "ios/chrome/browser/ui/main/main_feature_flags.h"
-#import "ios/chrome/browser/ui/ntp/new_tab_page_toolbar_controller.h"
 #import "ios/chrome/browser/ui/reversed_animation.h"
 #import "ios/chrome/browser/ui/rtl_geometry.h"
 #import "ios/chrome/browser/ui/stack_view/card_stack_layout_manager.h"
diff --git a/ios/chrome/browser/ui/stack_view/stack_view_egtest.mm b/ios/chrome/browser/ui/stack_view/stack_view_egtest.mm
index e459ead3..1bcfb189 100644
--- a/ios/chrome/browser/ui/stack_view/stack_view_egtest.mm
+++ b/ios/chrome/browser/ui/stack_view/stack_view_egtest.mm
@@ -13,10 +13,10 @@
 #import "ios/chrome/browser/tabs/tab.h"
 #import "ios/chrome/browser/tabs/tab_model.h"
 #import "ios/chrome/browser/ui/browser_view_controller.h"
+#include "ios/chrome/browser/ui/main/tab_switcher_mode.h"
 #import "ios/chrome/browser/ui/stack_view/card_view.h"
 #import "ios/chrome/browser/ui/stack_view/stack_view_controller.h"
 #import "ios/chrome/browser/ui/stack_view/stack_view_controller_private.h"
-#include "ios/chrome/browser/ui/tab_switcher/tab_switcher_mode.h"
 #import "ios/chrome/browser/ui/toolbar/buttons/toolbar_constants.h"
 #import "ios/chrome/browser/ui/toolbar/legacy/toolbar_controller_constants.h"
 #include "ios/chrome/browser/ui/tools_menu/public/tools_menu_constants.h"
diff --git a/ios/chrome/browser/ui/tab_grid/BUILD.gn b/ios/chrome/browser/ui/tab_grid/BUILD.gn
index cf6d177..830f696 100644
--- a/ios/chrome/browser/ui/tab_grid/BUILD.gn
+++ b/ios/chrome/browser/ui/tab_grid/BUILD.gn
@@ -23,6 +23,7 @@
     "grid:grid_ui",
     "//base",
     "//components/favicon/ios",
+    "//components/sessions",
     "//ios/chrome/browser",
     "//ios/chrome/browser/browser_state",
     "//ios/chrome/browser/sessions",
@@ -35,9 +36,10 @@
     "//ios/chrome/browser/ui/history",
     "//ios/chrome/browser/ui/history/public",
     "//ios/chrome/browser/ui/main",
+    "//ios/chrome/browser/ui/main:tab_switcher",
+    "//ios/chrome/browser/ui/ntp/recent_tabs",
     "//ios/chrome/browser/ui/recent_tabs",
     "//ios/chrome/browser/ui/recent_tabs:recent_tabs_ui",
-    "//ios/chrome/browser/ui/tab_switcher",
     "//ios/chrome/browser/web",
     "//ios/chrome/browser/web_state_list",
     "//ios/chrome/browser/web_state_list/web_usage_enabler",
@@ -106,7 +108,7 @@
     "//ios/chrome/browser",
     "//ios/chrome/browser/browser_state:test_support",
     "//ios/chrome/browser/tabs",
-    "//ios/chrome/browser/ui/tab_switcher",
+    "//ios/chrome/browser/ui/main:tab_switcher",
     "//ios/chrome/browser/web",
     "//ios/chrome/browser/web:test_support",
     "//ios/chrome/browser/web_state_list",
diff --git a/ios/chrome/browser/ui/tab_grid/tab_grid_adaptor.h b/ios/chrome/browser/ui/tab_grid/tab_grid_adaptor.h
index d73966b..0d447257 100644
--- a/ios/chrome/browser/ui/tab_grid/tab_grid_adaptor.h
+++ b/ios/chrome/browser/ui/tab_grid/tab_grid_adaptor.h
@@ -7,8 +7,8 @@
 
 #import <Foundation/Foundation.h>
 
+#import "ios/chrome/browser/ui/main/tab_switcher.h"
 #import "ios/chrome/browser/ui/tab_grid/tab_grid_mediator.h"
-#import "ios/chrome/browser/ui/tab_switcher/tab_switcher.h"
 
 @protocol TabGridPaging;
 @class TabGridURLLoader;
diff --git a/ios/chrome/browser/ui/tab_grid/tab_grid_coordinator_unittest.mm b/ios/chrome/browser/ui/tab_grid/tab_grid_coordinator_unittest.mm
index 845b86ef..5c89622 100644
--- a/ios/chrome/browser/ui/tab_grid/tab_grid_coordinator_unittest.mm
+++ b/ios/chrome/browser/ui/tab_grid/tab_grid_coordinator_unittest.mm
@@ -7,7 +7,7 @@
 #import <UIKit/UIKit.h>
 
 #import "base/test/ios/wait_util.h"
-#import "ios/chrome/browser/ui/tab_switcher/tab_switcher.h"
+#import "ios/chrome/browser/ui/main/tab_switcher.h"
 #import "ios/chrome/test/block_cleanup_test.h"
 #include "testing/gtest_mac.h"
 #include "third_party/ocmock/OCMock/OCMock.h"
diff --git a/ios/chrome/browser/ui/tab_grid/tab_grid_egtests_hook.mm b/ios/chrome/browser/ui/tab_grid/tab_grid_egtests_hook.mm
index b170a3c..6b1ee7ac 100644
--- a/ios/chrome/browser/ui/tab_grid/tab_grid_egtests_hook.mm
+++ b/ios/chrome/browser/ui/tab_grid/tab_grid_egtests_hook.mm
@@ -42,6 +42,11 @@
   return true;
 }
 
+// TODO(crbug.com/885003) : Remove this hook.
+bool ForceWKWebViewSnapshots() {
+  return false;
+}
+
 void SetUpTestsIfPresent() {
   // No-op for Earl Grey.
 }
diff --git a/ios/chrome/browser/ui/tab_switcher/BUILD.gn b/ios/chrome/browser/ui/tab_switcher/BUILD.gn
index 4c6b2255..a5ed97a 100644
--- a/ios/chrome/browser/ui/tab_switcher/BUILD.gn
+++ b/ios/chrome/browser/ui/tab_switcher/BUILD.gn
@@ -7,7 +7,6 @@
   sources = [
     "tab_model_snapshot.h",
     "tab_model_snapshot.mm",
-    "tab_switcher.h",
     "tab_switcher_button.h",
     "tab_switcher_button.mm",
     "tab_switcher_cache.h",
@@ -78,12 +77,14 @@
     "//ios/chrome/browser/sync",
     "//ios/chrome/browser/tabs",
     "//ios/chrome/browser/ui",
+    "//ios/chrome/browser/ui:ui_internal",
     "//ios/chrome/browser/ui/authentication",
     "//ios/chrome/browser/ui/authentication:authentication_ui",
     "//ios/chrome/browser/ui/colors",
     "//ios/chrome/browser/ui/commands",
     "//ios/chrome/browser/ui/image_util",
     "//ios/chrome/browser/ui/keyboard",
+    "//ios/chrome/browser/ui/main:tab_switcher",
     "//ios/chrome/browser/ui/material_components",
     "//ios/chrome/browser/ui/ntp/recent_tabs/views",
     "//ios/chrome/browser/ui/settings/sync_utils",
@@ -112,18 +113,6 @@
   ]
 }
 
-source_set("modes") {
-  configs += [ "//build/config/compiler:enable_arc" ]
-  sources = [
-    "tab_switcher_mode.h",
-    "tab_switcher_mode.mm",
-  ]
-  deps = [
-    "//base",
-    "//ios/chrome/browser/ui",
-  ]
-}
-
 source_set("utils") {
   configs += [ "//build/config/compiler:enable_arc" ]
   sources = [
@@ -199,7 +188,6 @@
   ]
   deps = [
     ":egtest_support",
-    ":modes",
     ":tab_switcher",
     "//base",
     "//base/test:test_support",
@@ -209,6 +197,7 @@
     "//ios/chrome/browser/tabs",
     "//ios/chrome/browser/ui",
     "//ios/chrome/browser/ui/authentication:eg_test_support",
+    "//ios/chrome/browser/ui/main:tab_switcher",
     "//ios/chrome/browser/ui/tab_grid:egtest_support",
     "//ios/chrome/test/app:test_support",
     "//ios/chrome/test/earl_grey:test_support",
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_switcher_controller.h b/ios/chrome/browser/ui/tab_switcher/tab_switcher_controller.h
index 307c207..14e4fbf7 100644
--- a/ios/chrome/browser/ui/tab_switcher/tab_switcher_controller.h
+++ b/ios/chrome/browser/ui/tab_switcher/tab_switcher_controller.h
@@ -7,7 +7,7 @@
 
 #import <UIKit/UIKit.h>
 
-#import "ios/chrome/browser/ui/tab_switcher/tab_switcher.h"
+#import "ios/chrome/browser/ui/main/tab_switcher.h"
 
 @protocol ApplicationCommands;
 @protocol BrowserCommands;
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_switcher_controller_egtest.mm b/ios/chrome/browser/ui/tab_switcher/tab_switcher_controller_egtest.mm
index 51d9900..670c3fe 100644
--- a/ios/chrome/browser/ui/tab_switcher/tab_switcher_controller_egtest.mm
+++ b/ios/chrome/browser/ui/tab_switcher/tab_switcher_controller_egtest.mm
@@ -11,8 +11,8 @@
 #include "ios/chrome/browser/chrome_switches.h"
 #import "ios/chrome/browser/ui/authentication/signin_earl_grey_ui.h"
 #import "ios/chrome/browser/ui/authentication/signin_earlgrey_utils.h"
+#include "ios/chrome/browser/ui/main/tab_switcher_mode.h"
 #import "ios/chrome/browser/ui/tab_switcher/tab_switcher_egtest_util.h"
-#include "ios/chrome/browser/ui/tab_switcher/tab_switcher_mode.h"
 #import "ios/chrome/browser/ui/tab_switcher/tab_switcher_panel_cell.h"
 #import "ios/chrome/browser/ui/ui_util.h"
 #include "ios/chrome/grit/ios_strings.h"
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_switcher_transition_egtest.mm b/ios/chrome/browser/ui/tab_switcher/tab_switcher_transition_egtest.mm
index 957173b..2b6fa15 100644
--- a/ios/chrome/browser/ui/tab_switcher/tab_switcher_transition_egtest.mm
+++ b/ios/chrome/browser/ui/tab_switcher/tab_switcher_transition_egtest.mm
@@ -11,9 +11,9 @@
 #import "base/test/ios/wait_util.h"
 #import "ios/chrome/app/main_controller.h"
 #import "ios/chrome/browser/tabs/tab_model.h"
+#import "ios/chrome/browser/ui/main/tab_switcher_mode.h"
 #import "ios/chrome/browser/ui/tab_grid/tab_grid_egtest_util.h"
 #import "ios/chrome/browser/ui/tab_switcher/tab_switcher_egtest_util.h"
-#import "ios/chrome/browser/ui/tab_switcher/tab_switcher_mode.h"
 #import "ios/chrome/browser/ui/tab_switcher/tab_switcher_panel_cell.h"
 #include "ios/chrome/grit/ios_strings.h"
 #import "ios/chrome/test/app/chrome_test_util.h"
diff --git a/ios/chrome/browser/ui/toolbar/adaptive/adaptive_toolbar_coordinator.mm b/ios/chrome/browser/ui/toolbar/adaptive/adaptive_toolbar_coordinator.mm
index c37ab64425..d37eb641 100644
--- a/ios/chrome/browser/ui/toolbar/adaptive/adaptive_toolbar_coordinator.mm
+++ b/ios/chrome/browser/ui/toolbar/adaptive/adaptive_toolbar_coordinator.mm
@@ -101,10 +101,6 @@
 
 #pragma mark - NewTabPageControllerDelegate
 
-- (void)setToolbarBackgroundToIncognitoNTPColorWithAlpha:(CGFloat)alpha {
-  // No-op, not needed in UI refresh.
-}
-
 - (void)setScrollProgressForTabletOmnibox:(CGFloat)progress {
   [self.viewController setScrollProgressForTabletOmnibox:progress];
 }
diff --git a/ios/chrome/browser/ui/toolbar/adaptive/adaptive_toolbar_eg_tests_hook.mm b/ios/chrome/browser/ui/toolbar/adaptive/adaptive_toolbar_eg_tests_hook.mm
index b170a3c..6b1ee7ac 100644
--- a/ios/chrome/browser/ui/toolbar/adaptive/adaptive_toolbar_eg_tests_hook.mm
+++ b/ios/chrome/browser/ui/toolbar/adaptive/adaptive_toolbar_eg_tests_hook.mm
@@ -42,6 +42,11 @@
   return true;
 }
 
+// TODO(crbug.com/885003) : Remove this hook.
+bool ForceWKWebViewSnapshots() {
+  return false;
+}
+
 void SetUpTestsIfPresent() {
   // No-op for Earl Grey.
 }
diff --git a/ios/chrome/browser/ui/toolbar/adaptive/adaptive_toolbar_view_controller.mm b/ios/chrome/browser/ui/toolbar/adaptive/adaptive_toolbar_view_controller.mm
index a9cd37f5..fde08322 100644
--- a/ios/chrome/browser/ui/toolbar/adaptive/adaptive_toolbar_view_controller.mm
+++ b/ios/chrome/browser/ui/toolbar/adaptive/adaptive_toolbar_view_controller.mm
@@ -224,10 +224,6 @@
 
 #pragma mark - NewTabPageControllerDelegate
 
-- (void)setToolbarBackgroundToIncognitoNTPColorWithAlpha:(CGFloat)alpha {
-  // No-op, not needed in UI refresh.
-}
-
 - (void)setScrollProgressForTabletOmnibox:(CGFloat)progress {
   // No-op, should be handled by the primary toolbar.
 }
diff --git a/ios/chrome/browser/ui/toolbar/adaptive/toolbar_coordinator_adaptor.mm b/ios/chrome/browser/ui/toolbar/adaptive/toolbar_coordinator_adaptor.mm
index ab6fd81b..823aaa8 100644
--- a/ios/chrome/browser/ui/toolbar/adaptive/toolbar_coordinator_adaptor.mm
+++ b/ios/chrome/browser/ui/toolbar/adaptive/toolbar_coordinator_adaptor.mm
@@ -41,10 +41,6 @@
 
 #pragma mark - NewTabPageControllerDelegate
 
-- (void)setToolbarBackgroundToIncognitoNTPColorWithAlpha:(CGFloat)alpha {
-  // No-op, not needed in UI refresh.
-}
-
 - (void)setScrollProgressForTabletOmnibox:(CGFloat)progress {
   for (id<NewTabPageControllerDelegate> coordinator in self.coordinators) {
     [coordinator setScrollProgressForTabletOmnibox:progress];
@@ -68,7 +64,9 @@
     // swipes from the right side.
     CGRect toolbarFrame =
         CGRectInset([coordinator viewController].view.frame, -1, -1);
-    if (CGRectContainsPoint(toolbarFrame, point))
+    CGRect frameInWindowCoordinates =
+        [[coordinator viewController].view convertRect:toolbarFrame toView:nil];
+    if (CGRectContainsPoint(frameInWindowCoordinates, point))
       return YES;
   }
   return NO;
diff --git a/ios/chrome/browser/ui/toolbar/clean/toolbar_coordinator.mm b/ios/chrome/browser/ui/toolbar/clean/toolbar_coordinator.mm
index c2eb6be..3edccb9 100644
--- a/ios/chrome/browser/ui/toolbar/clean/toolbar_coordinator.mm
+++ b/ios/chrome/browser/ui/toolbar/clean/toolbar_coordinator.mm
@@ -228,7 +228,6 @@
 
   [self.commandDispatcher stopDispatchingToTarget:self];
 
-  [self setToolbarBackgroundToIncognitoNTPColorWithAlpha:0];
   self.started = NO;
   self.delegate = nil;
   [self.mediator disconnect];
@@ -319,10 +318,6 @@
 
 #pragma mark - NewTabPageControllerDelegate
 
-- (void)setToolbarBackgroundToIncognitoNTPColorWithAlpha:(CGFloat)alpha {
-  [self.toolbarViewController setBackgroundToIncognitoNTPColorWithAlpha:alpha];
-}
-
 - (void)setScrollProgressForTabletOmnibox:(CGFloat)progress {
   NOTREACHED();
 }
diff --git a/ios/chrome/browser/ui/toolbar/public/side_swipe_toolbar_interacting.h b/ios/chrome/browser/ui/toolbar/public/side_swipe_toolbar_interacting.h
index 05d9287..354f5561 100644
--- a/ios/chrome/browser/ui/toolbar/public/side_swipe_toolbar_interacting.h
+++ b/ios/chrome/browser/ui/toolbar/public/side_swipe_toolbar_interacting.h
@@ -10,7 +10,8 @@
 // Protocol used by SideSwipe to interact with the toolbar.
 @protocol SideSwipeToolbarInteracting
 
-// Returns whether the |point| is inside a toolbar's frame.
+// Returns whether the |point| is inside a toolbar's frame. The |point| must be
+// in the window coordinates.
 - (BOOL)isInsideToolbar:(CGPoint)point;
 
 @end
diff --git a/ios/chrome/browser/ui/ui_feature_flags.cc b/ios/chrome/browser/ui/ui_feature_flags.cc
index d2fe578..20bfa86 100644
--- a/ios/chrome/browser/ui/ui_feature_flags.cc
+++ b/ios/chrome/browser/ui/ui_feature_flags.cc
@@ -8,3 +8,6 @@
                                              base::FEATURE_ENABLED_BY_DEFAULT};
 
 const base::Feature kCopyImage{"CopyImage", base::FEATURE_ENABLED_BY_DEFAULT};
+
+const base::Feature kWKWebViewSnapshots{"WKWebViewSnapshots",
+                                        base::FEATURE_DISABLED_BY_DEFAULT};
diff --git a/ios/chrome/browser/ui/ui_feature_flags.h b/ios/chrome/browser/ui/ui_feature_flags.h
index 4025df3..9fe790d 100644
--- a/ios/chrome/browser/ui/ui_feature_flags.h
+++ b/ios/chrome/browser/ui/ui_feature_flags.h
@@ -15,4 +15,7 @@
 // Feature to copy image to system pasteboard via context menu.
 extern const base::Feature kCopyImage;
 
+// Used to enable using the WKWebView snapshotting API for iOS 11+.
+extern const base::Feature kWKWebViewSnapshots;
+
 #endif  // IOS_CHROME_BROWSER_UI_UI_FEATURE_FLAGS_H_
diff --git a/ios/chrome/browser/ui/ui_util.h b/ios/chrome/browser/ui/ui_util.h
index c53aea15..e64fbfec8 100644
--- a/ios/chrome/browser/ui/ui_util.h
+++ b/ios/chrome/browser/ui/ui_util.h
@@ -55,6 +55,9 @@
 // TODO (crbug.com/884725): Remove all use of this flag.
 bool IsUIRefreshPhase1Enabled();
 
+// Returns whether the WKWebView snapshotting API will be used for iOS 11+.
+bool IsWKWebViewSnapshotsEnabled();
+
 // Returns the height of the status bar, accounting for orientation.
 CGFloat StatusBarHeight();
 
diff --git a/ios/chrome/browser/ui/ui_util.mm b/ios/chrome/browser/ui/ui_util.mm
index dd20044e..8800d26 100644
--- a/ios/chrome/browser/ui/ui_util.mm
+++ b/ios/chrome/browser/ui/ui_util.mm
@@ -74,6 +74,13 @@
   return true;
 }
 
+// TODO(crbug.com/885003) : Remove this flag.
+bool IsWKWebViewSnapshotsEnabled() {
+  if (tests_hook::ForceWKWebViewSnapshots())
+    return true;
+  return base::FeatureList::IsEnabled(kWKWebViewSnapshots);
+}
+
 CGFloat StatusBarHeight() {
   // This is a temporary solution until usage of StatusBarHeight has been
   // replaced with topLayoutGuide.
diff --git a/ios/chrome/test/app/BUILD.gn b/ios/chrome/test/app/BUILD.gn
index ba2a190..f7a80d614 100644
--- a/ios/chrome/test/app/BUILD.gn
+++ b/ios/chrome/test/app/BUILD.gn
@@ -71,12 +71,12 @@
     "//ios/chrome/browser/ui/authentication:authentication_ui",
     "//ios/chrome/browser/ui/commands",
     "//ios/chrome/browser/ui/main",
+    "//ios/chrome/browser/ui/main:tab_switcher",
     "//ios/chrome/browser/ui/ntp:ntp_controller",
     "//ios/chrome/browser/ui/settings",
     "//ios/chrome/browser/ui/settings:test_support",
     "//ios/chrome/browser/ui/stack_view",
     "//ios/chrome/browser/ui/static_content",
-    "//ios/chrome/browser/ui/tab_switcher",
     "//ios/chrome/browser/ui/tabs",
     "//ios/chrome/browser/ui/util",
     "//ios/chrome/browser/web_state_list",
diff --git a/ios/chrome/test/app/chrome_test_util.mm b/ios/chrome/test/app/chrome_test_util.mm
index 7fecf28..1ec3df99 100644
--- a/ios/chrome/test/app/chrome_test_util.mm
+++ b/ios/chrome/test/app/chrome_test_util.mm
@@ -23,9 +23,9 @@
 #import "ios/chrome/browser/tabs/tab.h"
 #import "ios/chrome/browser/ui/browser_view_controller.h"
 #import "ios/chrome/browser/ui/main/bvc_container_view_controller.h"
+#import "ios/chrome/browser/ui/main/tab_switcher.h"
 #import "ios/chrome/browser/ui/main/view_controller_swapping.h"
 #import "ios/chrome/browser/ui/ntp/new_tab_page_controller.h"
-#import "ios/chrome/browser/ui/tab_switcher/tab_switcher.h"
 #include "ios/chrome/browser/ui/ui_util.h"
 #include "ios/chrome/test/app/navigation_test_util.h"
 #import "ios/chrome/test/app/tab_test_util.h"
diff --git a/ios/chrome/test/app/tab_test_util.mm b/ios/chrome/test/app/tab_test_util.mm
index dc015c1..d02b755 100644
--- a/ios/chrome/test/app/tab_test_util.mm
+++ b/ios/chrome/test/app/tab_test_util.mm
@@ -17,7 +17,7 @@
 #import "ios/chrome/browser/ui/browser_view_controller.h"
 #import "ios/chrome/browser/ui/commands/browser_commands.h"
 #import "ios/chrome/browser/ui/commands/open_new_tab_command.h"
-#import "ios/chrome/browser/ui/tab_switcher/tab_switcher.h"
+#import "ios/chrome/browser/ui/main/tab_switcher.h"
 #include "ios/chrome/browser/ui/ui_util.h"
 #import "ios/chrome/browser/web_state_list/web_state_list.h"
 #import "ios/chrome/browser/web_state_list/web_usage_enabler/web_state_list_web_usage_enabler.h"
diff --git a/ios/chrome/test/earl_grey/BUILD.gn b/ios/chrome/test/earl_grey/BUILD.gn
index cdab9df..b146db6a 100644
--- a/ios/chrome/test/earl_grey/BUILD.gn
+++ b/ios/chrome/test/earl_grey/BUILD.gn
@@ -123,6 +123,7 @@
     "//ios/chrome/browser/ui/sad_tab:eg_tests",
     "//ios/chrome/browser/ui/safe_mode:eg_tests",
     "//ios/chrome/browser/ui/settings/sync_utils:eg_tests",
+    "//ios/chrome/browser/ui/side_swipe:eg_tests",
     "//ios/chrome/browser/ui/signin_interaction:eg_tests",
     "//ios/chrome/browser/ui/stack_view:eg_tests",
     "//ios/chrome/browser/ui/tab_switcher:eg_tests",
diff --git a/ios/chrome/test/earl_grey/eg_tests_hook.mm b/ios/chrome/test/earl_grey/eg_tests_hook.mm
index 4e63fe21..b7cd975 100644
--- a/ios/chrome/test/earl_grey/eg_tests_hook.mm
+++ b/ios/chrome/test/earl_grey/eg_tests_hook.mm
@@ -42,6 +42,11 @@
   return false;
 }
 
+// TODO(crbug.com/885003) : Remove this hook.
+bool ForceWKWebViewSnapshots() {
+  return false;
+}
+
 void SetUpTestsIfPresent() {
   // No-op for Earl Grey.
 }
diff --git a/ios/web/BUILD.gn b/ios/web/BUILD.gn
index 1367996..e4ecd823 100644
--- a/ios/web/BUILD.gn
+++ b/ios/web/BUILD.gn
@@ -405,6 +405,7 @@
     "web_state/session_certificate_policy_cache_impl_unittest.mm",
     "web_state/session_certificate_policy_cache_storage_builder_unittest.mm",
     "web_state/web_frame_impl_unittest.mm",
+    "web_state/web_frame_util_unittest.mm",
     "web_state/web_frames_manager_impl_unittest.mm",
     "web_state/web_state_delegate_bridge_unittest.mm",
     "web_state/web_state_impl_unittest.mm",
diff --git a/ios/web/public/BUILD.gn b/ios/web/public/BUILD.gn
index c279243..22c482a0 100644
--- a/ios/web/public/BUILD.gn
+++ b/ios/web/public/BUILD.gn
@@ -82,6 +82,7 @@
     "web_state/url_verification_constants.h",
     "web_state/web_frame.h",
     "web_state/web_frame_user_data.h",
+    "web_state/web_frame_util.h",
     "web_state/web_frames_manager.h",
     "web_state/web_state.h",
     "web_state/web_state_delegate.h",
diff --git a/ios/web/public/test/fakes/test_web_state.h b/ios/web/public/test/fakes/test_web_state.h
index 0126a56..4a0a335 100644
--- a/ios/web/public/test/fakes/test_web_state.h
+++ b/ios/web/public/test/fakes/test_web_state.h
@@ -104,6 +104,7 @@
   void SetIsEvicted(bool value);
   void SetWebViewProxy(CRWWebViewProxyType web_view_proxy);
   void ClearLastExecutedJavascript();
+  void CreateWebFramesManager();
   void AddWebFrame(std::unique_ptr<web::WebFrame> frame);
   void RemoveWebFrame(std::string frame_id);
 
diff --git a/ios/web/public/test/fakes/test_web_state.mm b/ios/web/public/test/fakes/test_web_state.mm
index e7efd81..9a38a14 100644
--- a/ios/web/public/test/fakes/test_web_state.mm
+++ b/ios/web/public/test/fakes/test_web_state.mm
@@ -334,6 +334,11 @@
   last_executed_javascript_.clear();
 }
 
+void TestWebState::CreateWebFramesManager() {
+  DCHECK(!web::WebFramesManagerImpl::FromWebState(this));
+  web::WebFramesManagerImpl::CreateForWebState(this);
+}
+
 void TestWebState::AddWebFrame(std::unique_ptr<web::WebFrame> frame) {
   DCHECK(frame);
   web::WebFramesManagerImpl* manager =
diff --git a/ios/web/public/web_state/web_frame_util.h b/ios/web/public/web_state/web_frame_util.h
new file mode 100644
index 0000000..d6db4b12
--- /dev/null
+++ b/ios/web/public/web_state/web_frame_util.h
@@ -0,0 +1,31 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_WEB_PUBLIC_WEB_STATE_WEB_FRAME_UTIL_H_
+#define IOS_WEB_PUBLIC_WEB_STATE_WEB_FRAME_UTIL_H_
+
+#include <string>
+
+namespace web {
+class WebFrame;
+class WebState;
+
+// Returns the main WebFrame in |web_state|.
+// Returns nullptr if |web_state| does not have a main frame.
+WebFrame* GetMainWebFrame(WebState* web_state);
+
+// Returns the ID of the main WebFrame in |web_state|.
+// Returns "" if |web_state| does not have a main frame.
+std::string GetMainWebFrameId(WebState* web_state);
+
+// Returns the frame with ID |frame_id| in |web_state|.
+// Returns nullptr if |web_state| does not have a frame with this ID.
+WebFrame* GetWebFrameWithId(WebState* web_state, const std::string& frame_id);
+
+// Returns the ID of |frame|. Returns std::string() if |frame| is nullptr.
+std::string GetWebFrameId(WebFrame* frame);
+
+}  // namespace web
+
+#endif  // IOS_WEB_PUBLIC_WEB_STATE_WEB_FRAME_UTIL_H_
diff --git a/ios/web/web_state/BUILD.gn b/ios/web/web_state/BUILD.gn
index 7f8ee022..3bd3c67 100644
--- a/ios/web/web_state/BUILD.gn
+++ b/ios/web/web_state/BUILD.gn
@@ -175,6 +175,7 @@
   sources = [
     "web_frame_impl.h",
     "web_frame_impl.mm",
+    "web_frame_util.mm",
     "web_frames_manager_impl.h",
     "web_frames_manager_impl.mm",
   ]
diff --git a/ios/web/web_state/ui/crw_web_controller.mm b/ios/web/web_state/ui/crw_web_controller.mm
index ee5406e..b4c94d08 100644
--- a/ios/web/web_state/ui/crw_web_controller.mm
+++ b/ios/web/web_state/ui/crw_web_controller.mm
@@ -78,7 +78,8 @@
 #import "ios/web/public/web_state/ui/crw_web_view_content_view.h"
 #import "ios/web/public/web_state/ui/crw_web_view_scroll_view_proxy.h"
 #include "ios/web/public/web_state/url_verification_constants.h"
-#import "ios/web/public/web_state/web_frame.h"
+#include "ios/web/public/web_state/web_frame.h"
+#include "ios/web/public/web_state/web_frame_util.h"
 #import "ios/web/public/web_state/web_state.h"
 #include "ios/web/public/web_state/web_state_interface_provider.h"
 #import "ios/web/public/web_state/web_state_policy_decider.h"
@@ -2440,9 +2441,7 @@
   web::WebFrame* senderFrame = nullptr;
   std::string frameID;
   if (message->GetString("crwFrameId", &frameID)) {
-    web::WebFramesManagerImpl* framesManager =
-        web::WebFramesManagerImpl::FromWebState([self webState]);
-    senderFrame = framesManager->GetFrameWithId(frameID);
+    senderFrame = web::GetWebFrameWithId([self webState], frameID);
   }
 
   if (base::FeatureList::IsEnabled(web::features::kWebFrameMessaging)) {
@@ -2500,10 +2499,9 @@
     return;
   }
 
+  std::string frameID = base::SysNSStringToUTF8(message.body[@"crwFrameId"]);
   web::WebFramesManagerImpl* framesManager =
       web::WebFramesManagerImpl::FromWebState([self webState]);
-
-  std::string frameID = base::SysNSStringToUTF8(message.body[@"crwFrameId"]);
   if (!framesManager->GetFrameWithId(frameID)) {
     GURL messageFrameOrigin =
         web::GURLOriginWithWKSecurityOrigin(message.frameInfo.securityOrigin);
diff --git a/ios/web/web_state/web_frame_impl_inttest.mm b/ios/web/web_state/web_frame_impl_inttest.mm
index e0a84b9..04d7341e 100644
--- a/ios/web/web_state/web_frame_impl_inttest.mm
+++ b/ios/web/web_state/web_frame_impl_inttest.mm
@@ -10,6 +10,7 @@
 #import "ios/web/public/test/js_test_util.h"
 #import "ios/web/public/test/web_js_test.h"
 #import "ios/web/public/test/web_test_with_web_state.h"
+#include "ios/web/public/web_state/web_frame_util.h"
 #include "ios/web/web_state/web_frames_manager_impl.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -21,19 +22,6 @@
 using base::test::ios::WaitUntilConditionOrTimeout;
 
 namespace {
-// Waits for the WebFrame respresenting the main frame in the given
-// |web_state|. Returns nullptr if a timeout of kWaitForJSCompletionTimeout
-// occurs before the WebFrame exists.
-web::WebFrame* GetMainWebFrameForWebState(web::WebState* web_state) {
-  __block web::WebFramesManagerImpl* manager =
-      web::WebFramesManagerImpl::FromWebState(web_state);
-  __unused bool success =
-      WaitUntilConditionOrTimeout(kWaitForJSCompletionTimeout, ^bool {
-        return manager->GetMainWebFrame();
-      });
-  return manager->GetMainWebFrame();
-}
-
 // Returns the first WebFrame found which is not the main frame in the given
 // |web_state|. Does not wait and returns null if such a frame is not found.
 web::WebFrame* GetChildWebFrameForWebState(web::WebState* web_state) {
@@ -53,14 +41,14 @@
 namespace web {
 
 // Test fixture to test WebFrameImpl with a real JavaScript context.
-typedef web::WebTestWithWebState WebFrameImplIntTest;
+typedef WebTestWithWebState WebFrameImplIntTest;
 
 // Tests that the expected result is received from executing a JavaScript
 // function via |CallJavaScriptFunction| on the main frame.
 TEST_F(WebFrameImplIntTest, CallJavaScriptFunctionOnMainFrame) {
   ASSERT_TRUE(LoadHtml("<p>"));
 
-  WebFrame* main_frame = GetMainWebFrameForWebState(web_state());
+  WebFrame* main_frame = GetMainWebFrame(web_state());
   ASSERT_TRUE(main_frame);
 
   NSTimeInterval js_timeout = kWaitForJSCompletionTimeout;
@@ -125,7 +113,7 @@
        "  while(true) {}"
        "};");
 
-  WebFrame* main_frame = GetMainWebFrameForWebState(web_state());
+  WebFrame* main_frame = GetMainWebFrame(web_state());
   ASSERT_TRUE(main_frame);
 
   __block bool called = false;
@@ -157,7 +145,7 @@
 
   ASSERT_TRUE(LoadHtml("<p>"));
 
-  WebFrame* main_frame = GetMainWebFrameForWebState(web_state());
+  WebFrame* main_frame = GetMainWebFrame(web_state());
   ASSERT_TRUE(main_frame);
 
   // Inject function into main frame to intercept encrypted message targeted for
@@ -233,16 +221,16 @@
   auto callback = base::BindRepeating(
       ^bool(const base::DictionaryValue& /* json */,
             const GURL& /* origin_url */, bool /* user_is_interacting */,
-            bool is_main_frame, web::WebFrame* sender_frame) {
+            bool is_main_frame, WebFrame* sender_frame) {
         command_received = true;
         EXPECT_TRUE(is_main_frame);
-        EXPECT_EQ(GetMainWebFrameForWebState(web_state()), sender_frame);
+        EXPECT_EQ(GetMainWebFrame(web_state()), sender_frame);
         return true;
       });
 
   web_state()->AddScriptCommandCallback(callback, "senderFrameTestCommand");
-  __block web::WebFramesManagerImpl* manager =
-      web::WebFramesManagerImpl::FromWebState(web_state());
+  __block WebFramesManagerImpl* manager =
+      WebFramesManagerImpl::FromWebState(web_state());
   EXPECT_TRUE(WaitUntilConditionOrTimeout(kWaitForJSCompletionTimeout, ^{
     return manager->GetAllWebFrames().size() == 1;
   }));
@@ -269,7 +257,7 @@
   auto callback = base::BindRepeating(
       ^bool(const base::DictionaryValue& /* json */,
             const GURL& /* origin_url */, bool /* user_is_interacting */,
-            bool is_main_frame, web::WebFrame* sender_frame) {
+            bool is_main_frame, WebFrame* sender_frame) {
         command_received = true;
         EXPECT_FALSE(is_main_frame);
         EXPECT_EQ(GetChildWebFrameForWebState(web_state()), sender_frame);
@@ -277,8 +265,8 @@
       });
 
   web_state()->AddScriptCommandCallback(callback, "senderFrameTestCommand");
-  __block web::WebFramesManagerImpl* manager =
-      web::WebFramesManagerImpl::FromWebState(web_state());
+  __block WebFramesManagerImpl* manager =
+      WebFramesManagerImpl::FromWebState(web_state());
   EXPECT_TRUE(WaitUntilConditionOrTimeout(kWaitForJSCompletionTimeout, ^{
     return manager->GetAllWebFrames().size() == 2;
   }));
diff --git a/ios/web/web_state/web_frame_util.mm b/ios/web/web_state/web_frame_util.mm
new file mode 100644
index 0000000..3550c57
--- /dev/null
+++ b/ios/web/web_state/web_frame_util.mm
@@ -0,0 +1,41 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ios/web/public/web_state/web_frame_util.h"
+
+#include "base/logging.h"
+#include "ios/web/public/web_state/web_frame.h"
+#include "ios/web/public/web_state/web_frames_manager.h"
+#include "ios/web/public/web_state/web_state.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+namespace web {
+WebFrame* GetMainWebFrame(WebState* web_state) {
+  WebFramesManager* manager = WebFramesManager::FromWebState(web_state);
+  DCHECK(manager);
+  return manager->GetMainWebFrame();
+}
+
+std::string GetMainWebFrameId(WebState* web_state) {
+  WebFrame* main_frame = GetMainWebFrame(web_state);
+  if (!main_frame) {
+    return std::string();
+  }
+  return main_frame->GetFrameId();
+}
+
+WebFrame* GetWebFrameWithId(WebState* web_state, const std::string& frame_id) {
+  WebFramesManager* manager = WebFramesManager::FromWebState(web_state);
+  DCHECK(manager);
+  return manager->GetFrameWithId(frame_id);
+}
+
+std::string GetWebFrameId(WebFrame* frame) {
+  return frame ? frame->GetFrameId() : std::string();
+}
+
+}  // namespace web
diff --git a/ios/web/web_state/web_frame_util_unittest.mm b/ios/web/web_state/web_frame_util_unittest.mm
new file mode 100644
index 0000000..404b7f31
--- /dev/null
+++ b/ios/web/web_state/web_frame_util_unittest.mm
@@ -0,0 +1,115 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ios/web/public/web_state/web_frame_util.h"
+
+#include "base/test/gtest_util.h"
+#include "ios/web/public/test/fakes/fake_web_frame.h"
+#import "ios/web/public/test/fakes/test_web_state.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/platform_test.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+namespace web {
+
+typedef PlatformTest WebFrameUtilTest;
+
+// Tests the GetMainWebFrame function.
+TEST_F(WebFrameUtilTest, GetMainWebFrame) {
+  TestWebState test_web_state;
+  test_web_state.CreateWebFramesManager();
+  // Still no main frame.
+  EXPECT_EQ(nullptr, GetMainWebFrame(&test_web_state));
+  auto iframe =
+      std::make_unique<FakeWebFrame>("iframe", false, GURL::EmptyGURL());
+  test_web_state.AddWebFrame(std::move(iframe));
+  // Still no main frame.
+  EXPECT_EQ(nullptr, GetMainWebFrame(&test_web_state));
+
+  auto main_frame =
+      std::make_unique<FakeWebFrame>("main_frame", true, GURL::EmptyGURL());
+  FakeWebFrame* main_frame_ptr = main_frame.get();
+  test_web_state.AddWebFrame(std::move(main_frame));
+  // Now there is a main frame.
+  EXPECT_EQ(main_frame_ptr, GetMainWebFrame(&test_web_state));
+
+  test_web_state.RemoveWebFrame(main_frame_ptr->GetFrameId());
+  // Now there is no main frame.
+  EXPECT_EQ(nullptr, GetMainWebFrame(&test_web_state));
+}
+
+// Tests the GetMainWebFrameId function.
+TEST_F(WebFrameUtilTest, GetMainWebFrameId) {
+  TestWebState test_web_state;
+  test_web_state.CreateWebFramesManager();
+  // Still no main frame.
+  EXPECT_TRUE(GetMainWebFrameId(&test_web_state).empty());
+  auto iframe =
+      std::make_unique<FakeWebFrame>("iframe", false, GURL::EmptyGURL());
+  test_web_state.AddWebFrame(std::move(iframe));
+  // Still no main frame.
+  EXPECT_TRUE(GetMainWebFrameId(&test_web_state).empty());
+
+  auto main_frame =
+      std::make_unique<FakeWebFrame>("main_frame", true, GURL::EmptyGURL());
+  FakeWebFrame* main_frame_ptr = main_frame.get();
+  test_web_state.AddWebFrame(std::move(main_frame));
+  // Now there is a main frame.
+  EXPECT_EQ("main_frame", GetMainWebFrameId(&test_web_state));
+
+  test_web_state.RemoveWebFrame(main_frame_ptr->GetFrameId());
+  // Now there is no main frame.
+  EXPECT_TRUE(GetMainWebFrameId(&test_web_state).empty());
+}
+
+// Tests the GetWebFrameWithId function.
+TEST_F(WebFrameUtilTest, GetWebFrameWithId) {
+  TestWebState test_web_state;
+  test_web_state.CreateWebFramesManager();
+  // Still no main frame.
+  EXPECT_EQ(nullptr, GetWebFrameWithId(&test_web_state, "iframe"));
+  EXPECT_EQ(nullptr, GetWebFrameWithId(&test_web_state, "main_frame"));
+  EXPECT_EQ(nullptr, GetWebFrameWithId(&test_web_state, "unused"));
+  auto iframe =
+      std::make_unique<FakeWebFrame>("iframe", false, GURL::EmptyGURL());
+  FakeWebFrame* iframe_ptr = iframe.get();
+  test_web_state.AddWebFrame(std::move(iframe));
+  // There is an iframe.
+  EXPECT_EQ(iframe_ptr, GetWebFrameWithId(&test_web_state, "iframe"));
+  EXPECT_EQ(nullptr, GetWebFrameWithId(&test_web_state, "main_frame"));
+  EXPECT_EQ(nullptr, GetWebFrameWithId(&test_web_state, "unused"));
+
+  auto main_frame =
+      std::make_unique<FakeWebFrame>("main_frame", true, GURL::EmptyGURL());
+  FakeWebFrame* main_frame_ptr = main_frame.get();
+  test_web_state.AddWebFrame(std::move(main_frame));
+  // Now there is a main frame.
+  EXPECT_EQ(iframe_ptr, GetWebFrameWithId(&test_web_state, "iframe"));
+  EXPECT_EQ(main_frame_ptr, GetWebFrameWithId(&test_web_state, "main_frame"));
+  EXPECT_EQ(nullptr, GetWebFrameWithId(&test_web_state, "unused"));
+
+  test_web_state.RemoveWebFrame(main_frame_ptr->GetFrameId());
+  // Now there is only an iframe.
+  EXPECT_EQ(iframe_ptr, GetWebFrameWithId(&test_web_state, "iframe"));
+  EXPECT_EQ(nullptr, GetWebFrameWithId(&test_web_state, "main_frame"));
+  EXPECT_EQ(nullptr, GetWebFrameWithId(&test_web_state, "unused"));
+
+  // Now there nothing left.
+  test_web_state.RemoveWebFrame(iframe_ptr->GetFrameId());
+  EXPECT_EQ(nullptr, GetWebFrameWithId(&test_web_state, "iframe"));
+  EXPECT_EQ(nullptr, GetWebFrameWithId(&test_web_state, "main_frame"));
+  EXPECT_EQ(nullptr, GetWebFrameWithId(&test_web_state, "unused"));
+}
+
+// Tests the GetWebFrameId GetWebFrameId function.
+TEST_F(WebFrameUtilTest, GetWebFrameId) {
+  EXPECT_EQ(std::string(), GetWebFrameId(nullptr));
+  FakeWebFrame frame("frame", true, GURL::EmptyGURL());
+  EXPECT_EQ("frame", GetWebFrameId(&frame));
+}
+
+}  // namespace web
diff --git a/ios/web/web_state/web_frame_web_state_observer_inttest.mm b/ios/web/web_state/web_frame_web_state_observer_inttest.mm
index ce732889..947bec5 100644
--- a/ios/web/web_state/web_frame_web_state_observer_inttest.mm
+++ b/ios/web/web_state/web_frame_web_state_observer_inttest.mm
@@ -5,6 +5,7 @@
 #import "ios/web/public/test/web_test_with_web_state.h"
 
 #include "base/ios/ios_util.h"
+#include "ios/web/public/web_state/web_frame_util.h"
 #import "ios/web/public/web_state/web_state.h"
 #import "ios/web/public/web_state/web_state_observer.h"
 #include "ios/web/web_state/web_frames_manager_impl.h"
@@ -33,10 +34,7 @@
 // Verifies that the web frame passed to the observer is the main frame.
 ACTION_P(VerifyMainWebFrame, web_state) {
   EXPECT_EQ(web_state, arg0);
-
-  web::WebFramesManagerImpl* manager =
-      web::WebFramesManagerImpl::FromWebState(web_state);
-  EXPECT_EQ(manager->GetMainWebFrame(), arg1);
+  EXPECT_EQ(web::GetMainWebFrame(web_state), arg1);
 }
 
 // Verifies that the web frame passed to the observer is a child frame.
diff --git a/ios/web/web_state/web_frames_manager_impl.h b/ios/web/web_state/web_frames_manager_impl.h
index 5de3c60..dba457a7 100644
--- a/ios/web/web_state/web_frames_manager_impl.h
+++ b/ios/web/web_state/web_frames_manager_impl.h
@@ -5,7 +5,7 @@
 #ifndef IOS_WEB_WEB_STATE_WEB_FRAMES_MANAGER_IMPL_H_
 #define IOS_WEB_WEB_STATE_WEB_FRAMES_MANAGER_IMPL_H_
 
-#include "ios/web/public/web_state/web_frames_manager.h"
+#import "ios/web/public/web_state/web_frames_manager.h"
 
 namespace web {
 
diff --git a/ios/web/web_state/web_frames_manager_impl.mm b/ios/web/web_state/web_frames_manager_impl.mm
index 5f60ffa..e20075f 100644
--- a/ios/web/web_state/web_frames_manager_impl.mm
+++ b/ios/web/web_state/web_frames_manager_impl.mm
@@ -6,7 +6,7 @@
 
 #include "base/strings/utf_string_conversions.h"
 #include "ios/web/public/web_state/web_frame.h"
-#include "ios/web/public/web_state/web_state.h"
+#import "ios/web/public/web_state/web_state.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
diff --git a/ios/web_view/internal/autofill/cwv_autofill_controller.mm b/ios/web_view/internal/autofill/cwv_autofill_controller.mm
index 2d4521e1..9773d61 100644
--- a/ios/web_view/internal/autofill/cwv_autofill_controller.mm
+++ b/ios/web_view/internal/autofill/cwv_autofill_controller.mm
@@ -14,6 +14,7 @@
 #import "components/autofill/ios/browser/autofill_agent.h"
 #include "components/autofill/ios/browser/autofill_driver_ios.h"
 #include "components/autofill/ios/browser/autofill_driver_ios_bridge.h"
+#include "components/autofill/ios/browser/autofill_switches.h"
 #import "components/autofill/ios/browser/js_autofill_manager.h"
 #import "components/autofill/ios/browser/js_suggestion_manager.h"
 #import "components/autofill/ios/form_util/form_activity_observer_bridge.h"
@@ -21,6 +22,9 @@
 #include "components/browser_sync/profile_sync_service.h"
 #include "components/keyed_service/core/service_access_type.h"
 #import "ios/web/public/web_state/js/crw_js_injection_receiver.h"
+#include "ios/web/public/web_state/web_frame.h"
+#include "ios/web/public/web_state/web_frame_util.h"
+#import "ios/web/public/web_state/web_frames_manager.h"
 #import "ios/web/public/web_state/web_state_observer_bridge.h"
 #include "ios/web_view/internal/app/application_context.h"
 #import "ios/web_view/internal/autofill/cwv_autofill_client_ios_bridge.h"
@@ -49,9 +53,6 @@
   // Autofill agent associated with |webState|.
   AutofillAgent* _autofillAgent;
 
-  // Autofill manager associated with |webState|.
-  autofill::AutofillManager* _autofillManager;
-
   // Autofill client associated with |webState|.
   std::unique_ptr<autofill::WebViewAutofillClientIOS> _autofillClient;
 
@@ -107,12 +108,10 @@
                 browserState, ServiceAccessType::EXPLICIT_ACCESS),
         ios_web_view::WebViewProfileSyncServiceFactory::GetForBrowserState(
             browserState)));
-    autofill::AutofillDriverIOS::CreateForWebStateAndDelegate(
+    autofill::AutofillDriverIOS::PrepareForWebStateWebFrameAndDelegate(
         _webState, _autofillClient.get(), self,
         ios_web_view::ApplicationContext::GetInstance()->GetApplicationLocale(),
         autofill::AutofillManager::ENABLE_AUTOFILL_DOWNLOAD_MANAGER);
-    _autofillManager = autofill::AutofillDriverIOS::FromWebState(_webState)
-                           ->autofill_manager();
 
     _JSAutofillManager = JSAutofillManager;
 
@@ -147,6 +146,7 @@
 - (void)fetchSuggestionsForFormWithName:(NSString*)formName
                               fieldName:(NSString*)fieldName
                         fieldIdentifier:(NSString*)fieldIdentifier
+                                frameID:(NSString*)frameID
                       completionHandler:
                           (void (^)(NSArray<CWVAutofillSuggestion*>*))
                               completionHandler {
@@ -161,12 +161,12 @@
                            id<FormSuggestionProvider> delegate) {
       NSMutableArray* autofillSuggestions = [NSMutableArray array];
       for (FormSuggestion* formSuggestion in suggestions) {
-        CWVAutofillSuggestion* autofillSuggestion =
-            [[CWVAutofillSuggestion alloc]
-                initWithFormSuggestion:formSuggestion
-                              formName:formName
-                             fieldName:fieldName
-                       fieldIdentifier:fieldIdentifier];
+        CWVAutofillSuggestion* autofillSuggestion = [
+            [CWVAutofillSuggestion alloc] initWithFormSuggestion:formSuggestion
+                                                        formName:formName
+                                                       fieldName:fieldName
+                                                 fieldIdentifier:fieldIdentifier
+                                                         frameID:frameID];
         [autofillSuggestions addObject:autofillSuggestion];
       }
       completionHandler([autofillSuggestions copy]);
@@ -177,6 +177,7 @@
                                                  fieldType:@""
                                                       type:nil
                                                 typedValue:@" "
+                                                   frameID:frameID
                                                   webState:strongSelf->_webState
                                          completionHandler:retrieveHandler];
   };
@@ -189,6 +190,7 @@
                                            fieldType:@""
                                                 type:nil
                                           typedValue:@" "
+                                             frameID:frameID
                                          isMainFrame:YES
                                       hasUserGesture:YES
                                             webState:_webState
@@ -201,6 +203,7 @@
                             fieldName:suggestion.fieldName
                       fieldIdentifier:suggestion.fieldIdentifier
                                  form:suggestion.formName
+                              frameID:suggestion.frameID
                     completionHandler:^{
                       if (completionHandler) {
                         completionHandler();
@@ -210,11 +213,17 @@
 
 - (void)removeSuggestion:(CWVAutofillSuggestion*)suggestion {
   // Identifier is greater than 0 when it is an autofill profile.
+  web::WebFrame* frame = web::GetWebFrameWithId(
+      _webState, base::SysNSStringToUTF8(suggestion.frameID));
+  autofill::AutofillManager* manager = [self autofillManagerForFrame:frame];
+  if (!manager) {
+    return;
+  }
   if (suggestion.formSuggestion.identifier > 0) {
-    _autofillManager->RemoveAutofillProfileOrCreditCard(
+    manager->RemoveAutofillProfileOrCreditCard(
         suggestion.formSuggestion.identifier);
   } else {
-    _autofillManager->RemoveAutocompleteEntry(
+    manager->RemoveAutocompleteEntry(
         base::SysNSStringToUTF16(suggestion.fieldName),
         base::SysNSStringToUTF16(suggestion.value));
   }
@@ -235,6 +244,17 @@
           completionHandler];
 }
 
+- (autofill::AutofillManager*)autofillManagerForFrame:(web::WebFrame*)frame {
+  if (!_webState) {
+    return nil;
+  }
+  if (autofill::switches::IsAutofillIFrameMessagingEnabled() && !frame) {
+    return nil;
+  }
+  return autofill::AutofillDriverIOS::FromWebStateAndWebFrame(_webState, frame)
+      ->autofill_manager();
+}
+
 #pragma mark - CWVAutofillClientIOSBridge
 
 - (void)showAutofillPopup:(const std::vector<autofill::Suggestion>&)suggestions
@@ -330,15 +350,18 @@
 #pragma mark - AutofillDriverIOSBridge
 
 - (void)onFormDataFilled:(uint16_t)query_id
+                 inFrame:(web::WebFrame*)frame
                   result:(const autofill::FormData&)result {
   [_autofillAgent onFormDataFilled:result];
-  if (_autofillManager) {
-    _autofillManager->OnDidFillAutofillFormData(result, base::TimeTicks::Now());
+  autofill::AutofillManager* manager = [self autofillManagerForFrame:frame];
+  if (manager) {
+    manager->OnDidFillAutofillFormData(result, base::TimeTicks::Now());
   }
 }
 
 - (void)sendAutofillTypePredictionsToRenderer:
-    (const std::vector<autofill::FormDataPredictions>&)forms {
+            (const std::vector<autofill::FormDataPredictions>&)forms
+                                      toFrame:(web::WebFrame*)frame {
   // Not supported.
 }
 
@@ -355,35 +378,39 @@
   NSString* nsFieldName = base::SysUTF8ToNSString(params.field_name);
   NSString* nsFieldIdentifier =
       base::SysUTF8ToNSString(params.field_identifier);
+  NSString* nsFrameID = base::SysUTF8ToNSString(GetWebFrameId(frame));
   NSString* nsValue = base::SysUTF8ToNSString(params.value);
   if (params.type == "focus") {
     if ([_delegate respondsToSelector:@selector
                    (autofillController:didFocusOnFieldWithName:fieldIdentifier
-                                         :formName:value:)]) {
+                                         :formName:frameID:value:)]) {
       [_delegate autofillController:self
             didFocusOnFieldWithName:nsFieldName
                     fieldIdentifier:nsFieldIdentifier
                            formName:nsFormName
+                            frameID:nsFrameID
                               value:nsValue];
     }
   } else if (params.type == "input") {
     if ([_delegate respondsToSelector:@selector
                    (autofillController:didInputInFieldWithName:fieldIdentifier
-                                         :formName:value:)]) {
+                                         :formName:frameID:value:)]) {
       [_delegate autofillController:self
             didInputInFieldWithName:nsFieldName
                     fieldIdentifier:nsFieldIdentifier
                            formName:nsFormName
+                            frameID:nsFrameID
                               value:nsValue];
     }
   } else if (params.type == "blur") {
     if ([_delegate respondsToSelector:@selector
                    (autofillController:didBlurOnFieldWithName:fieldIdentifier
-                                         :formName:value:)]) {
+                                         :formName:frameID:value:)]) {
       [_delegate autofillController:self
              didBlurOnFieldWithName:nsFieldName
                     fieldIdentifier:nsFieldIdentifier
                            formName:nsFormName
+                            frameID:nsFrameID
                               value:nsValue];
     }
   }
diff --git a/ios/web_view/internal/autofill/cwv_autofill_controller_unittest.mm b/ios/web_view/internal/autofill/cwv_autofill_controller_unittest.mm
index f60ece3b..e05b8003 100644
--- a/ios/web_view/internal/autofill/cwv_autofill_controller_unittest.mm
+++ b/ios/web_view/internal/autofill/cwv_autofill_controller_unittest.mm
@@ -19,6 +19,7 @@
 #import "components/autofill/ios/form_util/form_activity_tab_helper.h"
 #import "components/autofill/ios/form_util/test_form_activity_tab_helper.h"
 #import "ios/web/public/test/fakes/crw_test_js_injection_receiver.h"
+#import "ios/web/public/test/fakes/fake_web_frame.h"
 #import "ios/web/public/test/fakes/test_web_state.h"
 #include "ios/web/public/test/test_web_thread_bundle.h"
 #include "ios/web/public/web_client.h"
@@ -45,6 +46,7 @@
 NSString* const kTestFormName = @"FormName";
 NSString* const kTestFieldName = @"FieldName";
 NSString* const kTestFieldIdentifier = @"FieldIdentifier";
+NSString* const kTestFrameId = @"FrameID";
 NSString* const kTestFieldValue = @"FieldValue";
 
 }  // namespace
@@ -68,6 +70,7 @@
         [[FakeAutofillAgent alloc] initWithPrefService:browser_state_.GetPrefs()
                                               webState:&web_state_];
 
+    web_state_.CreateWebFramesManager();
     autofill_controller_ =
         [[CWVAutofillController alloc] initWithWebState:&web_state_
                                           autofillAgent:autofill_agent_
@@ -98,7 +101,8 @@
                                identifier:0];
   [autofill_agent_ addSuggestion:suggestion
                      forFormName:kTestFormName
-                 fieldIdentifier:kTestFieldIdentifier];
+                 fieldIdentifier:kTestFieldIdentifier
+                         frameID:kTestFrameId];
 
   __block BOOL fetch_completion_was_called = NO;
   id fetch_completion = ^(NSArray<CWVAutofillSuggestion*>* suggestions) {
@@ -112,6 +116,7 @@
   [autofill_controller_ fetchSuggestionsForFormWithName:kTestFormName
                                               fieldName:kTestFieldName
                                         fieldIdentifier:kTestFieldIdentifier
+                                                frameID:kTestFrameId
                                       completionHandler:fetch_completion];
 
   EXPECT_TRUE(WaitUntilConditionOrTimeout(kWaitForActionTimeout, ^bool {
@@ -127,11 +132,12 @@
                        displayDescription:nil
                                      icon:nil
                                identifier:0];
-  CWVAutofillSuggestion* suggestion = [[CWVAutofillSuggestion alloc]
-      initWithFormSuggestion:form_suggestion
-                    formName:kTestFormName
-                   fieldName:kTestFieldName
-             fieldIdentifier:kTestFieldIdentifier];
+  CWVAutofillSuggestion* suggestion =
+      [[CWVAutofillSuggestion alloc] initWithFormSuggestion:form_suggestion
+                                                   formName:kTestFormName
+                                                  fieldName:kTestFieldName
+                                            fieldIdentifier:kTestFieldIdentifier
+                                                    frameID:kTestFrameId];
   __block BOOL fill_completion_was_called = NO;
   [autofill_controller_ fillSuggestion:suggestion
                      completionHandler:^{
@@ -145,7 +151,8 @@
   EXPECT_NSEQ(
       form_suggestion,
       [autofill_agent_ selectedSuggestionForFormName:kTestFormName
-                                     fieldIdentifier:kTestFieldIdentifier]);
+                                     fieldIdentifier:kTestFieldIdentifier
+                                             frameID:kTestFrameId]);
 }
 
 // Tests CWVAutofillController clears form.
@@ -206,6 +213,7 @@
                     didFocusOnFieldWithName:kTestFieldName
                             fieldIdentifier:kTestFieldIdentifier
                                    formName:kTestFormName
+                                    frameID:kTestFrameId
                                       value:kTestFieldValue];
 
       autofill::FormActivityParams params;
@@ -213,9 +221,11 @@
       params.field_name = base::SysNSStringToUTF8(kTestFieldName);
       params.field_identifier = base::SysNSStringToUTF8(kTestFieldIdentifier);
       params.value = base::SysNSStringToUTF8(kTestFieldValue);
+      params.frame_id = base::SysNSStringToUTF8(kTestFrameId);
       params.type = "focus";
-      test_form_activity_tab_helper_->FormActivityRegistered(
-          /*sender_frame*/ nullptr, params);
+      web::FakeWebFrame frame(base::SysNSStringToUTF8(kTestFrameId), true,
+                              GURL::EmptyGURL());
+      test_form_activity_tab_helper_->FormActivityRegistered(&frame, params);
       [delegate verify];
   }
 }
@@ -233,6 +243,7 @@
                     didInputInFieldWithName:kTestFieldName
                             fieldIdentifier:kTestFieldIdentifier
                                    formName:kTestFormName
+                                    frameID:kTestFrameId
                                       value:kTestFieldValue];
 
       autofill::FormActivityParams params;
@@ -240,9 +251,11 @@
       params.field_name = base::SysNSStringToUTF8(kTestFieldName);
       params.field_identifier = base::SysNSStringToUTF8(kTestFieldIdentifier);
       params.value = base::SysNSStringToUTF8(kTestFieldValue);
+      params.frame_id = base::SysNSStringToUTF8(kTestFrameId);
       params.type = "input";
-      test_form_activity_tab_helper_->FormActivityRegistered(
-          /*sender_frame*/ nullptr, params);
+      web::FakeWebFrame frame(base::SysNSStringToUTF8(kTestFrameId), true,
+                              GURL::EmptyGURL());
+      test_form_activity_tab_helper_->FormActivityRegistered(&frame, params);
       [delegate verify];
   }
 }
@@ -259,6 +272,7 @@
                    didBlurOnFieldWithName:kTestFieldName
                           fieldIdentifier:kTestFieldIdentifier
                                  formName:kTestFormName
+                                  frameID:kTestFrameId
                                     value:kTestFieldValue];
 
     autofill::FormActivityParams params;
@@ -266,9 +280,11 @@
     params.field_name = base::SysNSStringToUTF8(kTestFieldName);
     params.field_identifier = base::SysNSStringToUTF8(kTestFieldIdentifier);
     params.value = base::SysNSStringToUTF8(kTestFieldValue);
+    params.frame_id = base::SysNSStringToUTF8(kTestFrameId);
     params.type = "blur";
-    test_form_activity_tab_helper_->FormActivityRegistered(
-        /*sender_frame*/ nullptr, params);
+    web::FakeWebFrame frame(base::SysNSStringToUTF8(kTestFrameId), true,
+                            GURL::EmptyGURL());
+    test_form_activity_tab_helper_->FormActivityRegistered(&frame, params);
 
     [delegate verify];
   }
diff --git a/ios/web_view/internal/autofill/cwv_autofill_suggestion.mm b/ios/web_view/internal/autofill/cwv_autofill_suggestion.mm
index e013ae16..a2a9998 100644
--- a/ios/web_view/internal/autofill/cwv_autofill_suggestion.mm
+++ b/ios/web_view/internal/autofill/cwv_autofill_suggestion.mm
@@ -20,17 +20,20 @@
 @synthesize formName = _formName;
 @synthesize fieldName = _fieldName;
 @synthesize fieldIdentifier = _fieldIdentifier;
+@synthesize frameID = _frameID;
 
 - (instancetype)initWithFormSuggestion:(FormSuggestion*)formSuggestion
                               formName:(NSString*)formName
                              fieldName:(NSString*)fieldName
-                       fieldIdentifier:(NSString*)fieldIdentifier {
+                       fieldIdentifier:(NSString*)fieldIdentifier
+                               frameID:(NSString*)frameID {
   self = [super init];
   if (self) {
     _formSuggestion = formSuggestion;
     _formName = [formName copy];
     _fieldName = [fieldName copy];
     _fieldIdentifier = [fieldIdentifier copy];
+    _frameID = [frameID copy];
   }
   return self;
 }
diff --git a/ios/web_view/internal/autofill/cwv_autofill_suggestion_internal.h b/ios/web_view/internal/autofill/cwv_autofill_suggestion_internal.h
index 251e489d..60d50dc 100644
--- a/ios/web_view/internal/autofill/cwv_autofill_suggestion_internal.h
+++ b/ios/web_view/internal/autofill/cwv_autofill_suggestion_internal.h
@@ -17,6 +17,7 @@
                               formName:(NSString*)formName
                              fieldName:(NSString*)fieldName
                        fieldIdentifier:(NSString*)fieldIdentifier
+                               frameID:(NSString*)frameID
     NS_DESIGNATED_INITIALIZER;
 
 // The internal autofill form suggestion.
diff --git a/ios/web_view/internal/autofill/cwv_autofill_suggestion_unittest.mm b/ios/web_view/internal/autofill/cwv_autofill_suggestion_unittest.mm
index ce712d47..19a2a54 100644
--- a/ios/web_view/internal/autofill/cwv_autofill_suggestion_unittest.mm
+++ b/ios/web_view/internal/autofill/cwv_autofill_suggestion_unittest.mm
@@ -24,6 +24,7 @@
   NSString* formName = @"TestFormName";
   NSString* fieldName = @"TestFieldName";
   NSString* fieldIdentifier = @"TestFieldIdentifier";
+  NSString* frameID = @"TestFrameID";
   FormSuggestion* formSuggestion =
       [FormSuggestion suggestionWithValue:@"TestValue"
                        displayDescription:@"TestDisplayDescription"
@@ -33,10 +34,12 @@
       [[CWVAutofillSuggestion alloc] initWithFormSuggestion:formSuggestion
                                                    formName:formName
                                                   fieldName:fieldName
-                                            fieldIdentifier:fieldIdentifier];
+                                            fieldIdentifier:fieldIdentifier
+                                                    frameID:frameID];
   EXPECT_NSEQ(formName, suggestion.formName);
   EXPECT_NSEQ(fieldName, suggestion.fieldName);
   EXPECT_NSEQ(fieldIdentifier, suggestion.fieldIdentifier);
+  EXPECT_NSEQ(frameID, suggestion.frameID);
   EXPECT_NSEQ(formSuggestion.displayDescription, suggestion.displayDescription);
   EXPECT_NSEQ(formSuggestion.value, suggestion.value);
   EXPECT_EQ(formSuggestion, suggestion.formSuggestion);
diff --git a/ios/web_view/public/cwv_autofill_controller.h b/ios/web_view/public/cwv_autofill_controller.h
index e9f4e661..fc7b69c 100644
--- a/ios/web_view/public/cwv_autofill_controller.h
+++ b/ios/web_view/public/cwv_autofill_controller.h
@@ -41,9 +41,11 @@
 // |fieldIdentifier| identifies the field that had focus. It is passed to
 // CWVAutofillControllerDelegate and forwarded to this method.
 // |completionHandler| will only be called on success.
+// |frameID| is the ID of the web frame containing the form.
 - (void)fetchSuggestionsForFormWithName:(NSString*)formName
                               fieldName:(NSString*)fieldName
                         fieldIdentifier:(NSString*)fieldIdentifier
+                                frameID:(NSString*)frameID
                       completionHandler:
                           (void (^)(NSArray<CWVAutofillSuggestion*>*))
                               completionHandler;
diff --git a/ios/web_view/public/cwv_autofill_controller_delegate.h b/ios/web_view/public/cwv_autofill_controller_delegate.h
index a02059b..24bf37bb 100644
--- a/ios/web_view/public/cwv_autofill_controller_delegate.h
+++ b/ios/web_view/public/cwv_autofill_controller_delegate.h
@@ -37,6 +37,7 @@
     didFocusOnFieldWithName:(NSString*)fieldName
             fieldIdentifier:(NSString*)fieldIdentifier
                    formName:(NSString*)formName
+                    frameID:(NSString*)frameID
                       value:(NSString*)value;
 
 // Called when a form field element receives an "input" event.
@@ -44,6 +45,7 @@
     didInputInFieldWithName:(NSString*)fieldName
             fieldIdentifier:(NSString*)fieldIdentifier
                    formName:(NSString*)formName
+                    frameID:(NSString*)frameID
                       value:(NSString*)value;
 
 // Called when a form field element receives a "blur" (un-focused) event.
@@ -51,6 +53,7 @@
     didBlurOnFieldWithName:(NSString*)fieldName
            fieldIdentifier:(NSString*)fieldIdentifier
                   formName:(NSString*)formName
+                   frameID:(NSString*)frameID
                      value:(NSString*)value;
 
 // Called when a form was submitted. |userInitiated| is YES if form is submitted
diff --git a/ios/web_view/public/cwv_autofill_suggestion.h b/ios/web_view/public/cwv_autofill_suggestion.h
index 05ca843..b1608c54 100644
--- a/ios/web_view/public/cwv_autofill_suggestion.h
+++ b/ios/web_view/public/cwv_autofill_suggestion.h
@@ -49,6 +49,9 @@
 // be used as identifier. Otherwise, an identifier will be generated.
 @property(nonatomic, copy, readonly) NSString* fieldIdentifier;
 
+// The identifier of the WebFrame containing the field.
+@property(nonatomic, copy, readonly) NSString* frameID;
+
 // The string that will replace the field with |fieldName|'s value attribute.
 @property(nonatomic, copy, readonly) NSString* value;
 
diff --git a/ios/web_view/shell/shell_autofill_delegate.m b/ios/web_view/shell/shell_autofill_delegate.m
index 2573ce69..3e0c3cd 100644
--- a/ios/web_view/shell/shell_autofill_delegate.m
+++ b/ios/web_view/shell/shell_autofill_delegate.m
@@ -44,6 +44,7 @@
     didFocusOnFieldWithName:(NSString*)fieldName
             fieldIdentifier:(NSString*)fieldIdentifier
                    formName:(NSString*)formName
+                    frameID:(NSString*)frameID
                       value:(NSString*)value {
   _autofillController = autofillController;
 
@@ -79,6 +80,7 @@
   [autofillController fetchSuggestionsForFormWithName:formName
                                             fieldName:fieldName
                                       fieldIdentifier:fieldIdentifier
+                                              frameID:frameID
                                     completionHandler:completionHandler];
 }
 
diff --git a/ios/web_view/test/web_view_autofill_inttest.mm b/ios/web_view/test/web_view_autofill_inttest.mm
index 284442a..c694a848 100644
--- a/ios/web_view/test/web_view_autofill_inttest.mm
+++ b/ios/web_view/test/web_view_autofill_inttest.mm
@@ -30,6 +30,7 @@
 NSString* const kTestFormID = @"FormID";
 NSString* const kTestFieldName = @"FieldName";
 NSString* const kTestFieldID = @"FieldID";
+NSString* const kTestFrameID = @"FrameID";
 NSString* const kTestFieldValue = @"FieldValue";
 NSString* const kTestSubmitID = @"SubmitID";
 NSString* const kTestFormHtml =
@@ -82,6 +83,7 @@
         fetchSuggestionsForFormWithName:kTestFormName
                               fieldName:kTestFieldName
                         fieldIdentifier:kTestFieldID
+                                frameID:kTestFrameID
                       completionHandler:^(
                           NSArray<CWVAutofillSuggestion*>* suggestions) {
                         fetched_suggestions = suggestions;
@@ -109,6 +111,7 @@
                 didFocusOnFieldWithName:kTestFieldName
                         fieldIdentifier:kTestFieldID
                                formName:kTestFormName
+                                frameID:[OCMArg any]
                                   value:kTestFieldValue];
   NSString* focus_script = [NSString
       stringWithFormat:@"document.getElementById('%@').focus();", kTestFieldID];
@@ -121,6 +124,7 @@
                  didBlurOnFieldWithName:kTestFieldName
                         fieldIdentifier:kTestFieldID
                                formName:kTestFormName
+                                frameID:[OCMArg any]
                                   value:kTestFieldValue];
   NSString* blur_script =
       [NSString stringWithFormat:
@@ -136,6 +140,7 @@
                 didInputInFieldWithName:kTestFieldName
                         fieldIdentifier:kTestFieldID
                                formName:kTestFormName
+                                frameID:[OCMArg any]
                                   value:kTestFieldValue];
   // The 'input' event listener defined in form.js is only called during the
   // bubbling phase.
@@ -180,6 +185,7 @@
   EXPECT_NSEQ(kTestFieldValue, fetched_suggestion.value);
   EXPECT_NSEQ(kTestFormName, fetched_suggestion.formName);
   EXPECT_NSEQ(kTestFieldName, fetched_suggestion.fieldName);
+  EXPECT_NSEQ(kTestFrameID, fetched_suggestion.frameID);
 
   // The input element needs to be focused before it can be filled or cleared.
   NSString* focus_script = [NSString
diff --git a/media/blink/webmediaplayer_impl.cc b/media/blink/webmediaplayer_impl.cc
index c9e7282..deb8cafa 100644
--- a/media/blink/webmediaplayer_impl.cc
+++ b/media/blink/webmediaplayer_impl.cc
@@ -565,7 +565,7 @@
   DCHECK(main_task_runner_->BelongsToCurrentThread());
 
   GURL gurl(url);
-  ReportMetrics(load_type, gurl, frame_->GetSecurityOrigin(), media_log_.get());
+  ReportMetrics(load_type, gurl, *frame_, media_log_.get());
 
   // Report poster availability for SRC=.
   if (load_type == kLoadTypeURL) {
diff --git a/media/blink/webmediaplayer_util.cc b/media/blink/webmediaplayer_util.cc
index 36fbb8a..d4f4160 100644
--- a/media/blink/webmediaplayer_util.cc
+++ b/media/blink/webmediaplayer_util.cc
@@ -14,6 +14,7 @@
 #include "media/base/media_log.h"
 #include "third_party/blink/public/platform/url_conversion.h"
 #include "third_party/blink/public/platform/web_media_player_encrypted_media_client.h"
+#include "third_party/blink/public/web/web_local_frame.h"
 
 namespace {
 
@@ -112,7 +113,7 @@
 
 void ReportMetrics(blink::WebMediaPlayer::LoadType load_type,
                    const GURL& url,
-                   const blink::WebSecurityOrigin& security_origin,
+                   const blink::WebLocalFrame& frame,
                    MediaLog* media_log) {
   DCHECK(media_log);
 
@@ -125,13 +126,19 @@
   UMA_HISTOGRAM_ENUMERATION("Media.LoadType", load_type,
                             blink::WebMediaPlayer::kLoadTypeMax + 1);
 
+  // Report load type separately for ad frames.
+  if (frame.IsAdSubframe()) {
+    UMA_HISTOGRAM_ENUMERATION("Ads.Media.LoadType", load_type,
+                              blink::WebMediaPlayer::kLoadTypeMax + 1);
+  }
+
   // Report the origin from where the media player is created.
   media_log->RecordRapporWithSecurityOrigin("Media.OriginUrl." +
                                             LoadTypeToString(load_type));
 
   // For MSE, also report usage by secure/insecure origin.
   if (load_type == blink::WebMediaPlayer::kLoadTypeMediaSource) {
-    if (security_origin.IsPotentiallyTrustworthy()) {
+    if (frame.GetSecurityOrigin().IsPotentiallyTrustworthy()) {
       media_log->RecordRapporWithSecurityOrigin("Media.OriginUrl.MSE.Secure");
     } else {
       media_log->RecordRapporWithSecurityOrigin("Media.OriginUrl.MSE.Insecure");
diff --git a/media/blink/webmediaplayer_util.h b/media/blink/webmediaplayer_util.h
index 7a6ab8f..260ae97a 100644
--- a/media/blink/webmediaplayer_util.h
+++ b/media/blink/webmediaplayer_util.h
@@ -20,6 +20,10 @@
 #include "third_party/blink/public/platform/web_time_range.h"
 #include "url/gurl.h"
 
+namespace blink {
+class WebLocalFrame;
+}  // namespace blink
+
 namespace media {
 
 class MediaLog;
@@ -34,11 +38,10 @@
 PipelineErrorToNetworkState(PipelineStatus error);
 
 // Report various metrics to UMA and RAPPOR.
-void MEDIA_BLINK_EXPORT
-ReportMetrics(blink::WebMediaPlayer::LoadType load_type,
-              const GURL& url,
-              const blink::WebSecurityOrigin& security_origin,
-              MediaLog* media_log);
+void MEDIA_BLINK_EXPORT ReportMetrics(blink::WebMediaPlayer::LoadType load_type,
+                                      const GURL& url,
+                                      const blink::WebLocalFrame& frame,
+                                      MediaLog* media_log);
 
 // Report metrics about pipeline errors.
 void MEDIA_BLINK_EXPORT
diff --git a/media/capture/mojom/video_capture_types.mojom b/media/capture/mojom/video_capture_types.mojom
index 74075a8..23e4a073 100644
--- a/media/capture/mojom/video_capture_types.mojom
+++ b/media/capture/mojom/video_capture_types.mojom
@@ -239,6 +239,16 @@
   PowerLineFrequency power_line_frequency;
 };
 
+// Contains one stride value per image plane. Stride means the number of bytes
+// per row. If the image format uses fewer than kMaxPlanes planes, the values
+// for higher plane indices are ignored. For example, for a YUV format, plane
+// index 0 corresponds to the Y plane, index 1 to the U plane, and index 2 to
+// the V plane.
+struct PlaneStrides {
+  // Size must be kept in sync with media::VideoFrame::kMaxPlanes.
+  array<uint32, 4> stride_by_plane;
+};
+
 struct VideoFrameInfo{
   mojo_base.mojom.TimeDelta timestamp;
   mojo_base.mojom.DictionaryValue metadata;
@@ -246,6 +256,9 @@
   gfx.mojom.Size coded_size;
   gfx.mojom.Rect visible_rect;
   gfx.mojom.ColorSpace color_space;
+  // Optionally, stride information can be provided.
+  // If not provided, it is assumed that frame data is tightly packed.
+  PlaneStrides? strides;
 };
 
 struct VideoCaptureDeviceDescriptorCameraCalibration {
diff --git a/media/capture/video/shared_memory_buffer_tracker.cc b/media/capture/video/shared_memory_buffer_tracker.cc
index e375a183..c07b1c8 100644
--- a/media/capture/video/shared_memory_buffer_tracker.cc
+++ b/media/capture/video/shared_memory_buffer_tracker.cc
@@ -7,6 +7,29 @@
 #include "base/logging.h"
 #include "ui/gfx/geometry/size.h"
 
+namespace {
+
+size_t CalculateRequiredBufferSize(
+    const gfx::Size& dimensions,
+    media::VideoPixelFormat format,
+    const media::mojom::PlaneStridesPtr& strides) {
+  if (strides) {
+    size_t result = 0u;
+    for (size_t plane_index = 0;
+         plane_index < media::VideoFrame::NumPlanes(format); plane_index++) {
+      result +=
+          strides->stride_by_plane[plane_index] *
+          media::VideoFrame::Rows(plane_index, format, dimensions.height());
+    }
+    return result;
+  } else {
+    return media::VideoCaptureFormat(dimensions, 0.0f, format)
+        .ImageAllocationSize();
+  }
+}
+
+}  // namespace
+
 namespace media {
 
 SharedMemoryBufferTracker::SharedMemoryBufferTracker() = default;
@@ -14,14 +37,19 @@
 SharedMemoryBufferTracker::~SharedMemoryBufferTracker() = default;
 
 bool SharedMemoryBufferTracker::Init(const gfx::Size& dimensions,
-                                     VideoPixelFormat format) {
-  DVLOG(2) << __func__ << "allocating ShMem of " << dimensions.ToString();
-  set_dimensions(dimensions);
-  // |dimensions| can be 0x0 for trackers that do not require memory backing.
-  set_max_pixel_count(dimensions.GetArea());
-  set_pixel_format(format);
-  return provider_.InitForSize(
-      VideoCaptureFormat(dimensions, 0.0f, format).ImageAllocationSize());
+                                     VideoPixelFormat format,
+                                     const mojom::PlaneStridesPtr& strides) {
+  const size_t buffer_size =
+      CalculateRequiredBufferSize(dimensions, format, strides);
+  return provider_.InitForSize(buffer_size);
+}
+
+bool SharedMemoryBufferTracker::IsReusableForFormat(
+    const gfx::Size& dimensions,
+    VideoPixelFormat format,
+    const mojom::PlaneStridesPtr& strides) {
+  return GetMemorySizeInBytes() >=
+         CalculateRequiredBufferSize(dimensions, format, strides);
 }
 
 std::unique_ptr<VideoCaptureBufferHandle>
diff --git a/media/capture/video/shared_memory_buffer_tracker.h b/media/capture/video/shared_memory_buffer_tracker.h
index 92abef7..9f937385 100644
--- a/media/capture/video/shared_memory_buffer_tracker.h
+++ b/media/capture/video/shared_memory_buffer_tracker.h
@@ -21,9 +21,14 @@
   SharedMemoryBufferTracker();
   ~SharedMemoryBufferTracker() override;
 
-  bool Init(const gfx::Size& dimensions, VideoPixelFormat format) override;
-
   // Implementation of VideoCaptureBufferTracker:
+  bool Init(const gfx::Size& dimensions,
+            VideoPixelFormat format,
+            const mojom::PlaneStridesPtr& strides) override;
+  bool IsReusableForFormat(const gfx::Size& dimensions,
+                           VideoPixelFormat format,
+                           const mojom::PlaneStridesPtr& strides) override;
+
   std::unique_ptr<VideoCaptureBufferHandle> GetMemoryMappedAccess() override;
   mojo::ScopedSharedBufferHandle GetHandleForTransit(bool read_only) override;
   base::SharedMemoryHandle GetNonOwnedSharedMemoryHandleForLegacyIPC() override;
diff --git a/media/capture/video/video_capture_buffer_pool.h b/media/capture/video/video_capture_buffer_pool.h
index 6e88143..416618f 100644
--- a/media/capture/video/video_capture_buffer_pool.h
+++ b/media/capture/video/video_capture_buffer_pool.h
@@ -73,6 +73,7 @@
   // returned via |buffer_id_to_drop|.
   virtual int ReserveForProducer(const gfx::Size& dimensions,
                                  VideoPixelFormat format,
+                                 const mojom::PlaneStridesPtr& strides,
                                  int frame_feedback_id,
                                  int* buffer_id_to_drop) = 0;
 
diff --git a/media/capture/video/video_capture_buffer_pool_impl.cc b/media/capture/video/video_capture_buffer_pool_impl.cc
index a7e9959..e2468bd8 100644
--- a/media/capture/video/video_capture_buffer_pool_impl.cc
+++ b/media/capture/video/video_capture_buffer_pool_impl.cc
@@ -93,13 +93,15 @@
   return tracker->GetMemoryMappedAccess();
 }
 
-int VideoCaptureBufferPoolImpl::ReserveForProducer(const gfx::Size& dimensions,
-                                                   VideoPixelFormat format,
-                                                   int frame_feedback_id,
-                                                   int* buffer_id_to_drop) {
+int VideoCaptureBufferPoolImpl::ReserveForProducer(
+    const gfx::Size& dimensions,
+    VideoPixelFormat format,
+    const mojom::PlaneStridesPtr& strides,
+    int frame_feedback_id,
+    int* buffer_id_to_drop) {
   base::AutoLock lock(lock_);
-  return ReserveForProducerInternal(dimensions, format, frame_feedback_id,
-                                    buffer_id_to_drop);
+  return ReserveForProducerInternal(dimensions, format, strides,
+                                    frame_feedback_id, buffer_id_to_drop);
 }
 
 void VideoCaptureBufferPoolImpl::RelinquishProducerReservation(int buffer_id) {
@@ -158,29 +160,27 @@
 int VideoCaptureBufferPoolImpl::ReserveForProducerInternal(
     const gfx::Size& dimensions,
     VideoPixelFormat pixel_format,
+    const mojom::PlaneStridesPtr& strides,
     int frame_feedback_id,
     int* buffer_id_to_drop) {
   lock_.AssertAcquired();
 
-  const size_t size_in_pixels = dimensions.GetArea();
   // Look for a tracker that's allocated, big enough, and not in use. Track the
   // largest one that's not big enough, in case we have to reallocate a tracker.
   *buffer_id_to_drop = kInvalidId;
-  size_t largest_size_in_pixels = 0;
+  uint32_t largest_memory_size_in_bytes = 0;
   auto tracker_to_drop = trackers_.end();
   for (auto it = trackers_.begin(); it != trackers_.end(); ++it) {
     VideoCaptureBufferTracker* const tracker = it->second.get();
     if (!tracker->consumer_hold_count() && !tracker->held_by_producer()) {
-      if (tracker->max_pixel_count() >= size_in_pixels &&
-          (tracker->pixel_format() == pixel_format)) {
-        // Existing tracker is big enough and has correct format. Reuse it.
-        tracker->set_dimensions(dimensions);
+      if (tracker->IsReusableForFormat(dimensions, pixel_format, strides)) {
+        // Reuse this buffer
         tracker->set_held_by_producer(true);
         tracker->set_frame_feedback_id(frame_feedback_id);
         return it->first;
       }
-      if (tracker->max_pixel_count() > largest_size_in_pixels) {
-        largest_size_in_pixels = tracker->max_pixel_count();
+      if (tracker->GetMemorySizeInBytes() > largest_memory_size_in_bytes) {
+        largest_memory_size_in_bytes = tracker->GetMemorySizeInBytes();
         tracker_to_drop = it;
       }
     }
@@ -202,7 +202,7 @@
 
   std::unique_ptr<VideoCaptureBufferTracker> tracker =
       buffer_tracker_factory_->CreateTracker();
-  if (!tracker->Init(dimensions, pixel_format)) {
+  if (!tracker->Init(dimensions, pixel_format, strides)) {
     DLOG(ERROR) << "Error initializing VideoCaptureBufferTracker";
     return kInvalidId;
   }
diff --git a/media/capture/video/video_capture_buffer_pool_impl.h b/media/capture/video/video_capture_buffer_pool_impl.h
index 7ff85f61..bce67ab 100644
--- a/media/capture/video/video_capture_buffer_pool_impl.h
+++ b/media/capture/video/video_capture_buffer_pool_impl.h
@@ -46,6 +46,7 @@
       int buffer_id) override;
   int ReserveForProducer(const gfx::Size& dimensions,
                          VideoPixelFormat format,
+                         const mojom::PlaneStridesPtr& strides,
                          int frame_feedback_id,
                          int* buffer_id_to_drop) override;
   void RelinquishProducerReservation(int buffer_id) override;
@@ -59,6 +60,7 @@
 
   int ReserveForProducerInternal(const gfx::Size& dimensions,
                                  VideoPixelFormat format,
+                                 const mojom::PlaneStridesPtr& strides,
                                  int frame_feedback_id,
                                  int* tracker_id_to_drop);
 
diff --git a/media/capture/video/video_capture_buffer_tracker.h b/media/capture/video/video_capture_buffer_tracker.h
index a4f7dff..8c7ccf6c 100644
--- a/media/capture/video/video_capture_buffer_tracker.h
+++ b/media/capture/video/video_capture_buffer_tracker.h
@@ -8,6 +8,7 @@
 #include <memory>
 
 #include "base/synchronization/lock.h"
+#include "media/capture/mojom/video_capture_types.mojom.h"
 #include "media/capture/video/video_capture_buffer_handle.h"
 #include "media/capture/video_capture_types.h"
 #include "mojo/public/cpp/system/buffer.h"
@@ -19,19 +20,14 @@
 class CAPTURE_EXPORT VideoCaptureBufferTracker {
  public:
   VideoCaptureBufferTracker()
-      : max_pixel_count_(0),
-        held_by_producer_(false),
+      : held_by_producer_(false),
         consumer_hold_count_(0),
         frame_feedback_id_(0) {}
-  virtual bool Init(const gfx::Size& dimensions, VideoPixelFormat format) = 0;
+  virtual bool Init(const gfx::Size& dimensions,
+                    VideoPixelFormat format,
+                    const mojom::PlaneStridesPtr& strides) = 0;
   virtual ~VideoCaptureBufferTracker(){};
 
-  const gfx::Size& dimensions() const { return dimensions_; }
-  void set_dimensions(const gfx::Size& dim) { dimensions_ = dim; }
-  size_t max_pixel_count() const { return max_pixel_count_; }
-  void set_max_pixel_count(size_t count) { max_pixel_count_ = count; }
-  VideoPixelFormat pixel_format() const { return pixel_format_; }
-  void set_pixel_format(VideoPixelFormat format) { pixel_format_ = format; }
   bool held_by_producer() const { return held_by_producer_; }
   void set_held_by_producer(bool value) { held_by_producer_ = value; }
   int consumer_hold_count() const { return consumer_hold_count_; }
@@ -39,21 +35,18 @@
   void set_frame_feedback_id(int value) { frame_feedback_id_ = value; }
   int frame_feedback_id() { return frame_feedback_id_; }
 
+  virtual bool IsReusableForFormat(const gfx::Size& dimensions,
+                                   VideoPixelFormat format,
+                                   const mojom::PlaneStridesPtr& strides) = 0;
+  virtual uint32_t GetMemorySizeInBytes() = 0;
+
   virtual std::unique_ptr<VideoCaptureBufferHandle> GetMemoryMappedAccess() = 0;
   virtual mojo::ScopedSharedBufferHandle GetHandleForTransit(
       bool read_only) = 0;
   virtual base::SharedMemoryHandle
   GetNonOwnedSharedMemoryHandleForLegacyIPC() = 0;
-  virtual uint32_t GetMemorySizeInBytes() = 0;
 
  private:
-  // |dimensions_| may change as a VideoCaptureBufferTracker is re-used, but
-  // |max_pixel_count_|, |pixel_format_|, and |storage_type_| are set once for
-  // the lifetime of a VideoCaptureBufferTracker.
-  gfx::Size dimensions_;
-  size_t max_pixel_count_;
-  VideoPixelFormat pixel_format_;
-
   // Indicates whether this VideoCaptureBufferTracker is currently referenced by
   // the producer.
   bool held_by_producer_;
diff --git a/media/capture/video/video_capture_device_client.cc b/media/capture/video/video_capture_device_client.cc
index 4566a90..94725a3c 100644
--- a/media/capture/video/video_capture_device_client.cc
+++ b/media/capture/video/video_capture_device_client.cc
@@ -394,7 +394,7 @@
 
   int buffer_id_to_drop = VideoCaptureBufferPool::kInvalidId;
   const int buffer_id = buffer_pool_->ReserveForProducer(
-      frame_size, pixel_format, frame_feedback_id, &buffer_id_to_drop);
+      frame_size, pixel_format, nullptr, frame_feedback_id, &buffer_id_to_drop);
   if (buffer_id_to_drop != VideoCaptureBufferPool::kInvalidId) {
     // |buffer_pool_| has decided to release a buffer. Notify receiver in case
     // the buffer has already been shared with it.
diff --git a/media/test/PRESUBMIT.py b/media/test/PRESUBMIT.py
new file mode 100644
index 0000000..d622b9c
--- /dev/null
+++ b/media/test/PRESUBMIT.py
@@ -0,0 +1,34 @@
+# Copyright 2018 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Top-level presubmit script for media/test/.
+
+See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts
+for more details about the presubmit API built into depot_tools.
+"""
+
+def _CheckTestDataReadmeUpdated(input_api, output_api):
+  """
+  Checks to make sure the README file is updated when changing test files.
+  """
+  test_data_dir = input_api.os_path.join('media', 'test', 'data')
+  readme_path = input_api.os_path.join('media', 'test', 'data', 'README')
+  test_files = []
+  readme_updated = False
+  errors = []
+  for f in input_api.AffectedFiles():
+    local_path = f.LocalPath()
+    if input_api.os_path.dirname(local_path) == test_data_dir:
+      test_files.append(f)
+      if local_path == readme_path:
+        readme_updated = True
+        break
+  if test_files and not readme_updated:
+    errors.append(output_api.PresubmitPromptWarning(
+        'When updating files in ' + test_data_dir + ', please also update '
+        + readme_path + ':', test_files))
+  return errors
+
+def CheckChangeOnUpload(input_api, output_api):
+  return _CheckTestDataReadmeUpdated(input_api, output_api)
diff --git a/net/BUILD.gn b/net/BUILD.gn
index af82fbf6..92762958 100644
--- a/net/BUILD.gn
+++ b/net/BUILD.gn
@@ -1195,6 +1195,10 @@
       "third_party/http2/hpack/http2_hpack_constants.h",
       "third_party/http2/hpack/huffman/hpack_huffman_decoder.cc",
       "third_party/http2/hpack/huffman/hpack_huffman_decoder.h",
+      "third_party/http2/hpack/huffman/hpack_huffman_encoder.cc",
+      "third_party/http2/hpack/huffman/hpack_huffman_encoder.h",
+      "third_party/http2/hpack/huffman/huffman_spec_tables.cc",
+      "third_party/http2/hpack/huffman/huffman_spec_tables.h",
       "third_party/http2/hpack/varint/hpack_varint_decoder.cc",
       "third_party/http2/hpack/varint/hpack_varint_decoder.h",
       "third_party/http2/hpack/varint/hpack_varint_encoder.cc",
@@ -1331,6 +1335,8 @@
       "third_party/quic/core/frames/quic_connection_close_frame.cc",
       "third_party/quic/core/frames/quic_connection_close_frame.h",
       "third_party/quic/core/frames/quic_control_frame.h",
+      "third_party/quic/core/frames/quic_crypto_frame.cc",
+      "third_party/quic/core/frames/quic_crypto_frame.h",
       "third_party/quic/core/frames/quic_frame.cc",
       "third_party/quic/core/frames/quic_frame.h",
       "third_party/quic/core/frames/quic_goaway_frame.cc",
@@ -4919,6 +4925,8 @@
     "third_party/http2/hpack/hpack_string_test.cc",
     "third_party/http2/hpack/http2_hpack_constants_test.cc",
     "third_party/http2/hpack/huffman/hpack_huffman_decoder_test.cc",
+    "third_party/http2/hpack/huffman/hpack_huffman_encoder_test.cc",
+    "third_party/http2/hpack/huffman/hpack_huffman_transcoder_test.cc",
     "third_party/http2/hpack/tools/hpack_block_builder.cc",
     "third_party/http2/hpack/tools/hpack_block_builder.h",
     "third_party/http2/hpack/tools/hpack_block_builder_test.cc",
@@ -4932,6 +4940,9 @@
     "third_party/http2/http2_structures_test_util.cc",
     "third_party/http2/http2_structures_test_util.h",
     "third_party/http2/platform/api/http2_string_utils_test.cc",
+    "third_party/http2/platform/api/random_util_helper.h",
+    "third_party/http2/platform/impl/random_util_helper_impl.cc",
+    "third_party/http2/platform/impl/random_util_helper_impl.h",
     "third_party/http2/test_tools/frame_parts.cc",
     "third_party/http2/test_tools/frame_parts.h",
     "third_party/http2/test_tools/frame_parts_collector.cc",
diff --git a/net/extras/sqlite/sqlite_persistent_cookie_store.cc b/net/extras/sqlite/sqlite_persistent_cookie_store.cc
index 3c6ae95..5129846 100644
--- a/net/extras/sqlite/sqlite_persistent_cookie_store.cc
+++ b/net/extras/sqlite/sqlite_persistent_cookie_store.cc
@@ -13,6 +13,7 @@
 #include "base/callback.h"
 #include "base/debug/alias.h"
 #include "base/debug/dump_without_crashing.h"
+#include "base/feature_list.h"
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
 #include "base/location.h"
@@ -42,6 +43,11 @@
 
 namespace {
 
+// Changes the recommended priority of |background_task_runner| to
+// USER_BLOCKING.
+const base::Feature kCookieStorePriorityBoost{
+    "CookieStorePriorityBoost", base::FEATURE_DISABLED_BY_DEFAULT};
+
 std::unique_ptr<base::Value> CookieKeyedLoadNetLogCallback(
     const std::string& key,
     net::NetLogCaptureMode capture_mode) {
@@ -147,6 +153,12 @@
 
 namespace net {
 
+base::TaskPriority GetCookieStoreBackgroundSequencePriority() {
+  return base::FeatureList::IsEnabled(kCookieStorePriorityBoost)
+             ? base::TaskPriority::USER_BLOCKING
+             : base::TaskPriority::BEST_EFFORT;
+}
+
 // This class is designed to be shared between any client thread and the
 // background task runner. It batches operations and commits them on a timer.
 //
diff --git a/net/extras/sqlite/sqlite_persistent_cookie_store.h b/net/extras/sqlite/sqlite_persistent_cookie_store.h
index 7c4dddc3..7ea56e2 100644
--- a/net/extras/sqlite/sqlite_persistent_cookie_store.h
+++ b/net/extras/sqlite/sqlite_persistent_cookie_store.h
@@ -14,6 +14,7 @@
 #include "base/component_export.h"
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
+#include "base/task/task_traits.h"
 #include "net/cookies/cookie_monster.h"
 #include "net/log/net_log_with_source.h"
 
@@ -26,6 +27,10 @@
 class CanonicalCookie;
 class CookieCryptoDelegate;
 
+// Returns recommended task priority for |background_task_runner|.
+base::TaskPriority COMPONENT_EXPORT(NET_EXTRAS)
+    GetCookieStoreBackgroundSequencePriority();
+
 // Implements the PersistentCookieStore interface in terms of a SQLite database.
 // For documentation about the actual member functions consult the documentation
 // of the parent class |CookieMonster::PersistentCookieStore|.
diff --git a/net/quic/quic_flags_list.h b/net/quic/quic_flags_list.h
index 31426270..7a5f95e 100644
--- a/net/quic/quic_flags_list.h
+++ b/net/quic/quic_flags_list.h
@@ -161,7 +161,7 @@
 
 // If true, try to aggregate acked stream frames.
 QUIC_FLAG(bool,
-          FLAGS_quic_reloadable_flag_quic_aggregate_acked_stream_frames,
+          FLAGS_quic_reloadable_flag_quic_aggregate_acked_stream_frames_2,
           false)
 
 // If true, only process stateless reset packets on the client side.
@@ -184,9 +184,7 @@
 // If true, when session decides what to write, set a approximate retransmission
 // for packets to be retransmitted. Also check packet state in
 // IsPacketUsefulForRetransmittableData.
-QUIC_FLAG(bool,
-          FLAGS_quic_reloadable_flag_quic_fix_is_useful_for_retrans,
-          false)
+QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_fix_is_useful_for_retrans, true)
 
 // If true, QUIC connection will notify the debug visitor after a connectivity
 // probing is sent.
@@ -233,4 +231,7 @@
           false)
 
 // If true, enable version 45.
-QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_enable_version_45, false)
\ No newline at end of file
+QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_enable_version_45, false)
+
+// This flag fixes a bug where a zombie stream cannot be correctly reset.
+QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_fix_reset_zombie_streams, false)
diff --git a/net/socket/stream_socket.cc b/net/socket/stream_socket.cc
index 62b4d62..dd83d61 100644
--- a/net/socket/stream_socket.cc
+++ b/net/socket/stream_socket.cc
@@ -8,6 +8,11 @@
 
 namespace net {
 
+void StreamSocket::SetBeforeConnectCallback(
+    const BeforeConnectCallback& before_connect_callback) {
+  NOTREACHED();
+}
+
 void StreamSocket::GetSSLCertRequestInfo(
     SSLCertRequestInfo* cert_request_info) const {
   NOTREACHED();
diff --git a/net/socket/stream_socket.h b/net/socket/stream_socket.h
index 838b21af..1c5a6f06 100644
--- a/net/socket/stream_socket.h
+++ b/net/socket/stream_socket.h
@@ -7,6 +7,7 @@
 
 #include <stdint.h>
 
+#include "base/bind.h"
 #include "base/macros.h"
 #include "net/base/net_errors.h"
 #include "net/base/net_export.h"
@@ -30,6 +31,8 @@
 
 class NET_EXPORT StreamSocket : public Socket {
  public:
+  using BeforeConnectCallback = base::RepeatingCallback<int()>;
+
   // This is used in DumpMemoryStats() to track the estimate of memory usage of
   // a socket.
   struct NET_EXPORT_PRIVATE SocketMemoryStats {
@@ -51,6 +54,16 @@
 
   ~StreamSocket() override {}
 
+  // Sets a callback to be invoked before establishing a connection. This allows
+  // setting options, like receive and send buffer size, when they will take
+  // effect. The callback should return net::OK on success, and an error on
+  // failure. It must not return net::ERR_IO_PENDING.
+  //
+  // If multiple connection attempts are made, the callback will be invoked for
+  // each one.
+  virtual void SetBeforeConnectCallback(
+      const BeforeConnectCallback& before_connect_callback);
+
   // Called to establish a connection.  Returns OK if the connection could be
   // established synchronously.  Otherwise, ERR_IO_PENDING is returned and the
   // given callback will run asynchronously when the connection is established
diff --git a/net/socket/tcp_client_socket.cc b/net/socket/tcp_client_socket.cc
index de3be900..bf9f91e 100644
--- a/net/socket/tcp_client_socket.cc
+++ b/net/socket/tcp_client_socket.cc
@@ -77,6 +77,20 @@
   return OK;
 }
 
+bool TCPClientSocket::SetKeepAlive(bool enable, int delay) {
+  return socket_->SetKeepAlive(enable, delay);
+}
+
+bool TCPClientSocket::SetNoDelay(bool no_delay) {
+  return socket_->SetNoDelay(no_delay);
+}
+
+void TCPClientSocket::SetBeforeConnectCallback(
+    const BeforeConnectCallback& before_connect_callback) {
+  DCHECK_EQ(CONNECT_STATE_NONE, next_connect_state_);
+  before_connect_callback_ = before_connect_callback;
+}
+
 int TCPClientSocket::Connect(CompletionOnceCallback callback) {
   DCHECK(!callback.is_null());
 
@@ -179,6 +193,13 @@
     }
   }
 
+  if (before_connect_callback_) {
+    int result = before_connect_callback_.Run();
+    DCHECK_NE(ERR_IO_PENDING, result);
+    if (result != net::OK)
+      return result;
+  }
+
   // Notify |socket_performance_watcher_| only if the |socket_| is reused to
   // connect to a different IP Address.
   if (socket_performance_watcher_ && current_address_index_ != 0)
@@ -321,12 +342,8 @@
     return socket_->SetSendBufferSize(size);
 }
 
-bool TCPClientSocket::SetKeepAlive(bool enable, int delay) {
-  return socket_->SetKeepAlive(enable, delay);
-}
-
-bool TCPClientSocket::SetNoDelay(bool no_delay) {
-  return socket_->SetNoDelay(no_delay);
+SocketDescriptor TCPClientSocket::SocketDescriptorForTesting() const {
+  return socket_->SocketDescriptorForTesting();
 }
 
 void TCPClientSocket::GetConnectionAttempts(ConnectionAttempts* out) const {
diff --git a/net/socket/tcp_client_socket.h b/net/socket/tcp_client_socket.h
index d6e70f7..e0fc34e 100644
--- a/net/socket/tcp_client_socket.h
+++ b/net/socket/tcp_client_socket.h
@@ -15,6 +15,7 @@
 #include "net/base/completion_callback.h"
 #include "net/base/net_export.h"
 #include "net/socket/connection_attempts.h"
+#include "net/socket/socket_descriptor.h"
 #include "net/socket/stream_socket.h"
 #include "net/socket/tcp_socket.h"
 #include "net/socket/transport_client_socket.h"
@@ -51,6 +52,8 @@
   bool SetNoDelay(bool no_delay) override;
 
   // StreamSocket implementation.
+  void SetBeforeConnectCallback(
+      const BeforeConnectCallback& before_connect_callback) override;
   int Connect(CompletionOnceCallback callback) override;
   void Disconnect() override;
   bool IsConnected() const override;
@@ -86,6 +89,10 @@
   int SetReceiveBufferSize(int32_t size) override;
   int SetSendBufferSize(int32_t size) override;
 
+  // Exposes the underlying socket descriptor for testing its state. Does not
+  // release ownership of the descriptor.
+  SocketDescriptor SocketDescriptorForTesting() const;
+
  private:
   // State machine for connecting the socket.
   enum ConnectState {
@@ -155,6 +162,8 @@
   // Total number of bytes received by the socket.
   int64_t total_received_bytes_;
 
+  BeforeConnectCallback before_connect_callback_;
+
   bool was_ever_used_;
 
   DISALLOW_COPY_AND_ASSIGN(TCPClientSocket);
diff --git a/net/socket/tcp_socket_posix.cc b/net/socket/tcp_socket_posix.cc
index 5d45f7a9..4244c614 100644
--- a/net/socket/tcp_socket_posix.cc
+++ b/net/socket/tcp_socket_posix.cc
@@ -591,6 +591,10 @@
   return socket_descriptor;
 }
 
+SocketDescriptor TCPSocketPosix::SocketDescriptorForTesting() const {
+  return socket_->socket_fd();
+}
+
 void TCPSocketPosix::ApplySocketTag(const SocketTag& tag) {
   if (IsValid() && tag != tag_) {
     tag.Apply(socket_->socket_fd());
diff --git a/net/socket/tcp_socket_posix.h b/net/socket/tcp_socket_posix.h
index 4512945f..89ad520d 100644
--- a/net/socket/tcp_socket_posix.h
+++ b/net/socket/tcp_socket_posix.h
@@ -155,6 +155,10 @@
   // write, or accept operations should be pending.
   SocketDescriptor ReleaseSocketDescriptorForTesting();
 
+  // Exposes the underlying socket descriptor for testing its state. Does not
+  // release ownership of the descriptor.
+  SocketDescriptor SocketDescriptorForTesting() const;
+
   // Apply |tag| to this socket.
   void ApplySocketTag(const SocketTag& tag);
 
diff --git a/net/socket/tcp_socket_unittest.cc b/net/socket/tcp_socket_unittest.cc
index 7af6c12..9274746 100644
--- a/net/socket/tcp_socket_unittest.cc
+++ b/net/socket/tcp_socket_unittest.cc
@@ -12,6 +12,7 @@
 #include <vector>
 
 #include "base/memory/ref_counted.h"
+#include "base/test/bind_test_util.h"
 #include "base/time/time.h"
 #include "build/build_config.h"
 #include "net/base/address_list.h"
@@ -32,6 +33,13 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/platform_test.h"
 
+// For getsockopt() call.
+#if defined(OS_WIN)
+#include <winsock2.h>
+#else  // !defined(OS_WIN)
+#include <sys/socket.h>
+#endif  //  !defined(OS_WIN)
+
 using net::test::IsError;
 using net::test::IsOk;
 
@@ -588,6 +596,85 @@
   ASSERT_EQ(0, memcmp(&kMsg, read_buffer->data(), msg_size));
 }
 
+// Tests that setting a socket option in the BeforeConnectCallback works. With
+// real sockets, socket options often have to be set before the connect() call,
+// and the BeforeConnectCallback is the only way to do that, with a
+// TCPClientSocket.
+TEST_F(TCPSocketTest, BeforeConnectCallback) {
+  // A receive buffer size that is between max and minimum buffer size limits,
+  // and weird enough to likely not be a default value.
+  const int kReceiveBufferSize = 32 * 1024 + 1117;
+  ASSERT_NO_FATAL_FAILURE(SetUpListenIPv4());
+
+  TestCompletionCallback accept_callback;
+  std::unique_ptr<TCPSocket> accepted_socket;
+  IPEndPoint accepted_address;
+  EXPECT_THAT(socket_.Accept(&accepted_socket, &accepted_address,
+                             accept_callback.callback()),
+              IsError(ERR_IO_PENDING));
+
+  TestCompletionCallback connect_callback;
+  TCPClientSocket connecting_socket(local_address_list(), nullptr, nullptr,
+                                    NetLogSource());
+
+  connecting_socket.SetBeforeConnectCallback(base::BindLambdaForTesting([&] {
+    EXPECT_FALSE(connecting_socket.IsConnected());
+    int result = connecting_socket.SetReceiveBufferSize(kReceiveBufferSize);
+    EXPECT_THAT(result, IsOk());
+    return result;
+  }));
+  int connect_result = connecting_socket.Connect(connect_callback.callback());
+
+  EXPECT_THAT(accept_callback.WaitForResult(), IsOk());
+  EXPECT_THAT(connect_callback.GetResult(connect_result), IsOk());
+
+  int actual_size = 0;
+  socklen_t actual_size_len = sizeof(actual_size);
+  int os_result = getsockopt(
+      connecting_socket.SocketDescriptorForTesting(), SOL_SOCKET, SO_RCVBUF,
+      reinterpret_cast<char*>(&actual_size), &actual_size_len);
+  ASSERT_EQ(0, os_result);
+// Linux platforms generally allocate twice as much buffer size is requested to
+// account for internal kernel data structures.
+#if defined(OS_LINUX) || defined(OS_ANDROID)
+  EXPECT_EQ(2 * kReceiveBufferSize, actual_size);
+// Unfortunately, Apple platform behavior doesn't seem to be documented, and
+// doesn't match behavior on any other platforms.
+#elif !defined(OS_IOS) && !defined(OS_MACOSX)
+  EXPECT_EQ(kReceiveBufferSize, actual_size);
+#endif
+}
+
+TEST_F(TCPSocketTest, BeforeConnectCallbackFails) {
+  // Setting up a server isn't strictly necessary, but it does allow checking
+  // the server was never connected to.
+  ASSERT_NO_FATAL_FAILURE(SetUpListenIPv4());
+
+  TestCompletionCallback accept_callback;
+  std::unique_ptr<TCPSocket> accepted_socket;
+  IPEndPoint accepted_address;
+  EXPECT_THAT(socket_.Accept(&accepted_socket, &accepted_address,
+                             accept_callback.callback()),
+              IsError(ERR_IO_PENDING));
+
+  TestCompletionCallback connect_callback;
+  TCPClientSocket connecting_socket(local_address_list(), nullptr, nullptr,
+                                    NetLogSource());
+
+  // Set a callback that returns a nonsensical error, and make sure it's
+  // returned.
+  connecting_socket.SetBeforeConnectCallback(base::BindRepeating(
+      [] { return static_cast<int>(net::ERR_NAME_NOT_RESOLVED); }));
+  int connect_result = connecting_socket.Connect(connect_callback.callback());
+  EXPECT_THAT(connect_callback.GetResult(connect_result),
+              IsError(net::ERR_NAME_NOT_RESOLVED));
+
+  // Best effort check that the socket wasn't accepted - may flakily pass on
+  // regression, unfortunately.
+  base::RunLoop().RunUntilIdle();
+  EXPECT_FALSE(accept_callback.have_result());
+}
+
 // These tests require kernel support for tcp_info struct, and so they are
 // enabled only on certain platforms.
 #if defined(TCP_INFO) || defined(OS_LINUX)
diff --git a/net/socket/tcp_socket_win.cc b/net/socket/tcp_socket_win.cc
index e243102..2c44ca0 100644
--- a/net/socket/tcp_socket_win.cc
+++ b/net/socket/tcp_socket_win.cc
@@ -762,6 +762,10 @@
   return socket_descriptor;
 }
 
+SocketDescriptor TCPSocketWin::SocketDescriptorForTesting() const {
+  return socket_;
+}
+
 int TCPSocketWin::AcceptInternal(std::unique_ptr<TCPSocketWin>* socket,
                                  IPEndPoint* address) {
   SockaddrStorage storage;
diff --git a/net/socket/tcp_socket_win.h b/net/socket/tcp_socket_win.h
index 45189f54..b42210e 100644
--- a/net/socket/tcp_socket_win.h
+++ b/net/socket/tcp_socket_win.h
@@ -128,6 +128,10 @@
   // write, or accept operations should be pending.
   SocketDescriptor ReleaseSocketDescriptorForTesting();
 
+  // Exposes the underlying socket descriptor for testing its state. Does not
+  // release ownership of the descriptor.
+  SocketDescriptor SocketDescriptorForTesting() const;
+
   // Apply |tag| to this socket.
   void ApplySocketTag(const SocketTag& tag);
 
diff --git a/net/third_party/http2/hpack/huffman/hpack_huffman_encoder.cc b/net/third_party/http2/hpack/huffman/hpack_huffman_encoder.cc
new file mode 100644
index 0000000..7c77f88
--- /dev/null
+++ b/net/third_party/http2/hpack/huffman/hpack_huffman_encoder.cc
@@ -0,0 +1,106 @@
+#include "net/third_party/http2/hpack/huffman/hpack_huffman_encoder.h"
+
+#include "base/logging.h"
+#include "net/third_party/http2/hpack/huffman/huffman_spec_tables.h"
+
+// TODO(jamessynge): Remove use of binary literals, that is a C++ 14 feature.
+
+namespace http2 {
+
+size_t ExactHuffmanSize(Http2StringPiece plain) {
+  size_t bits = 0;
+  for (const uint8_t c : plain) {
+    bits += HuffmanSpecTables::kCodeLengths[c];
+  }
+  return (bits + 7) / 8;
+}
+
+size_t BoundedHuffmanSize(Http2StringPiece plain) {
+  // TODO(jamessynge): Determine whether we should set the min size for Huffman
+  // encoding much higher (i.e. if less than N, then the savings isn't worth
+  // the cost of encoding and decoding). Of course, we need to decide on a
+  // value function, which might be throughput on a full load test, or a
+  // microbenchmark of the time to encode and then decode a HEADERS frame,
+  // possibly with the cost of crypto included (i.e. crypto is going to have
+  // a fairly constant per-byte cost, so reducing the number of bytes in-transit
+  // reduces the number that must be encrypted and later decrypted).
+  if (plain.size() < 3) {
+    // Huffman encoded string can't be smaller than the plain size for very
+    // short strings.
+    return plain.size();
+  }
+  // TODO(jamessynge): Measure whether this can be done more efficiently with
+  // nested loops (e.g. make exact measurement of 8 bytes, then check if min
+  // remaining is too long).
+  // Compute the number of bits in an encoding that is shorter than the plain
+  // string (i.e. the number of bits in a string 1 byte shorter than plain),
+  // and use this as the limit of the size of the encoding.
+  const size_t limit_bits = (plain.size() - 1) * 8;
+  // The shortest code length in the Huffman table of the HPACK spec has 5 bits
+  // (e.g. for 0, 1, a and e).
+  const size_t min_code_length = 5;
+  // We can therefore say that all plain text bytes whose code length we've not
+  // yet looked up will take at least 5 bits.
+  size_t min_bits_remaining = plain.size() * min_code_length;
+  size_t bits = 0;
+  for (const uint8_t c : plain) {
+    bits += HuffmanSpecTables::kCodeLengths[c];
+    min_bits_remaining -= min_code_length;
+    // If our minimum estimate of the total number of bits won't yield an
+    // encoding shorter the plain text, let's bail.
+    const size_t minimum_bits_total = bits + min_bits_remaining;
+    if (minimum_bits_total > limit_bits) {
+      bits += min_bits_remaining;
+      break;
+    }
+  }
+  return (bits + 7) / 8;
+}
+
+void HuffmanEncode(Http2StringPiece plain, Http2String* huffman) {
+  DCHECK(huffman != nullptr);
+  huffman->clear();         // Note that this doesn't release memory.
+  uint64_t bit_buffer = 0;  // High-bit is next bit to output. Not clear if that
+                            // is more performant than having the low-bit be the
+                            // last to be output.
+  size_t bits_unused = 64;  // Number of bits available for the next code.
+  for (uint8_t c : plain) {
+    size_t code_length = HuffmanSpecTables::kCodeLengths[c];
+    if (bits_unused < code_length) {
+      // There isn't enough room in bit_buffer for the code of c.
+      // Flush until bits_unused > 56 (i.e. 64 - 8).
+      do {
+        char h = static_cast<char>(bit_buffer >> 56);
+        bit_buffer <<= 8;
+        bits_unused += 8;
+        // Perhaps would be more efficient if we populated an array of chars,
+        // so we don't have to call push_back each time. Reconsider if used
+        // for production.
+        huffman->push_back(h);
+      } while (bits_unused <= 56);
+    }
+    uint64_t code = HuffmanSpecTables::kRightCodes[c];
+    size_t shift_by = bits_unused - code_length;
+    bit_buffer |= (code << shift_by);
+    bits_unused -= code_length;
+  }
+  // bit_buffer contains (64-bits_unused) bits that still need to be flushed.
+  // Output whole bytes until we don't have any whole bytes left.
+  size_t bits_used = 64 - bits_unused;
+  while (bits_used >= 8) {
+    char h = static_cast<char>(bit_buffer >> 56);
+    bit_buffer <<= 8;
+    bits_used -= 8;
+    huffman->push_back(h);
+  }
+  if (bits_used > 0) {
+    // We have less than a byte left to output. The spec calls for padding out
+    // the final byte with the leading bits of the EOS symbol (30 1-bits).
+    constexpr uint64_t leading_eos_bits = 0b11111111;
+    bit_buffer |= (leading_eos_bits << (56 - bits_used));
+    char h = static_cast<char>(bit_buffer >> 56);
+    huffman->push_back(h);
+  }
+}
+
+}  // namespace http2
diff --git a/net/third_party/http2/hpack/huffman/hpack_huffman_encoder.h b/net/third_party/http2/hpack/huffman/hpack_huffman_encoder.h
new file mode 100644
index 0000000..51d3435
--- /dev/null
+++ b/net/third_party/http2/hpack/huffman/hpack_huffman_encoder.h
@@ -0,0 +1,40 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_THIRD_PARTY_HTTP2_HPACK_HUFFMAN_HPACK_HUFFMAN_ENCODER_H_
+#define NET_THIRD_PARTY_HTTP2_HPACK_HUFFMAN_HPACK_HUFFMAN_ENCODER_H_
+
+// Functions supporting the encoding of strings using the HPACK-defined Huffman
+// table.
+
+#include <cstddef>  // For size_t
+
+#include "net/third_party/http2/platform/api/http2_export.h"
+#include "net/third_party/http2/platform/api/http2_string.h"
+#include "net/third_party/http2/platform/api/http2_string_piece.h"
+
+namespace http2 {
+
+// Returns the size of the Huffman encoding of |plain|, which may be greater
+// than plain.size(). Mostly present for testing.
+HTTP2_EXPORT_PRIVATE size_t ExactHuffmanSize(Http2StringPiece plain);
+
+// Returns the size of the Huffman encoding of |plain|, unless it is greater
+// than or equal to plain.size(), in which case a value greater than or equal to
+// plain.size() is returned. The advantage of this over ExactHuffmanSize is that
+// it doesn't read as much of the input string in the event that the string is
+// not compressible by HuffmanEncode (i.e. when the encoding is longer than the
+// original string, it stops reading the input string as soon as it knows that).
+HTTP2_EXPORT_PRIVATE size_t BoundedHuffmanSize(Http2StringPiece plain);
+
+// Encode the plain text string |plain| with the Huffman encoding defined in
+// the HPACK RFC, 7541.  |*huffman| does not have to be empty, it is cleared at
+// the beginning of this function.  This allows reusing the same string object
+// across multiple invocations.
+HTTP2_EXPORT_PRIVATE void HuffmanEncode(Http2StringPiece plain,
+                                        Http2String* huffman);
+
+}  // namespace http2
+
+#endif  // NET_THIRD_PARTY_HTTP2_HPACK_HUFFMAN_HPACK_HUFFMAN_DECODER_H_
diff --git a/net/third_party/http2/hpack/huffman/hpack_huffman_encoder_test.cc b/net/third_party/http2/hpack/huffman/hpack_huffman_encoder_test.cc
new file mode 100644
index 0000000..d75e75b
--- /dev/null
+++ b/net/third_party/http2/hpack/huffman/hpack_huffman_encoder_test.cc
@@ -0,0 +1,93 @@
+#include "net/third_party/http2/hpack/huffman/hpack_huffman_encoder.h"
+
+#include "base/macros.h"
+#include "base/stl_util.h"
+#include "net/third_party/http2/platform/api/http2_string_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace http2 {
+namespace {
+
+TEST(HuffmanEncoderTest, SpecRequestExamples) {
+  Http2String test_table[] = {
+      Http2HexDecode("f1e3c2e5f23a6ba0ab90f4ff"),
+      "www.example.com",
+      Http2HexDecode("a8eb10649cbf"),
+      "no-cache",
+      Http2HexDecode("25a849e95ba97d7f"),
+      "custom-key",
+      Http2HexDecode("25a849e95bb8e8b4bf"),
+      "custom-value",
+  };
+  for (size_t i = 0; i != base::size(test_table); i += 2) {
+    const Http2String& huffman_encoded(test_table[i]);
+    const Http2String& plain_string(test_table[i + 1]);
+    EXPECT_EQ(ExactHuffmanSize(plain_string), huffman_encoded.size());
+    EXPECT_EQ(BoundedHuffmanSize(plain_string), huffman_encoded.size());
+    Http2String buffer;
+    buffer.reserve();
+    HuffmanEncode(plain_string, &buffer);
+    EXPECT_EQ(buffer, huffman_encoded) << "Error encoding " << plain_string;
+  }
+}
+
+TEST(HuffmanEncoderTest, SpecResponseExamples) {
+  // clang-format off
+  Http2String test_table[] = {
+    Http2HexDecode("6402"),
+    "302",
+    Http2HexDecode("aec3771a4b"),
+    "private",
+    Http2HexDecode("d07abe941054d444a8200595040b8166"
+            "e082a62d1bff"),
+    "Mon, 21 Oct 2013 20:13:21 GMT",
+    Http2HexDecode("9d29ad171863c78f0b97c8e9ae82ae43"
+            "d3"),
+    "https://www.example.com",
+    Http2HexDecode("94e7821dd7f2e6c7b335dfdfcd5b3960"
+            "d5af27087f3672c1ab270fb5291f9587"
+            "316065c003ed4ee5b1063d5007"),
+    "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1",
+  };
+  // clang-format on
+  for (size_t i = 0; i != base::size(test_table); i += 2) {
+    const Http2String& huffman_encoded(test_table[i]);
+    const Http2String& plain_string(test_table[i + 1]);
+    EXPECT_EQ(ExactHuffmanSize(plain_string), huffman_encoded.size());
+    EXPECT_EQ(BoundedHuffmanSize(plain_string), huffman_encoded.size());
+    Http2String buffer;
+    buffer.reserve(huffman_encoded.size());
+    const size_t capacity = buffer.capacity();
+    HuffmanEncode(plain_string, &buffer);
+    EXPECT_EQ(buffer, huffman_encoded) << "Error encoding " << plain_string;
+    EXPECT_EQ(capacity, buffer.capacity());
+  }
+}
+
+TEST(HuffmanEncoderTest, EncodedSizeAgreesWithEncodeString) {
+  Http2String test_table[] = {
+      "",
+      "Mon, 21 Oct 2013 20:13:21 GMT",
+      "https://www.example.com",
+      "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1",
+      Http2String(1, '\0'),
+      Http2String("foo\0bar", 7),
+      Http2String(256, '\0'),
+  };
+  // Modify last |test_table| entry to cover all codes.
+  for (size_t i = 0; i != 256; ++i) {
+    test_table[base::size(test_table) - 1][i] = static_cast<char>(i);
+  }
+
+  for (size_t i = 0; i != base::size(test_table); ++i) {
+    const Http2String& plain_string = test_table[i];
+    Http2String huffman_encoded;
+    HuffmanEncode(plain_string, &huffman_encoded);
+    EXPECT_EQ(huffman_encoded.size(), ExactHuffmanSize(plain_string));
+    EXPECT_LE(BoundedHuffmanSize(plain_string), plain_string.size());
+    EXPECT_LE(BoundedHuffmanSize(plain_string), ExactHuffmanSize(plain_string));
+  }
+}
+
+}  // namespace
+}  // namespace http2
diff --git a/net/third_party/http2/hpack/huffman/hpack_huffman_transcoder_test.cc b/net/third_party/http2/hpack/huffman/hpack_huffman_transcoder_test.cc
new file mode 100644
index 0000000..bd8dd15f
--- /dev/null
+++ b/net/third_party/http2/hpack/huffman/hpack_huffman_transcoder_test.cc
@@ -0,0 +1,181 @@
+// A test of roundtrips through the encoder and decoder.
+
+#include <stddef.h>
+
+#include "net/third_party/http2/decoder/decode_buffer.h"
+#include "net/third_party/http2/decoder/decode_status.h"
+#include "net/third_party/http2/hpack/huffman/hpack_huffman_decoder.h"
+#include "net/third_party/http2/hpack/huffman/hpack_huffman_encoder.h"
+#include "net/third_party/http2/platform/api/http2_string.h"
+#include "net/third_party/http2/platform/api/http2_string_piece.h"
+#include "net/third_party/http2/platform/api/http2_string_utils.h"
+#include "net/third_party/http2/platform/api/random_util_helper.h"
+#include "net/third_party/http2/tools/random_decoder_test.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::testing::AssertionResult;
+using ::testing::AssertionSuccess;
+using ::testing::tuple;
+
+namespace http2 {
+namespace test {
+namespace {
+
+Http2String GenAsciiNonControlSet() {
+  Http2String s;
+  const char space = ' ';  // First character after the control characters: 0x20
+  const char del = 127;    // First character after the non-control characters.
+  for (char c = space; c < del; ++c) {
+    s.push_back(c);
+  }
+  return s;
+}
+
+class HpackHuffmanTranscoderTest : public RandomDecoderTest {
+ protected:
+  HpackHuffmanTranscoderTest()
+      : ascii_non_control_set_(GenAsciiNonControlSet()) {
+    // The decoder may return true, and its accumulator may be empty, at
+    // many boundaries while decoding, and yet the whole string hasn't
+    // been decoded.
+    stop_decode_on_done_ = false;
+  }
+
+  DecodeStatus StartDecoding(DecodeBuffer* b) override {
+    input_bytes_seen_ = 0;
+    output_buffer_.clear();
+    decoder_.Reset();
+    return ResumeDecoding(b);
+  }
+
+  DecodeStatus ResumeDecoding(DecodeBuffer* b) override {
+    input_bytes_seen_ += b->Remaining();
+    Http2StringPiece sp(b->cursor(), b->Remaining());
+    if (decoder_.Decode(sp, &output_buffer_)) {
+      b->AdvanceCursor(b->Remaining());
+      // Successfully decoded (or buffered) the bytes in Http2StringPiece.
+      EXPECT_LE(input_bytes_seen_, input_bytes_expected_);
+      // Have we reached the end of the encoded string?
+      if (input_bytes_expected_ == input_bytes_seen_) {
+        if (decoder_.InputProperlyTerminated()) {
+          return DecodeStatus::kDecodeDone;
+        } else {
+          return DecodeStatus::kDecodeError;
+        }
+      }
+      return DecodeStatus::kDecodeInProgress;
+    }
+    return DecodeStatus::kDecodeError;
+  }
+
+  AssertionResult TranscodeAndValidateSeveralWays(
+      Http2StringPiece plain,
+      Http2StringPiece expected_huffman) {
+    Http2String encoded;
+    HuffmanEncode(plain, &encoded);
+    if (expected_huffman.size() > 0 || plain.empty()) {
+      VERIFY_EQ(encoded, expected_huffman);
+    }
+    input_bytes_expected_ = encoded.size();
+    auto validator = [plain, this]() -> AssertionResult {
+      VERIFY_EQ(output_buffer_.size(), plain.size());
+      VERIFY_EQ(output_buffer_, plain);
+      return AssertionSuccess();
+    };
+    DecodeBuffer db(encoded);
+    bool return_non_zero_on_first = false;
+    return DecodeAndValidateSeveralWays(&db, return_non_zero_on_first,
+                                        ValidateDoneAndEmpty(validator));
+  }
+
+  AssertionResult TranscodeAndValidateSeveralWays(Http2StringPiece plain) {
+    return TranscodeAndValidateSeveralWays(plain, "");
+  }
+
+  Http2String RandomAsciiNonControlString(int length) {
+    return RandomString(RandomPtr(), length, ascii_non_control_set_);
+  }
+
+  Http2String RandomBytes(int length) { return Random().RandString(length); }
+
+  const Http2String ascii_non_control_set_;
+  HpackHuffmanDecoder decoder_;
+  Http2String output_buffer_;
+  size_t input_bytes_seen_;
+  size_t input_bytes_expected_;
+};
+
+TEST_F(HpackHuffmanTranscoderTest, RoundTripRandomAsciiNonControlString) {
+  for (size_t length = 0; length != 20; length++) {
+    const Http2String s = RandomAsciiNonControlString(length);
+    ASSERT_TRUE(TranscodeAndValidateSeveralWays(s))
+        << "Unable to decode:\n\n"
+        << Http2HexDump(s) << "\n\noutput_buffer_:\n"
+        << Http2HexDump(output_buffer_);
+  }
+}
+
+TEST_F(HpackHuffmanTranscoderTest, RoundTripRandomBytes) {
+  for (size_t length = 0; length != 20; length++) {
+    const Http2String s = RandomBytes(length);
+    ASSERT_TRUE(TranscodeAndValidateSeveralWays(s))
+        << "Unable to decode:\n\n"
+        << Http2HexDump(s) << "\n\noutput_buffer_:\n"
+        << Http2HexDump(output_buffer_);
+  }
+}
+
+// Two parameters: decoder choice, and the character to round-trip.
+class HpackHuffmanTranscoderAdjacentCharTest
+    : public HpackHuffmanTranscoderTest,
+      public ::testing::WithParamInterface<int> {
+ protected:
+  HpackHuffmanTranscoderAdjacentCharTest()
+      : c_(static_cast<char>(GetParam())) {}
+
+  const char c_;
+};
+
+INSTANTIATE_TEST_CASE_P(HpackHuffmanTranscoderAdjacentCharTest,
+                        HpackHuffmanTranscoderAdjacentCharTest,
+                        ::testing::Range(0, 256));
+
+// Test c_ adjacent to every other character, both before and after.
+TEST_P(HpackHuffmanTranscoderAdjacentCharTest, RoundTripAdjacentChar) {
+  Http2String s;
+  for (int a = 0; a < 256; ++a) {
+    s.push_back(static_cast<char>(a));
+    s.push_back(c_);
+    s.push_back(static_cast<char>(a));
+  }
+  ASSERT_TRUE(TranscodeAndValidateSeveralWays(s));
+}
+
+// Two parameters: character to repeat, number of repeats.
+class HpackHuffmanTranscoderRepeatedCharTest
+    : public HpackHuffmanTranscoderTest,
+      public ::testing::WithParamInterface<tuple<int, int>> {
+ protected:
+  HpackHuffmanTranscoderRepeatedCharTest()
+      : c_(static_cast<char>(::testing::get<0>(GetParam()))),
+        length_(::testing::get<1>(GetParam())) {}
+  Http2String MakeString() { return Http2String(length_, c_); }
+
+ private:
+  const char c_;
+  const size_t length_;
+};
+
+INSTANTIATE_TEST_CASE_P(
+    HpackHuffmanTranscoderRepeatedCharTest,
+    HpackHuffmanTranscoderRepeatedCharTest,
+    ::testing::Combine(::testing::Range(0, 256),
+                       ::testing::Values(1, 2, 3, 4, 8, 16, 32)));
+
+TEST_P(HpackHuffmanTranscoderRepeatedCharTest, RoundTripRepeatedChar) {
+  ASSERT_TRUE(TranscodeAndValidateSeveralWays(MakeString()));
+}
+
+}  // namespace
+}  // namespace test
+}  // namespace http2
diff --git a/net/third_party/http2/hpack/huffman/huffman_spec_tables.cc b/net/third_party/http2/hpack/huffman/huffman_spec_tables.cc
new file mode 100644
index 0000000..db15f99
--- /dev/null
+++ b/net/third_party/http2/hpack/huffman/huffman_spec_tables.cc
@@ -0,0 +1,574 @@
+#include "net/third_party/http2/hpack/huffman/huffman_spec_tables.h"
+
+namespace http2 {
+
+// clang-format off
+// static
+const uint8_t HuffmanSpecTables::kCodeLengths[] = {
+    13, 23, 28, 28, 28, 28, 28, 28,  //   0 -   7
+    28, 24, 30, 28, 28, 30, 28, 28,  //   8 -  15
+    28, 28, 28, 28, 28, 28, 30, 28,  //  16 -  23
+    28, 28, 28, 28, 28, 28, 28, 28,  //  24 -  31
+     6, 10, 10, 12, 13,  6,  8, 11,  //  32 -  39
+    10, 10,  8, 11,  8,  6,  6,  6,  //  40 -  47
+     5,  5,  5,  6,  6,  6,  6,  6,  //  48 -  55
+     6,  6,  7,  8, 15,  6, 12, 10,  //  56 -  63
+    13,  6,  7,  7,  7,  7,  7,  7,  //  64 -  71
+     7,  7,  7,  7,  7,  7,  7,  7,  //  72 -  79
+     7,  7,  7,  7,  7,  7,  7,  7,  //  80 -  87
+     8,  7,  8, 13, 19, 13, 14,  6,  //  88 -  95
+    15,  5,  6,  5,  6,  5,  6,  6,  //  96 - 103
+     6,  5,  7,  7,  6,  6,  6,  5,  // 104 - 111
+     6,  7,  6,  5,  5,  6,  7,  7,  // 112 - 119
+     7,  7,  7, 15, 11, 14, 13, 28,  // 120 - 127
+    20, 22, 20, 20, 22, 22, 22, 23,  // 128 - 135
+    22, 23, 23, 23, 23, 23, 24, 23,  // 136 - 143
+    24, 24, 22, 23, 24, 23, 23, 23,  // 144 - 151
+    23, 21, 22, 23, 22, 23, 23, 24,  // 152 - 159
+    22, 21, 20, 22, 22, 23, 23, 21,  // 160 - 167
+    23, 22, 22, 24, 21, 22, 23, 23,  // 168 - 175
+    21, 21, 22, 21, 23, 22, 23, 23,  // 176 - 183
+    20, 22, 22, 22, 23, 22, 22, 23,  // 184 - 191
+    26, 26, 20, 19, 22, 23, 22, 25,  // 192 - 199
+    26, 26, 26, 27, 27, 26, 24, 25,  // 200 - 207
+    19, 21, 26, 27, 27, 26, 27, 24,  // 208 - 215
+    21, 21, 26, 26, 28, 27, 27, 27,  // 216 - 223
+    20, 24, 20, 21, 22, 21, 21, 23,  // 224 - 231
+    22, 22, 25, 25, 24, 24, 26, 23,  // 232 - 239
+    26, 27, 26, 26, 27, 27, 27, 27,  // 240 - 247
+    27, 28, 27, 27, 27, 27, 27, 26,  // 248 - 255
+    30,                              // 256
+};
+
+// TODO(jamessynge): Remove use of binary literals, that is a C++ 14 feature.
+
+// Uncomment these codes if needed for generating Huffman output, as opposed
+// to decoding Huffman input.
+/*
+// The encoding of each symbol, left justified (as printed), which means that
+// the first bit of the encoding is the high-order bit of the uint32.
+// static
+const uint32_t HuffmanSpecTables::kLeftCodes[] = {
+    0b11111111110000000000000000000000,  // 0x00
+    0b11111111111111111011000000000000,  // 0x01
+    0b11111111111111111111111000100000,  // 0x02
+    0b11111111111111111111111000110000,  // 0x03
+    0b11111111111111111111111001000000,  // 0x04
+    0b11111111111111111111111001010000,  // 0x05
+    0b11111111111111111111111001100000,  // 0x06
+    0b11111111111111111111111001110000,  // 0x07
+    0b11111111111111111111111010000000,  // 0x08
+    0b11111111111111111110101000000000,  // 0x09
+    0b11111111111111111111111111110000,  // 0x0a
+    0b11111111111111111111111010010000,  // 0x0b
+    0b11111111111111111111111010100000,  // 0x0c
+    0b11111111111111111111111111110100,  // 0x0d
+    0b11111111111111111111111010110000,  // 0x0e
+    0b11111111111111111111111011000000,  // 0x0f
+    0b11111111111111111111111011010000,  // 0x10
+    0b11111111111111111111111011100000,  // 0x11
+    0b11111111111111111111111011110000,  // 0x12
+    0b11111111111111111111111100000000,  // 0x13
+    0b11111111111111111111111100010000,  // 0x14
+    0b11111111111111111111111100100000,  // 0x15
+    0b11111111111111111111111111111000,  // 0x16
+    0b11111111111111111111111100110000,  // 0x17
+    0b11111111111111111111111101000000,  // 0x18
+    0b11111111111111111111111101010000,  // 0x19
+    0b11111111111111111111111101100000,  // 0x1a
+    0b11111111111111111111111101110000,  // 0x1b
+    0b11111111111111111111111110000000,  // 0x1c
+    0b11111111111111111111111110010000,  // 0x1d
+    0b11111111111111111111111110100000,  // 0x1e
+    0b11111111111111111111111110110000,  // 0x1f
+    0b01010000000000000000000000000000,  // 0x20
+    0b11111110000000000000000000000000,  // '!'
+    0b11111110010000000000000000000000,  // '\"'
+    0b11111111101000000000000000000000,  // '#'
+    0b11111111110010000000000000000000,  // '$'
+    0b01010100000000000000000000000000,  // '%'
+    0b11111000000000000000000000000000,  // '&'
+    0b11111111010000000000000000000000,  // '\''
+    0b11111110100000000000000000000000,  // '('
+    0b11111110110000000000000000000000,  // ')'
+    0b11111001000000000000000000000000,  // '*'
+    0b11111111011000000000000000000000,  // '+'
+    0b11111010000000000000000000000000,  // ','
+    0b01011000000000000000000000000000,  // '-'
+    0b01011100000000000000000000000000,  // '.'
+    0b01100000000000000000000000000000,  // '/'
+    0b00000000000000000000000000000000,  // '0'
+    0b00001000000000000000000000000000,  // '1'
+    0b00010000000000000000000000000000,  // '2'
+    0b01100100000000000000000000000000,  // '3'
+    0b01101000000000000000000000000000,  // '4'
+    0b01101100000000000000000000000000,  // '5'
+    0b01110000000000000000000000000000,  // '6'
+    0b01110100000000000000000000000000,  // '7'
+    0b01111000000000000000000000000000,  // '8'
+    0b01111100000000000000000000000000,  // '9'
+    0b10111000000000000000000000000000,  // ':'
+    0b11111011000000000000000000000000,  // ';'
+    0b11111111111110000000000000000000,  // '<'
+    0b10000000000000000000000000000000,  // '='
+    0b11111111101100000000000000000000,  // '>'
+    0b11111111000000000000000000000000,  // '?'
+    0b11111111110100000000000000000000,  // '@'
+    0b10000100000000000000000000000000,  // 'A'
+    0b10111010000000000000000000000000,  // 'B'
+    0b10111100000000000000000000000000,  // 'C'
+    0b10111110000000000000000000000000,  // 'D'
+    0b11000000000000000000000000000000,  // 'E'
+    0b11000010000000000000000000000000,  // 'F'
+    0b11000100000000000000000000000000,  // 'G'
+    0b11000110000000000000000000000000,  // 'H'
+    0b11001000000000000000000000000000,  // 'I'
+    0b11001010000000000000000000000000,  // 'J'
+    0b11001100000000000000000000000000,  // 'K'
+    0b11001110000000000000000000000000,  // 'L'
+    0b11010000000000000000000000000000,  // 'M'
+    0b11010010000000000000000000000000,  // 'N'
+    0b11010100000000000000000000000000,  // 'O'
+    0b11010110000000000000000000000000,  // 'P'
+    0b11011000000000000000000000000000,  // 'Q'
+    0b11011010000000000000000000000000,  // 'R'
+    0b11011100000000000000000000000000,  // 'S'
+    0b11011110000000000000000000000000,  // 'T'
+    0b11100000000000000000000000000000,  // 'U'
+    0b11100010000000000000000000000000,  // 'V'
+    0b11100100000000000000000000000000,  // 'W'
+    0b11111100000000000000000000000000,  // 'X'
+    0b11100110000000000000000000000000,  // 'Y'
+    0b11111101000000000000000000000000,  // 'Z'
+    0b11111111110110000000000000000000,  // '['
+    0b11111111111111100000000000000000,  // '\\'
+    0b11111111111000000000000000000000,  // ']'
+    0b11111111111100000000000000000000,  // '^'
+    0b10001000000000000000000000000000,  // '_'
+    0b11111111111110100000000000000000,  // '`'
+    0b00011000000000000000000000000000,  // 'a'
+    0b10001100000000000000000000000000,  // 'b'
+    0b00100000000000000000000000000000,  // 'c'
+    0b10010000000000000000000000000000,  // 'd'
+    0b00101000000000000000000000000000,  // 'e'
+    0b10010100000000000000000000000000,  // 'f'
+    0b10011000000000000000000000000000,  // 'g'
+    0b10011100000000000000000000000000,  // 'h'
+    0b00110000000000000000000000000000,  // 'i'
+    0b11101000000000000000000000000000,  // 'j'
+    0b11101010000000000000000000000000,  // 'k'
+    0b10100000000000000000000000000000,  // 'l'
+    0b10100100000000000000000000000000,  // 'm'
+    0b10101000000000000000000000000000,  // 'n'
+    0b00111000000000000000000000000000,  // 'o'
+    0b10101100000000000000000000000000,  // 'p'
+    0b11101100000000000000000000000000,  // 'q'
+    0b10110000000000000000000000000000,  // 'r'
+    0b01000000000000000000000000000000,  // 's'
+    0b01001000000000000000000000000000,  // 't'
+    0b10110100000000000000000000000000,  // 'u'
+    0b11101110000000000000000000000000,  // 'v'
+    0b11110000000000000000000000000000,  // 'w'
+    0b11110010000000000000000000000000,  // 'x'
+    0b11110100000000000000000000000000,  // 'y'
+    0b11110110000000000000000000000000,  // 'z'
+    0b11111111111111000000000000000000,  // '{'
+    0b11111111100000000000000000000000,  // '|'
+    0b11111111111101000000000000000000,  // '}'
+    0b11111111111010000000000000000000,  // '~'
+    0b11111111111111111111111111000000,  // 0x7f
+    0b11111111111111100110000000000000,  // 0x80
+    0b11111111111111110100100000000000,  // 0x81
+    0b11111111111111100111000000000000,  // 0x82
+    0b11111111111111101000000000000000,  // 0x83
+    0b11111111111111110100110000000000,  // 0x84
+    0b11111111111111110101000000000000,  // 0x85
+    0b11111111111111110101010000000000,  // 0x86
+    0b11111111111111111011001000000000,  // 0x87
+    0b11111111111111110101100000000000,  // 0x88
+    0b11111111111111111011010000000000,  // 0x89
+    0b11111111111111111011011000000000,  // 0x8a
+    0b11111111111111111011100000000000,  // 0x8b
+    0b11111111111111111011101000000000,  // 0x8c
+    0b11111111111111111011110000000000,  // 0x8d
+    0b11111111111111111110101100000000,  // 0x8e
+    0b11111111111111111011111000000000,  // 0x8f
+    0b11111111111111111110110000000000,  // 0x90
+    0b11111111111111111110110100000000,  // 0x91
+    0b11111111111111110101110000000000,  // 0x92
+    0b11111111111111111100000000000000,  // 0x93
+    0b11111111111111111110111000000000,  // 0x94
+    0b11111111111111111100001000000000,  // 0x95
+    0b11111111111111111100010000000000,  // 0x96
+    0b11111111111111111100011000000000,  // 0x97
+    0b11111111111111111100100000000000,  // 0x98
+    0b11111111111111101110000000000000,  // 0x99
+    0b11111111111111110110000000000000,  // 0x9a
+    0b11111111111111111100101000000000,  // 0x9b
+    0b11111111111111110110010000000000,  // 0x9c
+    0b11111111111111111100110000000000,  // 0x9d
+    0b11111111111111111100111000000000,  // 0x9e
+    0b11111111111111111110111100000000,  // 0x9f
+    0b11111111111111110110100000000000,  // 0xa0
+    0b11111111111111101110100000000000,  // 0xa1
+    0b11111111111111101001000000000000,  // 0xa2
+    0b11111111111111110110110000000000,  // 0xa3
+    0b11111111111111110111000000000000,  // 0xa4
+    0b11111111111111111101000000000000,  // 0xa5
+    0b11111111111111111101001000000000,  // 0xa6
+    0b11111111111111101111000000000000,  // 0xa7
+    0b11111111111111111101010000000000,  // 0xa8
+    0b11111111111111110111010000000000,  // 0xa9
+    0b11111111111111110111100000000000,  // 0xaa
+    0b11111111111111111111000000000000,  // 0xab
+    0b11111111111111101111100000000000,  // 0xac
+    0b11111111111111110111110000000000,  // 0xad
+    0b11111111111111111101011000000000,  // 0xae
+    0b11111111111111111101100000000000,  // 0xaf
+    0b11111111111111110000000000000000,  // 0xb0
+    0b11111111111111110000100000000000,  // 0xb1
+    0b11111111111111111000000000000000,  // 0xb2
+    0b11111111111111110001000000000000,  // 0xb3
+    0b11111111111111111101101000000000,  // 0xb4
+    0b11111111111111111000010000000000,  // 0xb5
+    0b11111111111111111101110000000000,  // 0xb6
+    0b11111111111111111101111000000000,  // 0xb7
+    0b11111111111111101010000000000000,  // 0xb8
+    0b11111111111111111000100000000000,  // 0xb9
+    0b11111111111111111000110000000000,  // 0xba
+    0b11111111111111111001000000000000,  // 0xbb
+    0b11111111111111111110000000000000,  // 0xbc
+    0b11111111111111111001010000000000,  // 0xbd
+    0b11111111111111111001100000000000,  // 0xbe
+    0b11111111111111111110001000000000,  // 0xbf
+    0b11111111111111111111100000000000,  // 0xc0
+    0b11111111111111111111100001000000,  // 0xc1
+    0b11111111111111101011000000000000,  // 0xc2
+    0b11111111111111100010000000000000,  // 0xc3
+    0b11111111111111111001110000000000,  // 0xc4
+    0b11111111111111111110010000000000,  // 0xc5
+    0b11111111111111111010000000000000,  // 0xc6
+    0b11111111111111111111011000000000,  // 0xc7
+    0b11111111111111111111100010000000,  // 0xc8
+    0b11111111111111111111100011000000,  // 0xc9
+    0b11111111111111111111100100000000,  // 0xca
+    0b11111111111111111111101111000000,  // 0xcb
+    0b11111111111111111111101111100000,  // 0xcc
+    0b11111111111111111111100101000000,  // 0xcd
+    0b11111111111111111111000100000000,  // 0xce
+    0b11111111111111111111011010000000,  // 0xcf
+    0b11111111111111100100000000000000,  // 0xd0
+    0b11111111111111110001100000000000,  // 0xd1
+    0b11111111111111111111100110000000,  // 0xd2
+    0b11111111111111111111110000000000,  // 0xd3
+    0b11111111111111111111110000100000,  // 0xd4
+    0b11111111111111111111100111000000,  // 0xd5
+    0b11111111111111111111110001000000,  // 0xd6
+    0b11111111111111111111001000000000,  // 0xd7
+    0b11111111111111110010000000000000,  // 0xd8
+    0b11111111111111110010100000000000,  // 0xd9
+    0b11111111111111111111101000000000,  // 0xda
+    0b11111111111111111111101001000000,  // 0xdb
+    0b11111111111111111111111111010000,  // 0xdc
+    0b11111111111111111111110001100000,  // 0xdd
+    0b11111111111111111111110010000000,  // 0xde
+    0b11111111111111111111110010100000,  // 0xdf
+    0b11111111111111101100000000000000,  // 0xe0
+    0b11111111111111111111001100000000,  // 0xe1
+    0b11111111111111101101000000000000,  // 0xe2
+    0b11111111111111110011000000000000,  // 0xe3
+    0b11111111111111111010010000000000,  // 0xe4
+    0b11111111111111110011100000000000,  // 0xe5
+    0b11111111111111110100000000000000,  // 0xe6
+    0b11111111111111111110011000000000,  // 0xe7
+    0b11111111111111111010100000000000,  // 0xe8
+    0b11111111111111111010110000000000,  // 0xe9
+    0b11111111111111111111011100000000,  // 0xea
+    0b11111111111111111111011110000000,  // 0xeb
+    0b11111111111111111111010000000000,  // 0xec
+    0b11111111111111111111010100000000,  // 0xed
+    0b11111111111111111111101010000000,  // 0xee
+    0b11111111111111111110100000000000,  // 0xef
+    0b11111111111111111111101011000000,  // 0xf0
+    0b11111111111111111111110011000000,  // 0xf1
+    0b11111111111111111111101100000000,  // 0xf2
+    0b11111111111111111111101101000000,  // 0xf3
+    0b11111111111111111111110011100000,  // 0xf4
+    0b11111111111111111111110100000000,  // 0xf5
+    0b11111111111111111111110100100000,  // 0xf6
+    0b11111111111111111111110101000000,  // 0xf7
+    0b11111111111111111111110101100000,  // 0xf8
+    0b11111111111111111111111111100000,  // 0xf9
+    0b11111111111111111111110110000000,  // 0xfa
+    0b11111111111111111111110110100000,  // 0xfb
+    0b11111111111111111111110111000000,  // 0xfc
+    0b11111111111111111111110111100000,  // 0xfd
+    0b11111111111111111111111000000000,  // 0xfe
+    0b11111111111111111111101110000000,  // 0xff
+    0b11111111111111111111111111111100,  // 0x100
+};
+*/
+
+// static
+const uint32_t HuffmanSpecTables::kRightCodes[] = {
+    0b00000000000000000001111111111000,  // 0x00
+    0b00000000011111111111111111011000,  // 0x01
+    0b00001111111111111111111111100010,  // 0x02
+    0b00001111111111111111111111100011,  // 0x03
+    0b00001111111111111111111111100100,  // 0x04
+    0b00001111111111111111111111100101,  // 0x05
+    0b00001111111111111111111111100110,  // 0x06
+    0b00001111111111111111111111100111,  // 0x07
+    0b00001111111111111111111111101000,  // 0x08
+    0b00000000111111111111111111101010,  // 0x09
+    0b00111111111111111111111111111100,  // 0x0a
+    0b00001111111111111111111111101001,  // 0x0b
+    0b00001111111111111111111111101010,  // 0x0c
+    0b00111111111111111111111111111101,  // 0x0d
+    0b00001111111111111111111111101011,  // 0x0e
+    0b00001111111111111111111111101100,  // 0x0f
+    0b00001111111111111111111111101101,  // 0x10
+    0b00001111111111111111111111101110,  // 0x11
+    0b00001111111111111111111111101111,  // 0x12
+    0b00001111111111111111111111110000,  // 0x13
+    0b00001111111111111111111111110001,  // 0x14
+    0b00001111111111111111111111110010,  // 0x15
+    0b00111111111111111111111111111110,  // 0x16
+    0b00001111111111111111111111110011,  // 0x17
+    0b00001111111111111111111111110100,  // 0x18
+    0b00001111111111111111111111110101,  // 0x19
+    0b00001111111111111111111111110110,  // 0x1a
+    0b00001111111111111111111111110111,  // 0x1b
+    0b00001111111111111111111111111000,  // 0x1c
+    0b00001111111111111111111111111001,  // 0x1d
+    0b00001111111111111111111111111010,  // 0x1e
+    0b00001111111111111111111111111011,  // 0x1f
+    0b00000000000000000000000000010100,  // 0x20
+    0b00000000000000000000001111111000,  // '!'
+    0b00000000000000000000001111111001,  // '\"'
+    0b00000000000000000000111111111010,  // '#'
+    0b00000000000000000001111111111001,  // '$'
+    0b00000000000000000000000000010101,  // '%'
+    0b00000000000000000000000011111000,  // '&'
+    0b00000000000000000000011111111010,  // '\''
+    0b00000000000000000000001111111010,  // '('
+    0b00000000000000000000001111111011,  // ')'
+    0b00000000000000000000000011111001,  // '*'
+    0b00000000000000000000011111111011,  // '+'
+    0b00000000000000000000000011111010,  // ','
+    0b00000000000000000000000000010110,  // '-'
+    0b00000000000000000000000000010111,  // '.'
+    0b00000000000000000000000000011000,  // '/'
+    0b00000000000000000000000000000000,  // '0'
+    0b00000000000000000000000000000001,  // '1'
+    0b00000000000000000000000000000010,  // '2'
+    0b00000000000000000000000000011001,  // '3'
+    0b00000000000000000000000000011010,  // '4'
+    0b00000000000000000000000000011011,  // '5'
+    0b00000000000000000000000000011100,  // '6'
+    0b00000000000000000000000000011101,  // '7'
+    0b00000000000000000000000000011110,  // '8'
+    0b00000000000000000000000000011111,  // '9'
+    0b00000000000000000000000001011100,  // ':'
+    0b00000000000000000000000011111011,  // ';'
+    0b00000000000000000111111111111100,  // '<'
+    0b00000000000000000000000000100000,  // '='
+    0b00000000000000000000111111111011,  // '>'
+    0b00000000000000000000001111111100,  // '?'
+    0b00000000000000000001111111111010,  // '@'
+    0b00000000000000000000000000100001,  // 'A'
+    0b00000000000000000000000001011101,  // 'B'
+    0b00000000000000000000000001011110,  // 'C'
+    0b00000000000000000000000001011111,  // 'D'
+    0b00000000000000000000000001100000,  // 'E'
+    0b00000000000000000000000001100001,  // 'F'
+    0b00000000000000000000000001100010,  // 'G'
+    0b00000000000000000000000001100011,  // 'H'
+    0b00000000000000000000000001100100,  // 'I'
+    0b00000000000000000000000001100101,  // 'J'
+    0b00000000000000000000000001100110,  // 'K'
+    0b00000000000000000000000001100111,  // 'L'
+    0b00000000000000000000000001101000,  // 'M'
+    0b00000000000000000000000001101001,  // 'N'
+    0b00000000000000000000000001101010,  // 'O'
+    0b00000000000000000000000001101011,  // 'P'
+    0b00000000000000000000000001101100,  // 'Q'
+    0b00000000000000000000000001101101,  // 'R'
+    0b00000000000000000000000001101110,  // 'S'
+    0b00000000000000000000000001101111,  // 'T'
+    0b00000000000000000000000001110000,  // 'U'
+    0b00000000000000000000000001110001,  // 'V'
+    0b00000000000000000000000001110010,  // 'W'
+    0b00000000000000000000000011111100,  // 'X'
+    0b00000000000000000000000001110011,  // 'Y'
+    0b00000000000000000000000011111101,  // 'Z'
+    0b00000000000000000001111111111011,  // '['
+    0b00000000000001111111111111110000,  // '\\'
+    0b00000000000000000001111111111100,  // ']'
+    0b00000000000000000011111111111100,  // '^'
+    0b00000000000000000000000000100010,  // '_'
+    0b00000000000000000111111111111101,  // '`'
+    0b00000000000000000000000000000011,  // 'a'
+    0b00000000000000000000000000100011,  // 'b'
+    0b00000000000000000000000000000100,  // 'c'
+    0b00000000000000000000000000100100,  // 'd'
+    0b00000000000000000000000000000101,  // 'e'
+    0b00000000000000000000000000100101,  // 'f'
+    0b00000000000000000000000000100110,  // 'g'
+    0b00000000000000000000000000100111,  // 'h'
+    0b00000000000000000000000000000110,  // 'i'
+    0b00000000000000000000000001110100,  // 'j'
+    0b00000000000000000000000001110101,  // 'k'
+    0b00000000000000000000000000101000,  // 'l'
+    0b00000000000000000000000000101001,  // 'm'
+    0b00000000000000000000000000101010,  // 'n'
+    0b00000000000000000000000000000111,  // 'o'
+    0b00000000000000000000000000101011,  // 'p'
+    0b00000000000000000000000001110110,  // 'q'
+    0b00000000000000000000000000101100,  // 'r'
+    0b00000000000000000000000000001000,  // 's'
+    0b00000000000000000000000000001001,  // 't'
+    0b00000000000000000000000000101101,  // 'u'
+    0b00000000000000000000000001110111,  // 'v'
+    0b00000000000000000000000001111000,  // 'w'
+    0b00000000000000000000000001111001,  // 'x'
+    0b00000000000000000000000001111010,  // 'y'
+    0b00000000000000000000000001111011,  // 'z'
+    0b00000000000000000111111111111110,  // '{'
+    0b00000000000000000000011111111100,  // '|'
+    0b00000000000000000011111111111101,  // '}'
+    0b00000000000000000001111111111101,  // '~'
+    0b00001111111111111111111111111100,  // 0x7f
+    0b00000000000011111111111111100110,  // 0x80
+    0b00000000001111111111111111010010,  // 0x81
+    0b00000000000011111111111111100111,  // 0x82
+    0b00000000000011111111111111101000,  // 0x83
+    0b00000000001111111111111111010011,  // 0x84
+    0b00000000001111111111111111010100,  // 0x85
+    0b00000000001111111111111111010101,  // 0x86
+    0b00000000011111111111111111011001,  // 0x87
+    0b00000000001111111111111111010110,  // 0x88
+    0b00000000011111111111111111011010,  // 0x89
+    0b00000000011111111111111111011011,  // 0x8a
+    0b00000000011111111111111111011100,  // 0x8b
+    0b00000000011111111111111111011101,  // 0x8c
+    0b00000000011111111111111111011110,  // 0x8d
+    0b00000000111111111111111111101011,  // 0x8e
+    0b00000000011111111111111111011111,  // 0x8f
+    0b00000000111111111111111111101100,  // 0x90
+    0b00000000111111111111111111101101,  // 0x91
+    0b00000000001111111111111111010111,  // 0x92
+    0b00000000011111111111111111100000,  // 0x93
+    0b00000000111111111111111111101110,  // 0x94
+    0b00000000011111111111111111100001,  // 0x95
+    0b00000000011111111111111111100010,  // 0x96
+    0b00000000011111111111111111100011,  // 0x97
+    0b00000000011111111111111111100100,  // 0x98
+    0b00000000000111111111111111011100,  // 0x99
+    0b00000000001111111111111111011000,  // 0x9a
+    0b00000000011111111111111111100101,  // 0x9b
+    0b00000000001111111111111111011001,  // 0x9c
+    0b00000000011111111111111111100110,  // 0x9d
+    0b00000000011111111111111111100111,  // 0x9e
+    0b00000000111111111111111111101111,  // 0x9f
+    0b00000000001111111111111111011010,  // 0xa0
+    0b00000000000111111111111111011101,  // 0xa1
+    0b00000000000011111111111111101001,  // 0xa2
+    0b00000000001111111111111111011011,  // 0xa3
+    0b00000000001111111111111111011100,  // 0xa4
+    0b00000000011111111111111111101000,  // 0xa5
+    0b00000000011111111111111111101001,  // 0xa6
+    0b00000000000111111111111111011110,  // 0xa7
+    0b00000000011111111111111111101010,  // 0xa8
+    0b00000000001111111111111111011101,  // 0xa9
+    0b00000000001111111111111111011110,  // 0xaa
+    0b00000000111111111111111111110000,  // 0xab
+    0b00000000000111111111111111011111,  // 0xac
+    0b00000000001111111111111111011111,  // 0xad
+    0b00000000011111111111111111101011,  // 0xae
+    0b00000000011111111111111111101100,  // 0xaf
+    0b00000000000111111111111111100000,  // 0xb0
+    0b00000000000111111111111111100001,  // 0xb1
+    0b00000000001111111111111111100000,  // 0xb2
+    0b00000000000111111111111111100010,  // 0xb3
+    0b00000000011111111111111111101101,  // 0xb4
+    0b00000000001111111111111111100001,  // 0xb5
+    0b00000000011111111111111111101110,  // 0xb6
+    0b00000000011111111111111111101111,  // 0xb7
+    0b00000000000011111111111111101010,  // 0xb8
+    0b00000000001111111111111111100010,  // 0xb9
+    0b00000000001111111111111111100011,  // 0xba
+    0b00000000001111111111111111100100,  // 0xbb
+    0b00000000011111111111111111110000,  // 0xbc
+    0b00000000001111111111111111100101,  // 0xbd
+    0b00000000001111111111111111100110,  // 0xbe
+    0b00000000011111111111111111110001,  // 0xbf
+    0b00000011111111111111111111100000,  // 0xc0
+    0b00000011111111111111111111100001,  // 0xc1
+    0b00000000000011111111111111101011,  // 0xc2
+    0b00000000000001111111111111110001,  // 0xc3
+    0b00000000001111111111111111100111,  // 0xc4
+    0b00000000011111111111111111110010,  // 0xc5
+    0b00000000001111111111111111101000,  // 0xc6
+    0b00000001111111111111111111101100,  // 0xc7
+    0b00000011111111111111111111100010,  // 0xc8
+    0b00000011111111111111111111100011,  // 0xc9
+    0b00000011111111111111111111100100,  // 0xca
+    0b00000111111111111111111111011110,  // 0xcb
+    0b00000111111111111111111111011111,  // 0xcc
+    0b00000011111111111111111111100101,  // 0xcd
+    0b00000000111111111111111111110001,  // 0xce
+    0b00000001111111111111111111101101,  // 0xcf
+    0b00000000000001111111111111110010,  // 0xd0
+    0b00000000000111111111111111100011,  // 0xd1
+    0b00000011111111111111111111100110,  // 0xd2
+    0b00000111111111111111111111100000,  // 0xd3
+    0b00000111111111111111111111100001,  // 0xd4
+    0b00000011111111111111111111100111,  // 0xd5
+    0b00000111111111111111111111100010,  // 0xd6
+    0b00000000111111111111111111110010,  // 0xd7
+    0b00000000000111111111111111100100,  // 0xd8
+    0b00000000000111111111111111100101,  // 0xd9
+    0b00000011111111111111111111101000,  // 0xda
+    0b00000011111111111111111111101001,  // 0xdb
+    0b00001111111111111111111111111101,  // 0xdc
+    0b00000111111111111111111111100011,  // 0xdd
+    0b00000111111111111111111111100100,  // 0xde
+    0b00000111111111111111111111100101,  // 0xdf
+    0b00000000000011111111111111101100,  // 0xe0
+    0b00000000111111111111111111110011,  // 0xe1
+    0b00000000000011111111111111101101,  // 0xe2
+    0b00000000000111111111111111100110,  // 0xe3
+    0b00000000001111111111111111101001,  // 0xe4
+    0b00000000000111111111111111100111,  // 0xe5
+    0b00000000000111111111111111101000,  // 0xe6
+    0b00000000011111111111111111110011,  // 0xe7
+    0b00000000001111111111111111101010,  // 0xe8
+    0b00000000001111111111111111101011,  // 0xe9
+    0b00000001111111111111111111101110,  // 0xea
+    0b00000001111111111111111111101111,  // 0xeb
+    0b00000000111111111111111111110100,  // 0xec
+    0b00000000111111111111111111110101,  // 0xed
+    0b00000011111111111111111111101010,  // 0xee
+    0b00000000011111111111111111110100,  // 0xef
+    0b00000011111111111111111111101011,  // 0xf0
+    0b00000111111111111111111111100110,  // 0xf1
+    0b00000011111111111111111111101100,  // 0xf2
+    0b00000011111111111111111111101101,  // 0xf3
+    0b00000111111111111111111111100111,  // 0xf4
+    0b00000111111111111111111111101000,  // 0xf5
+    0b00000111111111111111111111101001,  // 0xf6
+    0b00000111111111111111111111101010,  // 0xf7
+    0b00000111111111111111111111101011,  // 0xf8
+    0b00001111111111111111111111111110,  // 0xf9
+    0b00000111111111111111111111101100,  // 0xfa
+    0b00000111111111111111111111101101,  // 0xfb
+    0b00000111111111111111111111101110,  // 0xfc
+    0b00000111111111111111111111101111,  // 0xfd
+    0b00000111111111111111111111110000,  // 0xfe
+    0b00000011111111111111111111101110,  // 0xff
+    0b00111111111111111111111111111111,  // 0x100
+};
+// clang-format off
+
+}  // namespace http2
diff --git a/net/third_party/http2/hpack/huffman/huffman_spec_tables.h b/net/third_party/http2/hpack/huffman/huffman_spec_tables.h
new file mode 100644
index 0000000..a926509
--- /dev/null
+++ b/net/third_party/http2/hpack/huffman/huffman_spec_tables.h
@@ -0,0 +1,21 @@
+#ifndef NET_THIRD_PARTY_HTTP2_HPACK_HUFFMAN_HUFFMAN_SPEC_TABLES_H_
+#define NET_THIRD_PARTY_HTTP2_HPACK_HUFFMAN_HUFFMAN_SPEC_TABLES_H_
+
+// Tables describing the Huffman encoding of bytes as specified by RFC7541.
+
+#include <cstdint>
+
+namespace http2 {
+
+struct HuffmanSpecTables {
+  // Number of bits in the encoding of each symbol (byte).
+  static const uint8_t kCodeLengths[257];
+
+  // The encoding of each symbol, right justified (as printed), which means that
+  // the last bit of the encoding is the low-order bit of the uint32.
+  static const uint32_t kRightCodes[257];
+};
+
+}  // namespace http2
+
+#endif  // NET_THIRD_PARTY_HTTP2_HPACK_HUFFMAN_HUFFMAN_SPEC_TABLES_H_
diff --git a/net/third_party/http2/platform/api/random_util_helper.h b/net/third_party/http2/platform/api/random_util_helper.h
new file mode 100644
index 0000000..343f7ea
--- /dev/null
+++ b/net/third_party/http2/platform/api/random_util_helper.h
@@ -0,0 +1,22 @@
+#ifndef NET_THIRD_PARTY_HTTP2_PLATFORM_API_RANDOM_UTIL_HELPER_H_
+#define NET_THIRD_PARTY_HTTP2_PLATFORM_API_RANDOM_UTIL_HELPER_H_
+
+#include "net/third_party/http2/platform/impl/random_util_helper_impl.h"
+
+namespace http2 {
+
+namespace test {
+
+class RandomBase;
+
+inline Http2String RandomString(RandomBase* random,
+                                int len,
+                                Http2StringPiece alphabet) {
+  return RandomStringImpl(random, len, alphabet);
+}
+
+}  // namespace test
+
+}  // namespace http2
+
+#endif  // NET_THIRD_PARTY_HTTP2_PLATFORM_API_RANDOM_UTIL_HELPER_H_
diff --git a/net/third_party/http2/platform/impl/random_util_helper_impl.cc b/net/third_party/http2/platform/impl/random_util_helper_impl.cc
new file mode 100644
index 0000000..fc098b6b
--- /dev/null
+++ b/net/third_party/http2/platform/impl/random_util_helper_impl.cc
@@ -0,0 +1,17 @@
+#include "net/third_party/http2/platform/impl/random_util_helper_impl.h"
+
+namespace http2 {
+namespace test {
+
+Http2String RandomStringImpl(RandomBase* random,
+                             int len,
+                             Http2StringPiece alphabet) {
+  Http2String random_string;
+  random_string.reserve(len);
+  for (int i = 0; i < len; ++i)
+    random_string.push_back(alphabet[random->Uniform(alphabet.size())]);
+  return random_string;
+}
+
+}  // namespace test
+}  // namespace http2
diff --git a/net/third_party/http2/platform/impl/random_util_helper_impl.h b/net/third_party/http2/platform/impl/random_util_helper_impl.h
new file mode 100644
index 0000000..5ca4dd5
--- /dev/null
+++ b/net/third_party/http2/platform/impl/random_util_helper_impl.h
@@ -0,0 +1,21 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_THIRD_PARTY_HTTP2_PLATFORM_IMPL_RANDOM_UTIL_HELPER_IMPL_H_
+#define NET_THIRD_PARTY_HTTP2_PLATFORM_IMPL_RANDOM_UTIL_HELPER_IMPL_H_
+
+#include "net/third_party/http2/platform/api/http2_string.h"
+#include "net/third_party/http2/platform/api/http2_string_piece.h"
+#include "net/third_party/http2/tools/http2_random.h"
+
+namespace http2 {
+namespace test {
+
+Http2String RandomStringImpl(RandomBase* random,
+                             int len,
+                             Http2StringPiece alphabet);
+}  // namespace test
+}  // namespace http2
+
+#endif  // NET_THIRD_PARTY_HTTP2_PLATFORM_IMPL_RANDOM_UTIL_HELPER_IMPL_H_
diff --git a/net/third_party/http2/tools/random_util.cc b/net/third_party/http2/tools/random_util.cc
index e5a8381..f373c3f 100644
--- a/net/third_party/http2/tools/random_util.cc
+++ b/net/third_party/http2/tools/random_util.cc
@@ -7,6 +7,7 @@
 #include <cmath>
 
 #include "base/rand_util.h"
+#include "net/third_party/http2/platform/api/random_util_helper.h"
 #include "net/third_party/http2/tools/http2_random.h"
 
 namespace http2 {
@@ -33,14 +34,6 @@
 
 }  // anonymous namespace
 
-Http2String RandomString(RandomBase* rng, int len, Http2StringPiece alphabet) {
-  Http2String random_string;
-  random_string.reserve(len);
-  for (int i = 0; i < len; ++i)
-    random_string.push_back(alphabet[rng->Uniform(alphabet.size())]);
-  return random_string;
-}
-
 size_t GenerateUniformInRange(size_t lo, size_t hi, RandomBase* rng) {
   if (lo + 1 >= hi) {
     return lo;
diff --git a/net/third_party/quic/core/chlo_extractor.cc b/net/third_party/quic/core/chlo_extractor.cc
index 60ac9654..27ab8e2 100644
--- a/net/third_party/quic/core/chlo_extractor.cc
+++ b/net/third_party/quic/core/chlo_extractor.cc
@@ -38,11 +38,11 @@
   void OnDecryptedPacket(EncryptionLevel level) override {}
   bool OnPacketHeader(const QuicPacketHeader& header) override;
   bool OnStreamFrame(const QuicStreamFrame& frame) override;
+  bool OnCryptoFrame(const QuicCryptoFrame& frame) override;
   bool OnAckFrameStart(QuicPacketNumber largest_acked,
                        QuicTime::Delta ack_delay_time) override;
-  bool OnAckRange(QuicPacketNumber start,
-                  QuicPacketNumber end,
-                  bool last_range) override;
+  bool OnAckRange(QuicPacketNumber start, QuicPacketNumber end) override;
+  bool OnAckFrameEnd(QuicPacketNumber start) override;
   bool OnStopWaitingFrame(const QuicStopWaitingFrame& frame) override;
   bool OnPingFrame(const QuicPingFrame& frame) override;
   bool OnRstStreamFrame(const QuicRstStreamFrame& frame) override;
@@ -140,14 +140,22 @@
   return true;
 }
 
+bool ChloFramerVisitor::OnCryptoFrame(const QuicCryptoFrame& frame) {
+  // TODO(nharper): Implement.
+  return false;
+}
+
 bool ChloFramerVisitor::OnAckFrameStart(QuicPacketNumber /*largest_acked*/,
                                         QuicTime::Delta /*ack_delay_time*/) {
   return true;
 }
 
 bool ChloFramerVisitor::OnAckRange(QuicPacketNumber /*start*/,
-                                   QuicPacketNumber /*end*/,
-                                   bool /*last_range*/) {
+                                   QuicPacketNumber /*end*/) {
+  return true;
+}
+
+bool ChloFramerVisitor::OnAckFrameEnd(QuicPacketNumber /*start*/) {
   return true;
 }
 
diff --git a/net/third_party/quic/core/frames/quic_crypto_frame.cc b/net/third_party/quic/core/frames/quic_crypto_frame.cc
new file mode 100644
index 0000000..a1c4cdb
--- /dev/null
+++ b/net/third_party/quic/core/frames/quic_crypto_frame.cc
@@ -0,0 +1,30 @@
+// Copyright (c) 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quic/core/frames/quic_crypto_frame.h"
+
+#include "net/third_party/quic/platform/api/quic_logging.h"
+
+namespace quic {
+
+QuicCryptoFrame::QuicCryptoFrame() : QuicCryptoFrame(0, nullptr, 0) {}
+
+QuicCryptoFrame::QuicCryptoFrame(QuicStreamOffset offset, QuicStringPiece data)
+    : QuicCryptoFrame(offset, data.data(), data.length()) {}
+
+QuicCryptoFrame::QuicCryptoFrame(QuicStreamOffset offset,
+                                 const char* data_buffer,
+                                 QuicPacketLength data_length)
+    : data_length(data_length), data_buffer(data_buffer), offset(offset) {}
+
+QuicCryptoFrame::~QuicCryptoFrame() {}
+
+std::ostream& operator<<(std::ostream& os,
+                         const QuicCryptoFrame& stream_frame) {
+  os << "{ offset: " << stream_frame.offset
+     << ", length: " << stream_frame.data_length << " }\n";
+  return os;
+}
+
+}  // namespace quic
diff --git a/net/third_party/quic/core/frames/quic_crypto_frame.h b/net/third_party/quic/core/frames/quic_crypto_frame.h
new file mode 100644
index 0000000..fed0d3a6
--- /dev/null
+++ b/net/third_party/quic/core/frames/quic_crypto_frame.h
@@ -0,0 +1,42 @@
+// Copyright (c) 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_THIRD_PARTY_QUIC_CORE_FRAMES_QUIC_CRYPTO_FRAME_H_
+#define NET_THIRD_PARTY_QUIC_CORE_FRAMES_QUIC_CRYPTO_FRAME_H_
+
+#include <memory>
+#include <ostream>
+
+#include "net/third_party/quic/core/quic_buffer_allocator.h"
+#include "net/third_party/quic/core/quic_types.h"
+#include "net/third_party/quic/platform/api/quic_export.h"
+#include "net/third_party/quic/platform/api/quic_string_piece.h"
+
+namespace quic {
+
+struct QUIC_EXPORT_PRIVATE QuicCryptoFrame {
+  QuicCryptoFrame();
+  QuicCryptoFrame(QuicStreamOffset offset, QuicStringPiece data);
+  ~QuicCryptoFrame();
+
+  friend QUIC_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& os,
+                                                      const QuicCryptoFrame& s);
+
+  QuicPacketLength data_length;
+  // When reading, |data_buffer| points to the data that was received in the
+  // frame. When writing, |data_buffer| must be a valid pointer for the lifetime
+  // of the frame, which may get serialized some time after creation.
+  const char* data_buffer;
+  QuicStreamOffset offset;  // Location of this data in the stream.
+
+  QuicCryptoFrame(QuicStreamOffset offset,
+                  const char* data_buffer,
+                  QuicPacketLength data_length);
+};
+static_assert(sizeof(QuicCryptoFrame) <= 64,
+              "Keep the QuicCryptoFrame size to a cacheline.");
+
+}  // namespace quic
+
+#endif  // NET_THIRD_PARTY_QUIC_CORE_FRAMES_QUIC_CRYPTO_FRAME_H_
diff --git a/net/third_party/quic/core/frames/quic_frame.cc b/net/third_party/quic/core/frames/quic_frame.cc
index 6a1902a..8ada26e 100644
--- a/net/third_party/quic/core/frames/quic_frame.cc
+++ b/net/third_party/quic/core/frames/quic_frame.cc
@@ -19,6 +19,9 @@
 QuicFrame::QuicFrame(QuicStreamFrame stream_frame)
     : stream_frame(stream_frame) {}
 
+QuicFrame::QuicFrame(QuicCryptoFrame* crypto_frame)
+    : type(CRYPTO_FRAME), crypto_frame(crypto_frame) {}
+
 QuicFrame::QuicFrame(QuicAckFrame* frame) : type(ACK_FRAME), ack_frame(frame) {}
 
 QuicFrame::QuicFrame(QuicMtuDiscoveryFrame frame)
@@ -125,6 +128,9 @@
     case MESSAGE_FRAME:
       delete frame->message_frame;
       break;
+    case CRYPTO_FRAME:
+      delete frame->crypto_frame;
+      break;
 
     case NUM_FRAME_TYPES:
       DCHECK(false) << "Cannot delete type: " << frame->type;
diff --git a/net/third_party/quic/core/frames/quic_frame.h b/net/third_party/quic/core/frames/quic_frame.h
index ab712a9b..5815eac 100644
--- a/net/third_party/quic/core/frames/quic_frame.h
+++ b/net/third_party/quic/core/frames/quic_frame.h
@@ -12,6 +12,7 @@
 #include "net/third_party/quic/core/frames/quic_application_close_frame.h"
 #include "net/third_party/quic/core/frames/quic_blocked_frame.h"
 #include "net/third_party/quic/core/frames/quic_connection_close_frame.h"
+#include "net/third_party/quic/core/frames/quic_crypto_frame.h"
 #include "net/third_party/quic/core/frames/quic_goaway_frame.h"
 #include "net/third_party/quic/core/frames/quic_max_stream_id_frame.h"
 #include "net/third_party/quic/core/frames/quic_message_frame.h"
@@ -56,6 +57,7 @@
   explicit QuicFrame(QuicPathChallengeFrame* frame);
   explicit QuicFrame(QuicStopSendingFrame* frame);
   explicit QuicFrame(QuicMessageFrame* message_frame);
+  explicit QuicFrame(QuicCryptoFrame* crypto_frame);
 
   QUIC_EXPORT_PRIVATE friend std::ostream& operator<<(std::ostream& os,
                                                       const QuicFrame& frame);
@@ -94,6 +96,7 @@
         QuicPathChallengeFrame* path_challenge_frame;
         QuicStopSendingFrame* stop_sending_frame;
         QuicMessageFrame* message_frame;
+        QuicCryptoFrame* crypto_frame;
       };
     };
   };
diff --git a/net/third_party/quic/core/quic_alarm_factory.h b/net/third_party/quic/core/quic_alarm_factory.h
index cde152f..8c94f8d 100644
--- a/net/third_party/quic/core/quic_alarm_factory.h
+++ b/net/third_party/quic/core/quic_alarm_factory.h
@@ -11,10 +11,6 @@
 
 namespace quic {
 
-// QuicConnections currently use around 1KB of polymorphic types which would
-// ordinarily be on the heap. Instead, store them inline in an arena.
-using QuicConnectionArena = QuicOneBlockArena<1024>;
-
 // Creates platform-specific alarms used throughout QUIC.
 class QUIC_EXPORT_PRIVATE QuicAlarmFactory {
  public:
diff --git a/net/third_party/quic/core/quic_connection.cc b/net/third_party/quic/core/quic_connection.cc
index ed0db5c..b894f77 100644
--- a/net/third_party/quic/core/quic_connection.cc
+++ b/net/third_party/quic/core/quic_connection.cc
@@ -855,6 +855,11 @@
   return connected_;
 }
 
+bool QuicConnection::OnCryptoFrame(const QuicCryptoFrame& frame) {
+  // TODO(nharper): Implement.
+  return false;
+}
+
 bool QuicConnection::OnAckFrameStart(QuicPacketNumber largest_acked,
                                      QuicTime::Delta ack_delay_time) {
   DCHECK(connected_);
@@ -909,12 +914,9 @@
   return true;
 }
 
-bool QuicConnection::OnAckRange(QuicPacketNumber start,
-                                QuicPacketNumber end,
-                                bool last_range) {
+bool QuicConnection::OnAckRange(QuicPacketNumber start, QuicPacketNumber end) {
   DCHECK(connected_);
-  QUIC_DVLOG(1) << ENDPOINT << "OnAckRange: [" << start << ", " << end
-                << "), last_range: " << last_range;
+  QUIC_DVLOG(1) << ENDPOINT << "OnAckRange: [" << start << ", " << end << ")";
 
   if (last_header_.packet_number <= largest_seen_packet_with_ack_) {
     QUIC_DLOG(INFO) << ENDPOINT << "Received an old ack frame: ignoring";
@@ -922,7 +924,15 @@
   }
 
   sent_packet_manager_.OnAckRange(start, end);
-  if (!last_range) {
+  return true;
+}
+
+bool QuicConnection::OnAckFrameEnd(QuicPacketNumber start) {
+  DCHECK(connected_);
+  QUIC_DVLOG(1) << ENDPOINT << "OnAckFrameEnd, start: " << start;
+
+  if (last_header_.packet_number <= largest_seen_packet_with_ack_) {
+    QUIC_DLOG(INFO) << ENDPOINT << "Received an old ack frame: ignoring";
     return true;
   }
   bool acked_new_packet =
@@ -3161,6 +3171,8 @@
     SetPathDegradingAlarm();
   }
 
+  // TODO(ianswett): Only increment stop_waiting_count_ if StopWaiting frames
+  // are sent.
   if (send_stop_waiting) {
     ++stop_waiting_count_;
   } else {
diff --git a/net/third_party/quic/core/quic_connection.h b/net/third_party/quic/core/quic_connection.h
index b7b5c19..49f9a1c 100644
--- a/net/third_party/quic/core/quic_connection.h
+++ b/net/third_party/quic/core/quic_connection.h
@@ -294,10 +294,6 @@
   virtual void OnRttChanged(QuicTime::Delta rtt) const {}
 };
 
-// QuicConnections currently use around 1KB of polymorphic types which would
-// ordinarily be on the heap. Instead, store them inline in an arena.
-using QuicConnectionArena = QuicOneBlockArena<1024>;
-
 class QUIC_EXPORT_PRIVATE QuicConnectionHelperInterface {
  public:
   virtual ~QuicConnectionHelperInterface() {}
@@ -471,11 +467,11 @@
   void OnDecryptedPacket(EncryptionLevel level) override;
   bool OnPacketHeader(const QuicPacketHeader& header) override;
   bool OnStreamFrame(const QuicStreamFrame& frame) override;
+  bool OnCryptoFrame(const QuicCryptoFrame& frame) override;
   bool OnAckFrameStart(QuicPacketNumber largest_acked,
                        QuicTime::Delta ack_delay_time) override;
-  bool OnAckRange(QuicPacketNumber start,
-                  QuicPacketNumber end,
-                  bool last_range) override;
+  bool OnAckRange(QuicPacketNumber start, QuicPacketNumber end) override;
+  bool OnAckFrameEnd(QuicPacketNumber start) override;
   bool OnStopWaitingFrame(const QuicStopWaitingFrame& frame) override;
   bool OnPaddingFrame(const QuicPaddingFrame& frame) override;
   bool OnPingFrame(const QuicPingFrame& frame) override;
diff --git a/net/third_party/quic/core/quic_dispatcher.cc b/net/third_party/quic/core/quic_dispatcher.cc
index 5e730cf..99a2537 100644
--- a/net/third_party/quic/core/quic_dispatcher.cc
+++ b/net/third_party/quic/core/quic_dispatcher.cc
@@ -709,6 +709,11 @@
   return false;
 }
 
+bool QuicDispatcher::OnCryptoFrame(const QuicCryptoFrame& /*frame*/) {
+  DCHECK(false);
+  return false;
+}
+
 bool QuicDispatcher::OnAckFrameStart(QuicPacketNumber /*largest_acked*/,
                                      QuicTime::Delta /*ack_delay_time*/) {
   DCHECK(false);
@@ -716,8 +721,12 @@
 }
 
 bool QuicDispatcher::OnAckRange(QuicPacketNumber /*start*/,
-                                QuicPacketNumber /*end*/,
-                                bool /*last_range*/) {
+                                QuicPacketNumber /*end*/) {
+  DCHECK(false);
+  return false;
+}
+
+bool QuicDispatcher::OnAckFrameEnd(QuicPacketNumber start) {
   DCHECK(false);
   return false;
 }
diff --git a/net/third_party/quic/core/quic_dispatcher.h b/net/third_party/quic/core/quic_dispatcher.h
index 8984e47c..3a3b04f 100644
--- a/net/third_party/quic/core/quic_dispatcher.h
+++ b/net/third_party/quic/core/quic_dispatcher.h
@@ -136,11 +136,11 @@
   void OnDecryptedPacket(EncryptionLevel level) override;
   bool OnPacketHeader(const QuicPacketHeader& header) override;
   bool OnStreamFrame(const QuicStreamFrame& frame) override;
+  bool OnCryptoFrame(const QuicCryptoFrame& frame) override;
   bool OnAckFrameStart(QuicPacketNumber largest_acked,
                        QuicTime::Delta ack_delay_time) override;
-  bool OnAckRange(QuicPacketNumber start,
-                  QuicPacketNumber end,
-                  bool last_range) override;
+  bool OnAckRange(QuicPacketNumber start, QuicPacketNumber end) override;
+  bool OnAckFrameEnd(QuicPacketNumber start) override;
   bool OnStopWaitingFrame(const QuicStopWaitingFrame& frame) override;
   bool OnPaddingFrame(const QuicPaddingFrame& frame) override;
   bool OnPingFrame(const QuicPingFrame& frame) override;
diff --git a/net/third_party/quic/core/quic_framer.cc b/net/third_party/quic/core/quic_framer.cc
index ab669d5..f026bef 100644
--- a/net/third_party/quic/core/quic_framer.cc
+++ b/net/third_party/quic/core/quic_framer.cc
@@ -472,6 +472,7 @@
     case MTU_DISCOVERY_FRAME:
     case PADDING_FRAME:
     case MESSAGE_FRAME:
+    case CRYPTO_FRAME:
     case NUM_FRAME_TYPES:
       DCHECK(false);
       return 0;
@@ -732,6 +733,10 @@
           return 0;
         }
         break;
+      case CRYPTO_FRAME:
+        set_detailed_error(
+            "Attempt to append CRYPTO frame and not in version 99.");
+        return RaiseError(QUIC_INTERNAL_ERROR);
 
       default:
         RaiseError(QUIC_INVALID_FRAME_DATA);
@@ -883,6 +888,12 @@
           return 0;
         }
         break;
+      case CRYPTO_FRAME:
+        if (!AppendCryptoFrame(*frame.crypto_frame, &writer)) {
+          QUIC_BUG << "AppendCryptoFrame failed";
+          return 0;
+        }
+        break;
       default:
         RaiseError(QUIC_INVALID_FRAME_DATA);
         QUIC_BUG << "QUIC_INVALID_FRAME_DATA";
@@ -2315,6 +2326,18 @@
           }
           break;
         }
+        case IETF_CRYPTO: {
+          QuicCryptoFrame frame;
+          if (!ProcessCryptoFrame(reader, &frame)) {
+            return RaiseError(QUIC_INVALID_FRAME_DATA);
+          }
+          if (!visitor_->OnCryptoFrame(frame)) {
+            QUIC_DVLOG(1) << "Visitor asked to stop further processing.";
+            // Returning true since there was no parsing error.
+            return true;
+          }
+          break;
+        }
 
         default:
           set_detailed_error("Illegal frame type.");
@@ -2470,6 +2493,30 @@
   return true;
 }
 
+bool QuicFramer::ProcessCryptoFrame(QuicDataReader* reader,
+                                    QuicCryptoFrame* frame) {
+  if (!reader->ReadVarInt62(&frame->offset)) {
+    set_detailed_error("Unable to read crypto data offset.");
+    return false;
+  }
+  uint64_t len;
+  if (!reader->ReadVarInt62(&len) ||
+      len > std::numeric_limits<QuicPacketLength>::max()) {
+    set_detailed_error("Invalid data length.");
+    return false;
+  }
+  frame->data_length = len;
+
+  // TODO(ianswett): Don't use QuicStringPiece as an intermediary.
+  QuicStringPiece data;
+  if (!reader->ReadStringPiece(&data, frame->data_length)) {
+    set_detailed_error("Unable to read frame data.");
+    return false;
+  }
+  frame->data_buffer = data.data();
+  return true;
+}
+
 bool QuicFramer::ProcessAckFrame(QuicDataReader* reader, uint8_t frame_type) {
   const bool has_ack_blocks =
       ExtractBit(frame_type, kQuicHasMultipleAckBlocksOffset);
@@ -2544,8 +2591,7 @@
   }
 
   QuicPacketNumber first_received = largest_acked + 1 - first_block_length;
-  if (!visitor_->OnAckRange(first_received, largest_acked + 1,
-                            /*last_range=*/!has_ack_blocks)) {
+  if (!visitor_->OnAckRange(first_received, largest_acked + 1)) {
     // The visitor suppresses further processing of the packet. Although
     // this is not a parsing error, returns false as this is in middle
     // of processing an ack frame,
@@ -2574,11 +2620,9 @@
       }
 
       first_received -= (gap + current_block_length);
-      const bool last_range = i + 1 == num_ack_blocks;
-      if (current_block_length > 0 || last_range) {
+      if (current_block_length > 0) {
         if (!visitor_->OnAckRange(first_received,
-                                  first_received + current_block_length,
-                                  last_range)) {
+                                  first_received + current_block_length)) {
           // The visitor suppresses further processing of the packet. Although
           // this is not a parsing error, returns false as this is in middle
           // of processing an ack frame,
@@ -2599,7 +2643,8 @@
     return false;
   }
 
-  return true;
+  // Done processing the ACK frame.
+  return visitor_->OnAckFrameEnd(first_received);
 }
 
 bool QuicFramer::ProcessTimestampsInAckFrame(uint8_t num_received_packets,
@@ -2701,8 +2746,7 @@
     return false;
   }
 
-  if (!visitor_->OnAckRange(block_low, block_high,
-                            /* last_range= */ (ack_block_count == 0))) {
+  if (!visitor_->OnAckRange(block_low, block_high)) {
     // The visitor suppresses further processing of the packet. Although
     // this is not a parsing error, returns false as this is in middle
     // of processing an ACK frame.
@@ -2755,8 +2799,7 @@
     // Calculate the low end of the new nth ack block. The +1 is
     // because the encoded value is the blocksize-1.
     block_low = block_high - 1 - ack_block_value;
-    if (!visitor_->OnAckRange(block_low, block_high,
-                              /*last_range=*/(ack_block_count == 1))) {
+    if (!visitor_->OnAckRange(block_low, block_high)) {
       // The visitor suppresses further processing of the packet. Although
       // this is not a parsing error, returns false as this is in middle
       // of processing an ACK frame.
@@ -2767,7 +2810,8 @@
     // Another one done.
     ack_block_count--;
   }
-  return true;
+
+  return visitor_->OnAckFrameEnd(block_low);
 }
 
 bool QuicFramer::ProcessStopWaitingFrame(QuicDataReader* reader,
@@ -3236,6 +3280,7 @@
                  frame.stream_frame.offset, last_frame_in_packet,
                  frame.stream_frame.data_length) +
              frame.stream_frame.data_length;
+    // TODO(nharper): Add a case for CRYPTO_FRAME here?
     case ACK_FRAME: {
       return GetAckFrameSize(*frame.ack_frame, packet_number_length);
     }
@@ -3391,6 +3436,9 @@
       break;
     case MESSAGE_FRAME:
       return true;
+    case CRYPTO_FRAME:
+      type_byte = IETF_CRYPTO;
+      break;
     default:
       QUIC_BUG << "Attempt to generate a frame type for an unsupported value: "
                << frame.type;
@@ -3563,6 +3611,20 @@
   return true;
 }
 
+bool QuicFramer::AppendCryptoFrame(const QuicCryptoFrame& frame,
+                                   QuicDataWriter* writer) {
+  if (!writer->WriteVarInt62(static_cast<uint64_t>(frame.offset))) {
+    set_detailed_error("Writing data offset failed.");
+    return false;
+  }
+  if (!writer->WriteVarInt62(static_cast<uint64_t>(frame.data_length))) {
+    set_detailed_error("Writing data length failed.");
+    return false;
+  }
+  // TODO(nharper): Append stream frame contents.
+  return false;
+}
+
 void QuicFramer::set_version(const ParsedQuicVersion version) {
   DCHECK(IsSupportedVersion(version)) << ParsedQuicVersionToString(version);
   version_ = version;
diff --git a/net/third_party/quic/core/quic_framer.h b/net/third_party/quic/core/quic_framer.h
index 4e996cc..44d580f2 100644
--- a/net/third_party/quic/core/quic_framer.h
+++ b/net/third_party/quic/core/quic_framer.h
@@ -114,14 +114,19 @@
   // Called when a StreamFrame has been parsed.
   virtual bool OnStreamFrame(const QuicStreamFrame& frame) = 0;
 
+  // Called when a CRYPTO frame has been parsed.
+  virtual bool OnCryptoFrame(const QuicCryptoFrame& frame) = 0;
+
   // Called when largest acked of an AckFrame has been parsed.
   virtual bool OnAckFrameStart(QuicPacketNumber largest_acked,
                                QuicTime::Delta ack_delay_time) = 0;
 
   // Called when ack range [start, end) of an AckFrame has been parsed.
-  virtual bool OnAckRange(QuicPacketNumber start,
-                          QuicPacketNumber end,
-                          bool last_range) = 0;
+  virtual bool OnAckRange(QuicPacketNumber start, QuicPacketNumber end) = 0;
+
+  // Called after the last ack range in an AckFrame has been parsed.
+  // |start| is the starting value of the last ack range.
+  virtual bool OnAckFrameEnd(QuicPacketNumber start) = 0;
 
   // Called when a StopWaitingFrame has been parsed.
   virtual bool OnStopWaitingFrame(const QuicStopWaitingFrame& frame) = 0;
@@ -381,6 +386,7 @@
   bool AppendStreamFrame(const QuicStreamFrame& frame,
                          bool last_frame_in_packet,
                          QuicDataWriter* writer);
+  bool AppendCryptoFrame(const QuicCryptoFrame& frame, QuicDataWriter* writer);
 
   // SetDecrypter sets the primary decrypter, replacing any that already exists.
   // If an alternative decrypter is in place then the function DCHECKs. This is
@@ -675,6 +681,7 @@
                                    QuicRstStreamFrame* frame);
   bool ProcessStopSendingFrame(QuicDataReader* reader,
                                QuicStopSendingFrame* stop_sending_frame);
+  bool ProcessCryptoFrame(QuicDataReader* reader, QuicCryptoFrame* frame);
 
   // IETF frame appending methods.  All methods append the type byte as well.
   bool AppendIetfStreamFrame(const QuicStreamFrame& frame,
diff --git a/net/third_party/quic/core/quic_framer_test.cc b/net/third_party/quic/core/quic_framer_test.cc
index cd231c4b..c8aefc2 100644
--- a/net/third_party/quic/core/quic_framer_test.cc
+++ b/net/third_party/quic/core/quic_framer_test.cc
@@ -213,6 +213,11 @@
     return true;
   }
 
+  bool OnCryptoFrame(const QuicCryptoFrame& frame) override {
+    // TODO(nharper): Implement.
+    return true;
+  }
+
   bool OnAckFrameStart(QuicPacketNumber largest_acked,
                        QuicTime::Delta ack_delay_time) override {
     ++frame_count_;
@@ -223,14 +228,14 @@
     return true;
   }
 
-  bool OnAckRange(QuicPacketNumber start,
-                  QuicPacketNumber end,
-                  bool /*last_range*/) override {
+  bool OnAckRange(QuicPacketNumber start, QuicPacketNumber end) override {
     DCHECK(!ack_frames_.empty());
     ack_frames_[ack_frames_.size() - 1]->packets.AddRange(start, end);
     return true;
   }
 
+  bool OnAckFrameEnd(QuicPacketNumber /*start*/) override { return true; }
+
   bool OnStopWaitingFrame(const QuicStopWaitingFrame& frame) override {
     ++frame_count_;
     stop_waiting_frames_.push_back(QuicMakeUnique<QuicStopWaitingFrame>(frame));
diff --git a/net/third_party/quic/core/quic_ietf_framer_test.cc b/net/third_party/quic/core/quic_ietf_framer_test.cc
index 6200fe4..4e94d9e4 100644
--- a/net/third_party/quic/core/quic_ietf_framer_test.cc
+++ b/net/third_party/quic/core/quic_ietf_framer_test.cc
@@ -106,17 +106,19 @@
 
   bool OnStreamFrame(const QuicStreamFrame& frame) override { return true; }
 
+  bool OnCryptoFrame(const QuicCryptoFrame& frame) override { return true; }
+
   bool OnAckFrameStart(QuicPacketNumber largest_acked,
                        QuicTime::Delta ack_delay_time) override {
     return true;
   }
 
-  bool OnAckRange(QuicPacketNumber start,
-                  QuicPacketNumber end,
-                  bool /*last_range*/) override {
+  bool OnAckRange(QuicPacketNumber start, QuicPacketNumber end) override {
     return true;
   }
 
+  bool OnAckFrameEnd(QuicPacketNumber start) override { return true; }
+
   bool OnStopWaitingFrame(const QuicStopWaitingFrame& frame) override {
     return true;
   }
diff --git a/net/third_party/quic/core/quic_one_block_arena.h b/net/third_party/quic/core/quic_one_block_arena.h
index c07c2cc..eb830711 100644
--- a/net/third_party/quic/core/quic_one_block_arena.h
+++ b/net/third_party/quic/core/quic_one_block_arena.h
@@ -74,6 +74,10 @@
                                QuicArenaScopedPtr<T>::ConstructFrom::kArena);
 }
 
+// QuicConnections currently use around 1KB of polymorphic types which would
+// ordinarily be on the heap. Instead, store them inline in an arena.
+using QuicConnectionArena = QuicOneBlockArena<1024>;
+
 }  // namespace quic
 
 #endif  // NET_THIRD_PARTY_QUIC_CORE_QUIC_ONE_BLOCK_ARENA_H_
diff --git a/net/third_party/quic/core/quic_packet_creator_test.cc b/net/third_party/quic/core/quic_packet_creator_test.cc
index 9088b3dbe..ba91a19c 100644
--- a/net/third_party/quic/core/quic_packet_creator_test.cc
+++ b/net/third_party/quic/core/quic_packet_creator_test.cc
@@ -290,13 +290,13 @@
       if (client_framer_.transport_version() != QUIC_VERSION_99) {
         // pre-version 99; ensure that the error is gracefully
         // handled.
-        EXPECT_CALL(framer_visitor_, OnAckRange(1, 1, true))
-            .WillOnce(Return(true));
+        EXPECT_CALL(framer_visitor_, OnAckRange(1, 1)).WillOnce(Return(true));
+        EXPECT_CALL(framer_visitor_, OnAckFrameEnd(1)).WillOnce(Return(true));
       } else {
         // version 99; ensure that the correct packet is signalled
         // properly.
-        EXPECT_CALL(framer_visitor_, OnAckRange(0, 1, true))
-            .WillOnce(Return(true));
+        EXPECT_CALL(framer_visitor_, OnAckRange(0, 1)).WillOnce(Return(true));
+        EXPECT_CALL(framer_visitor_, OnAckFrameEnd(0)).WillOnce(Return(true));
       }
       EXPECT_CALL(framer_visitor_, OnStreamFrame(_));
       EXPECT_CALL(framer_visitor_, OnStreamFrame(_));
diff --git a/net/third_party/quic/core/quic_sent_packet_manager.cc b/net/third_party/quic/core/quic_sent_packet_manager.cc
index 9583edf..fc467e4a 100644
--- a/net/third_party/quic/core/quic_sent_packet_manager.cc
+++ b/net/third_party/quic/core/quic_sent_packet_manager.cc
@@ -112,7 +112,7 @@
       rtt_updated_(false),
       acked_packets_iter_(last_ack_frame_.packets.rbegin()),
       aggregate_acked_stream_frames_(
-          GetQuicReloadableFlag(quic_aggregate_acked_stream_frames)),
+          GetQuicReloadableFlag(quic_aggregate_acked_stream_frames_2)),
       fix_mark_for_loss_retransmission_(
           GetQuicReloadableFlag(quic_fix_mark_for_loss_retransmission)) {
   SetSendAlgorithm(congestion_control_type);
diff --git a/net/third_party/quic/core/quic_sent_packet_manager.h b/net/third_party/quic/core/quic_sent_packet_manager.h
index 87ed9df..2c8e1e98 100644
--- a/net/third_party/quic/core/quic_sent_packet_manager.h
+++ b/net/third_party/quic/core/quic_sent_packet_manager.h
@@ -565,7 +565,7 @@
   // OnAckRangeStart, and gradually moves in OnAckRange..
   PacketNumberQueue::const_reverse_iterator acked_packets_iter_;
 
-  // Latched value of quic_reloadable_flag_quic_aggregate_acked_stream_frames.
+  // Latched value of quic_reloadable_flag_quic_aggregate_acked_stream_frames_2.
   const bool aggregate_acked_stream_frames_;
 
   // Latched value of
diff --git a/net/third_party/quic/core/quic_session.cc b/net/third_party/quic/core/quic_session.cc
index dff75cc..743ec7e 100644
--- a/net/third_party/quic/core/quic_session.cc
+++ b/net/third_party/quic/core/quic_session.cc
@@ -450,6 +450,12 @@
     control_frame_manager_.WriteOrBufferRstStream(id, error, bytes_written);
     connection_->OnStreamReset(id, error);
   }
+  if (GetQuicReloadableFlag(quic_fix_reset_zombie_streams) &&
+      error != QUIC_STREAM_NO_ERROR && QuicContainsKey(zombie_streams_, id)) {
+    QUIC_FLAG_COUNT(quic_reloadable_flag_quic_fix_reset_zombie_streams);
+    OnStreamDoneWaitingForAcks(id);
+    return;
+  }
   CloseStreamInner(id, true);
 }
 
diff --git a/net/third_party/quic/core/quic_session_test.cc b/net/third_party/quic/core/quic_session_test.cc
index 2d570cc..342a3bc 100644
--- a/net/third_party/quic/core/quic_session_test.cc
+++ b/net/third_party/quic/core/quic_session_test.cc
@@ -106,6 +106,7 @@
   TestStream(QuicStreamId id, QuicSession* session)
       : QuicStream(id, session, /*is_static=*/false) {}
 
+  using QuicStream::CloseReadSide;
   using QuicStream::CloseWriteSide;
 
   void OnDataAvailable() override {}
@@ -1469,6 +1470,42 @@
   EXPECT_FALSE(session_.IsFrameOutstanding(QuicFrame(&frame)));
 }
 
+// Regression test of b/115323618.
+TEST_P(QuicSessionTestServer, LocallyResetZombieStreams) {
+  QuicConnectionPeer::SetSessionDecidesWhatToWrite(connection_);
+
+  session_.set_writev_consumes_all_data(true);
+  TestStream* stream2 = session_.CreateOutgoingDynamicStream();
+  QuicString body(100, '.');
+  stream2->CloseReadSide();
+  stream2->WriteOrBufferData(body, true, nullptr);
+  EXPECT_TRUE(stream2->IsWaitingForAcks());
+  // Verify stream2 is a zombie streams.
+  EXPECT_TRUE(QuicContainsKey(session_.zombie_streams(), stream2->id()));
+
+  QuicStreamFrame frame(stream2->id(), true, 0, 100);
+  EXPECT_CALL(*stream2, HasPendingRetransmission())
+      .WillRepeatedly(Return(true));
+  session_.OnFrameLost(QuicFrame(frame));
+
+  // Reset stream2 locally.
+  EXPECT_CALL(*connection_, SendControlFrame(_))
+      .WillRepeatedly(Invoke(&session_, &TestSession::ClearControlFrame));
+  EXPECT_CALL(*connection_, OnStreamReset(stream2->id(), _));
+  stream2->Reset(QUIC_STREAM_CANCELLED);
+
+  if (GetQuicReloadableFlag(quic_fix_reset_zombie_streams)) {
+    // Verify stream 2 gets closed.
+    EXPECT_FALSE(QuicContainsKey(session_.zombie_streams(), stream2->id()));
+    EXPECT_TRUE(session_.IsClosedStream(stream2->id()));
+    EXPECT_CALL(*stream2, OnCanWrite()).Times(0);
+  } else {
+    EXPECT_TRUE(QuicContainsKey(session_.zombie_streams(), stream2->id()));
+    EXPECT_CALL(*stream2, OnCanWrite());
+  }
+  session_.OnCanWrite();
+}
+
 }  // namespace
 }  // namespace test
 }  // namespace quic
diff --git a/net/third_party/quic/core/quic_trace_visitor.cc b/net/third_party/quic/core/quic_trace_visitor.cc
index ca1112d8..4768bc9 100644
--- a/net/third_party/quic/core/quic_trace_visitor.cc
+++ b/net/third_party/quic/core/quic_trace_visitor.cc
@@ -88,6 +88,7 @@
       case PATH_CHALLENGE_FRAME:
       case STOP_SENDING_FRAME:
       case MESSAGE_FRAME:
+      case CRYPTO_FRAME:
         break;
 
       // Ignore gQUIC-specific frames.
@@ -213,6 +214,7 @@
     case PATH_CHALLENGE_FRAME:
     case STOP_SENDING_FRAME:
     case MESSAGE_FRAME:
+    case CRYPTO_FRAME:
       break;
 
     case NUM_FRAME_TYPES:
diff --git a/net/third_party/quic/core/quic_types.h b/net/third_party/quic/core/quic_types.h
index f0a31980..3ae1d3c 100644
--- a/net/third_party/quic/core/quic_types.h
+++ b/net/third_party/quic/core/quic_types.h
@@ -184,6 +184,7 @@
   PATH_CHALLENGE_FRAME,
   STOP_SENDING_FRAME,
   MESSAGE_FRAME,
+  CRYPTO_FRAME,
 
   NUM_FRAME_TYPES
 };
@@ -217,6 +218,7 @@
   // whether the frame is a stream frame or not, and then examine each
   // bit specifically when/as needed.
   IETF_STREAM = 0x10,
+  IETF_CRYPTO = 0x18,
   // MESSAGE frame type is not yet determined, use 0x2x temporarily to give
   // stream frame some wiggle room.
   IETF_EXTENSION_MESSAGE_NO_LENGTH = 0x20,
@@ -434,15 +436,16 @@
 enum QuicIetfTransportErrorCodes : uint16_t {
   NO_IETF_QUIC_ERROR = 0x0,
   INTERNAL_ERROR = 0x1,
+  SERVER_BUSY_ERROR = 0x2,
   FLOW_CONTROL_ERROR = 0x3,
   STREAM_ID_ERROR = 0x4,
   STREAM_STATE_ERROR = 0x5,
   FINAL_OFFSET_ERROR = 0x6,
-  FRAME_FORMAT_ERROR = 0x7,
+  FRAME_ENCODING_ERROR = 0x7,
   TRANSPORT_PARAMETER_ERROR = 0x8,
   VERSION_NEGOTIATION_ERROR = 0x9,
   PROTOCOL_VIOLATION = 0xA,
-  UNSOLICITED_PONG = 0xB,
+  INVALID_MIGRATION = 0xC,
   FRAME_ERROR_base = 0x100,  // add frame type to this base
 };
 
diff --git a/net/third_party/quic/core/quic_unacked_packet_map.cc b/net/third_party/quic/core/quic_unacked_packet_map.cc
index a6edfec..2ea78e0 100644
--- a/net/third_party/quic/core/quic_unacked_packet_map.cc
+++ b/net/third_party/quic/core/quic_unacked_packet_map.cc
@@ -4,6 +4,9 @@
 
 #include "net/third_party/quic/core/quic_unacked_packet_map.h"
 
+#include <limits>
+#include <type_traits>
+
 #include "net/third_party/quic/core/quic_connection_stats.h"
 #include "net/third_party/quic/core/quic_utils.h"
 #include "net/third_party/quic/platform/api/quic_bug_tracker.h"
@@ -11,6 +14,16 @@
 
 namespace quic {
 
+namespace {
+bool WillStreamFrameLengthSumWrapAround(QuicPacketLength lhs,
+                                        QuicPacketLength rhs) {
+  static_assert(
+      std::is_unsigned<QuicPacketLength>::value,
+      "This function assumes QuicPacketLength is an unsigned integer type.");
+  return std::numeric_limits<QuicPacketLength>::max() - lhs < rhs;
+}
+}  // namespace
+
 QuicUnackedPacketMap::QuicUnackedPacketMap()
     : largest_sent_packet_(0),
       largest_sent_retransmittable_packet_(0),
@@ -418,7 +431,14 @@
         frame.type == STREAM_FRAME &&
         frame.stream_frame.stream_id == aggregated_stream_frame_.stream_id &&
         frame.stream_frame.offset == aggregated_stream_frame_.offset +
-                                         aggregated_stream_frame_.data_length;
+                                         aggregated_stream_frame_.data_length &&
+        // We would like to increment aggregated_stream_frame_.data_length by
+        // frame.stream_frame.data_length, so we need to make sure their sum is
+        // representable by QuicPacketLength, which is the type of the former.
+        !WillStreamFrameLengthSumWrapAround(
+            aggregated_stream_frame_.data_length,
+            frame.stream_frame.data_length);
+
     if (can_aggregate) {
       // Aggregate stream frame.
       aggregated_stream_frame_.data_length += frame.stream_frame.data_length;
diff --git a/net/third_party/quic/core/quic_unacked_packet_map.h b/net/third_party/quic/core/quic_unacked_packet_map.h
index a12436d..7cc3db3 100644
--- a/net/third_party/quic/core/quic_unacked_packet_map.h
+++ b/net/third_party/quic/core/quic_unacked_packet_map.h
@@ -16,6 +16,10 @@
 
 namespace quic {
 
+namespace test {
+class QuicUnackedPacketMapPeer;
+}  // namespace test
+
 // Class which tracks unacked packets for three purposes:
 // 1) Track retransmittable data, including multiple transmissions of frames.
 // 2) Track packets and bytes in flight for congestion control.
@@ -190,6 +194,8 @@
   }
 
  private:
+  friend class test::QuicUnackedPacketMapPeer;
+
   // Called when a packet is retransmitted with a new packet number.
   // |old_packet_number| will remain unacked, but will have no
   // retransmittable data associated with it. Retransmittable frames will be
diff --git a/net/third_party/quic/core/quic_unacked_packet_map_test.cc b/net/third_party/quic/core/quic_unacked_packet_map_test.cc
index dd133aed..3896cfb 100644
--- a/net/third_party/quic/core/quic_unacked_packet_map_test.cc
+++ b/net/third_party/quic/core/quic_unacked_packet_map_test.cc
@@ -3,7 +3,10 @@
 // found in the LICENSE file.
 
 #include "net/third_party/quic/core/quic_unacked_packet_map.h"
+#include <limits>
 
+#include "net/third_party/quic/core/frames/quic_stream_frame.h"
+#include "net/third_party/quic/core/quic_transmission_info.h"
 #include "net/third_party/quic/core/quic_utils.h"
 #include "net/third_party/quic/platform/api/quic_arraysize.h"
 #include "net/third_party/quic/platform/api/quic_test.h"
@@ -15,6 +18,15 @@
 
 namespace quic {
 namespace test {
+
+class QuicUnackedPacketMapPeer {
+ public:
+  static const QuicStreamFrame& GetAggregatedStreamFrame(
+      const QuicUnackedPacketMap& unacked_packets) {
+    return unacked_packets.aggregated_stream_frame_;
+  }
+};
+
 namespace {
 
 // Default packet length.
@@ -544,6 +556,62 @@
                                                   QuicTime::Delta::Zero());
 }
 
+// Regression test for b/112930090.
+TEST_P(QuicUnackedPacketMapTest, CannotAggregateIfDataLengthOverflow) {
+  testing::InSequence s;
+
+  QuicByteCount kMaxAggregatedDataLength =
+      std::numeric_limits<decltype(QuicStreamFrame().data_length)>::max();
+  QuicStreamId stream_id = 2;
+
+  // acked_stream_length=512 covers the case where a frame will cause the
+  // aggregated frame length to be exactly 64K.
+  // acked_stream_length=1300 covers the case where a frame will cause the
+  // aggregated frame length to exceed 64K.
+  for (const QuicPacketLength acked_stream_length : {512, 1300}) {
+    ++stream_id;
+    QuicStreamOffset offset = 0;
+    // Expected length of the aggregated stream frame.
+    QuicByteCount aggregated_data_length = 0;
+
+    while (offset < 1e6) {
+      QuicTransmissionInfo info;
+      QuicStreamFrame stream_frame(stream_id, false, offset,
+                                   acked_stream_length);
+      info.retransmittable_frames.push_back(QuicFrame(stream_frame));
+
+      const QuicStreamFrame& aggregated_stream_frame =
+          QuicUnackedPacketMapPeer::GetAggregatedStreamFrame(unacked_packets_);
+      if (aggregated_stream_frame.data_length + acked_stream_length <=
+          kMaxAggregatedDataLength) {
+        // Verify the acked stream frame can be aggregated.
+        EXPECT_CALL(notifier_, OnFrameAcked(_, _)).Times(0);
+        unacked_packets_.MaybeAggregateAckedStreamFrame(
+            info, QuicTime::Delta::Zero());
+        aggregated_data_length += acked_stream_length;
+      } else {
+        // Verify the acked stream frame cannot be aggregated because
+        // data_length is overflow.
+        EXPECT_CALL(notifier_, OnFrameAcked(_, _)).Times(1);
+        unacked_packets_.MaybeAggregateAckedStreamFrame(
+            info, QuicTime::Delta::Zero());
+        aggregated_data_length = acked_stream_length;
+      }
+
+      EXPECT_EQ(aggregated_data_length, aggregated_stream_frame.data_length);
+      offset += acked_stream_length;
+    }
+
+    // Ack the last frame of the stream.
+    QuicTransmissionInfo info;
+    QuicStreamFrame stream_frame(stream_id, true, offset, acked_stream_length);
+    info.retransmittable_frames.push_back(QuicFrame(stream_frame));
+    EXPECT_CALL(notifier_, OnFrameAcked(_, _)).Times(1);
+    unacked_packets_.MaybeAggregateAckedStreamFrame(info,
+                                                    QuicTime::Delta::Zero());
+  }
+}
+
 TEST_P(QuicUnackedPacketMapTest, CannotAggregateAckedControlFrames) {
   testing::InSequence s;
   QuicWindowUpdateFrame window_update(1, 5, 100);
diff --git a/net/third_party/quic/quartc/quartc_factory.cc b/net/third_party/quic/quartc/quartc_factory.cc
index 7e467b3..66d3873 100644
--- a/net/third_party/quic/quartc/quartc_factory.cc
+++ b/net/third_party/quic/quartc/quartc_factory.cc
@@ -31,6 +31,12 @@
   // Fixes behavior of StopReading() with level-triggered stream sequencers.
   SetQuicReloadableFlag(quic_stop_reading_when_level_triggered, true);
 
+  // Quartc uses zombie streams -- we close the stream for read as soon as we
+  // create a stream -- this makes the stream a zombie stream. b/115323618
+  // revealed that closing zombie streams is problematic, and enabling this flag
+  // fixes it.
+  SetQuicReloadableFlag(quic_fix_reset_zombie_streams, true);
+
   std::unique_ptr<QuicConnection> quic_connection =
       CreateQuicConnection(perspective, writer.get());
 
diff --git a/net/third_party/quic/test_tools/quic_framer_peer.cc b/net/third_party/quic/test_tools/quic_framer_peer.cc
index f701a48..0b164105 100644
--- a/net/third_party/quic/test_tools/quic_framer_peer.cc
+++ b/net/third_party/quic/test_tools/quic_framer_peer.cc
@@ -58,6 +58,20 @@
 }
 
 // static
+bool QuicFramerPeer::ProcessCryptoFrame(QuicFramer* framer,
+                                        QuicDataReader* reader,
+                                        QuicCryptoFrame* frame) {
+  return framer->ProcessCryptoFrame(reader, frame);
+}
+
+// static
+bool QuicFramerPeer::AppendCryptoFrame(QuicFramer* framer,
+                                       const QuicCryptoFrame& frame,
+                                       QuicDataWriter* writer) {
+  return framer->AppendCryptoFrame(frame, writer);
+}
+
+// static
 bool QuicFramerPeer::ProcessIetfAckFrame(QuicFramer* framer,
                                          QuicDataReader* reader,
                                          QuicAckFrame* ack_frame) {
diff --git a/net/third_party/quic/test_tools/quic_framer_peer.h b/net/third_party/quic/test_tools/quic_framer_peer.h
index d7903bb..45942691 100644
--- a/net/third_party/quic/test_tools/quic_framer_peer.h
+++ b/net/third_party/quic/test_tools/quic_framer_peer.h
@@ -47,6 +47,12 @@
                                     const QuicStreamFrame& frame,
                                     bool last_frame_in_packet,
                                     QuicDataWriter* writer);
+  static bool ProcessCryptoFrame(QuicFramer* framer,
+                                 QuicDataReader* reader,
+                                 QuicCryptoFrame* frame);
+  static bool AppendCryptoFrame(QuicFramer* framer,
+                                const QuicCryptoFrame& frame,
+                                QuicDataWriter* writer);
 
   static bool AppendIetfConnectionCloseFrame(
       QuicFramer* framer,
diff --git a/net/third_party/quic/test_tools/quic_test_utils.cc b/net/third_party/quic/test_tools/quic_test_utils.cc
index ed1adbb..0b8a016 100644
--- a/net/third_party/quic/test_tools/quic_test_utils.cc
+++ b/net/third_party/quic/test_tools/quic_test_utils.cc
@@ -139,6 +139,8 @@
 
   ON_CALL(*this, OnStreamFrame(_)).WillByDefault(testing::Return(true));
 
+  ON_CALL(*this, OnCryptoFrame(_)).WillByDefault(testing::Return(true));
+
   ON_CALL(*this, OnStopWaitingFrame(_)).WillByDefault(testing::Return(true));
 
   ON_CALL(*this, OnPaddingFrame(_)).WillByDefault(testing::Return(true));
@@ -189,14 +191,21 @@
   return true;
 }
 
+bool NoOpFramerVisitor::OnCryptoFrame(const QuicCryptoFrame& frame) {
+  return true;
+}
+
 bool NoOpFramerVisitor::OnAckFrameStart(QuicPacketNumber largest_acked,
                                         QuicTime::Delta ack_delay_time) {
   return true;
 }
 
 bool NoOpFramerVisitor::OnAckRange(QuicPacketNumber start,
-                                   QuicPacketNumber end,
-                                   bool last_range) {
+                                   QuicPacketNumber end) {
+  return true;
+}
+
+bool NoOpFramerVisitor::OnAckFrameEnd(QuicPacketNumber start) {
   return true;
 }
 
diff --git a/net/third_party/quic/test_tools/quic_test_utils.h b/net/third_party/quic/test_tools/quic_test_utils.h
index 6df6c0e..f23f033 100644
--- a/net/third_party/quic/test_tools/quic_test_utils.h
+++ b/net/third_party/quic/test_tools/quic_test_utils.h
@@ -256,8 +256,10 @@
   MOCK_METHOD1(OnDecryptedPacket, void(EncryptionLevel level));
   MOCK_METHOD1(OnPacketHeader, bool(const QuicPacketHeader& header));
   MOCK_METHOD1(OnStreamFrame, bool(const QuicStreamFrame& frame));
+  MOCK_METHOD1(OnCryptoFrame, bool(const QuicCryptoFrame& frame));
   MOCK_METHOD2(OnAckFrameStart, bool(QuicPacketNumber, QuicTime::Delta));
-  MOCK_METHOD3(OnAckRange, bool(QuicPacketNumber, QuicPacketNumber, bool));
+  MOCK_METHOD2(OnAckRange, bool(QuicPacketNumber, QuicPacketNumber));
+  MOCK_METHOD1(OnAckFrameEnd, bool(QuicPacketNumber));
   MOCK_METHOD1(OnStopWaitingFrame, bool(const QuicStopWaitingFrame& frame));
   MOCK_METHOD1(OnPaddingFrame, bool(const QuicPaddingFrame& frame));
   MOCK_METHOD1(OnPingFrame, bool(const QuicPingFrame& frame));
@@ -301,11 +303,11 @@
   void OnDecryptedPacket(EncryptionLevel level) override {}
   bool OnPacketHeader(const QuicPacketHeader& header) override;
   bool OnStreamFrame(const QuicStreamFrame& frame) override;
+  bool OnCryptoFrame(const QuicCryptoFrame& frame) override;
   bool OnAckFrameStart(QuicPacketNumber largest_acked,
                        QuicTime::Delta ack_delay_time) override;
-  bool OnAckRange(QuicPacketNumber start,
-                  QuicPacketNumber end,
-                  bool last_range) override;
+  bool OnAckRange(QuicPacketNumber start, QuicPacketNumber end) override;
+  bool OnAckFrameEnd(QuicPacketNumber start) override;
   bool OnStopWaitingFrame(const QuicStopWaitingFrame& frame) override;
   bool OnPaddingFrame(const QuicPaddingFrame& frame) override;
   bool OnPingFrame(const QuicPingFrame& frame) override;
diff --git a/net/third_party/quic/test_tools/simple_quic_framer.cc b/net/third_party/quic/test_tools/simple_quic_framer.cc
index cad44e3..e3bac246 100644
--- a/net/third_party/quic/test_tools/simple_quic_framer.cc
+++ b/net/third_party/quic/test_tools/simple_quic_framer.cc
@@ -64,6 +64,11 @@
     return true;
   }
 
+  bool OnCryptoFrame(const QuicCryptoFrame& frame) override {
+    // TODO(nharper): Implement this.
+    return false;
+  }
+
   bool OnAckFrameStart(QuicPacketNumber largest_acked,
                        QuicTime::Delta ack_delay_time) override {
     QuicAckFrame ack_frame;
@@ -73,14 +78,14 @@
     return true;
   }
 
-  bool OnAckRange(QuicPacketNumber start,
-                  QuicPacketNumber end,
-                  bool /*last_range*/) override {
+  bool OnAckRange(QuicPacketNumber start, QuicPacketNumber end) override {
     DCHECK(!ack_frames_.empty());
     ack_frames_[ack_frames_.size() - 1].packets.AddRange(start, end);
     return true;
   }
 
+  bool OnAckFrameEnd(QuicPacketNumber /*start*/) override { return true; }
+
   bool OnStopWaitingFrame(const QuicStopWaitingFrame& frame) override {
     stop_waiting_frames_.push_back(frame);
     return true;
diff --git a/net/third_party/quic/tools/quic_packet_printer_bin.cc b/net/third_party/quic/tools/quic_packet_printer_bin.cc
index 723c4b4..c5bbec1 100644
--- a/net/third_party/quic/tools/quic_packet_printer_bin.cc
+++ b/net/third_party/quic/tools/quic_packet_printer_bin.cc
@@ -105,16 +105,24 @@
               << " }\n";
     return true;
   }
+  bool OnCryptoFrame(const QuicCryptoFrame& frame) override {
+    std::cerr << "OnCryptoFrame: " << frame;
+    std::cerr << "         data: { "
+              << QuicTextUtils::HexEncode(frame.data_buffer, frame.data_length)
+              << " }\n";
+    return true;
+  }
   bool OnAckFrameStart(QuicPacketNumber largest_acked,
                        QuicTime::Delta /*ack_delay_time*/) override {
     std::cerr << "OnAckFrameStart, largest_acked: " << largest_acked;
     return true;
   }
-  bool OnAckRange(QuicPacketNumber start,
-                  QuicPacketNumber end,
-                  bool last_range) override {
-    std::cerr << "OnAckRange: [" << start << ", " << end
-              << "),  last_range: " << last_range;
+  bool OnAckRange(QuicPacketNumber start, QuicPacketNumber end) override {
+    std::cerr << "OnAckRange: [" << start << ", " << end << ")";
+    return true;
+  }
+  bool OnAckFrameEnd(QuicPacketNumber start) override {
+    std::cerr << "OnAckFrameEnd, start: " << start;
     return true;
   }
   bool OnStopWaitingFrame(const QuicStopWaitingFrame& frame) override {
diff --git a/services/identity/public/cpp/identity_manager.cc b/services/identity/public/cpp/identity_manager.cc
index 6bfbe19..996a61ef 100644
--- a/services/identity/public/cpp/identity_manager.cc
+++ b/services/identity/public/cpp/identity_manager.cc
@@ -51,9 +51,6 @@
       token_service_(token_service),
       account_tracker_service_(account_tracker_service),
       gaia_cookie_manager_service_(gaia_cookie_manager_service) {
-  // Initialize the state of the primary account.
-  primary_account_info_ = signin_manager_->GetAuthenticatedAccountInfo();
-
   // Initialize the state of accounts with refresh tokens.
   // |account_id| is moved into |accounts_with_refresh_tokens_|.
   // Do not change this to "const std::string&".
@@ -83,10 +80,6 @@
   }
 
   signin_manager_->AddObserver(this);
-#if !defined(OS_CHROMEOS)
-  SigninManager::FromSigninManagerBase(signin_manager_)
-      ->set_diagnostics_client(this);
-#endif
   token_service_->AddDiagnosticsObserver(this);
   token_service_->AddObserver(this);
   token_service_->set_diagnostics_client(this);
@@ -95,10 +88,6 @@
 
 IdentityManager::~IdentityManager() {
   signin_manager_->RemoveObserver(this);
-#if !defined(OS_CHROMEOS)
-  SigninManager::FromSigninManagerBase(signin_manager_)
-      ->set_diagnostics_client(nullptr);
-#endif
   token_service_->RemoveObserver(this);
   token_service_->RemoveDiagnosticsObserver(this);
   token_service_->set_diagnostics_client(nullptr);
@@ -106,52 +95,11 @@
 }
 
 AccountInfo IdentityManager::GetPrimaryAccountInfo() const {
-#if defined(OS_CHROMEOS)
-  // On ChromeOS in production, the authenticated account is set very early in
-  // startup and never changed. Hence, the information held by the
-  // IdentityManager should always correspond to that held by SigninManager.
-  // NOTE: the above invariant is not guaranteed to hold in tests. If you
-  // are seeing this DCHECK go off in a testing context, it means that you need
-  // to set the IdentityManager's primary account info in the test at the place
-  // where you are setting the authenticated account info in the SigninManager.
-  // TODO(blundell): Add the API to do this once we hit the first case and
-  // document the API to use here.
-  DCHECK_EQ(signin_manager_->GetAuthenticatedAccountId(),
-            primary_account_info_.account_id);
-
-  // Note: If the primary account's refresh token gets revoked, then the account
-  // gets removed from AccountTrackerService (via
-  // AccountFetcherService::OnRefreshTokenRevoked), and so SigninManager's
-  // GetAuthenticatedAccountInfo is empty (even though
-  // GetAuthenticatedAccountId is NOT empty).
-  if (!signin_manager_->GetAuthenticatedAccountInfo().account_id.empty()) {
-    DCHECK_EQ(signin_manager_->GetAuthenticatedAccountInfo().account_id,
-              primary_account_info_.account_id);
-    DCHECK_EQ(signin_manager_->GetAuthenticatedAccountInfo().gaia,
-              primary_account_info_.gaia);
-
-    // TODO(842670): As described in the bug, AccountTrackerService's email
-    // address can be updated after it is initially set on ChromeOS. Figure out
-    // right long-term solution for this problem.
-    if (signin_manager_->GetAuthenticatedAccountInfo().email !=
-        primary_account_info_.email) {
-      // This update should only be to move it from normalized form to the form
-      // in which the user entered the email when creating the account. The
-      // below check verifies that the normalized forms of the two email
-      // addresses are identical.
-      DCHECK(gaia::AreEmailsSame(
-          signin_manager_->GetAuthenticatedAccountInfo().email,
-          primary_account_info_.email));
-      primary_account_info_.email =
-          signin_manager_->GetAuthenticatedAccountInfo().email;
-    }
-  }
-#endif  // defined(OS_CHROMEOS)
-  return primary_account_info_;
+  return signin_manager_->GetAuthenticatedAccountInfo();
 }
 
 bool IdentityManager::HasPrimaryAccount() const {
-  return !primary_account_info_.account_id.empty();
+  return !GetPrimaryAccountInfo().account_id.empty();
 }
 
 #if !defined(OS_CHROMEOS)
@@ -176,8 +124,7 @@
       break;
   }
 
-  // NOTE: |primary_account_| member is cleared in WillFireGoogleSignedOut()
-  // and IdentityManager::Observers are notified in GoogleSignedOut();
+  // NOTE: IdentityManager::Observers are notified in GoogleSignedOut().
 }
 #endif  // defined(OS_CHROMEOS)
 
@@ -269,38 +216,14 @@
     const std::string& email_address,
     const std::string& refresh_token) {
   signin_manager_->SetAuthenticatedAccountInfo(gaia_id, email_address);
-  primary_account_info_ = signin_manager_->GetAuthenticatedAccountInfo();
 
   if (!refresh_token.empty()) {
-    token_service_->UpdateCredentials(primary_account_info_.account_id,
+    token_service_->UpdateCredentials(GetPrimaryAccountInfo().account_id,
                                       refresh_token);
   }
 }
 
-#if !defined(OS_CHROMEOS)
-void IdentityManager::WillFireGoogleSigninSucceeded(
-    const AccountInfo& account_info) {
-  // TODO(843510): Consider setting this info and notifying observers
-  // asynchronously in response to GoogleSigninSucceeded() once there are no
-  // direct clients of SigninManager.
-  primary_account_info_ = account_info;
-}
-
-void IdentityManager::WillFireGoogleSignedOut(const AccountInfo& account_info) {
-  // TODO(843510): Consider setting this info and notifying observers
-  // asynchronously in response to GoogleSigninSucceeded() once there are no
-  // direct clients of SigninManager.
-  DCHECK_EQ(account_info.account_id, primary_account_info_.account_id);
-  DCHECK_EQ(account_info.gaia, primary_account_info_.gaia);
-  DCHECK(gaia::AreEmailsSame(account_info.email, primary_account_info_.email));
-  primary_account_info_ = AccountInfo();
-}
-#endif
-
 void IdentityManager::GoogleSigninSucceeded(const AccountInfo& account_info) {
-  DCHECK(account_info.account_id == primary_account_info_.account_id);
-  DCHECK(account_info.gaia == primary_account_info_.gaia);
-  DCHECK(account_info.email == primary_account_info_.email);
   for (auto& observer : observer_list_) {
     observer.OnPrimaryAccountSet(account_info);
   }
diff --git a/services/identity/public/cpp/identity_manager.h b/services/identity/public/cpp/identity_manager.h
index 2c7e02f5..ccfb9947 100644
--- a/services/identity/public/cpp/identity_manager.h
+++ b/services/identity/public/cpp/identity_manager.h
@@ -43,9 +43,6 @@
 // Gives access to information about the user's Google identities. See
 // ./README.md for detailed documentation.
 class IdentityManager : public SigninManagerBase::Observer,
-#if !defined(OS_CHROMEOS)
-                        public SigninManager::DiagnosticsClient,
-#endif
                         public ProfileOAuth2TokenService::DiagnosticsClient,
                         public OAuth2TokenService::DiagnosticsObserver,
                         public OAuth2TokenService::Observer,
@@ -253,16 +250,6 @@
   void OnRefreshTokenAvailable(const std::string& account_id) override;
   void OnRefreshTokenRevoked(const std::string& account_id) override;
 
-#if !defined(OS_CHROMEOS)
-  // SigninManager::DiagnosticsClient:
-  // Override these to update |primary_account_info_| before any observers of
-  // SigninManager are notified of the signin state change, ensuring that any
-  // such observer flows that eventually interact with IdentityManager observe
-  // its state as being consistent with that of SigninManager.
-  void WillFireGoogleSigninSucceeded(const AccountInfo& account_info) override;
-  void WillFireGoogleSignedOut(const AccountInfo& account_info) override;
-#endif
-
   // GaiaCookieManagerService::Observer:
   void OnGaiaAccountsInCookieUpdated(
       const std::vector<gaia::ListedAccount>& accounts,
@@ -284,16 +271,6 @@
   AccountTrackerService* account_tracker_service_;
   GaiaCookieManagerService* gaia_cookie_manager_service_;
 
-  // The latest (cached) value of the primary account.
-#if defined(OS_CHROMEOS)
-  // On ChromeOS the primary account's email address needs to be modified from
-  // within  GetPrimaryAccountInfo(). TODO(842670): Remove this field being
-  // mutable if possible as part of solving the larger issue.
-  mutable AccountInfo primary_account_info_;
-#else
-  AccountInfo primary_account_info_;
-#endif
-
   // The latest (cached) value of the accounts with refresh tokens.
   using AccountIDToAccountInfoMap = std::map<std::string, AccountInfo>;
   AccountIDToAccountInfoMap accounts_with_refresh_tokens_;
diff --git a/services/network/network_context.cc b/services/network/network_context.cc
index 99da1677..08d8b42 100644
--- a/services/network/network_context.cc
+++ b/services/network/network_context.cc
@@ -1060,7 +1060,7 @@
         base::MessageLoopCurrent::Get()->task_runner();
     scoped_refptr<base::SequencedTaskRunner> background_task_runner =
         base::CreateSequencedTaskRunnerWithTraits(
-            {base::MayBlock(), base::TaskPriority::BEST_EFFORT,
+            {base::MayBlock(), net::GetCookieStoreBackgroundSequencePriority(),
              base::TaskShutdownBehavior::BLOCK_SHUTDOWN});
 
     std::unique_ptr<net::ChannelIDService> channel_id_service;
diff --git a/services/tracing/perfetto/json_trace_exporter.cc b/services/tracing/perfetto/json_trace_exporter.cc
index bc19b9bb..df3f0103 100644
--- a/services/tracing/perfetto/json_trace_exporter.cc
+++ b/services/tracing/perfetto/json_trace_exporter.cc
@@ -4,6 +4,7 @@
 
 #include "services/tracing/perfetto/json_trace_exporter.h"
 
+#include <unordered_map>
 #include <utility>
 
 #include "base/json/json_reader.h"
@@ -23,17 +24,36 @@
 using TraceEvent = base::trace_event::TraceEvent;
 
 namespace {
+const char* GetStringFromStringTable(
+    const std::unordered_map<int, std::string>& string_table,
+    int index) {
+  auto it = string_table.find(index);
+  DCHECK(it != string_table.end());
+
+  return it->second.c_str();
+}
 
 void OutputJSONFromTraceEventProto(
     const perfetto::protos::ChromeTraceEvent& event,
-    std::string* out) {
+    std::string* out,
+    const std::unordered_map<int, std::string>& string_table) {
   char phase = static_cast<char>(event.phase());
+  const char* name =
+      event.has_name_index()
+          ? GetStringFromStringTable(string_table, event.name_index())
+          : event.name().c_str();
+  const char* category_group_name =
+      event.has_category_group_name_index()
+          ? GetStringFromStringTable(string_table,
+                                     event.category_group_name_index())
+          : event.category_group_name().c_str();
+
   base::StringAppendF(out,
                       "{\"pid\":%i,\"tid\":%i,\"ts\":%" PRId64
                       ",\"ph\":\"%c\",\"cat\":\"%s\",\"name\":",
                       event.process_id(), event.thread_id(), event.timestamp(),
-                      phase, event.category_group_name().c_str());
-  base::EscapeJSONString(event.name(), true, out);
+                      phase, category_group_name);
+  base::EscapeJSONString(name, true, out);
 
   if (event.has_duration()) {
     base::StringAppendF(out, ",\"dur\":%" PRId64, event.duration());
@@ -126,7 +146,9 @@
     }
 
     *out += "\"";
-    *out += arg.name();
+    *out += arg.has_name_index()
+                ? GetStringFromStringTable(string_table, arg.name_index())
+                : arg.name();
     *out += "\":";
 
     TraceEvent::TraceValue value;
@@ -244,19 +266,24 @@
       continue;
     }
 
-    const perfetto::protos::ChromeEventBundle& bundle = packet.chrome_events();
-    for (const perfetto::protos::ChromeTraceEvent& event :
-         bundle.trace_events()) {
+    auto& bundle = packet.chrome_events();
+
+    std::unordered_map<int, std::string> string_table;
+    for (auto& string_table_entry : bundle.string_table()) {
+      string_table[string_table_entry.index()] = string_table_entry.value();
+    }
+
+    for (auto& event : bundle.trace_events()) {
       if (has_output_first_event_) {
         out += ",";
       } else {
         has_output_first_event_ = true;
       }
 
-      OutputJSONFromTraceEventProto(event, &out);
+      OutputJSONFromTraceEventProto(event, &out, string_table);
     }
 
-    for (const perfetto::protos::ChromeMetadata& metadata : bundle.metadata()) {
+    for (auto& metadata : bundle.metadata()) {
       if (metadata.has_string_value()) {
         metadata_->SetString(metadata.name(), metadata.string_value());
       } else if (metadata.has_int_value()) {
diff --git a/services/tracing/perfetto/json_trace_exporter_unittest.cc b/services/tracing/perfetto/json_trace_exporter_unittest.cc
index 4792316..ff783b9 100644
--- a/services/tracing/perfetto/json_trace_exporter_unittest.cc
+++ b/services/tracing/perfetto/json_trace_exporter_unittest.cc
@@ -221,9 +221,10 @@
     return trace_event;
   }
 
-  const trace_analyzer::TraceAnalyzer* trace_analyzer() const {
+  trace_analyzer::TraceAnalyzer* trace_analyzer() {
     return trace_analyzer_.get();
   }
+
   MockService* service() { return service_.get(); }
   const base::DictionaryValue* parsed_trace_data() const {
     return parsed_trace_data_.get();
@@ -307,6 +308,58 @@
   ValidateAndGetBasicTestPacket();
 }
 
+TEST_F(JSONTraceExporterTest, TestStringTable) {
+  CreateJSONTraceExporter("foo");
+  service()->WaitForTracingEnabled();
+  StopAndFlush();
+
+  perfetto::protos::TracePacket trace_packet_proto;
+  auto* new_trace_event =
+      trace_packet_proto.mutable_chrome_events()->add_trace_events();
+
+  {
+    auto* string_table_entry =
+        trace_packet_proto.mutable_chrome_events()->add_string_table();
+    string_table_entry->set_index(1);
+    string_table_entry->set_value("foo_name");
+  }
+
+  {
+    auto* string_table_entry =
+        trace_packet_proto.mutable_chrome_events()->add_string_table();
+    string_table_entry->set_index(2);
+    string_table_entry->set_value("foo_cat");
+  }
+
+  {
+    auto* string_table_entry =
+        trace_packet_proto.mutable_chrome_events()->add_string_table();
+    string_table_entry->set_index(3);
+    string_table_entry->set_value("foo_arg");
+  }
+
+  new_trace_event->set_name_index(1);
+  new_trace_event->set_category_group_name_index(2);
+
+  auto* new_arg = new_trace_event->add_args();
+  new_arg->set_name_index(3);
+  new_arg->set_bool_value(true);
+
+  FinalizePacket(trace_packet_proto);
+
+  service()->WaitForTracingDisabled();
+
+  auto* trace_event = trace_analyzer()->FindFirstOf(
+      trace_analyzer::Query(trace_analyzer::Query::EVENT_NAME) ==
+      trace_analyzer::Query::String("foo_name"));
+  EXPECT_TRUE(trace_event);
+
+  EXPECT_EQ("foo_name", trace_event->name);
+  EXPECT_EQ("foo_cat", trace_event->category);
+
+  EXPECT_TRUE(trace_event->GetKnownArgAsBool("foo_arg"));
+}
+
 TEST_F(JSONTraceExporterTest, TestEventWithBoolArgs) {
   CreateJSONTraceExporter("foo");
   service()->WaitForTracingEnabled();
diff --git a/services/tracing/public/cpp/perfetto/trace_event_data_source.cc b/services/tracing/public/cpp/perfetto/trace_event_data_source.cc
index 1a288ce..9c72208 100644
--- a/services/tracing/public/cpp/perfetto/trace_event_data_source.cc
+++ b/services/tracing/public/cpp/perfetto/trace_event_data_source.cc
@@ -4,6 +4,7 @@
 
 #include "services/tracing/public/cpp/perfetto/trace_event_data_source.h"
 
+#include <map>
 #include <utility>
 
 #include "base/json/json_writer.h"
@@ -23,6 +24,9 @@
 
 namespace tracing {
 
+using ChromeEventBundleHandle =
+    protozero::MessageHandle<perfetto::protos::pbzero::ChromeEventBundle>;
+
 TraceEventMetadataSource::TraceEventMetadataSource()
     : DataSourceBase(mojom::kMetaDataSourceName),
       origin_task_runner_(base::SequencedTaskRunnerHandle::Get()) {}
@@ -41,8 +45,7 @@
   DCHECK(origin_task_runner_->RunsTasksInCurrentSequence());
 
   auto trace_packet = trace_writer->NewTracePacket();
-  protozero::MessageHandle<perfetto::protos::pbzero::ChromeEventBundle>
-      event_bundle(trace_packet->set_chrome_events());
+  ChromeEventBundleHandle event_bundle(trace_packet->set_chrome_events());
 
   base::AutoLock lock(lock_);
   for (auto& generator : generator_functions_) {
@@ -111,6 +114,46 @@
                                                 std::move(trace_writer_));
   }
 
+  void EnsureValidHandles() {
+    if (trace_packet_handle_) {
+      return;
+    }
+
+    trace_packet_handle_ = trace_writer_->NewTracePacket();
+    event_bundle_ =
+        ChromeEventBundleHandle(trace_packet_handle_->set_chrome_events());
+    string_table_.clear();
+    next_string_table_index_ = 0;
+  }
+
+  int GetStringTableIndexForString(const char* str_value) {
+    EnsureValidHandles();
+
+    auto it = string_table_.find(reinterpret_cast<intptr_t>(str_value));
+    if (it != string_table_.end()) {
+      CHECK_EQ(std::string(reinterpret_cast<const char*>(it->first)),
+               std::string(str_value));
+
+      return it->second;
+    }
+
+    int string_table_index = ++next_string_table_index_;
+    string_table_[reinterpret_cast<intptr_t>(str_value)] = string_table_index;
+
+    auto* new_string_table_entry = event_bundle_->add_string_table();
+    new_string_table_entry->set_value(str_value);
+    new_string_table_entry->set_index(string_table_index);
+
+    return string_table_index;
+  }
+
+  void AddConvertableToTraceFormat(
+      const base::trace_event::ConvertableToTraceFormat* value,
+      perfetto::protos::pbzero::ChromeTraceEvent_Arg* arg) {
+    std::string json = value->ToString();
+    arg->set_json_value(json.c_str());
+  }
+
   void AddTraceEvent(const TraceEvent& trace_event) {
     // TODO(oysteine): Adding trace events to Perfetto will
     // stall in some situations, specifically when we overflow
@@ -126,14 +169,46 @@
       return;
     }
 
-    // TODO(oysteine): Consider batching several trace events per trace packet,
-    // and only add repeated data once per batch.
-    auto trace_packet = trace_writer_->NewTracePacket();
-    protozero::MessageHandle<perfetto::protos::pbzero::ChromeEventBundle>
-        event_bundle(trace_packet->set_chrome_events());
+    EnsureValidHandles();
 
-    auto* new_trace_event = event_bundle->add_trace_events();
-    new_trace_event->set_name(trace_event.name());
+    int name_index = 0;
+    int category_name_index = 0;
+    int arg_name_indices[base::trace_event::kTraceMaxNumArgs] = {0};
+
+    // Populate any new string table parts first; has to be done before
+    // the add_trace_events() call (as the string table is part of the outer
+    // proto message).
+    // If the TRACE_EVENT_FLAG_COPY flag is set, the char* pointers aren't
+    // necessarily valid after the TRACE_EVENT* call, and so we need to store
+    // the string every time.
+    bool string_table_enabled = !(trace_event.flags() & TRACE_EVENT_FLAG_COPY);
+    if (string_table_enabled) {
+      name_index = GetStringTableIndexForString(trace_event.name());
+      category_name_index = GetStringTableIndexForString(
+          TraceLog::GetCategoryGroupName(trace_event.category_group_enabled()));
+
+      for (int i = 0;
+           i < base::trace_event::kTraceMaxNumArgs && trace_event.arg_name(i);
+           ++i) {
+        arg_name_indices[i] =
+            GetStringTableIndexForString(trace_event.arg_name(i));
+      }
+    }
+
+    auto* new_trace_event = event_bundle_->add_trace_events();
+
+    if (name_index) {
+      new_trace_event->set_name_index(name_index);
+    } else {
+      new_trace_event->set_name(trace_event.name());
+    }
+
+    if (category_name_index) {
+      new_trace_event->set_category_group_name_index(category_name_index);
+    } else {
+      new_trace_event->set_category_group_name(
+          TraceLog::GetCategoryGroupName(trace_event.category_group_enabled()));
+    }
 
     new_trace_event->set_timestamp(
         trace_event.timestamp().since_origin().InMicroseconds());
@@ -155,9 +230,6 @@
     new_trace_event->set_process_id(process_id);
     new_trace_event->set_thread_id(thread_id);
 
-    new_trace_event->set_category_group_name(
-        TraceLog::GetCategoryGroupName(trace_event.category_group_enabled()));
-
     char phase = trace_event.phase();
     new_trace_event->set_phase(phase);
 
@@ -166,11 +238,16 @@
          ++i) {
       auto type = trace_event.arg_type(i);
       auto* new_arg = new_trace_event->add_args();
-      new_arg->set_name(trace_event.arg_name(i));
+
+      if (arg_name_indices[i]) {
+        new_arg->set_name_index(arg_name_indices[i]);
+      } else {
+        new_arg->set_name(trace_event.arg_name(i));
+      }
 
       if (type == TRACE_VALUE_TYPE_CONVERTABLE) {
-        std::string json = trace_event.arg_convertible_value(i)->ToString();
-        new_arg->set_json_value(json.c_str());
+        AddConvertableToTraceFormat(trace_event.arg_convertible_value(i),
+                                    new_arg);
         continue;
       }
 
@@ -243,10 +320,20 @@
     }
   }
 
-  void Flush() { trace_writer_->Flush(); }
+  void Flush() {
+    event_bundle_ = ChromeEventBundleHandle();
+    trace_packet_handle_ = perfetto::TraceWriter::TracePacketHandle();
+    trace_writer_->Flush();
+    token_ = "";
+  }
 
  private:
   std::unique_ptr<perfetto::TraceWriter> trace_writer_;
+  ChromeEventBundleHandle event_bundle_;
+  perfetto::TraceWriter::TracePacketHandle trace_packet_handle_;
+  std::map<intptr_t, int> string_table_;
+  int next_string_table_index_ = 0;
+  std::string token_;
 };
 
 namespace {
diff --git a/services/tracing/public/cpp/perfetto/trace_event_data_source_unittest.cc b/services/tracing/public/cpp/perfetto/trace_event_data_source_unittest.cc
index a43ea2b..77b678f9 100644
--- a/services/tracing/public/cpp/perfetto/trace_event_data_source_unittest.cc
+++ b/services/tracing/public/cpp/perfetto/trace_event_data_source_unittest.cc
@@ -4,6 +4,7 @@
 
 #include "services/tracing/public/cpp/perfetto/trace_event_data_source.h"
 
+#include <map>
 #include <utility>
 #include <vector>
 
@@ -31,12 +32,10 @@
 class MockProducerClient : public ProducerClient {
  public:
   explicit MockProducerClient(
-      scoped_refptr<base::SequencedTaskRunner> main_thread_task_runner,
-      const char* wanted_event_category)
+      scoped_refptr<base::SequencedTaskRunner> main_thread_task_runner)
       : delegate_(perfetto::base::kPageSize),
         stream_(&delegate_),
-        main_thread_task_runner_(std::move(main_thread_task_runner)),
-        wanted_event_category_(wanted_event_category) {
+        main_thread_task_runner_(std::move(main_thread_task_runner)) {
     trace_packet_.Reset(&stream_);
   }
 
@@ -56,9 +55,7 @@
       auto proto = std::make_unique<perfetto::protos::TracePacket>();
       EXPECT_TRUE(proto->ParseFromArray(buffer.begin, message_size));
       if (proto->has_chrome_events() &&
-          proto->chrome_events().trace_events().size() > 0 &&
-          proto->chrome_events().trace_events()[0].category_group_name() ==
-              wanted_event_category_) {
+          proto->chrome_events().trace_events().size() > 0) {
         finalized_packets_.push_back(std::move(proto));
       } else if (proto->has_chrome_events() &&
                  proto->chrome_events().metadata().size() > 0) {
@@ -87,6 +84,7 @@
     EXPECT_GT(finalized_packets_.size(), packet_index);
 
     auto event_bundle = finalized_packets_[packet_index]->chrome_events();
+    PopulateStringTable(event_bundle);
     return event_bundle.trace_events();
   }
 
@@ -99,7 +97,25 @@
     return event_bundle.metadata();
   }
 
+  void PopulateStringTable(
+      const perfetto::protos::ChromeEventBundle& event_bundle) {
+    string_table_.clear();
+
+    auto& string_table = event_bundle.string_table();
+    for (int i = 0; i < string_table.size(); ++i) {
+      string_table_[string_table[i].index()] = string_table[i].value();
+    }
+  }
+
+  std::string GetStringTableEntry(int index) {
+    auto it = string_table_.find(index);
+    CHECK(it != string_table_.end());
+
+    return it->second;
+  }
+
  private:
+  std::map<int, std::string> string_table_;
   std::vector<std::unique_ptr<perfetto::protos::TracePacket>>
       finalized_packets_;
   std::vector<std::unique_ptr<perfetto::protos::TracePacket>> metadata_packets_;
@@ -107,7 +123,6 @@
   protozero::ScatteredStreamWriterNullDelegate delegate_;
   protozero::ScatteredStreamWriter stream_;
   scoped_refptr<base::SequencedTaskRunner> main_thread_task_runner_;
-  const char* wanted_event_category_;
 };
 
 // For sequences/threads other than our own, we just want to ignore
@@ -196,11 +211,9 @@
     producer_client_.reset();
   }
 
-  void CreateTraceEventDataSource(
-      const char* wanted_event_category = kCategoryGroup) {
+  void CreateTraceEventDataSource() {
     producer_client_ = std::make_unique<MockProducerClient>(
-        scoped_task_environment_.GetMainThreadTaskRunner(),
-        wanted_event_category);
+        scoped_task_environment_.GetMainThreadTaskRunner());
 
     auto data_source_config = mojom::DataSourceConfig::New();
     TraceEventDataSource::GetInstance()->StartTracing(producer_client(),
@@ -292,7 +305,7 @@
 }
 
 TEST_F(TraceEventDataSourceTest, TraceLogMetadataEvents) {
-  CreateTraceEventDataSource("__metadata");
+  CreateTraceEventDataSource();
 
   base::RunLoop wait_for_flush;
   TraceEventDataSource::GetInstance()->StopTracing(
@@ -303,7 +316,8 @@
   for (size_t i = 0; i < producer_client()->GetFinalizedPacketCount(); ++i) {
     auto trace_events = producer_client()->GetChromeTraceEvents(i);
     for (auto& event : trace_events) {
-      if (event.name() == "process_uptime_seconds") {
+      if (producer_client()->GetStringTableEntry(event.name_index()) ==
+          "process_uptime_seconds") {
         has_process_uptime_event = true;
         break;
       }
@@ -322,8 +336,10 @@
   EXPECT_EQ(trace_events.size(), 1);
 
   auto trace_event = trace_events[0];
-  EXPECT_EQ("bar", trace_event.name());
-  EXPECT_EQ(kCategoryGroup, trace_event.category_group_name());
+  EXPECT_EQ("bar",
+            producer_client()->GetStringTableEntry(trace_event.name_index()));
+  EXPECT_EQ(kCategoryGroup, producer_client()->GetStringTableEntry(
+                                trace_event.category_group_name_index()));
   EXPECT_EQ(TRACE_EVENT_PHASE_BEGIN, trace_event.phase());
 }
 
@@ -338,8 +354,10 @@
   EXPECT_EQ(trace_events.size(), 1);
 
   auto trace_event = trace_events[0];
-  EXPECT_EQ("bar", trace_event.name());
-  EXPECT_EQ(kCategoryGroup, trace_event.category_group_name());
+  EXPECT_EQ("bar",
+            producer_client()->GetStringTableEntry(trace_event.name_index()));
+  EXPECT_EQ(kCategoryGroup, producer_client()->GetStringTableEntry(
+                                trace_event.category_group_name_index()));
   EXPECT_EQ(42u, trace_event.id());
   EXPECT_EQ(4242, trace_event.thread_id());
   EXPECT_EQ(424242, trace_event.timestamp());
@@ -355,8 +373,10 @@
   EXPECT_EQ(trace_events.size(), 1);
 
   auto trace_event = trace_events[0];
-  EXPECT_EQ("bar", trace_event.name());
-  EXPECT_EQ(kCategoryGroup, trace_event.category_group_name());
+  EXPECT_EQ("bar",
+            producer_client()->GetStringTableEntry(trace_event.name_index()));
+  EXPECT_EQ(kCategoryGroup, producer_client()->GetStringTableEntry(
+                                trace_event.category_group_name_index()));
   EXPECT_EQ(TRACE_EVENT_SCOPE_THREAD, trace_event.flags());
   EXPECT_EQ(TRACE_EVENT_PHASE_INSTANT, trace_event.phase());
 }
@@ -373,12 +393,34 @@
   auto trace_args = trace_events[0].args();
   EXPECT_EQ(trace_args.size(), 2);
 
-  EXPECT_EQ("arg1_name", trace_args[0].name());
+  EXPECT_EQ("arg1_name",
+            producer_client()->GetStringTableEntry(trace_args[0].name_index()));
   EXPECT_EQ("arg1_val", trace_args[0].string_value());
-  EXPECT_EQ("arg2_name", trace_args[1].name());
+  EXPECT_EQ("arg2_name",
+            producer_client()->GetStringTableEntry(trace_args[1].name_index()));
   EXPECT_EQ("arg2_val", trace_args[1].string_value());
 }
 
+TEST_F(TraceEventDataSourceTest, NoStringTableTest) {
+  CreateTraceEventDataSource();
+
+  TRACE_EVENT_INSTANT2(kCategoryGroup, "bar",
+                       TRACE_EVENT_SCOPE_THREAD | TRACE_EVENT_FLAG_COPY,
+                       "arg1_name", "arg1_val", "arg2_name", "arg2_val");
+
+  auto trace_events = producer_client()->GetChromeTraceEvents();
+  EXPECT_EQ(trace_events.size(), 1);
+
+  EXPECT_EQ("bar", trace_events[0].name());
+  EXPECT_EQ(kCategoryGroup, trace_events[0].category_group_name());
+
+  auto trace_args = trace_events[0].args();
+  EXPECT_EQ(trace_args.size(), 2);
+
+  EXPECT_EQ("arg1_name", trace_args[0].name());
+  EXPECT_EQ("arg2_name", trace_args[1].name());
+}
+
 TEST_F(TraceEventDataSourceTest, EventWithUIntArgs) {
   CreateTraceEventDataSource();
 
@@ -532,19 +574,14 @@
   // TRACE_EVENT_PHASE_COMPLETE events should internally emit a
   // TRACE_EVENT_PHASE_BEGIN event first, and then a TRACE_EVENT_PHASE_END event
   // when the duration is attempted set on the first event.
-  EXPECT_EQ(2u, producer_client()->GetFinalizedPacketCount());
+  auto events = producer_client()->GetChromeTraceEvents(0);
+  EXPECT_EQ(events.size(), 2);
 
-  auto events_from_first_packet = producer_client()->GetChromeTraceEvents(0);
-  EXPECT_EQ(events_from_first_packet.size(), 1);
-
-  auto begin_trace_event = events_from_first_packet[0];
+  auto begin_trace_event = events[0];
   EXPECT_EQ(TRACE_EVENT_PHASE_BEGIN, begin_trace_event.phase());
   EXPECT_EQ(10, begin_trace_event.timestamp());
 
-  auto events_from_second_packet = producer_client()->GetChromeTraceEvents(1);
-  EXPECT_EQ(events_from_second_packet.size(), 1);
-
-  auto end_trace_event = events_from_second_packet[0];
+  auto end_trace_event = events[1];
   EXPECT_EQ(TRACE_EVENT_PHASE_END, end_trace_event.phase());
   EXPECT_EQ(20, end_trace_event.timestamp());
   EXPECT_EQ(50, end_trace_event.thread_timestamp());
diff --git a/services/video_capture/public/mojom/virtual_device.mojom b/services/video_capture/public/mojom/virtual_device.mojom
index 18559a8..71898688 100644
--- a/services/video_capture/public/mojom/virtual_device.mojom
+++ b/services/video_capture/public/mojom/virtual_device.mojom
@@ -35,7 +35,8 @@
   // |Producer.OnNewBufferHandle| and/or |Producer.OnBufferRetired|
   // will be invoked.
   RequestFrameBuffer(gfx.mojom.Size dimension,
-                 media.mojom.VideoCapturePixelFormat pixel_format)
+                     media.mojom.VideoCapturePixelFormat pixel_format,
+                     media.mojom.PlaneStrides? strides)
       => (int32 buffer_id);
 
   // Called to indicate that a video frame is ready in the given buffer
@@ -54,6 +55,9 @@
       int32 buffer_id, media.mojom.MailboxBufferHandleSet mailbox_handles);
   // The invoker must guarantee that the textures with |buffer_id| stay valid
   // until |access_permission| is released by the invocation target.
+  // In |frame_info|, |visible_rect| must be equivalent to the full |coded_size|
+  // of the frame, i.e. using |visible_rect| to crop to subregions of the frame
+  // is not supported.
   OnFrameReadyInBuffer(int32 buffer_id,
                        ScopedAccessPermission access_permission,
                        media.mojom.VideoFrameInfo frame_info);
diff --git a/services/video_capture/shared_memory_virtual_device_mojo_adapter.cc b/services/video_capture/shared_memory_virtual_device_mojo_adapter.cc
index f14b848..7d57c32 100644
--- a/services/video_capture/shared_memory_virtual_device_mojo_adapter.cc
+++ b/services/video_capture/shared_memory_virtual_device_mojo_adapter.cc
@@ -55,12 +55,14 @@
 void SharedMemoryVirtualDeviceMojoAdapter::RequestFrameBuffer(
     const gfx::Size& dimension,
     media::VideoPixelFormat pixel_format,
+    media::mojom::PlaneStridesPtr strides,
     RequestFrameBufferCallback callback) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
   int buffer_id_to_drop = media::VideoCaptureBufferPool::kInvalidId;
   const int buffer_id = buffer_pool_->ReserveForProducer(
-      dimension, pixel_format, 0 /* frame_feedback_id */, &buffer_id_to_drop);
+      dimension, pixel_format, strides, 0 /* frame_feedback_id */,
+      &buffer_id_to_drop);
 
   // Remove dropped buffer if there is one.
   if (buffer_id_to_drop != media::VideoCaptureBufferPool::kInvalidId) {
diff --git a/services/video_capture/shared_memory_virtual_device_mojo_adapter.h b/services/video_capture/shared_memory_virtual_device_mojo_adapter.h
index 78e59af..013a31a 100644
--- a/services/video_capture/shared_memory_virtual_device_mojo_adapter.h
+++ b/services/video_capture/shared_memory_virtual_device_mojo_adapter.h
@@ -28,6 +28,7 @@
   // mojom::SharedMemoryVirtualDevice implementation.
   void RequestFrameBuffer(const gfx::Size& dimension,
                           media::VideoPixelFormat pixel_format,
+                          media::mojom::PlaneStridesPtr strides,
                           RequestFrameBufferCallback callback) override;
   void OnFrameReadyInBuffer(
       int32_t buffer_id,
diff --git a/services/video_capture/test/virtual_device_unittest.cc b/services/video_capture/test/virtual_device_unittest.cc
index 3370903..9c4ba5d 100644
--- a/services/video_capture/test/virtual_device_unittest.cc
+++ b/services/video_capture/test/virtual_device_unittest.cc
@@ -70,14 +70,14 @@
          SharedMemoryVirtualDeviceMojoAdapter::max_buffer_pool_buffer_count();
          i++) {
       device_adapter_->RequestFrameBuffer(
-          kTestFrameSize, kTestPixelFormat,
+          kTestFrameSize, kTestPixelFormat, nullptr,
           base::Bind(&VirtualDeviceTest::OnFrameBufferReceived,
                      base::Unretained(this), true /* valid_buffer_expected */));
     }
 
     // No more buffer available. Invalid buffer id should be returned.
     device_adapter_->RequestFrameBuffer(
-        kTestFrameSize, kTestPixelFormat,
+        kTestFrameSize, kTestPixelFormat, nullptr,
         base::Bind(&VirtualDeviceTest::OnFrameBufferReceived,
                    base::Unretained(this), false /* valid_buffer_expected */));
 
@@ -115,7 +115,7 @@
   // buffer.
   EXPECT_CALL(*producer_, DoOnNewBuffer(_, _, _)).Times(0);
   device_adapter_->RequestFrameBuffer(
-      kTestFrameSize, kTestPixelFormat,
+      kTestFrameSize, kTestPixelFormat, nullptr,
       base::Bind(&VirtualDeviceTest::OnFrameBufferReceived,
                  base::Unretained(this), true /* valid_buffer_expected */));
 
@@ -161,7 +161,7 @@
         // Verify that the returned |buffer_id| is a known buffer ID.
         EXPECT_TRUE(base::ContainsValue(received_buffer_ids_, buffer_id));
       }));
-  device_adapter_->RequestFrameBuffer(kTestFrameSize, kTestPixelFormat,
+  device_adapter_->RequestFrameBuffer(kTestFrameSize, kTestPixelFormat, nullptr,
                                       request_frame_buffer_callback.Get());
   wait_loop2.RunUntilIdle();
 }
diff --git a/services/viz/privileged/interfaces/compositing/display_private.mojom b/services/viz/privileged/interfaces/compositing/display_private.mojom
index 888386d..5d61d35 100644
--- a/services/viz/privileged/interfaces/compositing/display_private.mojom
+++ b/services/viz/privileged/interfaces/compositing/display_private.mojom
@@ -33,11 +33,6 @@
                        gfx.mojom.ColorSpace device_color_space);
   SetOutputIsSecure(bool secure);
 
-  // Locks the vsync interval used to generate BeginFrames for this display to
-  // |interval|. Changes to vsync interval from other sources will be ignored.
-  // This will do nothing if the display is using an external BeginFrame source.
-  SetAuthoritativeVSyncInterval(mojo_base.mojom.TimeDelta interval);
-
   // Updates vsync parameters used to generate BeginFrames for this display.
   // This will do nothing if the display is using an external BeginFrame source.
   SetDisplayVSyncParameters(
diff --git a/services/viz/privileged/interfaces/compositing/frame_sink_manager.mojom b/services/viz/privileged/interfaces/compositing/frame_sink_manager.mojom
index fa56117..d1a1433d1 100644
--- a/services/viz/privileged/interfaces/compositing/frame_sink_manager.mojom
+++ b/services/viz/privileged/interfaces/compositing/frame_sink_manager.mojom
@@ -27,6 +27,8 @@
   bool gpu_compositing = true;
   RendererSettings renderer_settings;
   bool send_swap_size_notifications = false;
+  // Disables begin frame rate limiting for the display compositor.
+  bool disable_frame_rate_limit = false;
 
   associated CompositorFrameSink& compositor_frame_sink;
   CompositorFrameSinkClient compositor_frame_sink_client;
diff --git a/services/ws/window_tree.cc b/services/ws/window_tree.cc
index e2db8b0..875f907 100644
--- a/services/ws/window_tree.cc
+++ b/services/ws/window_tree.cc
@@ -164,19 +164,22 @@
   DCHECK(!event.IsGestureEvent());
 
   const uint32_t event_id = GenerateEventAckId();
-  std::unique_ptr<InFlightEvent> in_flight_event =
-      std::make_unique<InFlightEvent>();
-  in_flight_event->id = event_id;
-  if (event.type() == ui::ET_KEY_PRESSED ||
-      event.type() == ui::ET_KEY_RELEASED) {
-    in_flight_event->event = ui::Event::Clone(event);
-  }
-  in_flight_events_.push(std::move(in_flight_event));
-  if (in_flight_events_.size() == kMaxQueuedEvents) {
+  auto* in_flight_event_queue =
+      event.IsKeyEvent() ? &in_flight_key_events_ : &in_flight_other_events_;
+  if (in_flight_event_queue->size() < kMaxQueuedEvents) {
+    std::unique_ptr<InFlightEvent> in_flight_event =
+        std::make_unique<InFlightEvent>();
+    in_flight_event->id = event_id;
+    if (event.type() == ui::ET_KEY_PRESSED ||
+        event.type() == ui::ET_KEY_RELEASED) {
+      in_flight_event->event = ui::Event::Clone(event);
+    }
+    in_flight_event_queue->push(std::move(in_flight_event));
+  } else {
     DVLOG(1) << "client not responding to events in a timely manner, "
              << "dropping event";
-    in_flight_events_.pop();
   }
+
   // Events should only come to windows connected to displays.
   DCHECK(window->GetHost());
   const int64_t display_id = window->GetHost()->GetDisplayId();
@@ -1738,7 +1741,16 @@
 
 void WindowTree::OnWindowInputEventAck(uint32_t event_id,
                                        mojom::EventResult result) {
-  if (in_flight_events_.empty() || in_flight_events_.front()->id != event_id) {
+  std::unique_ptr<ui::Event> key_event;
+  if (!in_flight_key_events_.empty() &&
+      in_flight_key_events_.front()->id == event_id) {
+    key_event = std::move(in_flight_key_events_.front()->event);
+    in_flight_key_events_.pop();
+  } else if (!in_flight_other_events_.empty() &&
+             in_flight_other_events_.front()->id == event_id) {
+    DVLOG_IF(1, in_flight_other_events_.front()->event) << "Unexpected event";
+    in_flight_other_events_.pop();
+  } else {
     DVLOG(1) << "client acked unknown event";
     return;
   }
@@ -1746,12 +1758,9 @@
   for (WindowServiceObserver& observer : window_service_->observers())
     observer.OnClientAckedEvent(client_id_, event_id);
 
-  std::unique_ptr<InFlightEvent> in_flight_event =
-      std::move(in_flight_events_.front());
-  in_flight_events_.pop();
-  if (in_flight_event->event && result == mojom::EventResult::UNHANDLED) {
+  if (key_event && result == mojom::EventResult::UNHANDLED) {
     window_service_->delegate()->OnUnhandledKeyEvent(
-        *(in_flight_event->event->AsKeyEvent()));
+        *(key_event->AsKeyEvent()));
   }
 }
 
diff --git a/services/ws/window_tree.h b/services/ws/window_tree.h
index a533f0c..8ba6219 100644
--- a/services/ws/window_tree.h
+++ b/services/ws/window_tree.h
@@ -519,8 +519,10 @@
       base::flat_map<base::UnguessableToken, ClientSpecificId>;
   ScheduledEmbedsForExistingClient scheduled_embeds_for_existing_client_;
 
-  // Used to track events sent to the client.
-  std::queue<std::unique_ptr<InFlightEvent>> in_flight_events_;
+  // Used to track events sent to the client. Key events are tracked separately,
+  // as their acks may be delayed when the client uses async IME handling.
+  std::queue<std::unique_ptr<InFlightEvent>> in_flight_key_events_;
+  std::queue<std::unique_ptr<InFlightEvent>> in_flight_other_events_;
 
   // Set while a window move loop is in progress.
   aura::Window* window_moving_ = nullptr;
diff --git a/services/ws/window_tree_test_helper.h b/services/ws/window_tree_test_helper.h
index 62907305f..df326cd 100644
--- a/services/ws/window_tree_test_helper.h
+++ b/services/ws/window_tree_test_helper.h
@@ -110,6 +110,15 @@
   aura::Window* GetWindowByClientId(const ClientWindowId& id);
   ClientWindowId ClientWindowIdForWindow(aura::Window* window);
 
+  const std::queue<std::unique_ptr<WindowTree::InFlightEvent>>&
+  in_flight_key_events() {
+    return window_tree_->in_flight_key_events_;
+  }
+  const std::queue<std::unique_ptr<WindowTree::InFlightEvent>>&
+  in_flight_other_events() {
+    return window_tree_->in_flight_other_events_;
+  }
+
  private:
   WindowTree* window_tree_;
 
diff --git a/services/ws/window_tree_unittest.cc b/services/ws/window_tree_unittest.cc
index c33843d..bde4bdd9 100644
--- a/services/ws/window_tree_unittest.cc
+++ b/services/ws/window_tree_unittest.cc
@@ -403,6 +403,81 @@
   EXPECT_FALSE(top_level->GetProperty(kTestPropertyKey));
 }
 
+TEST(WindowTreeTest, OnWindowInputEventAck) {
+  WindowServiceTestSetup setup;
+  TestWindowTreeClient* window_tree_client = setup.window_tree_client();
+  WindowTreeTestHelper* tree = setup.window_tree_test_helper();
+  aura::Window* top_level = tree->NewTopLevelWindow();
+  ASSERT_TRUE(top_level);
+
+  top_level->Show();
+  top_level->Focus();
+  top_level->SetBounds(gfx::Rect(100, 100));
+
+  // Send a key event and a mouse event to the client.
+  ui::test::EventGenerator event_generator(setup.root());
+  event_generator.PressKey(ui::VKEY_A, ui::EF_NONE);
+  event_generator.MoveMouseTo(10, 10);
+  ASSERT_EQ(2u, window_tree_client->input_events().size());
+  TestWindowTreeClient::InputEvent event1 = window_tree_client->PopInputEvent();
+  ASSERT_TRUE(event1.event->IsKeyEvent());
+  TestWindowTreeClient::InputEvent event2 = window_tree_client->PopInputEvent();
+  ASSERT_TRUE(event2.event->IsLocatedEvent());
+
+  // Acking the events in the order they were received works fine.
+  EXPECT_EQ(1u, tree->in_flight_key_events().size());
+  tree->OnWindowInputEventAck(event1.event_id, ws::mojom::EventResult::HANDLED);
+  EXPECT_EQ(0u, tree->in_flight_key_events().size());
+  EXPECT_EQ(1u, tree->in_flight_other_events().size());
+  tree->OnWindowInputEventAck(event2.event_id, ws::mojom::EventResult::HANDLED);
+  EXPECT_EQ(0u, tree->in_flight_other_events().size());
+
+  // Send another key event and a mouse event.
+  event_generator.ReleaseKey(ui::VKEY_A, ui::EF_NONE);
+  event_generator.MoveMouseTo(11, 11);
+  ASSERT_EQ(2u, window_tree_client->input_events().size());
+  TestWindowTreeClient::InputEvent event3 = window_tree_client->PopInputEvent();
+  ASSERT_TRUE(event3.event->IsKeyEvent());
+  TestWindowTreeClient::InputEvent event4 = window_tree_client->PopInputEvent();
+  ASSERT_TRUE(event4.event->IsLocatedEvent());
+
+  // Acking the mouse and key events out of order from one another is okay.
+  EXPECT_EQ(1u, tree->in_flight_other_events().size());
+  tree->OnWindowInputEventAck(event4.event_id, ws::mojom::EventResult::HANDLED);
+  EXPECT_EQ(0u, tree->in_flight_other_events().size());
+  EXPECT_EQ(1u, tree->in_flight_key_events().size());
+  tree->OnWindowInputEventAck(event3.event_id, ws::mojom::EventResult::HANDLED);
+  EXPECT_EQ(0u, tree->in_flight_key_events().size());
+
+  // Send two more mouse events.
+  event_generator.MoveMouseTo(12, 12);
+  event_generator.MoveMouseTo(13, 13);
+  ASSERT_EQ(2u, window_tree_client->input_events().size());
+  TestWindowTreeClient::InputEvent event5 = window_tree_client->PopInputEvent();
+  ASSERT_TRUE(event5.event->IsLocatedEvent());
+  TestWindowTreeClient::InputEvent event6 = window_tree_client->PopInputEvent();
+  ASSERT_TRUE(event6.event->IsLocatedEvent());
+
+  // The client cannot ack the second mouse event before the first.
+  EXPECT_EQ(2u, tree->in_flight_other_events().size());
+  tree->OnWindowInputEventAck(event6.event_id, ws::mojom::EventResult::HANDLED);
+  EXPECT_EQ(2u, tree->in_flight_other_events().size());
+
+  // Send two more key events.
+  event_generator.PressKey(ui::VKEY_A, ui::EF_NONE);
+  event_generator.ReleaseKey(ui::VKEY_A, ui::EF_NONE);
+  ASSERT_EQ(2u, window_tree_client->input_events().size());
+  TestWindowTreeClient::InputEvent event7 = window_tree_client->PopInputEvent();
+  ASSERT_TRUE(event7.event->IsKeyEvent());
+  TestWindowTreeClient::InputEvent event8 = window_tree_client->PopInputEvent();
+  ASSERT_TRUE(event8.event->IsKeyEvent());
+
+  // The client cannot ack the second key event before the first.
+  EXPECT_EQ(2u, tree->in_flight_key_events().size());
+  tree->OnWindowInputEventAck(event8.event_id, ws::mojom::EventResult::HANDLED);
+  EXPECT_EQ(2u, tree->in_flight_key_events().size());
+}
+
 TEST(WindowTreeTest, EventLocation) {
   WindowServiceTestSetup setup;
   TestWindowTreeClient* window_tree_client = setup.window_tree_client();
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index ffbc103..3cca235 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -3609,6 +3609,21 @@
             ]
         }
     ],
+    "ResourceLoadingHints": [
+        {
+            "platforms": [
+                "android"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "ResourceLoadingHints"
+                    ]
+                }
+            ]
+        }
+    ],
     "SRTPromptFieldTrial": [
         {
             "platforms": [
diff --git a/third_party/WebKit/LayoutTests/SmokeTests b/third_party/WebKit/LayoutTests/SmokeTests
index 00adc3e..73b8ab5 100644
--- a/third_party/WebKit/LayoutTests/SmokeTests
+++ b/third_party/WebKit/LayoutTests/SmokeTests
@@ -52,6 +52,7 @@
 css3/flexbox/assert-generated-new-flexbox.html
 css3/fonts/font-feature-settings-parsing-prefixed.html
 css3/masking/clip-path-reference-of-fake-clipPath.html
+css3/masking/mask-serializing.html
 css3/motion-path/use-count-offset.html
 css3/motion-path/use-count-offset-position.html
 css3/selectors3/html/css3-modsel-18.html
@@ -683,7 +684,6 @@
 fast/loader/stateobjects/pushstate-within-popstate-handler-assert.html
 fast/loader/stateobjects/replacestate-updates-location.html
 fast/loader/url-data-replace-backslash.html
-fast/masking/mask-serializing.html
 fast/mediacapturefromelement/CanvasCaptureMediaStream-clone-track.html
 fast/media/matchmedium-query-api.html
 fast/mediastream/MediaDevices-getSupportedConstraints.html
diff --git a/third_party/WebKit/LayoutTests/TestExpectations b/third_party/WebKit/LayoutTests/TestExpectations
index e05120a..485a0f7 100644
--- a/third_party/WebKit/LayoutTests/TestExpectations
+++ b/third_party/WebKit/LayoutTests/TestExpectations
@@ -5105,3 +5105,8 @@
 
 crbug.com/v8/8186 external/wpt/wasm/jsapi/module/customSections.any.html [ Skip ]
 crbug.com/v8/8186 external/wpt/wasm/jsapi/module/customSections.any.worker.html [ Skip ]
+
+# Sheriff 2018-09-18
+crbug.com/884445 virtual/unified-autoplay/external/wpt/feature-policy/feature-policy-nested-header-policy-disallowed-for-all.https.sub.html [ Pass Timeout ]
+crbug.com/884445 virtual/video-surface-layer/external/wpt/feature-policy/feature-policy-nested-header-policy-disallowed-for-all.https.sub.html [ Pass Timeout ]
+
diff --git a/third_party/WebKit/LayoutTests/animations/custom-properties/custom-angle-list-type-interpolation-expected.txt b/third_party/WebKit/LayoutTests/animations/custom-properties/custom-angle-list-type-interpolation-expected.txt
new file mode 100644
index 0000000..a1df7e5
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/animations/custom-properties/custom-angle-list-type-interpolation-expected.txt
@@ -0,0 +1,119 @@
+This is a testharness.js-based test.
+PASS This test uses interpolation-test.js.
+PASS CSS Transitions: property <--angle-list> from [initial] to [20deg 200deg] at (-0.3) is [46deg 460deg]
+PASS CSS Transitions: property <--angle-list> from [initial] to [20deg 200deg] at (0) is [40deg 400deg]
+PASS CSS Transitions: property <--angle-list> from [initial] to [20deg 200deg] at (0.5) is [30deg 300deg]
+PASS CSS Transitions: property <--angle-list> from [initial] to [20deg 200deg] at (1) is [20deg 200deg]
+PASS CSS Transitions: property <--angle-list> from [initial] to [20deg 200deg] at (1.5) is [10deg 100deg]
+PASS CSS Transitions: property <--angle-list> from [inherit] to [20deg 200deg] at (-0.3) is [33deg 330deg]
+PASS CSS Transitions: property <--angle-list> from [inherit] to [20deg 200deg] at (0) is [30deg 300deg]
+PASS CSS Transitions: property <--angle-list> from [inherit] to [20deg 200deg] at (0.5) is [25deg 250deg]
+PASS CSS Transitions: property <--angle-list> from [inherit] to [20deg 200deg] at (1) is [20deg 200deg]
+PASS CSS Transitions: property <--angle-list> from [inherit] to [20deg 200deg] at (1.5) is [15deg 150deg]
+PASS CSS Transitions: property <--angle-list> from [unset] to [20deg 200deg] at (-0.3) is [46deg 460deg]
+PASS CSS Transitions: property <--angle-list> from [unset] to [20deg 200deg] at (0) is [40deg 400deg]
+PASS CSS Transitions: property <--angle-list> from [unset] to [20deg 200deg] at (0.5) is [30deg 300deg]
+PASS CSS Transitions: property <--angle-list> from [unset] to [20deg 200deg] at (1) is [20deg 200deg]
+PASS CSS Transitions: property <--angle-list> from [unset] to [20deg 200deg] at (1.5) is [10deg 100deg]
+PASS CSS Transitions: property <--angle-list> from [-10deg -100deg] to [10deg 100deg] at (-0.3) is [-16deg -160deg]
+PASS CSS Transitions: property <--angle-list> from [-10deg -100deg] to [10deg 100deg] at (0) is [-10deg -100deg]
+PASS CSS Transitions: property <--angle-list> from [-10deg -100deg] to [10deg 100deg] at (0.5) is [0deg 0deg]
+PASS CSS Transitions: property <--angle-list> from [-10deg -100deg] to [10deg 100deg] at (1) is [10deg 100deg]
+PASS CSS Transitions: property <--angle-list> from [-10deg -100deg] to [10deg 100deg] at (1.5) is [20deg 200deg]
+PASS CSS Transitions: property <--angle-list> from [10deg] to [100deg] at (-0.3) is [-17deg]
+PASS CSS Transitions: property <--angle-list> from [10deg] to [100deg] at (0) is [10deg]
+PASS CSS Transitions: property <--angle-list> from [10deg] to [100deg] at (0.5) is [55deg]
+PASS CSS Transitions: property <--angle-list> from [10deg] to [100deg] at (1) is [100deg]
+PASS CSS Transitions: property <--angle-list> from [10deg] to [100deg] at (1.5) is [145deg]
+PASS CSS Transitions: property <--angle-list> from [0deg 0grad] to [1turn 400grad] at (-0.3) is [-108deg -108deg]
+PASS CSS Transitions: property <--angle-list> from [0deg 0grad] to [1turn 400grad] at (0) is [0deg 0deg]
+PASS CSS Transitions: property <--angle-list> from [0deg 0grad] to [1turn 400grad] at (0.5) is [180deg 180deg]
+PASS CSS Transitions: property <--angle-list> from [0deg 0grad] to [1turn 400grad] at (1) is [360deg 360deg]
+PASS CSS Transitions: property <--angle-list> from [0deg 0grad] to [1turn 400grad] at (1.5) is [540deg 540deg]
+PASS CSS Transitions: property <--angle-list> from neutral to [20deg 200deg] at (-0.3) is [7deg 70deg]
+PASS CSS Transitions: property <--angle-list> from neutral to [20deg 200deg] at (0) is [10deg 100deg]
+PASS CSS Transitions: property <--angle-list> from neutral to [20deg 200deg] at (0.5) is [15deg 150deg]
+PASS CSS Transitions: property <--angle-list> from neutral to [20deg 200deg] at (1) is [20deg 200deg]
+PASS CSS Transitions: property <--angle-list> from neutral to [20deg 200deg] at (1.5) is [25deg 250deg]
+PASS CSS Animations: property <--angle-list> from [initial] to [20deg 200deg] at (-0.3) is [46deg 460deg]
+PASS CSS Animations: property <--angle-list> from [initial] to [20deg 200deg] at (0) is [40deg 400deg]
+PASS CSS Animations: property <--angle-list> from [initial] to [20deg 200deg] at (0.5) is [30deg 300deg]
+PASS CSS Animations: property <--angle-list> from [initial] to [20deg 200deg] at (1) is [20deg 200deg]
+PASS CSS Animations: property <--angle-list> from [initial] to [20deg 200deg] at (1.5) is [10deg 100deg]
+PASS CSS Animations: property <--angle-list> from [inherit] to [20deg 200deg] at (-0.3) is [33deg 330deg]
+PASS CSS Animations: property <--angle-list> from [inherit] to [20deg 200deg] at (0) is [30deg 300deg]
+PASS CSS Animations: property <--angle-list> from [inherit] to [20deg 200deg] at (0.5) is [25deg 250deg]
+PASS CSS Animations: property <--angle-list> from [inherit] to [20deg 200deg] at (1) is [20deg 200deg]
+PASS CSS Animations: property <--angle-list> from [inherit] to [20deg 200deg] at (1.5) is [15deg 150deg]
+PASS CSS Animations: property <--angle-list> from [unset] to [20deg 200deg] at (-0.3) is [46deg 460deg]
+PASS CSS Animations: property <--angle-list> from [unset] to [20deg 200deg] at (0) is [40deg 400deg]
+PASS CSS Animations: property <--angle-list> from [unset] to [20deg 200deg] at (0.5) is [30deg 300deg]
+PASS CSS Animations: property <--angle-list> from [unset] to [20deg 200deg] at (1) is [20deg 200deg]
+PASS CSS Animations: property <--angle-list> from [unset] to [20deg 200deg] at (1.5) is [10deg 100deg]
+PASS CSS Animations: property <--angle-list> from [-10deg -100deg] to [10deg 100deg] at (-0.3) is [-16deg -160deg]
+PASS CSS Animations: property <--angle-list> from [-10deg -100deg] to [10deg 100deg] at (0) is [-10deg -100deg]
+PASS CSS Animations: property <--angle-list> from [-10deg -100deg] to [10deg 100deg] at (0.5) is [0deg 0deg]
+PASS CSS Animations: property <--angle-list> from [-10deg -100deg] to [10deg 100deg] at (1) is [10deg 100deg]
+PASS CSS Animations: property <--angle-list> from [-10deg -100deg] to [10deg 100deg] at (1.5) is [20deg 200deg]
+PASS CSS Animations: property <--angle-list> from [10deg] to [100deg] at (-0.3) is [-17deg]
+PASS CSS Animations: property <--angle-list> from [10deg] to [100deg] at (0) is [10deg]
+PASS CSS Animations: property <--angle-list> from [10deg] to [100deg] at (0.5) is [55deg]
+PASS CSS Animations: property <--angle-list> from [10deg] to [100deg] at (1) is [100deg]
+PASS CSS Animations: property <--angle-list> from [10deg] to [100deg] at (1.5) is [145deg]
+PASS CSS Animations: property <--angle-list> from [0deg 0grad] to [1turn 400grad] at (-0.3) is [-108deg -108deg]
+PASS CSS Animations: property <--angle-list> from [0deg 0grad] to [1turn 400grad] at (0) is [0deg 0deg]
+PASS CSS Animations: property <--angle-list> from [0deg 0grad] to [1turn 400grad] at (0.5) is [180deg 180deg]
+PASS CSS Animations: property <--angle-list> from [0deg 0grad] to [1turn 400grad] at (1) is [360deg 360deg]
+PASS CSS Animations: property <--angle-list> from [0deg 0grad] to [1turn 400grad] at (1.5) is [540deg 540deg]
+FAIL CSS Animations: property <--angle-list> from neutral to [20deg 200deg] at (-0.3) is [-6deg -60deg] assert_equals: expected "7deg 70deg " but got "- 6deg - 60deg "
+FAIL CSS Animations: property <--angle-list> from neutral to [20deg 200deg] at (0) is [0deg 0deg] assert_equals: expected "10deg 100deg " but got "0deg 0deg "
+FAIL CSS Animations: property <--angle-list> from neutral to [20deg 200deg] at (0.5) is [10deg 100deg] assert_equals: expected "15deg 150deg " but got "10deg 100deg "
+PASS CSS Animations: property <--angle-list> from neutral to [20deg 200deg] at (1) is [20deg 200deg]
+FAIL CSS Animations: property <--angle-list> from neutral to [20deg 200deg] at (1.5) is [30deg 300deg] assert_equals: expected "25deg 250deg " but got "30deg 300deg "
+PASS Web Animations: property <--angle-list> from [initial] to [20deg 200deg] at (-0.3) is [46deg 460deg]
+PASS Web Animations: property <--angle-list> from [initial] to [20deg 200deg] at (0) is [40deg 400deg]
+PASS Web Animations: property <--angle-list> from [initial] to [20deg 200deg] at (0.5) is [30deg 300deg]
+PASS Web Animations: property <--angle-list> from [initial] to [20deg 200deg] at (1) is [20deg 200deg]
+PASS Web Animations: property <--angle-list> from [initial] to [20deg 200deg] at (1.5) is [10deg 100deg]
+PASS Web Animations: property <--angle-list> from [inherit] to [20deg 200deg] at (-0.3) is [33deg 330deg]
+PASS Web Animations: property <--angle-list> from [inherit] to [20deg 200deg] at (0) is [30deg 300deg]
+PASS Web Animations: property <--angle-list> from [inherit] to [20deg 200deg] at (0.5) is [25deg 250deg]
+PASS Web Animations: property <--angle-list> from [inherit] to [20deg 200deg] at (1) is [20deg 200deg]
+PASS Web Animations: property <--angle-list> from [inherit] to [20deg 200deg] at (1.5) is [15deg 150deg]
+PASS Web Animations: property <--angle-list> from [unset] to [20deg 200deg] at (-0.3) is [46deg 460deg]
+PASS Web Animations: property <--angle-list> from [unset] to [20deg 200deg] at (0) is [40deg 400deg]
+PASS Web Animations: property <--angle-list> from [unset] to [20deg 200deg] at (0.5) is [30deg 300deg]
+PASS Web Animations: property <--angle-list> from [unset] to [20deg 200deg] at (1) is [20deg 200deg]
+PASS Web Animations: property <--angle-list> from [unset] to [20deg 200deg] at (1.5) is [10deg 100deg]
+PASS Web Animations: property <--angle-list> from [-10deg -100deg] to [10deg 100deg] at (-0.3) is [-16deg -160deg]
+PASS Web Animations: property <--angle-list> from [-10deg -100deg] to [10deg 100deg] at (0) is [-10deg -100deg]
+PASS Web Animations: property <--angle-list> from [-10deg -100deg] to [10deg 100deg] at (0.5) is [0deg 0deg]
+PASS Web Animations: property <--angle-list> from [-10deg -100deg] to [10deg 100deg] at (1) is [10deg 100deg]
+PASS Web Animations: property <--angle-list> from [-10deg -100deg] to [10deg 100deg] at (1.5) is [20deg 200deg]
+PASS Web Animations: property <--angle-list> from [10deg] to [100deg] at (-0.3) is [-17deg]
+PASS Web Animations: property <--angle-list> from [10deg] to [100deg] at (0) is [10deg]
+PASS Web Animations: property <--angle-list> from [10deg] to [100deg] at (0.5) is [55deg]
+PASS Web Animations: property <--angle-list> from [10deg] to [100deg] at (1) is [100deg]
+PASS Web Animations: property <--angle-list> from [10deg] to [100deg] at (1.5) is [145deg]
+PASS Web Animations: property <--angle-list> from [0deg 0grad] to [1turn 400grad] at (-0.3) is [-108deg -108deg]
+PASS Web Animations: property <--angle-list> from [0deg 0grad] to [1turn 400grad] at (0) is [0deg 0deg]
+PASS Web Animations: property <--angle-list> from [0deg 0grad] to [1turn 400grad] at (0.5) is [180deg 180deg]
+PASS Web Animations: property <--angle-list> from [0deg 0grad] to [1turn 400grad] at (1) is [360deg 360deg]
+PASS Web Animations: property <--angle-list> from [0deg 0grad] to [1turn 400grad] at (1.5) is [540deg 540deg]
+FAIL Web Animations: property <--angle-list> from neutral to [20deg 200deg] at (-0.3) is [-6deg -60deg] assert_equals: expected "7deg 70deg " but got "- 6deg - 60deg "
+FAIL Web Animations: property <--angle-list> from neutral to [20deg 200deg] at (0) is [0deg 0deg] assert_equals: expected "10deg 100deg " but got "0deg 0deg "
+FAIL Web Animations: property <--angle-list> from neutral to [20deg 200deg] at (0.5) is [10deg 100deg] assert_equals: expected "15deg 150deg " but got "10deg 100deg "
+PASS Web Animations: property <--angle-list> from neutral to [20deg 200deg] at (1) is [20deg 200deg]
+FAIL Web Animations: property <--angle-list> from neutral to [20deg 200deg] at (1.5) is [30deg 300deg] assert_equals: expected "25deg 250deg " but got "30deg 300deg "
+FAIL Compositing: property <--angle-list> underlying [50deg 60deg] from add [10deg 140deg] to add [110deg 40deg] at (-0.3) is [-20deg 170deg] assert_equals: expected "30deg 230deg " but got "- 20deg 170deg "
+FAIL Compositing: property <--angle-list> underlying [50deg 60deg] from add [10deg 140deg] to add [110deg 40deg] at (0) is [10deg 140deg] assert_equals: expected "60deg 200deg " but got "10deg 140deg "
+FAIL Compositing: property <--angle-list> underlying [50deg 60deg] from add [10deg 140deg] to add [110deg 40deg] at (0.5) is [60deg 90deg] assert_equals: expected "110deg 150deg " but got "60deg 90deg "
+FAIL Compositing: property <--angle-list> underlying [50deg 60deg] from add [10deg 140deg] to add [110deg 40deg] at (1) is [110deg 40deg] assert_equals: expected "160deg 100deg " but got "110deg 40deg "
+FAIL Compositing: property <--angle-list> underlying [50deg 60deg] from add [10deg 140deg] to add [110deg 40deg] at (1.5) is [160deg -10deg] assert_equals: expected "210deg 50deg " but got "160deg - 10deg "
+FAIL Compositing: property <--angle-list> underlying [50deg 60deg] from add [10deg 140deg] to replace [110deg 40deg] at (-0.3) is [-20deg 170deg] assert_equals: expected "45deg 248deg " but got "- 20deg 170deg "
+FAIL Compositing: property <--angle-list> underlying [50deg 60deg] from add [10deg 140deg] to replace [110deg 40deg] at (0) is [10deg 140deg] assert_equals: expected "60deg 200deg " but got "10deg 140deg "
+FAIL Compositing: property <--angle-list> underlying [50deg 60deg] from add [10deg 140deg] to replace [110deg 40deg] at (0.5) is [60deg 90deg] assert_equals: expected "85deg 120deg " but got "60deg 90deg "
+PASS Compositing: property <--angle-list> underlying [50deg 60deg] from add [10deg 140deg] to replace [110deg 40deg] at (1) is [110deg 40deg]
+FAIL Compositing: property <--angle-list> underlying [50deg 60deg] from add [10deg 140deg] to replace [110deg 40deg] at (1.5) is [160deg -10deg] assert_equals: expected "135deg - 40deg " but got "160deg - 10deg "
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/animations/custom-properties/custom-angle-list-type-interpolation.html b/third_party/WebKit/LayoutTests/animations/custom-properties/custom-angle-list-type-interpolation.html
new file mode 100644
index 0000000..895be37
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/animations/custom-properties/custom-angle-list-type-interpolation.html
@@ -0,0 +1,136 @@
+<!DOCTYPE html>
+<meta charset="UTF-8">
+<style>
+.parent {
+  --angle-list: 30deg 300deg;
+}
+.target {
+  --angle-list: 10deg 100deg;
+}
+</style>
+<body>
+<script src="../interpolation/resources/interpolation-test.js"></script>
+<script>
+CSS.registerProperty({
+  name: '--angle-list',
+  syntax: '<angle>+',
+  initialValue: '40deg 400deg',
+  inherits: false,
+});
+
+assertInterpolation({
+  property: '--angle-list',
+  from: 'initial',
+  to: '20deg 200deg',
+}, [
+  {at: -0.3, is: '46deg 460deg'},
+  {at: 0, is: '40deg 400deg'},
+  {at: 0.5, is: '30deg 300deg'},
+  {at: 1, is: '20deg 200deg'},
+  {at: 1.5, is: '10deg 100deg'},
+]);
+
+assertInterpolation({
+  property: '--angle-list',
+  from: 'inherit',
+  to: '20deg 200deg',
+}, [
+  {at: -0.3, is: '33deg 330deg'},
+  {at: 0, is: '30deg 300deg'},
+  {at: 0.5, is: '25deg 250deg'},
+  {at: 1, is: '20deg 200deg'},
+  {at: 1.5, is: '15deg 150deg'},
+]);
+
+assertInterpolation({
+  property: '--angle-list',
+  from: 'unset',
+  to: '20deg 200deg',
+}, [
+  {at: -0.3, is: '46deg 460deg'},
+  {at: 0, is: '40deg 400deg'},
+  {at: 0.5, is: '30deg 300deg'},
+  {at: 1, is: '20deg 200deg'},
+  {at: 1.5, is: '10deg 100deg'},
+]);
+
+assertInterpolation({
+  property: '--angle-list',
+  from: '-10deg -100deg',
+  to: '10deg 100deg',
+}, [
+  {at: -0.3, is: '-16deg -160deg'},
+  {at: 0, is: '-10deg -100deg'},
+  {at: 0.5, is: '0deg 0deg'},
+  {at: 1, is: '10deg 100deg'},
+  {at: 1.5, is: '20deg 200deg'}
+]); 
+
+assertInterpolation({
+  property: '--angle-list',
+  from: '10deg',
+  to: '100deg',
+}, [
+  {at: -0.3, is: '-17deg'},
+  {at: 0, is: '10deg'},
+  {at: 0.5, is: '55deg'},
+  {at: 1, is: '100deg'},
+  {at: 1.5, is: '145deg'}
+]);
+
+assertInterpolation({
+  property: '--angle-list',
+  from: '0deg 0grad',
+  to: '1turn 400grad',
+}, [
+  {at: -0.3, is: '-108deg -108deg'},
+  {at: 0, is: '0deg 0deg'},
+  {at: 0.5, is: '180deg 180deg'},
+  {at: 1, is: '360deg 360deg'},
+  {at: 1.5, is: '540deg 540deg'}
+]);
+
+// Composition and neutralKeyframe tests assume that composite:add means
+// component-wise addition, which may or may not be the behavior we want.
+// https://github.com/w3c/css-houdini-drafts/issues/799
+
+assertInterpolation({
+  property: '--angle-list',
+  from: neutralKeyframe,
+  to: '20deg 200deg',
+}, [
+  {at: -0.3, is: '7deg 70deg'},
+  {at: 0, is: '10deg 100deg'},
+  {at: 0.5, is: '15deg 150deg'},
+  {at: 1, is: '20deg 200deg'},
+  {at: 1.5, is: '25deg 250deg'},
+]);
+
+assertComposition({
+  property: '--angle-list',
+  underlying: '50deg 60deg',
+  addFrom: '10deg 140deg',
+  addTo: '110deg 40deg',
+}, [
+  {at: -0.3, is: '30deg 230deg'},
+  {at: 0, is: '60deg 200deg'},
+  {at: 0.5, is: '110deg 150deg'},
+  {at: 1, is: '160deg 100deg'},
+  {at: 1.5, is: '210deg 50deg'},
+]);
+
+assertComposition({
+  property: '--angle-list',
+  underlying: '50deg 60deg',
+  addFrom: '10deg 140deg',
+  replaceTo: '110deg 40deg',
+}, [
+  {at: -0.3, is: '45deg 248deg'},
+  {at: 0, is: '60deg 200deg'},
+  {at: 0.5, is: '85deg 120deg'},
+  {at: 1, is: '110deg 40deg'},
+  {at: 1.5, is: '135deg -40deg'},
+]);
+
+</script>
+</body>
diff --git a/third_party/WebKit/LayoutTests/animations/custom-properties/custom-color-list-type-interpolation-expected.txt b/third_party/WebKit/LayoutTests/animations/custom-properties/custom-color-list-type-interpolation-expected.txt
new file mode 100644
index 0000000..9496ab6
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/animations/custom-properties/custom-color-list-type-interpolation-expected.txt
@@ -0,0 +1,89 @@
+This is a testharness.js-based test.
+PASS This test uses interpolation-test.js.
+PASS CSS Transitions: property <--color-list> from [initial] to [blue red] at (-0.3) is [rgb(255, 0, 0) rgb(0, 0, 255)]
+PASS CSS Transitions: property <--color-list> from [initial] to [blue red] at (0) is [rgb(255, 0, 0) rgb(0, 0, 255)]
+PASS CSS Transitions: property <--color-list> from [initial] to [blue red] at (0.5) is [rgb(128, 0, 128) rgb(128, 0, 128)]
+PASS CSS Transitions: property <--color-list> from [initial] to [blue red] at (1) is [rgb(0, 0, 255) rgb(255, 0, 0)]
+PASS CSS Transitions: property <--color-list> from [initial] to [blue red] at (1.5) is [rgb(0, 0, 255) rgb(255, 0, 0)]
+PASS CSS Transitions: property <--color-list> from [inherit] to [rgb(80, 80, 80) rgb(0, 0, 0)] at (-0.3) is [rgb(15, 15, 15) rgb(78, 78, 78)]
+PASS CSS Transitions: property <--color-list> from [inherit] to [rgb(80, 80, 80) rgb(0, 0, 0)] at (0) is [rgb(30, 30, 30) rgb(60, 60, 60)]
+PASS CSS Transitions: property <--color-list> from [inherit] to [rgb(80, 80, 80) rgb(0, 0, 0)] at (0.5) is [rgb(55, 55, 55) rgb(30, 30, 30)]
+PASS CSS Transitions: property <--color-list> from [inherit] to [rgb(80, 80, 80) rgb(0, 0, 0)] at (1) is [rgb(80, 80, 80) rgb(0, 0, 0)]
+PASS CSS Transitions: property <--color-list> from [inherit] to [rgb(80, 80, 80) rgb(0, 0, 0)] at (1.5) is [rgb(105, 105, 105) rgb(0, 0, 0)]
+PASS CSS Transitions: property <--color-list> from [unset] to [tan tomato] at (-0.3) is [rgb(255, 0, 0) rgb(0, 0, 255)]
+PASS CSS Transitions: property <--color-list> from [unset] to [tan tomato] at (0) is [rgb(255, 0, 0) rgb(0, 0, 255)]
+PASS CSS Transitions: property <--color-list> from [unset] to [tan tomato] at (0.5) is [rgb(233, 90, 70) rgb(128, 50, 163)]
+PASS CSS Transitions: property <--color-list> from [unset] to [tan tomato] at (1) is [rgb(210, 180, 140) rgb(255, 99, 71)]
+PASS CSS Transitions: property <--color-list> from [unset] to [tan tomato] at (1.5) is [rgb(188, 255, 210) rgb(255, 149, 0)]
+PASS CSS Transitions: property <--color-list> from [hsl(120, 100%, 50%) coral] to [navy hsl(240, 100%, 50%)] at (-0.3) is [rgb(0, 255, 0) rgb(255, 165, 28)]
+PASS CSS Transitions: property <--color-list> from [hsl(120, 100%, 50%) coral] to [navy hsl(240, 100%, 50%)] at (0) is [rgb(0, 255, 0) rgb(255, 127, 80)]
+PASS CSS Transitions: property <--color-list> from [hsl(120, 100%, 50%) coral] to [navy hsl(240, 100%, 50%)] at (0.5) is [rgb(0, 128, 64) rgb(128, 64, 168)]
+PASS CSS Transitions: property <--color-list> from [hsl(120, 100%, 50%) coral] to [navy hsl(240, 100%, 50%)] at (1) is [rgb(0, 0, 128) rgb(0, 0, 255)]
+PASS CSS Transitions: property <--color-list> from [hsl(120, 100%, 50%) coral] to [navy hsl(240, 100%, 50%)] at (1.5) is [rgb(0, 0, 192) rgb(0, 0, 255)]
+PASS CSS Transitions: property <--color-list> from neutral to [rgb(0, 40, 40) black] at (-0.3) is [rgb(52, 40, 40) rgb(104, 104, 104)]
+PASS CSS Transitions: property <--color-list> from neutral to [rgb(0, 40, 40) black] at (0) is [rgb(40, 40, 40) rgb(80, 80, 80)]
+PASS CSS Transitions: property <--color-list> from neutral to [rgb(0, 40, 40) black] at (0.5) is [rgb(20, 40, 40) rgb(40, 40, 40)]
+PASS CSS Transitions: property <--color-list> from neutral to [rgb(0, 40, 40) black] at (1) is [rgb(0, 40, 40) rgb(0, 0, 0)]
+PASS CSS Transitions: property <--color-list> from neutral to [rgb(0, 40, 40) black] at (1.5) is [rgb(0, 40, 40) rgb(0, 0, 0)]
+PASS CSS Animations: property <--color-list> from [initial] to [blue red] at (-0.3) is [rgb(255, 0, 0) rgb(0, 0, 255)]
+PASS CSS Animations: property <--color-list> from [initial] to [blue red] at (0) is [rgb(255, 0, 0) rgb(0, 0, 255)]
+PASS CSS Animations: property <--color-list> from [initial] to [blue red] at (0.5) is [rgb(128, 0, 128) rgb(128, 0, 128)]
+PASS CSS Animations: property <--color-list> from [initial] to [blue red] at (1) is [rgb(0, 0, 255) rgb(255, 0, 0)]
+PASS CSS Animations: property <--color-list> from [initial] to [blue red] at (1.5) is [rgb(0, 0, 255) rgb(255, 0, 0)]
+PASS CSS Animations: property <--color-list> from [inherit] to [rgb(80, 80, 80) rgb(0, 0, 0)] at (-0.3) is [rgb(15, 15, 15) rgb(78, 78, 78)]
+PASS CSS Animations: property <--color-list> from [inherit] to [rgb(80, 80, 80) rgb(0, 0, 0)] at (0) is [rgb(30, 30, 30) rgb(60, 60, 60)]
+PASS CSS Animations: property <--color-list> from [inherit] to [rgb(80, 80, 80) rgb(0, 0, 0)] at (0.5) is [rgb(55, 55, 55) rgb(30, 30, 30)]
+PASS CSS Animations: property <--color-list> from [inherit] to [rgb(80, 80, 80) rgb(0, 0, 0)] at (1) is [rgb(80, 80, 80) rgb(0, 0, 0)]
+PASS CSS Animations: property <--color-list> from [inherit] to [rgb(80, 80, 80) rgb(0, 0, 0)] at (1.5) is [rgb(105, 105, 105) rgb(0, 0, 0)]
+PASS CSS Animations: property <--color-list> from [unset] to [tan tomato] at (-0.3) is [rgb(255, 0, 0) rgb(0, 0, 255)]
+PASS CSS Animations: property <--color-list> from [unset] to [tan tomato] at (0) is [rgb(255, 0, 0) rgb(0, 0, 255)]
+PASS CSS Animations: property <--color-list> from [unset] to [tan tomato] at (0.5) is [rgb(233, 90, 70) rgb(128, 50, 163)]
+PASS CSS Animations: property <--color-list> from [unset] to [tan tomato] at (1) is [rgb(210, 180, 140) rgb(255, 99, 71)]
+PASS CSS Animations: property <--color-list> from [unset] to [tan tomato] at (1.5) is [rgb(188, 255, 210) rgb(255, 149, 0)]
+PASS CSS Animations: property <--color-list> from [hsl(120, 100%, 50%) coral] to [navy hsl(240, 100%, 50%)] at (-0.3) is [rgb(0, 255, 0) rgb(255, 165, 28)]
+PASS CSS Animations: property <--color-list> from [hsl(120, 100%, 50%) coral] to [navy hsl(240, 100%, 50%)] at (0) is [rgb(0, 255, 0) rgb(255, 127, 80)]
+PASS CSS Animations: property <--color-list> from [hsl(120, 100%, 50%) coral] to [navy hsl(240, 100%, 50%)] at (0.5) is [rgb(0, 128, 64) rgb(128, 64, 168)]
+PASS CSS Animations: property <--color-list> from [hsl(120, 100%, 50%) coral] to [navy hsl(240, 100%, 50%)] at (1) is [rgb(0, 0, 128) rgb(0, 0, 255)]
+PASS CSS Animations: property <--color-list> from [hsl(120, 100%, 50%) coral] to [navy hsl(240, 100%, 50%)] at (1.5) is [rgb(0, 0, 192) rgb(0, 0, 255)]
+FAIL CSS Animations: property <--color-list> from neutral to [rgb(0, 40, 40) black] at (-0.3) is [rgba(0, 0, 0, 0) rgba(0, 0, 0, 0)] assert_equals: expected "rgb ( 52 , 40 , 40 ) rgb ( 104 , 104 , 104 ) " but got "rgba ( 0 , 0 , 0 , 0 ) rgba ( 0 , 0 , 0 , 0 ) "
+FAIL CSS Animations: property <--color-list> from neutral to [rgb(0, 40, 40) black] at (0) is [rgba(0, 0, 0, 0) rgba(0, 0, 0, 0)] assert_equals: expected "rgb ( 40 , 40 , 40 ) rgb ( 80 , 80 , 80 ) " but got "rgba ( 0 , 0 , 0 , 0 ) rgba ( 0 , 0 , 0 , 0 ) "
+FAIL CSS Animations: property <--color-list> from neutral to [rgb(0, 40, 40) black] at (0.5) is [rgba(0, 40, 40, 0.5) rgba(0, 0, 0, 0.5)] assert_equals: expected "rgb ( 20 , 40 , 40 ) rgb ( 40 , 40 , 40 ) " but got "rgba ( 0 , 40 , 40 , 0.5 ) rgba ( 0 , 0 , 0 , 0.5 ) "
+PASS CSS Animations: property <--color-list> from neutral to [rgb(0, 40, 40) black] at (1) is [rgb(0, 40, 40) rgb(0, 0, 0)]
+FAIL CSS Animations: property <--color-list> from neutral to [rgb(0, 40, 40) black] at (1.5) is [rgb(0, 60, 60) rgb(0, 0, 0)] assert_equals: expected "rgb ( 0 , 40 , 40 ) rgb ( 0 , 0 , 0 ) " but got "rgb ( 0 , 60 , 60 ) rgb ( 0 , 0 , 0 ) "
+PASS Web Animations: property <--color-list> from [initial] to [blue red] at (-0.3) is [rgb(255, 0, 0) rgb(0, 0, 255)]
+PASS Web Animations: property <--color-list> from [initial] to [blue red] at (0) is [rgb(255, 0, 0) rgb(0, 0, 255)]
+PASS Web Animations: property <--color-list> from [initial] to [blue red] at (0.5) is [rgb(128, 0, 128) rgb(128, 0, 128)]
+PASS Web Animations: property <--color-list> from [initial] to [blue red] at (1) is [rgb(0, 0, 255) rgb(255, 0, 0)]
+PASS Web Animations: property <--color-list> from [initial] to [blue red] at (1.5) is [rgb(0, 0, 255) rgb(255, 0, 0)]
+PASS Web Animations: property <--color-list> from [inherit] to [rgb(80, 80, 80) rgb(0, 0, 0)] at (-0.3) is [rgb(15, 15, 15) rgb(78, 78, 78)]
+PASS Web Animations: property <--color-list> from [inherit] to [rgb(80, 80, 80) rgb(0, 0, 0)] at (0) is [rgb(30, 30, 30) rgb(60, 60, 60)]
+PASS Web Animations: property <--color-list> from [inherit] to [rgb(80, 80, 80) rgb(0, 0, 0)] at (0.5) is [rgb(55, 55, 55) rgb(30, 30, 30)]
+PASS Web Animations: property <--color-list> from [inherit] to [rgb(80, 80, 80) rgb(0, 0, 0)] at (1) is [rgb(80, 80, 80) rgb(0, 0, 0)]
+PASS Web Animations: property <--color-list> from [inherit] to [rgb(80, 80, 80) rgb(0, 0, 0)] at (1.5) is [rgb(105, 105, 105) rgb(0, 0, 0)]
+PASS Web Animations: property <--color-list> from [unset] to [tan tomato] at (-0.3) is [rgb(255, 0, 0) rgb(0, 0, 255)]
+PASS Web Animations: property <--color-list> from [unset] to [tan tomato] at (0) is [rgb(255, 0, 0) rgb(0, 0, 255)]
+PASS Web Animations: property <--color-list> from [unset] to [tan tomato] at (0.5) is [rgb(233, 90, 70) rgb(128, 50, 163)]
+PASS Web Animations: property <--color-list> from [unset] to [tan tomato] at (1) is [rgb(210, 180, 140) rgb(255, 99, 71)]
+PASS Web Animations: property <--color-list> from [unset] to [tan tomato] at (1.5) is [rgb(188, 255, 210) rgb(255, 149, 0)]
+PASS Web Animations: property <--color-list> from [hsl(120, 100%, 50%) coral] to [navy hsl(240, 100%, 50%)] at (-0.3) is [rgb(0, 255, 0) rgb(255, 165, 28)]
+PASS Web Animations: property <--color-list> from [hsl(120, 100%, 50%) coral] to [navy hsl(240, 100%, 50%)] at (0) is [rgb(0, 255, 0) rgb(255, 127, 80)]
+PASS Web Animations: property <--color-list> from [hsl(120, 100%, 50%) coral] to [navy hsl(240, 100%, 50%)] at (0.5) is [rgb(0, 128, 64) rgb(128, 64, 168)]
+PASS Web Animations: property <--color-list> from [hsl(120, 100%, 50%) coral] to [navy hsl(240, 100%, 50%)] at (1) is [rgb(0, 0, 128) rgb(0, 0, 255)]
+PASS Web Animations: property <--color-list> from [hsl(120, 100%, 50%) coral] to [navy hsl(240, 100%, 50%)] at (1.5) is [rgb(0, 0, 192) rgb(0, 0, 255)]
+FAIL Web Animations: property <--color-list> from neutral to [rgb(0, 40, 40) black] at (-0.3) is [rgba(0, 0, 0, 0) rgba(0, 0, 0, 0)] assert_equals: expected "rgb ( 52 , 40 , 40 ) rgb ( 104 , 104 , 104 ) " but got "rgba ( 0 , 0 , 0 , 0 ) rgba ( 0 , 0 , 0 , 0 ) "
+FAIL Web Animations: property <--color-list> from neutral to [rgb(0, 40, 40) black] at (0) is [rgba(0, 0, 0, 0) rgba(0, 0, 0, 0)] assert_equals: expected "rgb ( 40 , 40 , 40 ) rgb ( 80 , 80 , 80 ) " but got "rgba ( 0 , 0 , 0 , 0 ) rgba ( 0 , 0 , 0 , 0 ) "
+FAIL Web Animations: property <--color-list> from neutral to [rgb(0, 40, 40) black] at (0.5) is [rgba(0, 40, 40, 0.5) rgba(0, 0, 0, 0.5)] assert_equals: expected "rgb ( 20 , 40 , 40 ) rgb ( 40 , 40 , 40 ) " but got "rgba ( 0 , 40 , 40 , 0.5 ) rgba ( 0 , 0 , 0 , 0.5 ) "
+PASS Web Animations: property <--color-list> from neutral to [rgb(0, 40, 40) black] at (1) is [rgb(0, 40, 40) rgb(0, 0, 0)]
+FAIL Web Animations: property <--color-list> from neutral to [rgb(0, 40, 40) black] at (1.5) is [rgb(0, 60, 60) rgb(0, 0, 0)] assert_equals: expected "rgb ( 0 , 40 , 40 ) rgb ( 0 , 0 , 0 ) " but got "rgb ( 0 , 60 , 60 ) rgb ( 0 , 0 , 0 ) "
+FAIL Compositing: property <--color-list> underlying [darkslategray rgb(10, 10, 10)] from add [rgb(10, 10, 10) navy] to add [rgb(30, 30, 30) teal] at (-0.3) is [rgb(4, 4, 4) rgb(0, 0, 128)] assert_equals: expected "rgb ( 51 , 83 , 83 ) rgb ( 10 , 0 , 138 ) " but got "rgb ( 4 , 4 , 4 ) rgb ( 0 , 0 , 128 ) "
+FAIL Compositing: property <--color-list> underlying [darkslategray rgb(10, 10, 10)] from add [rgb(10, 10, 10) navy] to add [rgb(30, 30, 30) teal] at (0) is [rgb(10, 10, 10) rgb(0, 0, 128)] assert_equals: expected "rgb ( 57 , 89 , 89 ) rgb ( 10 , 10 , 138 ) " but got "rgb ( 10 , 10 , 10 ) rgb ( 0 , 0 , 128 ) "
+FAIL Compositing: property <--color-list> underlying [darkslategray rgb(10, 10, 10)] from add [rgb(10, 10, 10) navy] to add [rgb(30, 30, 30) teal] at (0.5) is [rgb(20, 20, 20) rgb(0, 64, 128)] assert_equals: expected "rgb ( 67 , 99 , 99 ) rgb ( 10 , 74 , 138 ) " but got "rgb ( 20 , 20 , 20 ) rgb ( 0 , 64 , 128 ) "
+FAIL Compositing: property <--color-list> underlying [darkslategray rgb(10, 10, 10)] from add [rgb(10, 10, 10) navy] to add [rgb(30, 30, 30) teal] at (1) is [rgb(30, 30, 30) rgb(0, 128, 128)] assert_equals: expected "rgb ( 77 , 109 , 109 ) rgb ( 10 , 138 , 138 ) " but got "rgb ( 30 , 30 , 30 ) rgb ( 0 , 128 , 128 ) "
+FAIL Compositing: property <--color-list> underlying [darkslategray rgb(10, 10, 10)] from add [rgb(10, 10, 10) navy] to add [rgb(30, 30, 30) teal] at (1.5) is [rgb(40, 40, 40) rgb(0, 192, 128)] assert_equals: expected "rgb ( 87 , 119 , 119 ) rgb ( 10 , 202 , 138 ) " but got "rgb ( 40 , 40 , 40 ) rgb ( 0 , 192 , 128 ) "
+FAIL Compositing: property <--color-list> underlying [gold rgb(100, 100, 100)] from add [navy rgb(0, 0, 0)] to replace [rgb(17, 17, 17) rgb(1, 1, 1)] at (-0.3) is [rgb(0, 0, 161) rgb(0, 0, 0)] assert_equals: expected "rgb ( 255 , 255 , 161 ) rgb ( 130 , 130 , 130 ) " but got "rgb ( 0 , 0 , 161 ) rgb ( 0 , 0 , 0 ) "
+FAIL Compositing: property <--color-list> underlying [gold rgb(100, 100, 100)] from add [navy rgb(0, 0, 0)] to replace [rgb(17, 17, 17) rgb(1, 1, 1)] at (0) is [rgb(0, 0, 128) rgb(0, 0, 0)] assert_equals: expected "rgb ( 255 , 215 , 128 ) rgb ( 100 , 100 , 100 ) " but got "rgb ( 0 , 0 , 128 ) rgb ( 0 , 0 , 0 ) "
+FAIL Compositing: property <--color-list> underlying [gold rgb(100, 100, 100)] from add [navy rgb(0, 0, 0)] to replace [rgb(17, 17, 17) rgb(1, 1, 1)] at (0.5) is [rgb(9, 9, 73) rgb(1, 1, 1)] assert_equals: expected "rgb ( 136 , 116 , 73 ) rgb ( 51 , 51 , 51 ) " but got "rgb ( 9 , 9 , 73 ) rgb ( 1 , 1 , 1 ) "
+PASS Compositing: property <--color-list> underlying [gold rgb(100, 100, 100)] from add [navy rgb(0, 0, 0)] to replace [rgb(17, 17, 17) rgb(1, 1, 1)] at (1) is [rgb(17, 17, 17) rgb(1, 1, 1)]
+FAIL Compositing: property <--color-list> underlying [gold rgb(100, 100, 100)] from add [navy rgb(0, 0, 0)] to replace [rgb(17, 17, 17) rgb(1, 1, 1)] at (1.5) is [rgb(26, 26, 0) rgb(2, 2, 2)] assert_equals: expected "rgb ( 0 , 0 , 0 ) rgb ( 0 , 0 , 0 ) " but got "rgb ( 26 , 26 , 0 ) rgb ( 2 , 2 , 2 ) "
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/animations/custom-properties/custom-color-list-type-interpolation.html b/third_party/WebKit/LayoutTests/animations/custom-properties/custom-color-list-type-interpolation.html
new file mode 100644
index 0000000..4a1b80ac
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/animations/custom-properties/custom-color-list-type-interpolation.html
@@ -0,0 +1,112 @@
+<!DOCTYPE html>
+<meta charset="UTF-8">
+<style>
+.parent {
+  --color-list: rgb(30, 30, 30) rgb(60, 60, 60);
+}
+.target {
+  --color-list: rgb(40, 40, 40) rgb(80, 80, 80);
+}
+</style>
+<body>
+<script src="../interpolation/resources/interpolation-test.js"></script>
+<script>
+CSS.registerProperty({
+  name: '--color-list',
+  syntax: '<color>+',
+  initialValue: 'red blue',
+  inherits: false,
+});
+
+assertInterpolation({
+  property: '--color-list',
+  from: 'initial',
+  to: 'blue red',
+}, [
+  {at: -0.3, is: 'rgb(255, 0, 0) rgb(0, 0, 255)'},
+  {at: 0, is: 'rgb(255, 0, 0) rgb(0, 0, 255)'},
+  {at: 0.5, is: 'rgb(128, 0, 128) rgb(128, 0, 128)'},
+  {at: 1, is: 'rgb(0, 0, 255) rgb(255, 0, 0)'},
+  {at: 1.5, is: 'rgb(0, 0, 255) rgb(255, 0, 0)'},
+]);
+
+assertInterpolation({
+  property: '--color-list',
+  from: 'inherit',
+  to: 'rgb(80, 80, 80) rgb(0, 0, 0)',
+}, [
+  {at: -0.3, is: 'rgb(15, 15, 15) rgb(78, 78, 78)'},
+  {at: 0, is: 'rgb(30, 30, 30) rgb(60, 60, 60)'},
+  {at: 0.5, is: 'rgb(55, 55, 55) rgb(30, 30, 30)'},
+  {at: 1, is: 'rgb(80, 80, 80) rgb(0, 0, 0)'},
+  {at: 1.5, is: 'rgb(105, 105, 105) rgb(0, 0, 0)'},
+]);
+
+assertInterpolation({
+  property: '--color-list',
+  from: 'unset',
+  to: 'tan tomato',
+}, [
+  {at: -0.3, is: 'rgb(255, 0, 0) rgb(0, 0, 255)'},
+  {at: 0, is: 'rgb(255, 0, 0) rgb(0, 0, 255)'},
+  {at: 0.5, is: 'rgb(233, 90, 70) rgb(128, 50, 163)'},
+  {at: 1, is: 'rgb(210, 180, 140) rgb(255, 99, 71)'},
+  {at: 1.5, is: 'rgb(188, 255, 210) rgb(255, 149, 0)'},
+]);
+
+assertInterpolation({
+  property: '--color-list',
+  from: 'hsl(120, 100%, 50%) coral',
+  to: 'navy hsl(240, 100%, 50%)',
+}, [
+  {at: -0.3, is: 'rgb(0, 255, 0) rgb(255, 165, 28)'},
+  {at: 0, is: 'rgb(0, 255, 0) rgb(255, 127, 80)'},
+  {at: 0.5, is: 'rgb(0, 128, 64) rgb(128, 64, 168)'},
+  {at: 1, is: 'rgb(0, 0, 128) rgb(0, 0, 255)'},
+  {at: 1.5, is: 'rgb(0, 0, 192) rgb(0, 0, 255)'}
+]);
+
+// Composition and neutralKeyframe tests assume that composite:add means
+// component-wise addition, which may or may not be the behavior we want.
+// https://github.com/w3c/css-houdini-drafts/issues/799
+
+assertInterpolation({
+  property: '--color-list',
+  from: neutralKeyframe,
+  to: 'rgb(0, 40, 40) black',
+}, [
+  {at: -0.3, is: 'rgb(52, 40, 40) rgb(104, 104, 104)'},
+  {at: 0, is: 'rgb(40, 40, 40) rgb(80, 80, 80)'},
+  {at: 0.5, is: 'rgb(20, 40, 40) rgb(40, 40, 40)'},
+  {at: 1, is: 'rgb(0, 40, 40) rgb(0, 0, 0)'},
+  {at: 1.5, is: 'rgb(0, 40, 40) rgb(0, 0, 0)'},
+]);
+
+assertComposition({
+  property: '--color-list',
+  underlying: 'darkslategray rgb(10, 10, 10)',
+  addFrom: 'rgb(10, 10, 10) navy',
+  addTo: 'rgb(30, 30, 30) teal',
+}, [
+  {at: -0.3, is: 'rgb(51, 83, 83) rgb(10, 0, 138)'},
+  {at: 0, is: 'rgb(57, 89, 89) rgb(10, 10, 138)'},
+  {at: 0.5, is: 'rgb(67, 99, 99) rgb(10, 74, 138)'},
+  {at: 1, is: 'rgb(77, 109, 109) rgb(10, 138, 138)'},
+  {at: 1.5, is: 'rgb(87, 119, 119) rgb(10, 202, 138)'},
+]);
+
+assertComposition({
+  property: '--color-list',
+  underlying: 'gold rgb(100, 100, 100)',
+  addFrom: 'navy rgb(0, 0, 0)',
+  replaceTo: 'rgb(17, 17, 17) rgb(1, 1, 1)',
+}, [
+  {at: -0.3, is: 'rgb(255, 255, 161) rgb(130, 130, 130)'},
+  {at: 0, is: 'rgb(255, 215, 128) rgb(100, 100, 100)'},
+  {at: 0.5, is: 'rgb(136, 116, 73) rgb(51, 51, 51)'},
+  {at: 1, is: 'rgb(17, 17, 17) rgb(1, 1, 1)'},
+  {at: 1.5, is: 'rgb(0, 0, 0) rgb(0, 0, 0)'},
+]);
+
+</script>
+</body>
diff --git a/third_party/WebKit/LayoutTests/animations/custom-properties/custom-integer-list-type-interpolation-expected.txt b/third_party/WebKit/LayoutTests/animations/custom-properties/custom-integer-list-type-interpolation-expected.txt
new file mode 100644
index 0000000..877c3054
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/animations/custom-properties/custom-integer-list-type-interpolation-expected.txt
@@ -0,0 +1,119 @@
+This is a testharness.js-based test.
+PASS This test uses interpolation-test.js.
+PASS CSS Transitions: property <--integer-list> from [initial] to [20 200] at (-0.3) is [46 460]
+PASS CSS Transitions: property <--integer-list> from [initial] to [20 200] at (0) is [40 400]
+PASS CSS Transitions: property <--integer-list> from [initial] to [20 200] at (0.5) is [30 300]
+PASS CSS Transitions: property <--integer-list> from [initial] to [20 200] at (1) is [20 200]
+PASS CSS Transitions: property <--integer-list> from [initial] to [20 200] at (1.5) is [10 100]
+PASS CSS Transitions: property <--integer-list> from [inherit] to [20 200] at (-0.3) is [33 330]
+PASS CSS Transitions: property <--integer-list> from [inherit] to [20 200] at (0) is [30 300]
+PASS CSS Transitions: property <--integer-list> from [inherit] to [20 200] at (0.5) is [25 250]
+PASS CSS Transitions: property <--integer-list> from [inherit] to [20 200] at (1) is [20 200]
+PASS CSS Transitions: property <--integer-list> from [inherit] to [20 200] at (1.5) is [15 150]
+PASS CSS Transitions: property <--integer-list> from [unset] to [20 200] at (-0.3) is [46 460]
+PASS CSS Transitions: property <--integer-list> from [unset] to [20 200] at (0) is [40 400]
+PASS CSS Transitions: property <--integer-list> from [unset] to [20 200] at (0.5) is [30 300]
+PASS CSS Transitions: property <--integer-list> from [unset] to [20 200] at (1) is [20 200]
+PASS CSS Transitions: property <--integer-list> from [unset] to [20 200] at (1.5) is [10 100]
+PASS CSS Transitions: property <--integer-list> from [-10 -100] to [10 100] at (-0.3) is [-16 -160]
+PASS CSS Transitions: property <--integer-list> from [-10 -100] to [10 100] at (0) is [-10 -100]
+PASS CSS Transitions: property <--integer-list> from [-10 -100] to [10 100] at (0.5) is [0 0]
+PASS CSS Transitions: property <--integer-list> from [-10 -100] to [10 100] at (1) is [10 100]
+PASS CSS Transitions: property <--integer-list> from [-10 -100] to [10 100] at (1.5) is [20 200]
+PASS CSS Transitions: property <--integer-list> from [10] to [100] at (-0.3) is [-17]
+PASS CSS Transitions: property <--integer-list> from [10] to [100] at (0) is [10]
+PASS CSS Transitions: property <--integer-list> from [10] to [100] at (0.5) is [55]
+PASS CSS Transitions: property <--integer-list> from [10] to [100] at (1) is [100]
+PASS CSS Transitions: property <--integer-list> from [10] to [100] at (1.5) is [145]
+PASS CSS Transitions: property <--integer-list> from [0 15] to [15 0] at (-0.3) is [-4 19]
+PASS CSS Transitions: property <--integer-list> from [0 15] to [15 0] at (0) is [0 15]
+PASS CSS Transitions: property <--integer-list> from [0 15] to [15 0] at (0.45) is [7 8]
+PASS CSS Transitions: property <--integer-list> from [0 15] to [15 0] at (1) is [15 0]
+PASS CSS Transitions: property <--integer-list> from [0 15] to [15 0] at (1.45) is [22 -7]
+PASS CSS Transitions: property <--integer-list> from neutral to [20 200] at (-0.3) is [7 70]
+PASS CSS Transitions: property <--integer-list> from neutral to [20 200] at (0) is [10 100]
+PASS CSS Transitions: property <--integer-list> from neutral to [20 200] at (0.5) is [15 150]
+PASS CSS Transitions: property <--integer-list> from neutral to [20 200] at (1) is [20 200]
+PASS CSS Transitions: property <--integer-list> from neutral to [20 200] at (1.5) is [25 250]
+PASS CSS Animations: property <--integer-list> from [initial] to [20 200] at (-0.3) is [46 460]
+PASS CSS Animations: property <--integer-list> from [initial] to [20 200] at (0) is [40 400]
+PASS CSS Animations: property <--integer-list> from [initial] to [20 200] at (0.5) is [30 300]
+PASS CSS Animations: property <--integer-list> from [initial] to [20 200] at (1) is [20 200]
+PASS CSS Animations: property <--integer-list> from [initial] to [20 200] at (1.5) is [10 100]
+PASS CSS Animations: property <--integer-list> from [inherit] to [20 200] at (-0.3) is [33 330]
+PASS CSS Animations: property <--integer-list> from [inherit] to [20 200] at (0) is [30 300]
+PASS CSS Animations: property <--integer-list> from [inherit] to [20 200] at (0.5) is [25 250]
+PASS CSS Animations: property <--integer-list> from [inherit] to [20 200] at (1) is [20 200]
+PASS CSS Animations: property <--integer-list> from [inherit] to [20 200] at (1.5) is [15 150]
+PASS CSS Animations: property <--integer-list> from [unset] to [20 200] at (-0.3) is [46 460]
+PASS CSS Animations: property <--integer-list> from [unset] to [20 200] at (0) is [40 400]
+PASS CSS Animations: property <--integer-list> from [unset] to [20 200] at (0.5) is [30 300]
+PASS CSS Animations: property <--integer-list> from [unset] to [20 200] at (1) is [20 200]
+PASS CSS Animations: property <--integer-list> from [unset] to [20 200] at (1.5) is [10 100]
+PASS CSS Animations: property <--integer-list> from [-10 -100] to [10 100] at (-0.3) is [-16 -160]
+PASS CSS Animations: property <--integer-list> from [-10 -100] to [10 100] at (0) is [-10 -100]
+PASS CSS Animations: property <--integer-list> from [-10 -100] to [10 100] at (0.5) is [0 0]
+PASS CSS Animations: property <--integer-list> from [-10 -100] to [10 100] at (1) is [10 100]
+PASS CSS Animations: property <--integer-list> from [-10 -100] to [10 100] at (1.5) is [20 200]
+PASS CSS Animations: property <--integer-list> from [10] to [100] at (-0.3) is [-17]
+PASS CSS Animations: property <--integer-list> from [10] to [100] at (0) is [10]
+PASS CSS Animations: property <--integer-list> from [10] to [100] at (0.5) is [55]
+PASS CSS Animations: property <--integer-list> from [10] to [100] at (1) is [100]
+PASS CSS Animations: property <--integer-list> from [10] to [100] at (1.5) is [145]
+PASS CSS Animations: property <--integer-list> from [0 15] to [15 0] at (-0.3) is [-4 19]
+PASS CSS Animations: property <--integer-list> from [0 15] to [15 0] at (0) is [0 15]
+PASS CSS Animations: property <--integer-list> from [0 15] to [15 0] at (0.45) is [7 8]
+PASS CSS Animations: property <--integer-list> from [0 15] to [15 0] at (1) is [15 0]
+PASS CSS Animations: property <--integer-list> from [0 15] to [15 0] at (1.45) is [22 -7]
+FAIL CSS Animations: property <--integer-list> from neutral to [20 200] at (-0.3) is [-6 -60] assert_equals: expected "7 70 " but got "- 6 - 60 "
+FAIL CSS Animations: property <--integer-list> from neutral to [20 200] at (0) is [0 0] assert_equals: expected "10 100 " but got "0 0 "
+FAIL CSS Animations: property <--integer-list> from neutral to [20 200] at (0.5) is [10 100] assert_equals: expected "15 150 " but got "10 100 "
+PASS CSS Animations: property <--integer-list> from neutral to [20 200] at (1) is [20 200]
+FAIL CSS Animations: property <--integer-list> from neutral to [20 200] at (1.5) is [30 300] assert_equals: expected "25 250 " but got "30 300 "
+PASS Web Animations: property <--integer-list> from [initial] to [20 200] at (-0.3) is [46 460]
+PASS Web Animations: property <--integer-list> from [initial] to [20 200] at (0) is [40 400]
+PASS Web Animations: property <--integer-list> from [initial] to [20 200] at (0.5) is [30 300]
+PASS Web Animations: property <--integer-list> from [initial] to [20 200] at (1) is [20 200]
+PASS Web Animations: property <--integer-list> from [initial] to [20 200] at (1.5) is [10 100]
+PASS Web Animations: property <--integer-list> from [inherit] to [20 200] at (-0.3) is [33 330]
+PASS Web Animations: property <--integer-list> from [inherit] to [20 200] at (0) is [30 300]
+PASS Web Animations: property <--integer-list> from [inherit] to [20 200] at (0.5) is [25 250]
+PASS Web Animations: property <--integer-list> from [inherit] to [20 200] at (1) is [20 200]
+PASS Web Animations: property <--integer-list> from [inherit] to [20 200] at (1.5) is [15 150]
+PASS Web Animations: property <--integer-list> from [unset] to [20 200] at (-0.3) is [46 460]
+PASS Web Animations: property <--integer-list> from [unset] to [20 200] at (0) is [40 400]
+PASS Web Animations: property <--integer-list> from [unset] to [20 200] at (0.5) is [30 300]
+PASS Web Animations: property <--integer-list> from [unset] to [20 200] at (1) is [20 200]
+PASS Web Animations: property <--integer-list> from [unset] to [20 200] at (1.5) is [10 100]
+PASS Web Animations: property <--integer-list> from [-10 -100] to [10 100] at (-0.3) is [-16 -160]
+PASS Web Animations: property <--integer-list> from [-10 -100] to [10 100] at (0) is [-10 -100]
+PASS Web Animations: property <--integer-list> from [-10 -100] to [10 100] at (0.5) is [0 0]
+PASS Web Animations: property <--integer-list> from [-10 -100] to [10 100] at (1) is [10 100]
+PASS Web Animations: property <--integer-list> from [-10 -100] to [10 100] at (1.5) is [20 200]
+PASS Web Animations: property <--integer-list> from [10] to [100] at (-0.3) is [-17]
+PASS Web Animations: property <--integer-list> from [10] to [100] at (0) is [10]
+PASS Web Animations: property <--integer-list> from [10] to [100] at (0.5) is [55]
+PASS Web Animations: property <--integer-list> from [10] to [100] at (1) is [100]
+PASS Web Animations: property <--integer-list> from [10] to [100] at (1.5) is [145]
+PASS Web Animations: property <--integer-list> from [0 15] to [15 0] at (-0.3) is [-4 19]
+PASS Web Animations: property <--integer-list> from [0 15] to [15 0] at (0) is [0 15]
+PASS Web Animations: property <--integer-list> from [0 15] to [15 0] at (0.45) is [7 8]
+PASS Web Animations: property <--integer-list> from [0 15] to [15 0] at (1) is [15 0]
+PASS Web Animations: property <--integer-list> from [0 15] to [15 0] at (1.45) is [22 -7]
+FAIL Web Animations: property <--integer-list> from neutral to [20 200] at (-0.3) is [-6 -60] assert_equals: expected "7 70 " but got "- 6 - 60 "
+FAIL Web Animations: property <--integer-list> from neutral to [20 200] at (0) is [0 0] assert_equals: expected "10 100 " but got "0 0 "
+FAIL Web Animations: property <--integer-list> from neutral to [20 200] at (0.5) is [10 100] assert_equals: expected "15 150 " but got "10 100 "
+PASS Web Animations: property <--integer-list> from neutral to [20 200] at (1) is [20 200]
+FAIL Web Animations: property <--integer-list> from neutral to [20 200] at (1.5) is [30 300] assert_equals: expected "25 250 " but got "30 300 "
+FAIL Compositing: property <--integer-list> underlying [50 60] from add [10 140] to add [110 40] at (-0.3) is [-20 170] assert_equals: expected "30 230 " but got "- 20 170 "
+FAIL Compositing: property <--integer-list> underlying [50 60] from add [10 140] to add [110 40] at (0) is [10 140] assert_equals: expected "60 200 " but got "10 140 "
+FAIL Compositing: property <--integer-list> underlying [50 60] from add [10 140] to add [110 40] at (0.5) is [60 90] assert_equals: expected "110 150 " but got "60 90 "
+FAIL Compositing: property <--integer-list> underlying [50 60] from add [10 140] to add [110 40] at (1) is [110 40] assert_equals: expected "160 100 " but got "110 40 "
+FAIL Compositing: property <--integer-list> underlying [50 60] from add [10 140] to add [110 40] at (1.5) is [160 -10] assert_equals: expected "210 50 " but got "160 - 10 "
+FAIL Compositing: property <--integer-list> underlying [50 60] from add [10 140] to replace [110 40] at (-0.3) is [-20 170] assert_equals: expected "45 248 " but got "- 20 170 "
+FAIL Compositing: property <--integer-list> underlying [50 60] from add [10 140] to replace [110 40] at (0) is [10 140] assert_equals: expected "60 200 " but got "10 140 "
+FAIL Compositing: property <--integer-list> underlying [50 60] from add [10 140] to replace [110 40] at (0.5) is [60 90] assert_equals: expected "85 120 " but got "60 90 "
+PASS Compositing: property <--integer-list> underlying [50 60] from add [10 140] to replace [110 40] at (1) is [110 40]
+FAIL Compositing: property <--integer-list> underlying [50 60] from add [10 140] to replace [110 40] at (1.5) is [160 -10] assert_equals: expected "135 - 40 " but got "160 - 10 "
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/animations/custom-properties/custom-integer-list-type-interpolation.html b/third_party/WebKit/LayoutTests/animations/custom-properties/custom-integer-list-type-interpolation.html
new file mode 100644
index 0000000..e9193025
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/animations/custom-properties/custom-integer-list-type-interpolation.html
@@ -0,0 +1,136 @@
+<!DOCTYPE html>
+<meta charset="UTF-8">
+<style>
+.parent {
+  --integer-list: 30 300;
+}
+.target {
+  --integer-list: 10 100;
+}
+</style>
+<body>
+<script src="../interpolation/resources/interpolation-test.js"></script>
+<script>
+CSS.registerProperty({
+  name: '--integer-list',
+  syntax: '<integer>+',
+  initialValue: '40 400',
+  inherits: false,
+});
+
+assertInterpolation({
+  property: '--integer-list',
+  from: 'initial',
+  to: '20 200',
+}, [
+  {at: -0.3, is: '46 460'},
+  {at: 0, is: '40 400'},
+  {at: 0.5, is: '30 300'},
+  {at: 1, is: '20 200'},
+  {at: 1.5, is: '10 100'},
+]);
+
+assertInterpolation({
+  property: '--integer-list',
+  from: 'inherit',
+  to: '20 200',
+}, [
+  {at: -0.3, is: '33 330'},
+  {at: 0, is: '30 300'},
+  {at: 0.5, is: '25 250'},
+  {at: 1, is: '20 200'},
+  {at: 1.5, is: '15 150'},
+]);
+
+assertInterpolation({
+  property: '--integer-list',
+  from: 'unset',
+  to: '20 200',
+}, [
+  {at: -0.3, is: '46 460'},
+  {at: 0, is: '40 400'},
+  {at: 0.5, is: '30 300'},
+  {at: 1, is: '20 200'},
+  {at: 1.5, is: '10 100'},
+]);
+
+assertInterpolation({
+  property: '--integer-list',
+  from: '-10 -100',
+  to: '10 100',
+}, [
+  {at: -0.3, is: '-16 -160'},
+  {at: 0, is: '-10 -100'},
+  {at: 0.5, is: '0 0'},
+  {at: 1, is: '10 100'},
+  {at: 1.5, is: '20 200'}
+]); 
+
+assertInterpolation({
+  property: '--integer-list',
+  from: '10',
+  to: '100',
+}, [
+  {at: -0.3, is: '-17'},
+  {at: 0, is: '10'},
+  {at: 0.5, is: '55'},
+  {at: 1, is: '100'},
+  {at: 1.5, is: '145'}
+]);
+
+assertInterpolation({
+  property: '--integer-list',
+  from: '0 15',
+  to: '15 0',
+}, [
+  {at: -0.3, is: '-4 19'},
+  {at: 0, is: '0 15'},
+  {at: 0.45, is: '7 8'},
+  {at: 1, is: '15 0'},
+  {at: 1.45, is: '22 -7'}
+]);
+
+// Composition and neutralKeyframe tests assume that composite:add means
+// component-wise addition, which may or may not be the behavior we want.
+// https://github.com/w3c/css-houdini-drafts/issues/799
+
+assertInterpolation({
+  property: '--integer-list',
+  from: neutralKeyframe,
+  to: '20 200',
+}, [
+  {at: -0.3, is: '7 70'},
+  {at: 0, is: '10 100'},
+  {at: 0.5, is: '15 150'},
+  {at: 1, is: '20 200'},
+  {at: 1.5, is: '25 250'},
+]);
+
+assertComposition({
+  property: '--integer-list',
+  underlying: '50 60',
+  addFrom: '10 140',
+  addTo: '110 40',
+}, [
+  {at: -0.3, is: '30 230'},
+  {at: 0, is: '60 200'},
+  {at: 0.5, is: '110 150'},
+  {at: 1, is: '160 100'},
+  {at: 1.5, is: '210 50'},
+]);
+
+assertComposition({
+  property: '--integer-list',
+  underlying: '50 60',
+  addFrom: '10 140',
+  replaceTo: '110 40',
+}, [
+  {at: -0.3, is: '45 248'},
+  {at: 0, is: '60 200'},
+  {at: 0.5, is: '85 120'},
+  {at: 1, is: '110 40'},
+  {at: 1.5, is: '135 -40'},
+]);
+
+</script>
+</body>
diff --git a/third_party/WebKit/LayoutTests/animations/custom-properties/custom-length-list-type-interpolation-expected.txt b/third_party/WebKit/LayoutTests/animations/custom-properties/custom-length-list-type-interpolation-expected.txt
new file mode 100644
index 0000000..ff96d632
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/animations/custom-properties/custom-length-list-type-interpolation-expected.txt
@@ -0,0 +1,134 @@
+This is a testharness.js-based test.
+PASS This test uses interpolation-test.js.
+PASS CSS Transitions: property <--length-list> from [initial] to [20px 200px] at (-0.3) is [46px 460px]
+PASS CSS Transitions: property <--length-list> from [initial] to [20px 200px] at (0) is [40px 400px]
+PASS CSS Transitions: property <--length-list> from [initial] to [20px 200px] at (0.5) is [30px 300px]
+PASS CSS Transitions: property <--length-list> from [initial] to [20px 200px] at (1) is [20px 200px]
+PASS CSS Transitions: property <--length-list> from [initial] to [20px 200px] at (1.5) is [10px 100px]
+PASS CSS Transitions: property <--length-list> from [inherit] to [20px 200px] at (-0.3) is [33px 330px]
+PASS CSS Transitions: property <--length-list> from [inherit] to [20px 200px] at (0) is [30px 300px]
+PASS CSS Transitions: property <--length-list> from [inherit] to [20px 200px] at (0.5) is [25px 250px]
+PASS CSS Transitions: property <--length-list> from [inherit] to [20px 200px] at (1) is [20px 200px]
+PASS CSS Transitions: property <--length-list> from [inherit] to [20px 200px] at (1.5) is [15px 150px]
+PASS CSS Transitions: property <--length-list> from [unset] to [20px 200px] at (-0.3) is [46px 460px]
+PASS CSS Transitions: property <--length-list> from [unset] to [20px 200px] at (0) is [40px 400px]
+PASS CSS Transitions: property <--length-list> from [unset] to [20px 200px] at (0.5) is [30px 300px]
+PASS CSS Transitions: property <--length-list> from [unset] to [20px 200px] at (1) is [20px 200px]
+PASS CSS Transitions: property <--length-list> from [unset] to [20px 200px] at (1.5) is [10px 100px]
+PASS CSS Transitions: property <--length-list> from [-10px -100px] to [10px 100px] at (-0.3) is [-16px -160px]
+PASS CSS Transitions: property <--length-list> from [-10px -100px] to [10px 100px] at (0) is [-10px -100px]
+PASS CSS Transitions: property <--length-list> from [-10px -100px] to [10px 100px] at (0.5) is [0px 0px]
+PASS CSS Transitions: property <--length-list> from [-10px -100px] to [10px 100px] at (1) is [10px 100px]
+PASS CSS Transitions: property <--length-list> from [-10px -100px] to [10px 100px] at (1.5) is [20px 200px]
+PASS CSS Transitions: property <--length-list> from [10em 100em] to [20em 200em] at (-0.3) is [140px 1400px]
+PASS CSS Transitions: property <--length-list> from [10em 100em] to [20em 200em] at (0) is [200px 2000px]
+PASS CSS Transitions: property <--length-list> from [10em 100em] to [20em 200em] at (0.5) is [300px 3000px]
+PASS CSS Transitions: property <--length-list> from [10em 100em] to [20em 200em] at (1) is [400px 4000px]
+PASS CSS Transitions: property <--length-list> from [10em 100em] to [20em 200em] at (1.5) is [500px 5000px]
+PASS CSS Transitions: property <--length-list> from [10em 100em] to [100px 1000px] at (-0.3) is [230px 2300px]
+PASS CSS Transitions: property <--length-list> from [10em 100em] to [100px 1000px] at (0) is [200px 2000px]
+PASS CSS Transitions: property <--length-list> from [10em 100em] to [100px 1000px] at (0.5) is [150px 1500px]
+PASS CSS Transitions: property <--length-list> from [10em 100em] to [100px 1000px] at (1) is [100px 1000px]
+PASS CSS Transitions: property <--length-list> from [10em 100em] to [100px 1000px] at (1.5) is [50px 500px]
+PASS CSS Transitions: property <--length-list> from [10px] to [100px] at (-0.3) is [-17px]
+PASS CSS Transitions: property <--length-list> from [10px] to [100px] at (0) is [10px]
+PASS CSS Transitions: property <--length-list> from [10px] to [100px] at (0.5) is [55px]
+PASS CSS Transitions: property <--length-list> from [10px] to [100px] at (1) is [100px]
+PASS CSS Transitions: property <--length-list> from [10px] to [100px] at (1.5) is [145px]
+PASS CSS Transitions: property <--length-list> from neutral to [20px 200px] at (-0.3) is [7px 70px]
+PASS CSS Transitions: property <--length-list> from neutral to [20px 200px] at (0) is [10px 100px]
+PASS CSS Transitions: property <--length-list> from neutral to [20px 200px] at (0.5) is [15px 150px]
+PASS CSS Transitions: property <--length-list> from neutral to [20px 200px] at (1) is [20px 200px]
+PASS CSS Transitions: property <--length-list> from neutral to [20px 200px] at (1.5) is [25px 250px]
+PASS CSS Animations: property <--length-list> from [initial] to [20px 200px] at (-0.3) is [46px 460px]
+PASS CSS Animations: property <--length-list> from [initial] to [20px 200px] at (0) is [40px 400px]
+PASS CSS Animations: property <--length-list> from [initial] to [20px 200px] at (0.5) is [30px 300px]
+PASS CSS Animations: property <--length-list> from [initial] to [20px 200px] at (1) is [20px 200px]
+PASS CSS Animations: property <--length-list> from [initial] to [20px 200px] at (1.5) is [10px 100px]
+PASS CSS Animations: property <--length-list> from [inherit] to [20px 200px] at (-0.3) is [33px 330px]
+PASS CSS Animations: property <--length-list> from [inherit] to [20px 200px] at (0) is [30px 300px]
+PASS CSS Animations: property <--length-list> from [inherit] to [20px 200px] at (0.5) is [25px 250px]
+PASS CSS Animations: property <--length-list> from [inherit] to [20px 200px] at (1) is [20px 200px]
+PASS CSS Animations: property <--length-list> from [inherit] to [20px 200px] at (1.5) is [15px 150px]
+PASS CSS Animations: property <--length-list> from [unset] to [20px 200px] at (-0.3) is [46px 460px]
+PASS CSS Animations: property <--length-list> from [unset] to [20px 200px] at (0) is [40px 400px]
+PASS CSS Animations: property <--length-list> from [unset] to [20px 200px] at (0.5) is [30px 300px]
+PASS CSS Animations: property <--length-list> from [unset] to [20px 200px] at (1) is [20px 200px]
+PASS CSS Animations: property <--length-list> from [unset] to [20px 200px] at (1.5) is [10px 100px]
+PASS CSS Animations: property <--length-list> from [-10px -100px] to [10px 100px] at (-0.3) is [-16px -160px]
+PASS CSS Animations: property <--length-list> from [-10px -100px] to [10px 100px] at (0) is [-10px -100px]
+PASS CSS Animations: property <--length-list> from [-10px -100px] to [10px 100px] at (0.5) is [0px 0px]
+PASS CSS Animations: property <--length-list> from [-10px -100px] to [10px 100px] at (1) is [10px 100px]
+PASS CSS Animations: property <--length-list> from [-10px -100px] to [10px 100px] at (1.5) is [20px 200px]
+PASS CSS Animations: property <--length-list> from [10em 100em] to [20em 200em] at (-0.3) is [140px 1400px]
+PASS CSS Animations: property <--length-list> from [10em 100em] to [20em 200em] at (0) is [200px 2000px]
+PASS CSS Animations: property <--length-list> from [10em 100em] to [20em 200em] at (0.5) is [300px 3000px]
+PASS CSS Animations: property <--length-list> from [10em 100em] to [20em 200em] at (1) is [400px 4000px]
+PASS CSS Animations: property <--length-list> from [10em 100em] to [20em 200em] at (1.5) is [500px 5000px]
+PASS CSS Animations: property <--length-list> from [10em 100em] to [100px 1000px] at (-0.3) is [230px 2300px]
+PASS CSS Animations: property <--length-list> from [10em 100em] to [100px 1000px] at (0) is [200px 2000px]
+PASS CSS Animations: property <--length-list> from [10em 100em] to [100px 1000px] at (0.5) is [150px 1500px]
+PASS CSS Animations: property <--length-list> from [10em 100em] to [100px 1000px] at (1) is [100px 1000px]
+PASS CSS Animations: property <--length-list> from [10em 100em] to [100px 1000px] at (1.5) is [50px 500px]
+PASS CSS Animations: property <--length-list> from [10px] to [100px] at (-0.3) is [-17px]
+PASS CSS Animations: property <--length-list> from [10px] to [100px] at (0) is [10px]
+PASS CSS Animations: property <--length-list> from [10px] to [100px] at (0.5) is [55px]
+PASS CSS Animations: property <--length-list> from [10px] to [100px] at (1) is [100px]
+PASS CSS Animations: property <--length-list> from [10px] to [100px] at (1.5) is [145px]
+FAIL CSS Animations: property <--length-list> from neutral to [20px 200px] at (-0.3) is [-6px -60px] assert_equals: expected "7px 70px " but got "- 6px - 60px "
+FAIL CSS Animations: property <--length-list> from neutral to [20px 200px] at (0) is [0px 0px] assert_equals: expected "10px 100px " but got "0px 0px "
+FAIL CSS Animations: property <--length-list> from neutral to [20px 200px] at (0.5) is [10px 100px] assert_equals: expected "15px 150px " but got "10px 100px "
+PASS CSS Animations: property <--length-list> from neutral to [20px 200px] at (1) is [20px 200px]
+FAIL CSS Animations: property <--length-list> from neutral to [20px 200px] at (1.5) is [30px 300px] assert_equals: expected "25px 250px " but got "30px 300px "
+PASS Web Animations: property <--length-list> from [initial] to [20px 200px] at (-0.3) is [46px 460px]
+PASS Web Animations: property <--length-list> from [initial] to [20px 200px] at (0) is [40px 400px]
+PASS Web Animations: property <--length-list> from [initial] to [20px 200px] at (0.5) is [30px 300px]
+PASS Web Animations: property <--length-list> from [initial] to [20px 200px] at (1) is [20px 200px]
+PASS Web Animations: property <--length-list> from [initial] to [20px 200px] at (1.5) is [10px 100px]
+PASS Web Animations: property <--length-list> from [inherit] to [20px 200px] at (-0.3) is [33px 330px]
+PASS Web Animations: property <--length-list> from [inherit] to [20px 200px] at (0) is [30px 300px]
+PASS Web Animations: property <--length-list> from [inherit] to [20px 200px] at (0.5) is [25px 250px]
+PASS Web Animations: property <--length-list> from [inherit] to [20px 200px] at (1) is [20px 200px]
+PASS Web Animations: property <--length-list> from [inherit] to [20px 200px] at (1.5) is [15px 150px]
+PASS Web Animations: property <--length-list> from [unset] to [20px 200px] at (-0.3) is [46px 460px]
+PASS Web Animations: property <--length-list> from [unset] to [20px 200px] at (0) is [40px 400px]
+PASS Web Animations: property <--length-list> from [unset] to [20px 200px] at (0.5) is [30px 300px]
+PASS Web Animations: property <--length-list> from [unset] to [20px 200px] at (1) is [20px 200px]
+PASS Web Animations: property <--length-list> from [unset] to [20px 200px] at (1.5) is [10px 100px]
+PASS Web Animations: property <--length-list> from [-10px -100px] to [10px 100px] at (-0.3) is [-16px -160px]
+PASS Web Animations: property <--length-list> from [-10px -100px] to [10px 100px] at (0) is [-10px -100px]
+PASS Web Animations: property <--length-list> from [-10px -100px] to [10px 100px] at (0.5) is [0px 0px]
+PASS Web Animations: property <--length-list> from [-10px -100px] to [10px 100px] at (1) is [10px 100px]
+PASS Web Animations: property <--length-list> from [-10px -100px] to [10px 100px] at (1.5) is [20px 200px]
+PASS Web Animations: property <--length-list> from [10em 100em] to [20em 200em] at (-0.3) is [140px 1400px]
+PASS Web Animations: property <--length-list> from [10em 100em] to [20em 200em] at (0) is [200px 2000px]
+PASS Web Animations: property <--length-list> from [10em 100em] to [20em 200em] at (0.5) is [300px 3000px]
+PASS Web Animations: property <--length-list> from [10em 100em] to [20em 200em] at (1) is [400px 4000px]
+PASS Web Animations: property <--length-list> from [10em 100em] to [20em 200em] at (1.5) is [500px 5000px]
+PASS Web Animations: property <--length-list> from [10em 100em] to [100px 1000px] at (-0.3) is [230px 2300px]
+PASS Web Animations: property <--length-list> from [10em 100em] to [100px 1000px] at (0) is [200px 2000px]
+PASS Web Animations: property <--length-list> from [10em 100em] to [100px 1000px] at (0.5) is [150px 1500px]
+PASS Web Animations: property <--length-list> from [10em 100em] to [100px 1000px] at (1) is [100px 1000px]
+PASS Web Animations: property <--length-list> from [10em 100em] to [100px 1000px] at (1.5) is [50px 500px]
+PASS Web Animations: property <--length-list> from [10px] to [100px] at (-0.3) is [-17px]
+PASS Web Animations: property <--length-list> from [10px] to [100px] at (0) is [10px]
+PASS Web Animations: property <--length-list> from [10px] to [100px] at (0.5) is [55px]
+PASS Web Animations: property <--length-list> from [10px] to [100px] at (1) is [100px]
+PASS Web Animations: property <--length-list> from [10px] to [100px] at (1.5) is [145px]
+FAIL Web Animations: property <--length-list> from neutral to [20px 200px] at (-0.3) is [-6px -60px] assert_equals: expected "7px 70px " but got "- 6px - 60px "
+FAIL Web Animations: property <--length-list> from neutral to [20px 200px] at (0) is [0px 0px] assert_equals: expected "10px 100px " but got "0px 0px "
+FAIL Web Animations: property <--length-list> from neutral to [20px 200px] at (0.5) is [10px 100px] assert_equals: expected "15px 150px " but got "10px 100px "
+PASS Web Animations: property <--length-list> from neutral to [20px 200px] at (1) is [20px 200px]
+FAIL Web Animations: property <--length-list> from neutral to [20px 200px] at (1.5) is [30px 300px] assert_equals: expected "25px 250px " but got "30px 300px "
+FAIL Compositing: property <--length-list> underlying [50px 60px] from add [10px 140px] to add [110px 40px] at (-0.3) is [-20px 170px] assert_equals: expected "30px 230px " but got "- 20px 170px "
+FAIL Compositing: property <--length-list> underlying [50px 60px] from add [10px 140px] to add [110px 40px] at (0) is [10px 140px] assert_equals: expected "60px 200px " but got "10px 140px "
+FAIL Compositing: property <--length-list> underlying [50px 60px] from add [10px 140px] to add [110px 40px] at (0.5) is [60px 90px] assert_equals: expected "110px 150px " but got "60px 90px "
+FAIL Compositing: property <--length-list> underlying [50px 60px] from add [10px 140px] to add [110px 40px] at (1) is [110px 40px] assert_equals: expected "160px 100px " but got "110px 40px "
+FAIL Compositing: property <--length-list> underlying [50px 60px] from add [10px 140px] to add [110px 40px] at (1.5) is [160px -10px] assert_equals: expected "210px 50px " but got "160px - 10px "
+FAIL Compositing: property <--length-list> underlying [50px 60px] from add [10px 140px] to replace [110px 40px] at (-0.3) is [-20px 170px] assert_equals: expected "45px 248px " but got "- 20px 170px "
+FAIL Compositing: property <--length-list> underlying [50px 60px] from add [10px 140px] to replace [110px 40px] at (0) is [10px 140px] assert_equals: expected "60px 200px " but got "10px 140px "
+FAIL Compositing: property <--length-list> underlying [50px 60px] from add [10px 140px] to replace [110px 40px] at (0.5) is [60px 90px] assert_equals: expected "85px 120px " but got "60px 90px "
+PASS Compositing: property <--length-list> underlying [50px 60px] from add [10px 140px] to replace [110px 40px] at (1) is [110px 40px]
+FAIL Compositing: property <--length-list> underlying [50px 60px] from add [10px 140px] to replace [110px 40px] at (1.5) is [160px -10px] assert_equals: expected "135px - 40px " but got "160px - 10px "
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/animations/custom-properties/custom-length-list-type-interpolation.html b/third_party/WebKit/LayoutTests/animations/custom-properties/custom-length-list-type-interpolation.html
new file mode 100644
index 0000000..419c8ca
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/animations/custom-properties/custom-length-list-type-interpolation.html
@@ -0,0 +1,149 @@
+<!DOCTYPE html>
+<meta charset="UTF-8">
+<style>
+.parent {
+  --length-list: 30px 300px;
+}
+.target {
+  --length-list: 10px 100px;
+  font-size: 20px;
+}
+</style>
+<body>
+<script src="../interpolation/resources/interpolation-test.js"></script>
+<script>
+CSS.registerProperty({
+  name: '--length-list',
+  syntax: '<length>+',
+  initialValue: '40px 400px',
+  inherits: false,
+});
+
+assertInterpolation({
+  property: '--length-list',
+  from: 'initial',
+  to: '20px 200px',
+}, [
+  {at: -0.3, is: '46px 460px'},
+  {at: 0, is: '40px 400px'},
+  {at: 0.5, is: '30px 300px'},
+  {at: 1, is: '20px 200px'},
+  {at: 1.5, is: '10px 100px'},
+]);
+
+assertInterpolation({
+  property: '--length-list',
+  from: 'inherit',
+  to: '20px 200px',
+}, [
+  {at: -0.3, is: '33px 330px'},
+  {at: 0, is: '30px 300px'},
+  {at: 0.5, is: '25px 250px'},
+  {at: 1, is: '20px 200px'},
+  {at: 1.5, is: '15px 150px'},
+]);
+
+assertInterpolation({
+  property: '--length-list',
+  from: 'unset',
+  to: '20px 200px',
+}, [
+  {at: -0.3, is: '46px 460px'},
+  {at: 0, is: '40px 400px'},
+  {at: 0.5, is: '30px 300px'},
+  {at: 1, is: '20px 200px'},
+  {at: 1.5, is: '10px 100px'},
+]);
+
+assertInterpolation({
+  property: '--length-list',
+  from: '-10px -100px',
+  to: '10px 100px',
+}, [
+  {at: -0.3, is: '-16px -160px'},
+  {at: 0, is: '-10px -100px'},
+  {at: 0.5, is: '0px 0px'},
+  {at: 1, is: '10px 100px'},
+  {at: 1.5, is: '20px 200px'}
+]);
+
+assertInterpolation({
+  property: '--length-list',
+  from: '10em 100em',
+  to: '20em 200em',
+}, [
+  {at: -0.3, is: '140px 1400px'},
+  {at: 0, is: '200px 2000px'},
+  {at: 0.5, is: '300px 3000px'},
+  {at: 1, is: '400px 4000px'},
+  {at: 1.5, is: '500px 5000px'}
+]);
+
+assertInterpolation({
+  property: '--length-list',
+  from: '10em 100em',
+  to: '100px 1000px',
+}, [
+  {at: -0.3, is: '230px 2300px'},
+  {at: 0, is: '200px 2000px'},
+  {at: 0.5, is: '150px 1500px'},
+  {at: 1, is: '100px 1000px'},
+  {at: 1.5, is: '50px 500px'}
+]);
+
+assertInterpolation({
+  property: '--length-list',
+  from: '10px',
+  to: '100px',
+}, [
+  {at: -0.3, is: '-17px'},
+  {at: 0, is: '10px'},
+  {at: 0.5, is: '55px'},
+  {at: 1, is: '100px'},
+  {at: 1.5, is: '145px'}
+]);
+
+// Composition and neutralKeyframe tests assume that composite:add means
+// component-wise addition, which may or may not be the behavior we want.
+// https://github.com/w3c/css-houdini-drafts/issues/799
+
+assertInterpolation({
+  property: '--length-list',
+  from: neutralKeyframe,
+  to: '20px 200px',
+}, [
+  {at: -0.3, is: '7px 70px'},
+  {at: 0, is: '10px 100px'},
+  {at: 0.5, is: '15px 150px'},
+  {at: 1, is: '20px 200px'},
+  {at: 1.5, is: '25px 250px'},
+]);
+
+assertComposition({
+  property: '--length-list',
+  underlying: '50px 60px',
+  addFrom: '10px 140px',
+  addTo: '110px 40px',
+}, [
+  {at: -0.3, is: '30px 230px'},
+  {at: 0, is: '60px 200px'},
+  {at: 0.5, is: '110px 150px'},
+  {at: 1, is: '160px 100px'},
+  {at: 1.5, is: '210px 50px'},
+]);
+
+assertComposition({
+  property: '--length-list',
+  underlying: '50px 60px',
+  addFrom: '10px 140px',
+  replaceTo: '110px 40px',
+}, [
+  {at: -0.3, is: '45px 248px'},
+  {at: 0, is: '60px 200px'},
+  {at: 0.5, is: '85px 120px'},
+  {at: 1, is: '110px 40px'},
+  {at: 1.5, is: '135px -40px'},
+]);
+
+</script>
+</body>
diff --git a/third_party/WebKit/LayoutTests/animations/custom-properties/custom-length-percentage-list-type-interpolation-expected.txt b/third_party/WebKit/LayoutTests/animations/custom-properties/custom-length-percentage-list-type-interpolation-expected.txt
new file mode 100644
index 0000000..bb01e7f
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/animations/custom-properties/custom-length-percentage-list-type-interpolation-expected.txt
@@ -0,0 +1,119 @@
+This is a testharness.js-based test.
+PASS This test uses interpolation-test.js.
+PASS CSS Transitions: property <--length-percentage-list> from [initial] to [20% 200%] at (-0.3) is [46% calc(520px + -60%)]
+PASS CSS Transitions: property <--length-percentage-list> from [initial] to [20% 200%] at (0) is [40% 400px]
+PASS CSS Transitions: property <--length-percentage-list> from [initial] to [20% 200%] at (0.5) is [30% calc(200px + 100%)]
+PASS CSS Transitions: property <--length-percentage-list> from [initial] to [20% 200%] at (1) is [20% 200%]
+PASS CSS Transitions: property <--length-percentage-list> from [initial] to [20% 200%] at (1.5) is [10% calc(-200px + 300%)]
+PASS CSS Transitions: property <--length-percentage-list> from [inherit] to [20px 200%] at (-0.3) is [calc(-6px + 39%) calc(390px + -60%)]
+PASS CSS Transitions: property <--length-percentage-list> from [inherit] to [20px 200%] at (0) is [30% 300px]
+PASS CSS Transitions: property <--length-percentage-list> from [inherit] to [20px 200%] at (0.5) is [calc(10px + 15%) calc(150px + 100%)]
+PASS CSS Transitions: property <--length-percentage-list> from [inherit] to [20px 200%] at (1) is [20px 200%]
+PASS CSS Transitions: property <--length-percentage-list> from [inherit] to [20px 200%] at (1.5) is [calc(30px + -15%) calc(-150px + 300%)]
+PASS CSS Transitions: property <--length-percentage-list> from [unset] to [calc(100px + 120%) calc(100px + 120%)] at (-0.3) is [calc(-30px + 16%) calc(490px + -36%)]
+PASS CSS Transitions: property <--length-percentage-list> from [unset] to [calc(100px + 120%) calc(100px + 120%)] at (0) is [40% 400px]
+PASS CSS Transitions: property <--length-percentage-list> from [unset] to [calc(100px + 120%) calc(100px + 120%)] at (0.5) is [calc(50px + 80%) calc(250px + 60%)]
+PASS CSS Transitions: property <--length-percentage-list> from [unset] to [calc(100px + 120%) calc(100px + 120%)] at (1) is [calc(100px + 120%) calc(100px + 120%)]
+PASS CSS Transitions: property <--length-percentage-list> from [unset] to [calc(100px + 120%) calc(100px + 120%)] at (1.5) is [calc(150px + 160%) calc(-50px + 180%)]
+PASS CSS Transitions: property <--length-percentage-list> from [calc(-10px - 10%) -100px] to [calc(10px + 10%) 100px] at (-0.3) is [calc(-16px + -16%) -160px]
+PASS CSS Transitions: property <--length-percentage-list> from [calc(-10px - 10%) -100px] to [calc(10px + 10%) 100px] at (0) is [calc(-10px + -10%) -100px]
+PASS CSS Transitions: property <--length-percentage-list> from [calc(-10px - 10%) -100px] to [calc(10px + 10%) 100px] at (0.5) is [0% 0px]
+PASS CSS Transitions: property <--length-percentage-list> from [calc(-10px - 10%) -100px] to [calc(10px + 10%) 100px] at (1) is [calc(10px + 10%) 100px]
+PASS CSS Transitions: property <--length-percentage-list> from [calc(-10px - 10%) -100px] to [calc(10px + 10%) 100px] at (1.5) is [calc(20px + 20%) 200px]
+PASS CSS Transitions: property <--length-percentage-list> from [calc(10em + 10%) 100em] to [20em calc(200em + 200%)] at (-0.3) is [calc(140px + 13%) calc(1400px + -60%)]
+PASS CSS Transitions: property <--length-percentage-list> from [calc(10em + 10%) 100em] to [20em calc(200em + 200%)] at (0) is [calc(200px + 10%) 2000px]
+PASS CSS Transitions: property <--length-percentage-list> from [calc(10em + 10%) 100em] to [20em calc(200em + 200%)] at (0.5) is [calc(300px + 5%) calc(3000px + 100%)]
+PASS CSS Transitions: property <--length-percentage-list> from [calc(10em + 10%) 100em] to [20em calc(200em + 200%)] at (1) is [400px calc(4000px + 200%)]
+PASS CSS Transitions: property <--length-percentage-list> from [calc(10em + 10%) 100em] to [20em calc(200em + 200%)] at (1.5) is [calc(500px + -5%) calc(5000px + 300%)]
+PASS CSS Transitions: property <--length-percentage-list> from [0px] to [calc(100px + 100%)] at (-0.3) is [calc(-30px + -30%)]
+PASS CSS Transitions: property <--length-percentage-list> from [0px] to [calc(100px + 100%)] at (0) is [0px]
+PASS CSS Transitions: property <--length-percentage-list> from [0px] to [calc(100px + 100%)] at (0.5) is [calc(50px + 50%)]
+PASS CSS Transitions: property <--length-percentage-list> from [0px] to [calc(100px + 100%)] at (1) is [calc(100px + 100%)]
+PASS CSS Transitions: property <--length-percentage-list> from [0px] to [calc(100px + 100%)] at (1.5) is [calc(150px + 150%)]
+PASS CSS Transitions: property <--length-percentage-list> from neutral to [20% 200px] at (-0.3) is [7% 70px]
+PASS CSS Transitions: property <--length-percentage-list> from neutral to [20% 200px] at (0) is [10% 100px]
+PASS CSS Transitions: property <--length-percentage-list> from neutral to [20% 200px] at (0.5) is [15% 150px]
+PASS CSS Transitions: property <--length-percentage-list> from neutral to [20% 200px] at (1) is [20% 200px]
+PASS CSS Transitions: property <--length-percentage-list> from neutral to [20% 200px] at (1.5) is [25% 250px]
+PASS CSS Animations: property <--length-percentage-list> from [initial] to [20% 200%] at (-0.3) is [46% calc(520px + -60%)]
+PASS CSS Animations: property <--length-percentage-list> from [initial] to [20% 200%] at (0) is [40% 400px]
+PASS CSS Animations: property <--length-percentage-list> from [initial] to [20% 200%] at (0.5) is [30% calc(200px + 100%)]
+PASS CSS Animations: property <--length-percentage-list> from [initial] to [20% 200%] at (1) is [20% 200%]
+PASS CSS Animations: property <--length-percentage-list> from [initial] to [20% 200%] at (1.5) is [10% calc(-200px + 300%)]
+PASS CSS Animations: property <--length-percentage-list> from [inherit] to [20px 200%] at (-0.3) is [calc(-6px + 39%) calc(390px + -60%)]
+PASS CSS Animations: property <--length-percentage-list> from [inherit] to [20px 200%] at (0) is [30% 300px]
+PASS CSS Animations: property <--length-percentage-list> from [inherit] to [20px 200%] at (0.5) is [calc(10px + 15%) calc(150px + 100%)]
+PASS CSS Animations: property <--length-percentage-list> from [inherit] to [20px 200%] at (1) is [20px 200%]
+PASS CSS Animations: property <--length-percentage-list> from [inherit] to [20px 200%] at (1.5) is [calc(30px + -15%) calc(-150px + 300%)]
+PASS CSS Animations: property <--length-percentage-list> from [unset] to [calc(100px + 120%) calc(100px + 120%)] at (-0.3) is [calc(-30px + 16%) calc(490px + -36%)]
+PASS CSS Animations: property <--length-percentage-list> from [unset] to [calc(100px + 120%) calc(100px + 120%)] at (0) is [40% 400px]
+PASS CSS Animations: property <--length-percentage-list> from [unset] to [calc(100px + 120%) calc(100px + 120%)] at (0.5) is [calc(50px + 80%) calc(250px + 60%)]
+PASS CSS Animations: property <--length-percentage-list> from [unset] to [calc(100px + 120%) calc(100px + 120%)] at (1) is [calc(100px + 120%) calc(100px + 120%)]
+PASS CSS Animations: property <--length-percentage-list> from [unset] to [calc(100px + 120%) calc(100px + 120%)] at (1.5) is [calc(150px + 160%) calc(-50px + 180%)]
+PASS CSS Animations: property <--length-percentage-list> from [calc(-10px - 10%) -100px] to [calc(10px + 10%) 100px] at (-0.3) is [calc(-16px + -16%) -160px]
+PASS CSS Animations: property <--length-percentage-list> from [calc(-10px - 10%) -100px] to [calc(10px + 10%) 100px] at (0) is [calc(-10px + -10%) -100px]
+PASS CSS Animations: property <--length-percentage-list> from [calc(-10px - 10%) -100px] to [calc(10px + 10%) 100px] at (0.5) is [0% 0px]
+PASS CSS Animations: property <--length-percentage-list> from [calc(-10px - 10%) -100px] to [calc(10px + 10%) 100px] at (1) is [calc(10px + 10%) 100px]
+PASS CSS Animations: property <--length-percentage-list> from [calc(-10px - 10%) -100px] to [calc(10px + 10%) 100px] at (1.5) is [calc(20px + 20%) 200px]
+PASS CSS Animations: property <--length-percentage-list> from [calc(10em + 10%) 100em] to [20em calc(200em + 200%)] at (-0.3) is [calc(140px + 13%) calc(1400px + -60%)]
+PASS CSS Animations: property <--length-percentage-list> from [calc(10em + 10%) 100em] to [20em calc(200em + 200%)] at (0) is [calc(200px + 10%) 2000px]
+PASS CSS Animations: property <--length-percentage-list> from [calc(10em + 10%) 100em] to [20em calc(200em + 200%)] at (0.5) is [calc(300px + 5%) calc(3000px + 100%)]
+PASS CSS Animations: property <--length-percentage-list> from [calc(10em + 10%) 100em] to [20em calc(200em + 200%)] at (1) is [400px calc(4000px + 200%)]
+PASS CSS Animations: property <--length-percentage-list> from [calc(10em + 10%) 100em] to [20em calc(200em + 200%)] at (1.5) is [calc(500px + -5%) calc(5000px + 300%)]
+PASS CSS Animations: property <--length-percentage-list> from [0px] to [calc(100px + 100%)] at (-0.3) is [calc(-30px + -30%)]
+PASS CSS Animations: property <--length-percentage-list> from [0px] to [calc(100px + 100%)] at (0) is [0px]
+PASS CSS Animations: property <--length-percentage-list> from [0px] to [calc(100px + 100%)] at (0.5) is [calc(50px + 50%)]
+PASS CSS Animations: property <--length-percentage-list> from [0px] to [calc(100px + 100%)] at (1) is [calc(100px + 100%)]
+PASS CSS Animations: property <--length-percentage-list> from [0px] to [calc(100px + 100%)] at (1.5) is [calc(150px + 150%)]
+FAIL CSS Animations: property <--length-percentage-list> from neutral to [20% 200px] at (-0.3) is [-6% -60px] assert_equals: expected "7 % 70px " but got "- 6 % - 60px "
+FAIL CSS Animations: property <--length-percentage-list> from neutral to [20% 200px] at (0) is [0px 0px] assert_equals: expected "10 % 100px " but got "0px 0px "
+FAIL CSS Animations: property <--length-percentage-list> from neutral to [20% 200px] at (0.5) is [10% 100px] assert_equals: expected "15 % 150px " but got "10 % 100px "
+PASS CSS Animations: property <--length-percentage-list> from neutral to [20% 200px] at (1) is [20% 200px]
+FAIL CSS Animations: property <--length-percentage-list> from neutral to [20% 200px] at (1.5) is [30% 300px] assert_equals: expected "25 % 250px " but got "30 % 300px "
+PASS Web Animations: property <--length-percentage-list> from [initial] to [20% 200%] at (-0.3) is [46% calc(520px + -60%)]
+PASS Web Animations: property <--length-percentage-list> from [initial] to [20% 200%] at (0) is [40% 400px]
+PASS Web Animations: property <--length-percentage-list> from [initial] to [20% 200%] at (0.5) is [30% calc(200px + 100%)]
+PASS Web Animations: property <--length-percentage-list> from [initial] to [20% 200%] at (1) is [20% 200%]
+PASS Web Animations: property <--length-percentage-list> from [initial] to [20% 200%] at (1.5) is [10% calc(-200px + 300%)]
+PASS Web Animations: property <--length-percentage-list> from [inherit] to [20px 200%] at (-0.3) is [calc(-6px + 39%) calc(390px + -60%)]
+PASS Web Animations: property <--length-percentage-list> from [inherit] to [20px 200%] at (0) is [30% 300px]
+PASS Web Animations: property <--length-percentage-list> from [inherit] to [20px 200%] at (0.5) is [calc(10px + 15%) calc(150px + 100%)]
+PASS Web Animations: property <--length-percentage-list> from [inherit] to [20px 200%] at (1) is [20px 200%]
+PASS Web Animations: property <--length-percentage-list> from [inherit] to [20px 200%] at (1.5) is [calc(30px + -15%) calc(-150px + 300%)]
+PASS Web Animations: property <--length-percentage-list> from [unset] to [calc(100px + 120%) calc(100px + 120%)] at (-0.3) is [calc(-30px + 16%) calc(490px + -36%)]
+PASS Web Animations: property <--length-percentage-list> from [unset] to [calc(100px + 120%) calc(100px + 120%)] at (0) is [40% 400px]
+PASS Web Animations: property <--length-percentage-list> from [unset] to [calc(100px + 120%) calc(100px + 120%)] at (0.5) is [calc(50px + 80%) calc(250px + 60%)]
+PASS Web Animations: property <--length-percentage-list> from [unset] to [calc(100px + 120%) calc(100px + 120%)] at (1) is [calc(100px + 120%) calc(100px + 120%)]
+PASS Web Animations: property <--length-percentage-list> from [unset] to [calc(100px + 120%) calc(100px + 120%)] at (1.5) is [calc(150px + 160%) calc(-50px + 180%)]
+PASS Web Animations: property <--length-percentage-list> from [calc(-10px - 10%) -100px] to [calc(10px + 10%) 100px] at (-0.3) is [calc(-16px + -16%) -160px]
+PASS Web Animations: property <--length-percentage-list> from [calc(-10px - 10%) -100px] to [calc(10px + 10%) 100px] at (0) is [calc(-10px + -10%) -100px]
+PASS Web Animations: property <--length-percentage-list> from [calc(-10px - 10%) -100px] to [calc(10px + 10%) 100px] at (0.5) is [0% 0px]
+PASS Web Animations: property <--length-percentage-list> from [calc(-10px - 10%) -100px] to [calc(10px + 10%) 100px] at (1) is [calc(10px + 10%) 100px]
+PASS Web Animations: property <--length-percentage-list> from [calc(-10px - 10%) -100px] to [calc(10px + 10%) 100px] at (1.5) is [calc(20px + 20%) 200px]
+PASS Web Animations: property <--length-percentage-list> from [calc(10em + 10%) 100em] to [20em calc(200em + 200%)] at (-0.3) is [calc(140px + 13%) calc(1400px + -60%)]
+PASS Web Animations: property <--length-percentage-list> from [calc(10em + 10%) 100em] to [20em calc(200em + 200%)] at (0) is [calc(200px + 10%) 2000px]
+PASS Web Animations: property <--length-percentage-list> from [calc(10em + 10%) 100em] to [20em calc(200em + 200%)] at (0.5) is [calc(300px + 5%) calc(3000px + 100%)]
+PASS Web Animations: property <--length-percentage-list> from [calc(10em + 10%) 100em] to [20em calc(200em + 200%)] at (1) is [400px calc(4000px + 200%)]
+PASS Web Animations: property <--length-percentage-list> from [calc(10em + 10%) 100em] to [20em calc(200em + 200%)] at (1.5) is [calc(500px + -5%) calc(5000px + 300%)]
+PASS Web Animations: property <--length-percentage-list> from [0px] to [calc(100px + 100%)] at (-0.3) is [calc(-30px + -30%)]
+PASS Web Animations: property <--length-percentage-list> from [0px] to [calc(100px + 100%)] at (0) is [0px]
+PASS Web Animations: property <--length-percentage-list> from [0px] to [calc(100px + 100%)] at (0.5) is [calc(50px + 50%)]
+PASS Web Animations: property <--length-percentage-list> from [0px] to [calc(100px + 100%)] at (1) is [calc(100px + 100%)]
+PASS Web Animations: property <--length-percentage-list> from [0px] to [calc(100px + 100%)] at (1.5) is [calc(150px + 150%)]
+FAIL Web Animations: property <--length-percentage-list> from neutral to [20% 200px] at (-0.3) is [-6% -60px] assert_equals: expected "7 % 70px " but got "- 6 % - 60px "
+FAIL Web Animations: property <--length-percentage-list> from neutral to [20% 200px] at (0) is [0px 0px] assert_equals: expected "10 % 100px " but got "0px 0px "
+FAIL Web Animations: property <--length-percentage-list> from neutral to [20% 200px] at (0.5) is [10% 100px] assert_equals: expected "15 % 150px " but got "10 % 100px "
+PASS Web Animations: property <--length-percentage-list> from neutral to [20% 200px] at (1) is [20% 200px]
+FAIL Web Animations: property <--length-percentage-list> from neutral to [20% 200px] at (1.5) is [30% 300px] assert_equals: expected "25 % 250px " but got "30 % 300px "
+FAIL Compositing: property <--length-percentage-list> underlying [50px 60%] from add [10% 140px] to add [110% 40px] at (-0.3) is [-20% 170px] assert_equals: expected "calc ( 50px + - 20 % ) calc ( 170px + 60 % ) " but got "- 20 % 170px "
+FAIL Compositing: property <--length-percentage-list> underlying [50px 60%] from add [10% 140px] to add [110% 40px] at (0) is [10% 140px] assert_equals: expected "calc ( 50px + 10 % ) calc ( 140px + 60 % ) " but got "10 % 140px "
+FAIL Compositing: property <--length-percentage-list> underlying [50px 60%] from add [10% 140px] to add [110% 40px] at (0.5) is [60% 90px] assert_equals: expected "calc ( 50px + 60 % ) calc ( 90px + 60 % ) " but got "60 % 90px "
+FAIL Compositing: property <--length-percentage-list> underlying [50px 60%] from add [10% 140px] to add [110% 40px] at (1) is [110% 40px] assert_equals: expected "calc ( 50px + 110 % ) calc ( 40px + 60 % ) " but got "110 % 40px "
+FAIL Compositing: property <--length-percentage-list> underlying [50px 60%] from add [10% 140px] to add [110% 40px] at (1.5) is [160% -10px] assert_equals: expected "calc ( 50px + 160 % ) calc ( - 10px + 60 % ) " but got "160 % - 10px "
+FAIL Compositing: property <--length-percentage-list> underlying [50px 60%] from add [10% 140px] to replace [110% 40px] at (-0.3) is [-20% 170px] assert_equals: expected "calc ( 65px + - 20 % ) calc ( 170px + 78 % ) " but got "- 20 % 170px "
+FAIL Compositing: property <--length-percentage-list> underlying [50px 60%] from add [10% 140px] to replace [110% 40px] at (0) is [10% 140px] assert_equals: expected "calc ( 50px + 10 % ) calc ( 140px + 60 % ) " but got "10 % 140px "
+FAIL Compositing: property <--length-percentage-list> underlying [50px 60%] from add [10% 140px] to replace [110% 40px] at (0.5) is [60% 90px] assert_equals: expected "calc ( 25px + 60 % ) calc ( 90px + 30 % ) " but got "60 % 90px "
+PASS Compositing: property <--length-percentage-list> underlying [50px 60%] from add [10% 140px] to replace [110% 40px] at (1) is [110% 40px]
+FAIL Compositing: property <--length-percentage-list> underlying [50px 60%] from add [10% 140px] to replace [110% 40px] at (1.5) is [160% -10px] assert_equals: expected "calc ( - 25px + 160 % ) calc ( - 10px + - 30 % ) " but got "160 % - 10px "
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/animations/custom-properties/custom-length-percentage-list-type-interpolation.html b/third_party/WebKit/LayoutTests/animations/custom-properties/custom-length-percentage-list-type-interpolation.html
new file mode 100644
index 0000000..cc0332d
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/animations/custom-properties/custom-length-percentage-list-type-interpolation.html
@@ -0,0 +1,137 @@
+<!DOCTYPE html>
+<meta charset="UTF-8">
+<style>
+.parent {
+  --length-percentage-list: 30% 300px;
+}
+.target {
+  --length-percentage-list: 10% 100px;
+  font-size: 20px;
+}
+</style>
+<body>
+<script src="../interpolation/resources/interpolation-test.js"></script>
+<script>
+CSS.registerProperty({
+  name: '--length-percentage-list',
+  syntax: '<length-percentage>+',
+  initialValue: '40% 400px',
+  inherits: false,
+});
+
+assertInterpolation({
+  property: '--length-percentage-list',
+  from: 'initial',
+  to: '20% 200%',
+}, [
+  {at: -0.3, is: '46% calc(520px - 60%)'},
+  {at: 0, is: '40% 400px'},
+  {at: 0.5, is: '30% calc(200px + 100%)'},
+  {at: 1, is: '20% 200%'},
+  {at: 1.5, is: '10% calc(-200px + 300%)'},
+]);
+
+assertInterpolation({
+  property: '--length-percentage-list',
+  from: 'inherit',
+  to: '20px 200%',
+}, [
+  {at: -0.3, is: 'calc(-6px + 39%) calc(390px - 60%)'},
+  {at: 0, is: '30% 300px'},
+  {at: 0.5, is: 'calc(10px + 15%) calc(150px + 100%)'},
+  {at: 1, is: '20px 200%'},
+  {at: 1.5, is: 'calc(30px - 15%) calc(-150px + 300%)'},
+]);
+
+assertInterpolation({
+  property: '--length-percentage-list',
+  from: 'unset',
+  to: 'calc(100px + 120%) calc(100px + 120%)',
+}, [
+  {at: -0.3, is: 'calc(-30px + 16%) calc(490px - 36%)'},
+  {at: 0, is: '40% 400px'},
+  {at: 0.5, is: 'calc(50px + 80%) calc(250px + 60%)'},
+  {at: 1, is: 'calc(100px + 120%) calc(100px + 120%)'},
+  {at: 1.5, is: 'calc(150px + 160%) calc(-50px + 180%)'},
+]);
+
+assertInterpolation({
+  property: '--length-percentage-list',
+  from: 'calc(-10px - 10%) -100px',
+  to: 'calc(10px + 10%) 100px',
+}, [
+  {at: -0.3, is: 'calc(-16px - 16%) -160px'},
+  {at: 0, is: 'calc(-10px - 10%) -100px'},
+  {at: 0.5, is: '0% 0px'},
+  {at: 1, is: 'calc(10px + 10%) 100px'},
+  {at: 1.5, is: 'calc(20px + 20%) 200px'}
+]);
+
+assertInterpolation({
+  property: '--length-percentage-list',
+  from: 'calc(10em + 10%) 100em',
+  to: '20em calc(200em + 200%)',
+}, [
+  {at: -0.3, is: 'calc(140px + 13%) calc(1400px - 60%)'},
+  {at: 0, is: 'calc(200px + 10%) 2000px'},
+  {at: 0.5, is: 'calc(300px + 5%) calc(3000px + 100%)'},
+  {at: 1, is: '400px calc(4000px + 200%)'},
+  {at: 1.5, is: 'calc(500px - 5%) calc(5000px + 300%)'}
+]);
+
+assertInterpolation({
+  property: '--length-percentage-list',
+  from: '0px',
+  to: 'calc(100px + 100%)',
+}, [
+  {at: -0.3, is: 'calc(-30px - 30%)'},
+  {at: 0, is: '0px'},
+  {at: 0.5, is: 'calc(50px + 50%)'},
+  {at: 1, is: 'calc(100px + 100%)'},
+  {at: 1.5, is: 'calc(150px + 150%)'}
+]);
+
+// Composition and neutralKeyframe tests assume that composite:add means
+// component-wise addition, which may or may not be the behavior we want.
+// https://github.com/w3c/css-houdini-drafts/issues/799
+
+assertInterpolation({
+  property: '--length-percentage-list',
+  from: neutralKeyframe,
+  to: '20% 200px',
+}, [
+  {at: -0.3, is: '7% 70px'},
+  {at: 0, is: '10% 100px'},
+  {at: 0.5, is: '15% 150px'},
+  {at: 1, is: '20% 200px'},
+  {at: 1.5, is: '25% 250px'},
+]);
+
+assertComposition({
+  property: '--length-percentage-list',
+  underlying: '50px 60%',
+  addFrom: '10% 140px',
+  addTo: '110% 40px',
+}, [
+  {at: -0.3, is: 'calc(50px - 20%) calc(170px + 60%)'},
+  {at: 0, is: 'calc(50px + 10%) calc(140px + 60%)'},
+  {at: 0.5, is: 'calc(50px + 60%) calc(90px + 60%)'},
+  {at: 1, is: 'calc(50px + 110%) calc(40px + 60%)'},
+  {at: 1.5, is: 'calc(50px + 160%) calc(-10px + 60%)'},
+]);
+
+assertComposition({
+  property: '--length-percentage-list',
+  underlying: '50px 60%',
+  addFrom: '10% 140px',
+  replaceTo: '110% 40px',
+}, [
+  {at: -0.3, is: 'calc(65px - 20%) calc(170px + 78%)'},
+  {at: 0, is: 'calc(50px + 10%) calc(140px + 60%)'},
+  {at: 0.5, is: 'calc(25px + 60%) calc(90px + 30%)'},
+  {at: 1, is: '110% 40px'},
+  {at: 1.5, is: 'calc(-25px + 160%) calc(-10px - 30%)'},
+]);
+
+</script>
+</body>
diff --git a/third_party/WebKit/LayoutTests/animations/custom-properties/custom-list-type-interpolation-expected.txt b/third_party/WebKit/LayoutTests/animations/custom-properties/custom-list-type-interpolation-expected.txt
new file mode 100644
index 0000000..8599f64
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/animations/custom-properties/custom-list-type-interpolation-expected.txt
@@ -0,0 +1,160 @@
+This is a testharness.js-based test.
+PASS This test uses interpolation-test.js.
+PASS CSS Transitions: property <--length-list-comma> from [0px, 0px] to [10px, 100px] at (-0.3) is [-3px, -30px]
+PASS CSS Transitions: property <--length-list-comma> from [0px, 0px] to [10px, 100px] at (0) is [0px, 0px]
+PASS CSS Transitions: property <--length-list-comma> from [0px, 0px] to [10px, 100px] at (0.5) is [5px, 50px]
+PASS CSS Transitions: property <--length-list-comma> from [0px, 0px] to [10px, 100px] at (1) is [10px, 100px]
+PASS CSS Transitions: property <--length-list-comma> from [0px, 0px] to [10px, 100px] at (1.5) is [15px, 150px]
+PASS CSS Transitions: property <--length-list-space> from [0px 0px] to [10px 100px] at (-0.3) is [-3px -30px]
+PASS CSS Transitions: property <--length-list-space> from [0px 0px] to [10px 100px] at (0) is [0px 0px]
+PASS CSS Transitions: property <--length-list-space> from [0px 0px] to [10px 100px] at (0.5) is [5px 50px]
+PASS CSS Transitions: property <--length-list-space> from [0px 0px] to [10px 100px] at (1) is [10px 100px]
+PASS CSS Transitions: property <--length-list-space> from [0px 0px] to [10px 100px] at (1.5) is [15px 150px]
+PASS CSS Transitions: property <--length-list-comma> from [0px] to [10px, 100px] at (-0.3) is [10px, 100px]
+PASS CSS Transitions: property <--length-list-comma> from [0px] to [10px, 100px] at (0) is [10px, 100px]
+PASS CSS Transitions: property <--length-list-comma> from [0px] to [10px, 100px] at (0.3) is [10px, 100px]
+PASS CSS Transitions: property <--length-list-comma> from [0px] to [10px, 100px] at (0.5) is [10px, 100px]
+PASS CSS Transitions: property <--length-list-comma> from [0px] to [10px, 100px] at (0.6) is [10px, 100px]
+PASS CSS Transitions: property <--length-list-comma> from [0px] to [10px, 100px] at (1) is [10px, 100px]
+PASS CSS Transitions: property <--length-list-comma> from [0px] to [10px, 100px] at (1.5) is [10px, 100px]
+PASS CSS Transitions: property <--length-list-comma> from [initial] to [10px] at (-0.3) is [10px]
+PASS CSS Transitions: property <--length-list-comma> from [initial] to [10px] at (0) is [10px]
+PASS CSS Transitions: property <--length-list-comma> from [initial] to [10px] at (0.3) is [10px]
+PASS CSS Transitions: property <--length-list-comma> from [initial] to [10px] at (0.5) is [10px]
+PASS CSS Transitions: property <--length-list-comma> from [initial] to [10px] at (0.6) is [10px]
+PASS CSS Transitions: property <--length-list-comma> from [initial] to [10px] at (1) is [10px]
+PASS CSS Transitions: property <--length-list-comma> from [initial] to [10px] at (1.5) is [10px]
+PASS CSS Transitions: property <--length-list-comma> from [unset] to [10px] at (-0.3) is [10px]
+PASS CSS Transitions: property <--length-list-comma> from [unset] to [10px] at (0) is [10px]
+PASS CSS Transitions: property <--length-list-comma> from [unset] to [10px] at (0.3) is [10px]
+PASS CSS Transitions: property <--length-list-comma> from [unset] to [10px] at (0.5) is [10px]
+PASS CSS Transitions: property <--length-list-comma> from [unset] to [10px] at (0.6) is [10px]
+PASS CSS Transitions: property <--length-list-comma> from [unset] to [10px] at (1) is [10px]
+PASS CSS Transitions: property <--length-list-comma> from [unset] to [10px] at (1.5) is [10px]
+PASS CSS Transitions: property <--length-list-comma> from [inherit] to [10px] at (-0.3) is [10px]
+PASS CSS Transitions: property <--length-list-comma> from [inherit] to [10px] at (0) is [10px]
+PASS CSS Transitions: property <--length-list-comma> from [inherit] to [10px] at (0.3) is [10px]
+PASS CSS Transitions: property <--length-list-comma> from [inherit] to [10px] at (0.5) is [10px]
+PASS CSS Transitions: property <--length-list-comma> from [inherit] to [10px] at (0.6) is [10px]
+PASS CSS Transitions: property <--length-list-comma> from [inherit] to [10px] at (1) is [10px]
+PASS CSS Transitions: property <--length-list-comma> from [inherit] to [10px] at (1.5) is [10px]
+PASS CSS Transitions: property <--length-list-comma> from neutral to [10px, 100px, 1000px] at (-0.3) is [10px, 100px, 1000px]
+PASS CSS Transitions: property <--length-list-comma> from neutral to [10px, 100px, 1000px] at (0) is [10px, 100px, 1000px]
+PASS CSS Transitions: property <--length-list-comma> from neutral to [10px, 100px, 1000px] at (0.3) is [10px, 100px, 1000px]
+PASS CSS Transitions: property <--length-list-comma> from neutral to [10px, 100px, 1000px] at (0.5) is [10px, 100px, 1000px]
+PASS CSS Transitions: property <--length-list-comma> from neutral to [10px, 100px, 1000px] at (0.6) is [10px, 100px, 1000px]
+PASS CSS Transitions: property <--length-list-comma> from neutral to [10px, 100px, 1000px] at (1) is [10px, 100px, 1000px]
+PASS CSS Transitions: property <--length-list-comma> from neutral to [10px, 100px, 1000px] at (1.5) is [10px, 100px, 1000px]
+PASS CSS Animations: property <--length-list-comma> from [0px, 0px] to [10px, 100px] at (-0.3) is [-3px, -30px]
+PASS CSS Animations: property <--length-list-comma> from [0px, 0px] to [10px, 100px] at (0) is [0px, 0px]
+PASS CSS Animations: property <--length-list-comma> from [0px, 0px] to [10px, 100px] at (0.5) is [5px, 50px]
+PASS CSS Animations: property <--length-list-comma> from [0px, 0px] to [10px, 100px] at (1) is [10px, 100px]
+PASS CSS Animations: property <--length-list-comma> from [0px, 0px] to [10px, 100px] at (1.5) is [15px, 150px]
+PASS CSS Animations: property <--length-list-space> from [0px 0px] to [10px 100px] at (-0.3) is [-3px -30px]
+PASS CSS Animations: property <--length-list-space> from [0px 0px] to [10px 100px] at (0) is [0px 0px]
+PASS CSS Animations: property <--length-list-space> from [0px 0px] to [10px 100px] at (0.5) is [5px 50px]
+PASS CSS Animations: property <--length-list-space> from [0px 0px] to [10px 100px] at (1) is [10px 100px]
+PASS CSS Animations: property <--length-list-space> from [0px 0px] to [10px 100px] at (1.5) is [15px 150px]
+PASS CSS Animations: property <--length-list-comma> from [0px] to [10px, 100px] at (-0.3) is [0px]
+PASS CSS Animations: property <--length-list-comma> from [0px] to [10px, 100px] at (0) is [0px]
+PASS CSS Animations: property <--length-list-comma> from [0px] to [10px, 100px] at (0.3) is [0px]
+PASS CSS Animations: property <--length-list-comma> from [0px] to [10px, 100px] at (0.5) is [10px, 100px]
+PASS CSS Animations: property <--length-list-comma> from [0px] to [10px, 100px] at (0.6) is [10px, 100px]
+PASS CSS Animations: property <--length-list-comma> from [0px] to [10px, 100px] at (1) is [10px, 100px]
+PASS CSS Animations: property <--length-list-comma> from [0px] to [10px, 100px] at (1.5) is [10px, 100px]
+PASS CSS Animations: property <--length-list-comma> from [initial] to [10px] at (-0.3) is [40px, 20px]
+PASS CSS Animations: property <--length-list-comma> from [initial] to [10px] at (0) is [40px, 20px]
+PASS CSS Animations: property <--length-list-comma> from [initial] to [10px] at (0.3) is [40px, 20px]
+PASS CSS Animations: property <--length-list-comma> from [initial] to [10px] at (0.5) is [10px]
+PASS CSS Animations: property <--length-list-comma> from [initial] to [10px] at (0.6) is [10px]
+PASS CSS Animations: property <--length-list-comma> from [initial] to [10px] at (1) is [10px]
+PASS CSS Animations: property <--length-list-comma> from [initial] to [10px] at (1.5) is [10px]
+PASS CSS Animations: property <--length-list-comma> from [unset] to [10px] at (-0.3) is [40px, 20px]
+PASS CSS Animations: property <--length-list-comma> from [unset] to [10px] at (0) is [40px, 20px]
+PASS CSS Animations: property <--length-list-comma> from [unset] to [10px] at (0.3) is [40px, 20px]
+PASS CSS Animations: property <--length-list-comma> from [unset] to [10px] at (0.5) is [10px]
+PASS CSS Animations: property <--length-list-comma> from [unset] to [10px] at (0.6) is [10px]
+PASS CSS Animations: property <--length-list-comma> from [unset] to [10px] at (1) is [10px]
+PASS CSS Animations: property <--length-list-comma> from [unset] to [10px] at (1.5) is [10px]
+PASS CSS Animations: property <--length-list-comma> from [inherit] to [10px] at (-0.3) is [30px, 300px]
+PASS CSS Animations: property <--length-list-comma> from [inherit] to [10px] at (0) is [30px, 300px]
+PASS CSS Animations: property <--length-list-comma> from [inherit] to [10px] at (0.3) is [30px, 300px]
+PASS CSS Animations: property <--length-list-comma> from [inherit] to [10px] at (0.5) is [10px]
+PASS CSS Animations: property <--length-list-comma> from [inherit] to [10px] at (0.6) is [10px]
+PASS CSS Animations: property <--length-list-comma> from [inherit] to [10px] at (1) is [10px]
+PASS CSS Animations: property <--length-list-comma> from [inherit] to [10px] at (1.5) is [10px]
+FAIL CSS Animations: property <--length-list-comma> from neutral to [10px, 100px, 1000px] at (-0.3) is [0px, 0px] assert_equals: expected "40px , 400px " but got "0px , 0px "
+FAIL CSS Animations: property <--length-list-comma> from neutral to [10px, 100px, 1000px] at (0) is [0px, 0px] assert_equals: expected "40px , 400px " but got "0px , 0px "
+FAIL CSS Animations: property <--length-list-comma> from neutral to [10px, 100px, 1000px] at (0.3) is [0px, 0px] assert_equals: expected "40px , 400px " but got "0px , 0px "
+PASS CSS Animations: property <--length-list-comma> from neutral to [10px, 100px, 1000px] at (0.5) is [10px, 100px, 1000px]
+PASS CSS Animations: property <--length-list-comma> from neutral to [10px, 100px, 1000px] at (0.6) is [10px, 100px, 1000px]
+PASS CSS Animations: property <--length-list-comma> from neutral to [10px, 100px, 1000px] at (1) is [10px, 100px, 1000px]
+PASS CSS Animations: property <--length-list-comma> from neutral to [10px, 100px, 1000px] at (1.5) is [10px, 100px, 1000px]
+PASS Web Animations: property <--length-list-comma> from [0px, 0px] to [10px, 100px] at (-0.3) is [-3px, -30px]
+PASS Web Animations: property <--length-list-comma> from [0px, 0px] to [10px, 100px] at (0) is [0px, 0px]
+PASS Web Animations: property <--length-list-comma> from [0px, 0px] to [10px, 100px] at (0.5) is [5px, 50px]
+PASS Web Animations: property <--length-list-comma> from [0px, 0px] to [10px, 100px] at (1) is [10px, 100px]
+PASS Web Animations: property <--length-list-comma> from [0px, 0px] to [10px, 100px] at (1.5) is [15px, 150px]
+PASS Web Animations: property <--length-list-space> from [0px 0px] to [10px 100px] at (-0.3) is [-3px -30px]
+PASS Web Animations: property <--length-list-space> from [0px 0px] to [10px 100px] at (0) is [0px 0px]
+PASS Web Animations: property <--length-list-space> from [0px 0px] to [10px 100px] at (0.5) is [5px 50px]
+PASS Web Animations: property <--length-list-space> from [0px 0px] to [10px 100px] at (1) is [10px 100px]
+PASS Web Animations: property <--length-list-space> from [0px 0px] to [10px 100px] at (1.5) is [15px 150px]
+PASS Web Animations: property <--length-list-comma> from [0px] to [10px, 100px] at (-0.3) is [0px]
+PASS Web Animations: property <--length-list-comma> from [0px] to [10px, 100px] at (0) is [0px]
+PASS Web Animations: property <--length-list-comma> from [0px] to [10px, 100px] at (0.3) is [0px]
+PASS Web Animations: property <--length-list-comma> from [0px] to [10px, 100px] at (0.5) is [10px, 100px]
+PASS Web Animations: property <--length-list-comma> from [0px] to [10px, 100px] at (0.6) is [10px, 100px]
+PASS Web Animations: property <--length-list-comma> from [0px] to [10px, 100px] at (1) is [10px, 100px]
+PASS Web Animations: property <--length-list-comma> from [0px] to [10px, 100px] at (1.5) is [10px, 100px]
+PASS Web Animations: property <--length-list-comma> from [initial] to [10px] at (-0.3) is [40px, 20px]
+PASS Web Animations: property <--length-list-comma> from [initial] to [10px] at (0) is [40px, 20px]
+PASS Web Animations: property <--length-list-comma> from [initial] to [10px] at (0.3) is [40px, 20px]
+PASS Web Animations: property <--length-list-comma> from [initial] to [10px] at (0.5) is [10px]
+PASS Web Animations: property <--length-list-comma> from [initial] to [10px] at (0.6) is [10px]
+PASS Web Animations: property <--length-list-comma> from [initial] to [10px] at (1) is [10px]
+PASS Web Animations: property <--length-list-comma> from [initial] to [10px] at (1.5) is [10px]
+PASS Web Animations: property <--length-list-comma> from [unset] to [10px] at (-0.3) is [40px, 20px]
+PASS Web Animations: property <--length-list-comma> from [unset] to [10px] at (0) is [40px, 20px]
+PASS Web Animations: property <--length-list-comma> from [unset] to [10px] at (0.3) is [40px, 20px]
+PASS Web Animations: property <--length-list-comma> from [unset] to [10px] at (0.5) is [10px]
+PASS Web Animations: property <--length-list-comma> from [unset] to [10px] at (0.6) is [10px]
+PASS Web Animations: property <--length-list-comma> from [unset] to [10px] at (1) is [10px]
+PASS Web Animations: property <--length-list-comma> from [unset] to [10px] at (1.5) is [10px]
+PASS Web Animations: property <--length-list-comma> from [inherit] to [10px] at (-0.3) is [30px, 300px]
+PASS Web Animations: property <--length-list-comma> from [inherit] to [10px] at (0) is [30px, 300px]
+PASS Web Animations: property <--length-list-comma> from [inherit] to [10px] at (0.3) is [30px, 300px]
+PASS Web Animations: property <--length-list-comma> from [inherit] to [10px] at (0.5) is [10px]
+PASS Web Animations: property <--length-list-comma> from [inherit] to [10px] at (0.6) is [10px]
+PASS Web Animations: property <--length-list-comma> from [inherit] to [10px] at (1) is [10px]
+PASS Web Animations: property <--length-list-comma> from [inherit] to [10px] at (1.5) is [10px]
+FAIL Web Animations: property <--length-list-comma> from neutral to [10px, 100px, 1000px] at (-0.3) is [0px, 0px] assert_equals: expected "40px , 400px " but got "0px , 0px "
+FAIL Web Animations: property <--length-list-comma> from neutral to [10px, 100px, 1000px] at (0) is [0px, 0px] assert_equals: expected "40px , 400px " but got "0px , 0px "
+FAIL Web Animations: property <--length-list-comma> from neutral to [10px, 100px, 1000px] at (0.3) is [0px, 0px] assert_equals: expected "40px , 400px " but got "0px , 0px "
+PASS Web Animations: property <--length-list-comma> from neutral to [10px, 100px, 1000px] at (0.5) is [10px, 100px, 1000px]
+PASS Web Animations: property <--length-list-comma> from neutral to [10px, 100px, 1000px] at (0.6) is [10px, 100px, 1000px]
+PASS Web Animations: property <--length-list-comma> from neutral to [10px, 100px, 1000px] at (1) is [10px, 100px, 1000px]
+PASS Web Animations: property <--length-list-comma> from neutral to [10px, 100px, 1000px] at (1.5) is [10px, 100px, 1000px]
+PASS Compositing: property <--length-list-space> underlying [50px] from add [10px 10px] to add [100px 100px] at (-0.3) is [-17px -17px]
+PASS Compositing: property <--length-list-space> underlying [50px] from add [10px 10px] to add [100px 100px] at (0) is [10px 10px]
+PASS Compositing: property <--length-list-space> underlying [50px] from add [10px 10px] to add [100px 100px] at (0.3) is [37px 37px]
+PASS Compositing: property <--length-list-space> underlying [50px] from add [10px 10px] to add [100px 100px] at (0.5) is [55px 55px]
+PASS Compositing: property <--length-list-space> underlying [50px] from add [10px 10px] to add [100px 100px] at (0.7) is [73px 73px]
+PASS Compositing: property <--length-list-space> underlying [50px] from add [10px 10px] to add [100px 100px] at (1) is [100px 100px]
+PASS Compositing: property <--length-list-space> underlying [50px] from add [10px 10px] to add [100px 100px] at (1.5) is [145px 145px]
+FAIL Compositing: property <--length-list-space> underlying [50px 50px] from add [10px 10px] to add [100px] at (-0.3) is [10px 10px] assert_equals: expected "60px 60px " but got "10px 10px "
+FAIL Compositing: property <--length-list-space> underlying [50px 50px] from add [10px 10px] to add [100px] at (0) is [10px 10px] assert_equals: expected "60px 60px " but got "10px 10px "
+FAIL Compositing: property <--length-list-space> underlying [50px 50px] from add [10px 10px] to add [100px] at (0.3) is [10px 10px] assert_equals: expected "60px 60px " but got "10px 10px "
+PASS Compositing: property <--length-list-space> underlying [50px 50px] from add [10px 10px] to add [100px] at (0.5) is [100px]
+PASS Compositing: property <--length-list-space> underlying [50px 50px] from add [10px 10px] to add [100px] at (0.7) is [100px]
+PASS Compositing: property <--length-list-space> underlying [50px 50px] from add [10px 10px] to add [100px] at (1) is [100px]
+PASS Compositing: property <--length-list-space> underlying [50px 50px] from add [10px 10px] to add [100px] at (1.5) is [100px]
+FAIL Compositing: property <--length-list-space> underlying [50px 50px] from add [10px 10px] to replace [100px] at (-0.3) is [10px 10px] assert_equals: expected "60px 60px " but got "10px 10px "
+FAIL Compositing: property <--length-list-space> underlying [50px 50px] from add [10px 10px] to replace [100px] at (0) is [10px 10px] assert_equals: expected "60px 60px " but got "10px 10px "
+FAIL Compositing: property <--length-list-space> underlying [50px 50px] from add [10px 10px] to replace [100px] at (0.3) is [10px 10px] assert_equals: expected "60px 60px " but got "10px 10px "
+PASS Compositing: property <--length-list-space> underlying [50px 50px] from add [10px 10px] to replace [100px] at (0.5) is [100px]
+PASS Compositing: property <--length-list-space> underlying [50px 50px] from add [10px 10px] to replace [100px] at (0.7) is [100px]
+PASS Compositing: property <--length-list-space> underlying [50px 50px] from add [10px 10px] to replace [100px] at (1) is [100px]
+PASS Compositing: property <--length-list-space> underlying [50px 50px] from add [10px 10px] to replace [100px] at (1.5) is [100px]
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/animations/custom-properties/custom-list-type-interpolation.html b/third_party/WebKit/LayoutTests/animations/custom-properties/custom-list-type-interpolation.html
new file mode 100644
index 0000000..0aed2bca
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/animations/custom-properties/custom-list-type-interpolation.html
@@ -0,0 +1,152 @@
+<!DOCTYPE html>
+<meta charset="UTF-8">
+<style>
+.parent {
+  --length-list-comma: 30px, 300px;
+}
+.target {
+  --length-list-comma: 40px, 400px;
+}
+</style>
+<body>
+<script src="../interpolation/resources/interpolation-test.js"></script>
+<script>
+
+// This file verifies correct interpolation behavior for the "list aspect" of
+// custom property lists. The type of the values contained in the list are
+// assumed to not matter, hence <length> was chosen here arbitrarily.
+
+CSS.registerProperty({
+  name: '--length-list-comma',
+  syntax: '<length>#',
+  initialValue: '40px, 20px',
+  inherits: false
+});
+
+CSS.registerProperty({
+  name: '--length-list-space',
+  syntax: '<length>+',
+  initialValue: '40px 20px',
+  inherits: false
+});
+
+// Verify that a property registered as a comma-separated list produces
+// comma-separated values.
+
+assertInterpolation({
+  property: '--length-list-comma',
+  from: '0px, 0px',
+  to: '10px, 100px',
+}, [
+  {at: -0.3, is: '-3px, -30px'},
+  {at: 0, is: '0px, 0px'},
+  {at: 0.5, is: '5px, 50px'},
+  {at: 1, is: '10px, 100px'},
+  {at: 1.5, is: '15px, 150px'}
+]);
+
+// Verify that a property registered as a space-separated list produces
+// space-separated values.
+
+assertInterpolation({
+  property: '--length-list-space',
+  from: '0px 0px',
+  to: '10px 100px',
+}, [
+  {at: -0.3, is: '-3px -30px'},
+  {at: 0, is: '0px 0px'},
+  {at: 0.5, is: '5px 50px'},
+  {at: 1, is: '10px 100px'},
+  {at: 1.5, is: '15px 150px'}
+]);
+
+// Verify that lists of different lengths don't interpolate:
+
+assertNoInterpolation({
+  property: '--length-list-comma',
+  from: '0px',
+  to: '10px, 100px'
+});
+
+assertNoInterpolation({
+  property: '--length-list-comma',
+  from: 'initial',
+  to: '10px'
+});
+
+assertNoInterpolation({
+  property: '--length-list-comma',
+  from: 'unset',
+  to: '10px'
+});
+
+assertNoInterpolation({
+  property: '--length-list-comma',
+  from: 'inherit',
+  to: '10px'
+});
+
+// Composition tests assume that composite:add mean component-wise addition,
+// which may or may not be the behavior we want.
+// https://github.com/w3c/css-houdini-drafts/issues/799
+
+// Verify that lists of different lengths don't interpolate:
+
+assertNoInterpolation({
+  property: '--length-list-comma',
+  from: neutralKeyframe,
+  to: '10px, 100px, 1000px'
+});
+
+// Verify that attempting to composite:add to an incompatible value
+// replaces that value instead.
+
+assertComposition({
+  property: '--length-list-space',
+  underlying: '50px',
+  addFrom: '10px 10px',
+  addTo: '100px 100px',
+}, [
+  {at: -0.3, is: '-17px -17px'},
+  {at: 0, is: '10px 10px'},
+  {at: 0.3, is: '37px 37px'},
+  {at: 0.5, is: '55px 55px'},
+  {at: 0.7, is: '73px 73px'},
+  {at: 1, is: '100px 100px'},
+  {at: 1.5, is: '145px 145px'},
+]);
+
+// No interpolation between two incompatible results of a composite:
+
+assertComposition({
+  property: '--length-list-space',
+  underlying: '50px 50px',
+  addFrom: '10px 10px',
+  addTo: '100px',
+}, [
+  {at: -0.3, is: '60px 60px'},
+  {at: 0, is: '60px 60px'},
+  {at: 0.3, is: '60px 60px'},
+  {at: 0.5, is: '100px'},
+  {at: 0.7, is: '100px'},
+  {at: 1, is: '100px'},
+  {at: 1.5, is: '100px'},
+]);
+
+assertComposition({
+  property: '--length-list-space',
+  underlying: '50px 50px',
+  addFrom: '10px 10px',
+  replaceTo: '100px',
+}, [
+  {at: -0.3, is: '60px 60px'},
+  {at: 0, is: '60px 60px'},
+  {at: 0.3, is: '60px 60px'},
+  {at: 0.5, is: '100px'},
+  {at: 0.7, is: '100px'},
+  {at: 1, is: '100px'},
+  {at: 1.5, is: '100px'},
+]);
+
+</script>
+</body>
diff --git a/third_party/WebKit/LayoutTests/animations/custom-properties/custom-number-list-type-interpolation-expected.txt b/third_party/WebKit/LayoutTests/animations/custom-properties/custom-number-list-type-interpolation-expected.txt
new file mode 100644
index 0000000..fc9fc4a
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/animations/custom-properties/custom-number-list-type-interpolation-expected.txt
@@ -0,0 +1,104 @@
+This is a testharness.js-based test.
+PASS This test uses interpolation-test.js.
+PASS CSS Transitions: property <--number-list> from [initial] to [20 200] at (-0.3) is [46 460]
+PASS CSS Transitions: property <--number-list> from [initial] to [20 200] at (0) is [40 400]
+PASS CSS Transitions: property <--number-list> from [initial] to [20 200] at (0.5) is [30 300]
+PASS CSS Transitions: property <--number-list> from [initial] to [20 200] at (1) is [20 200]
+PASS CSS Transitions: property <--number-list> from [initial] to [20 200] at (1.5) is [10 100]
+PASS CSS Transitions: property <--number-list> from [inherit] to [20 200] at (-0.3) is [33 330]
+PASS CSS Transitions: property <--number-list> from [inherit] to [20 200] at (0) is [30 300]
+PASS CSS Transitions: property <--number-list> from [inherit] to [20 200] at (0.5) is [25 250]
+PASS CSS Transitions: property <--number-list> from [inherit] to [20 200] at (1) is [20 200]
+PASS CSS Transitions: property <--number-list> from [inherit] to [20 200] at (1.5) is [15 150]
+PASS CSS Transitions: property <--number-list> from [unset] to [20 200] at (-0.3) is [46 460]
+PASS CSS Transitions: property <--number-list> from [unset] to [20 200] at (0) is [40 400]
+PASS CSS Transitions: property <--number-list> from [unset] to [20 200] at (0.5) is [30 300]
+PASS CSS Transitions: property <--number-list> from [unset] to [20 200] at (1) is [20 200]
+PASS CSS Transitions: property <--number-list> from [unset] to [20 200] at (1.5) is [10 100]
+PASS CSS Transitions: property <--number-list> from [-10 -100] to [10 100] at (-0.3) is [-16 -160]
+PASS CSS Transitions: property <--number-list> from [-10 -100] to [10 100] at (0) is [-10 -100]
+PASS CSS Transitions: property <--number-list> from [-10 -100] to [10 100] at (0.5) is [0 0]
+PASS CSS Transitions: property <--number-list> from [-10 -100] to [10 100] at (1) is [10 100]
+PASS CSS Transitions: property <--number-list> from [-10 -100] to [10 100] at (1.5) is [20 200]
+PASS CSS Transitions: property <--number-list> from [10] to [100] at (-0.3) is [-17]
+PASS CSS Transitions: property <--number-list> from [10] to [100] at (0) is [10]
+PASS CSS Transitions: property <--number-list> from [10] to [100] at (0.5) is [55]
+PASS CSS Transitions: property <--number-list> from [10] to [100] at (1) is [100]
+PASS CSS Transitions: property <--number-list> from [10] to [100] at (1.5) is [145]
+PASS CSS Transitions: property <--number-list> from neutral to [20 200] at (-0.3) is [7 70]
+PASS CSS Transitions: property <--number-list> from neutral to [20 200] at (0) is [10 100]
+PASS CSS Transitions: property <--number-list> from neutral to [20 200] at (0.5) is [15 150]
+PASS CSS Transitions: property <--number-list> from neutral to [20 200] at (1) is [20 200]
+PASS CSS Transitions: property <--number-list> from neutral to [20 200] at (1.5) is [25 250]
+PASS CSS Animations: property <--number-list> from [initial] to [20 200] at (-0.3) is [46 460]
+PASS CSS Animations: property <--number-list> from [initial] to [20 200] at (0) is [40 400]
+PASS CSS Animations: property <--number-list> from [initial] to [20 200] at (0.5) is [30 300]
+PASS CSS Animations: property <--number-list> from [initial] to [20 200] at (1) is [20 200]
+PASS CSS Animations: property <--number-list> from [initial] to [20 200] at (1.5) is [10 100]
+PASS CSS Animations: property <--number-list> from [inherit] to [20 200] at (-0.3) is [33 330]
+PASS CSS Animations: property <--number-list> from [inherit] to [20 200] at (0) is [30 300]
+PASS CSS Animations: property <--number-list> from [inherit] to [20 200] at (0.5) is [25 250]
+PASS CSS Animations: property <--number-list> from [inherit] to [20 200] at (1) is [20 200]
+PASS CSS Animations: property <--number-list> from [inherit] to [20 200] at (1.5) is [15 150]
+PASS CSS Animations: property <--number-list> from [unset] to [20 200] at (-0.3) is [46 460]
+PASS CSS Animations: property <--number-list> from [unset] to [20 200] at (0) is [40 400]
+PASS CSS Animations: property <--number-list> from [unset] to [20 200] at (0.5) is [30 300]
+PASS CSS Animations: property <--number-list> from [unset] to [20 200] at (1) is [20 200]
+PASS CSS Animations: property <--number-list> from [unset] to [20 200] at (1.5) is [10 100]
+PASS CSS Animations: property <--number-list> from [-10 -100] to [10 100] at (-0.3) is [-16 -160]
+PASS CSS Animations: property <--number-list> from [-10 -100] to [10 100] at (0) is [-10 -100]
+PASS CSS Animations: property <--number-list> from [-10 -100] to [10 100] at (0.5) is [0 0]
+PASS CSS Animations: property <--number-list> from [-10 -100] to [10 100] at (1) is [10 100]
+PASS CSS Animations: property <--number-list> from [-10 -100] to [10 100] at (1.5) is [20 200]
+PASS CSS Animations: property <--number-list> from [10] to [100] at (-0.3) is [-17]
+PASS CSS Animations: property <--number-list> from [10] to [100] at (0) is [10]
+PASS CSS Animations: property <--number-list> from [10] to [100] at (0.5) is [55]
+PASS CSS Animations: property <--number-list> from [10] to [100] at (1) is [100]
+PASS CSS Animations: property <--number-list> from [10] to [100] at (1.5) is [145]
+FAIL CSS Animations: property <--number-list> from neutral to [20 200] at (-0.3) is [-6 -60] assert_equals: expected "7 70 " but got "- 6 - 60 "
+FAIL CSS Animations: property <--number-list> from neutral to [20 200] at (0) is [0 0] assert_equals: expected "10 100 " but got "0 0 "
+FAIL CSS Animations: property <--number-list> from neutral to [20 200] at (0.5) is [10 100] assert_equals: expected "15 150 " but got "10 100 "
+PASS CSS Animations: property <--number-list> from neutral to [20 200] at (1) is [20 200]
+FAIL CSS Animations: property <--number-list> from neutral to [20 200] at (1.5) is [30 300] assert_equals: expected "25 250 " but got "30 300 "
+PASS Web Animations: property <--number-list> from [initial] to [20 200] at (-0.3) is [46 460]
+PASS Web Animations: property <--number-list> from [initial] to [20 200] at (0) is [40 400]
+PASS Web Animations: property <--number-list> from [initial] to [20 200] at (0.5) is [30 300]
+PASS Web Animations: property <--number-list> from [initial] to [20 200] at (1) is [20 200]
+PASS Web Animations: property <--number-list> from [initial] to [20 200] at (1.5) is [10 100]
+PASS Web Animations: property <--number-list> from [inherit] to [20 200] at (-0.3) is [33 330]
+PASS Web Animations: property <--number-list> from [inherit] to [20 200] at (0) is [30 300]
+PASS Web Animations: property <--number-list> from [inherit] to [20 200] at (0.5) is [25 250]
+PASS Web Animations: property <--number-list> from [inherit] to [20 200] at (1) is [20 200]
+PASS Web Animations: property <--number-list> from [inherit] to [20 200] at (1.5) is [15 150]
+PASS Web Animations: property <--number-list> from [unset] to [20 200] at (-0.3) is [46 460]
+PASS Web Animations: property <--number-list> from [unset] to [20 200] at (0) is [40 400]
+PASS Web Animations: property <--number-list> from [unset] to [20 200] at (0.5) is [30 300]
+PASS Web Animations: property <--number-list> from [unset] to [20 200] at (1) is [20 200]
+PASS Web Animations: property <--number-list> from [unset] to [20 200] at (1.5) is [10 100]
+PASS Web Animations: property <--number-list> from [-10 -100] to [10 100] at (-0.3) is [-16 -160]
+PASS Web Animations: property <--number-list> from [-10 -100] to [10 100] at (0) is [-10 -100]
+PASS Web Animations: property <--number-list> from [-10 -100] to [10 100] at (0.5) is [0 0]
+PASS Web Animations: property <--number-list> from [-10 -100] to [10 100] at (1) is [10 100]
+PASS Web Animations: property <--number-list> from [-10 -100] to [10 100] at (1.5) is [20 200]
+PASS Web Animations: property <--number-list> from [10] to [100] at (-0.3) is [-17]
+PASS Web Animations: property <--number-list> from [10] to [100] at (0) is [10]
+PASS Web Animations: property <--number-list> from [10] to [100] at (0.5) is [55]
+PASS Web Animations: property <--number-list> from [10] to [100] at (1) is [100]
+PASS Web Animations: property <--number-list> from [10] to [100] at (1.5) is [145]
+FAIL Web Animations: property <--number-list> from neutral to [20 200] at (-0.3) is [-6 -60] assert_equals: expected "7 70 " but got "- 6 - 60 "
+FAIL Web Animations: property <--number-list> from neutral to [20 200] at (0) is [0 0] assert_equals: expected "10 100 " but got "0 0 "
+FAIL Web Animations: property <--number-list> from neutral to [20 200] at (0.5) is [10 100] assert_equals: expected "15 150 " but got "10 100 "
+PASS Web Animations: property <--number-list> from neutral to [20 200] at (1) is [20 200]
+FAIL Web Animations: property <--number-list> from neutral to [20 200] at (1.5) is [30 300] assert_equals: expected "25 250 " but got "30 300 "
+FAIL Compositing: property <--number-list> underlying [50 60] from add [10 140] to add [110 40] at (-0.3) is [-20 170] assert_equals: expected "30 230 " but got "- 20 170 "
+FAIL Compositing: property <--number-list> underlying [50 60] from add [10 140] to add [110 40] at (0) is [10 140] assert_equals: expected "60 200 " but got "10 140 "
+FAIL Compositing: property <--number-list> underlying [50 60] from add [10 140] to add [110 40] at (0.5) is [60 90] assert_equals: expected "110 150 " but got "60 90 "
+FAIL Compositing: property <--number-list> underlying [50 60] from add [10 140] to add [110 40] at (1) is [110 40] assert_equals: expected "160 100 " but got "110 40 "
+FAIL Compositing: property <--number-list> underlying [50 60] from add [10 140] to add [110 40] at (1.5) is [160 -10] assert_equals: expected "210 50 " but got "160 - 10 "
+FAIL Compositing: property <--number-list> underlying [50 60] from add [10 140] to replace [110 40] at (-0.3) is [-20 170] assert_equals: expected "45 248 " but got "- 20 170 "
+FAIL Compositing: property <--number-list> underlying [50 60] from add [10 140] to replace [110 40] at (0) is [10 140] assert_equals: expected "60 200 " but got "10 140 "
+FAIL Compositing: property <--number-list> underlying [50 60] from add [10 140] to replace [110 40] at (0.5) is [60 90] assert_equals: expected "85 120 " but got "60 90 "
+PASS Compositing: property <--number-list> underlying [50 60] from add [10 140] to replace [110 40] at (1) is [110 40]
+FAIL Compositing: property <--number-list> underlying [50 60] from add [10 140] to replace [110 40] at (1.5) is [160 -10] assert_equals: expected "135 - 40 " but got "160 - 10 "
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/animations/custom-properties/custom-number-list-type-interpolation.html b/third_party/WebKit/LayoutTests/animations/custom-properties/custom-number-list-type-interpolation.html
new file mode 100644
index 0000000..93bc02b
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/animations/custom-properties/custom-number-list-type-interpolation.html
@@ -0,0 +1,124 @@
+<!DOCTYPE html>
+<meta charset="UTF-8">
+<style>
+.parent {
+  --number-list: 30 300;
+}
+.target {
+  --number-list: 10 100;
+}
+</style>
+<body>
+<script src="../interpolation/resources/interpolation-test.js"></script>
+<script>
+CSS.registerProperty({
+  name: '--number-list',
+  syntax: '<number>+',
+  initialValue: '40 400',
+  inherits: false,
+});
+
+assertInterpolation({
+  property: '--number-list',
+  from: 'initial',
+  to: '20 200',
+}, [
+  {at: -0.3, is: '46 460'},
+  {at: 0, is: '40 400'},
+  {at: 0.5, is: '30 300'},
+  {at: 1, is: '20 200'},
+  {at: 1.5, is: '10 100'},
+]);
+
+assertInterpolation({
+  property: '--number-list',
+  from: 'inherit',
+  to: '20 200',
+}, [
+  {at: -0.3, is: '33 330'},
+  {at: 0, is: '30 300'},
+  {at: 0.5, is: '25 250'},
+  {at: 1, is: '20 200'},
+  {at: 1.5, is: '15 150'},
+]);
+
+assertInterpolation({
+  property: '--number-list',
+  from: 'unset',
+  to: '20 200',
+}, [
+  {at: -0.3, is: '46 460'},
+  {at: 0, is: '40 400'},
+  {at: 0.5, is: '30 300'},
+  {at: 1, is: '20 200'},
+  {at: 1.5, is: '10 100'},
+]);
+
+assertInterpolation({
+  property: '--number-list',
+  from: '-10 -100',
+  to: '10 100',
+}, [
+  {at: -0.3, is: '-16 -160'},
+  {at: 0, is: '-10 -100'},
+  {at: 0.5, is: '0 0'},
+  {at: 1, is: '10 100'},
+  {at: 1.5, is: '20 200'}
+]); 
+
+assertInterpolation({
+  property: '--number-list',
+  from: '10',
+  to: '100',
+}, [
+  {at: -0.3, is: '-17'},
+  {at: 0, is: '10'},
+  {at: 0.5, is: '55'},
+  {at: 1, is: '100'},
+  {at: 1.5, is: '145'}
+]);
+
+// Composition and neutralKeyframe tests assume that composite:add means
+// component-wise addition, which may or may not be the behavior we want.
+// https://github.com/w3c/css-houdini-drafts/issues/799
+
+assertInterpolation({
+  property: '--number-list',
+  from: neutralKeyframe,
+  to: '20 200',
+}, [
+  {at: -0.3, is: '7 70'},
+  {at: 0, is: '10 100'},
+  {at: 0.5, is: '15 150'},
+  {at: 1, is: '20 200'},
+  {at: 1.5, is: '25 250'},
+]);
+
+assertComposition({
+  property: '--number-list',
+  underlying: '50 60',
+  addFrom: '10 140',
+  addTo: '110 40',
+}, [
+  {at: -0.3, is: '30 230'},
+  {at: 0, is: '60 200'},
+  {at: 0.5, is: '110 150'},
+  {at: 1, is: '160 100'},
+  {at: 1.5, is: '210 50'},
+]);
+
+assertComposition({
+  property: '--number-list',
+  underlying: '50 60',
+  addFrom: '10 140',
+  replaceTo: '110 40',
+}, [
+  {at: -0.3, is: '45 248'},
+  {at: 0, is: '60 200'},
+  {at: 0.5, is: '85 120'},
+  {at: 1, is: '110 40'},
+  {at: 1.5, is: '135 -40'},
+]);
+
+</script>
+</body>
diff --git a/third_party/WebKit/LayoutTests/animations/custom-properties/custom-percentage-list-type-interpolation-expected.txt b/third_party/WebKit/LayoutTests/animations/custom-properties/custom-percentage-list-type-interpolation-expected.txt
new file mode 100644
index 0000000..461b2741
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/animations/custom-properties/custom-percentage-list-type-interpolation-expected.txt
@@ -0,0 +1,104 @@
+This is a testharness.js-based test.
+PASS This test uses interpolation-test.js.
+PASS CSS Transitions: property <--percentage-list> from [initial] to [20% 200%] at (-0.3) is [46% 460%]
+PASS CSS Transitions: property <--percentage-list> from [initial] to [20% 200%] at (0) is [40% 400%]
+PASS CSS Transitions: property <--percentage-list> from [initial] to [20% 200%] at (0.5) is [30% 300%]
+PASS CSS Transitions: property <--percentage-list> from [initial] to [20% 200%] at (1) is [20% 200%]
+PASS CSS Transitions: property <--percentage-list> from [initial] to [20% 200%] at (1.5) is [10% 100%]
+PASS CSS Transitions: property <--percentage-list> from [inherit] to [20% 200%] at (-0.3) is [33% 330%]
+PASS CSS Transitions: property <--percentage-list> from [inherit] to [20% 200%] at (0) is [30% 300%]
+PASS CSS Transitions: property <--percentage-list> from [inherit] to [20% 200%] at (0.5) is [25% 250%]
+PASS CSS Transitions: property <--percentage-list> from [inherit] to [20% 200%] at (1) is [20% 200%]
+PASS CSS Transitions: property <--percentage-list> from [inherit] to [20% 200%] at (1.5) is [15% 150%]
+PASS CSS Transitions: property <--percentage-list> from [unset] to [20% 200%] at (-0.3) is [46% 460%]
+PASS CSS Transitions: property <--percentage-list> from [unset] to [20% 200%] at (0) is [40% 400%]
+PASS CSS Transitions: property <--percentage-list> from [unset] to [20% 200%] at (0.5) is [30% 300%]
+PASS CSS Transitions: property <--percentage-list> from [unset] to [20% 200%] at (1) is [20% 200%]
+PASS CSS Transitions: property <--percentage-list> from [unset] to [20% 200%] at (1.5) is [10% 100%]
+PASS CSS Transitions: property <--percentage-list> from [-10% -100%] to [10% 100%] at (-0.3) is [-16% -160%]
+PASS CSS Transitions: property <--percentage-list> from [-10% -100%] to [10% 100%] at (0) is [-10% -100%]
+PASS CSS Transitions: property <--percentage-list> from [-10% -100%] to [10% 100%] at (0.5) is [0% 0%]
+PASS CSS Transitions: property <--percentage-list> from [-10% -100%] to [10% 100%] at (1) is [10% 100%]
+PASS CSS Transitions: property <--percentage-list> from [-10% -100%] to [10% 100%] at (1.5) is [20% 200%]
+PASS CSS Transitions: property <--percentage-list> from [10%] to [100%] at (-0.3) is [-17%]
+PASS CSS Transitions: property <--percentage-list> from [10%] to [100%] at (0) is [10%]
+PASS CSS Transitions: property <--percentage-list> from [10%] to [100%] at (0.5) is [55%]
+PASS CSS Transitions: property <--percentage-list> from [10%] to [100%] at (1) is [100%]
+PASS CSS Transitions: property <--percentage-list> from [10%] to [100%] at (1.5) is [145%]
+PASS CSS Transitions: property <--percentage-list> from neutral to [20% 200%] at (-0.3) is [7% 70%]
+PASS CSS Transitions: property <--percentage-list> from neutral to [20% 200%] at (0) is [10% 100%]
+PASS CSS Transitions: property <--percentage-list> from neutral to [20% 200%] at (0.5) is [15% 150%]
+PASS CSS Transitions: property <--percentage-list> from neutral to [20% 200%] at (1) is [20% 200%]
+PASS CSS Transitions: property <--percentage-list> from neutral to [20% 200%] at (1.5) is [25% 250%]
+PASS CSS Animations: property <--percentage-list> from [initial] to [20% 200%] at (-0.3) is [46% 460%]
+PASS CSS Animations: property <--percentage-list> from [initial] to [20% 200%] at (0) is [40% 400%]
+PASS CSS Animations: property <--percentage-list> from [initial] to [20% 200%] at (0.5) is [30% 300%]
+PASS CSS Animations: property <--percentage-list> from [initial] to [20% 200%] at (1) is [20% 200%]
+PASS CSS Animations: property <--percentage-list> from [initial] to [20% 200%] at (1.5) is [10% 100%]
+PASS CSS Animations: property <--percentage-list> from [inherit] to [20% 200%] at (-0.3) is [33% 330%]
+PASS CSS Animations: property <--percentage-list> from [inherit] to [20% 200%] at (0) is [30% 300%]
+PASS CSS Animations: property <--percentage-list> from [inherit] to [20% 200%] at (0.5) is [25% 250%]
+PASS CSS Animations: property <--percentage-list> from [inherit] to [20% 200%] at (1) is [20% 200%]
+PASS CSS Animations: property <--percentage-list> from [inherit] to [20% 200%] at (1.5) is [15% 150%]
+PASS CSS Animations: property <--percentage-list> from [unset] to [20% 200%] at (-0.3) is [46% 460%]
+PASS CSS Animations: property <--percentage-list> from [unset] to [20% 200%] at (0) is [40% 400%]
+PASS CSS Animations: property <--percentage-list> from [unset] to [20% 200%] at (0.5) is [30% 300%]
+PASS CSS Animations: property <--percentage-list> from [unset] to [20% 200%] at (1) is [20% 200%]
+PASS CSS Animations: property <--percentage-list> from [unset] to [20% 200%] at (1.5) is [10% 100%]
+PASS CSS Animations: property <--percentage-list> from [-10% -100%] to [10% 100%] at (-0.3) is [-16% -160%]
+PASS CSS Animations: property <--percentage-list> from [-10% -100%] to [10% 100%] at (0) is [-10% -100%]
+PASS CSS Animations: property <--percentage-list> from [-10% -100%] to [10% 100%] at (0.5) is [0% 0%]
+PASS CSS Animations: property <--percentage-list> from [-10% -100%] to [10% 100%] at (1) is [10% 100%]
+PASS CSS Animations: property <--percentage-list> from [-10% -100%] to [10% 100%] at (1.5) is [20% 200%]
+PASS CSS Animations: property <--percentage-list> from [10%] to [100%] at (-0.3) is [-17%]
+PASS CSS Animations: property <--percentage-list> from [10%] to [100%] at (0) is [10%]
+PASS CSS Animations: property <--percentage-list> from [10%] to [100%] at (0.5) is [55%]
+PASS CSS Animations: property <--percentage-list> from [10%] to [100%] at (1) is [100%]
+PASS CSS Animations: property <--percentage-list> from [10%] to [100%] at (1.5) is [145%]
+FAIL CSS Animations: property <--percentage-list> from neutral to [20% 200%] at (-0.3) is [-6% -60%] assert_equals: expected "7 % 70 % " but got "- 6 % - 60 % "
+FAIL CSS Animations: property <--percentage-list> from neutral to [20% 200%] at (0) is [0px 0px] assert_equals: expected "10 % 100 % " but got "0px 0px "
+FAIL CSS Animations: property <--percentage-list> from neutral to [20% 200%] at (0.5) is [10% 100%] assert_equals: expected "15 % 150 % " but got "10 % 100 % "
+PASS CSS Animations: property <--percentage-list> from neutral to [20% 200%] at (1) is [20% 200%]
+FAIL CSS Animations: property <--percentage-list> from neutral to [20% 200%] at (1.5) is [30% 300%] assert_equals: expected "25 % 250 % " but got "30 % 300 % "
+PASS Web Animations: property <--percentage-list> from [initial] to [20% 200%] at (-0.3) is [46% 460%]
+PASS Web Animations: property <--percentage-list> from [initial] to [20% 200%] at (0) is [40% 400%]
+PASS Web Animations: property <--percentage-list> from [initial] to [20% 200%] at (0.5) is [30% 300%]
+PASS Web Animations: property <--percentage-list> from [initial] to [20% 200%] at (1) is [20% 200%]
+PASS Web Animations: property <--percentage-list> from [initial] to [20% 200%] at (1.5) is [10% 100%]
+PASS Web Animations: property <--percentage-list> from [inherit] to [20% 200%] at (-0.3) is [33% 330%]
+PASS Web Animations: property <--percentage-list> from [inherit] to [20% 200%] at (0) is [30% 300%]
+PASS Web Animations: property <--percentage-list> from [inherit] to [20% 200%] at (0.5) is [25% 250%]
+PASS Web Animations: property <--percentage-list> from [inherit] to [20% 200%] at (1) is [20% 200%]
+PASS Web Animations: property <--percentage-list> from [inherit] to [20% 200%] at (1.5) is [15% 150%]
+PASS Web Animations: property <--percentage-list> from [unset] to [20% 200%] at (-0.3) is [46% 460%]
+PASS Web Animations: property <--percentage-list> from [unset] to [20% 200%] at (0) is [40% 400%]
+PASS Web Animations: property <--percentage-list> from [unset] to [20% 200%] at (0.5) is [30% 300%]
+PASS Web Animations: property <--percentage-list> from [unset] to [20% 200%] at (1) is [20% 200%]
+PASS Web Animations: property <--percentage-list> from [unset] to [20% 200%] at (1.5) is [10% 100%]
+PASS Web Animations: property <--percentage-list> from [-10% -100%] to [10% 100%] at (-0.3) is [-16% -160%]
+PASS Web Animations: property <--percentage-list> from [-10% -100%] to [10% 100%] at (0) is [-10% -100%]
+PASS Web Animations: property <--percentage-list> from [-10% -100%] to [10% 100%] at (0.5) is [0% 0%]
+PASS Web Animations: property <--percentage-list> from [-10% -100%] to [10% 100%] at (1) is [10% 100%]
+PASS Web Animations: property <--percentage-list> from [-10% -100%] to [10% 100%] at (1.5) is [20% 200%]
+PASS Web Animations: property <--percentage-list> from [10%] to [100%] at (-0.3) is [-17%]
+PASS Web Animations: property <--percentage-list> from [10%] to [100%] at (0) is [10%]
+PASS Web Animations: property <--percentage-list> from [10%] to [100%] at (0.5) is [55%]
+PASS Web Animations: property <--percentage-list> from [10%] to [100%] at (1) is [100%]
+PASS Web Animations: property <--percentage-list> from [10%] to [100%] at (1.5) is [145%]
+FAIL Web Animations: property <--percentage-list> from neutral to [20% 200%] at (-0.3) is [-6% -60%] assert_equals: expected "7 % 70 % " but got "- 6 % - 60 % "
+FAIL Web Animations: property <--percentage-list> from neutral to [20% 200%] at (0) is [0px 0px] assert_equals: expected "10 % 100 % " but got "0px 0px "
+FAIL Web Animations: property <--percentage-list> from neutral to [20% 200%] at (0.5) is [10% 100%] assert_equals: expected "15 % 150 % " but got "10 % 100 % "
+PASS Web Animations: property <--percentage-list> from neutral to [20% 200%] at (1) is [20% 200%]
+FAIL Web Animations: property <--percentage-list> from neutral to [20% 200%] at (1.5) is [30% 300%] assert_equals: expected "25 % 250 % " but got "30 % 300 % "
+FAIL Compositing: property <--percentage-list> underlying [50% 60%] from add [10% 140%] to add [110% 40%] at (-0.3) is [-20% 170%] assert_equals: expected "30 % 230 % " but got "- 20 % 170 % "
+FAIL Compositing: property <--percentage-list> underlying [50% 60%] from add [10% 140%] to add [110% 40%] at (0) is [10% 140%] assert_equals: expected "60 % 200 % " but got "10 % 140 % "
+FAIL Compositing: property <--percentage-list> underlying [50% 60%] from add [10% 140%] to add [110% 40%] at (0.5) is [60% 90%] assert_equals: expected "110 % 150 % " but got "60 % 90 % "
+FAIL Compositing: property <--percentage-list> underlying [50% 60%] from add [10% 140%] to add [110% 40%] at (1) is [110% 40%] assert_equals: expected "160 % 100 % " but got "110 % 40 % "
+FAIL Compositing: property <--percentage-list> underlying [50% 60%] from add [10% 140%] to add [110% 40%] at (1.5) is [160% -10%] assert_equals: expected "210 % 50 % " but got "160 % - 10 % "
+FAIL Compositing: property <--percentage-list> underlying [50% 60%] from add [10% 140%] to replace [110% 40%] at (-0.3) is [-20% 170%] assert_equals: expected "45 % 248 % " but got "- 20 % 170 % "
+FAIL Compositing: property <--percentage-list> underlying [50% 60%] from add [10% 140%] to replace [110% 40%] at (0) is [10% 140%] assert_equals: expected "60 % 200 % " but got "10 % 140 % "
+FAIL Compositing: property <--percentage-list> underlying [50% 60%] from add [10% 140%] to replace [110% 40%] at (0.5) is [60% 90%] assert_equals: expected "85 % 120 % " but got "60 % 90 % "
+PASS Compositing: property <--percentage-list> underlying [50% 60%] from add [10% 140%] to replace [110% 40%] at (1) is [110% 40%]
+FAIL Compositing: property <--percentage-list> underlying [50% 60%] from add [10% 140%] to replace [110% 40%] at (1.5) is [160% -10%] assert_equals: expected "135 % - 40 % " but got "160 % - 10 % "
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/animations/custom-properties/custom-percentage-list-type-interpolation.html b/third_party/WebKit/LayoutTests/animations/custom-properties/custom-percentage-list-type-interpolation.html
new file mode 100644
index 0000000..03f4a1878
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/animations/custom-properties/custom-percentage-list-type-interpolation.html
@@ -0,0 +1,124 @@
+<!DOCTYPE html>
+<meta charset="UTF-8">
+<style>
+.parent {
+  --percentage-list: 30% 300%;
+}
+.target {
+  --percentage-list: 10% 100%;
+}
+</style>
+<body>
+<script src="../interpolation/resources/interpolation-test.js"></script>
+<script>
+CSS.registerProperty({
+  name: '--percentage-list',
+  syntax: '<percentage>+',
+  initialValue: '40% 400%',
+  inherits: false,
+});
+
+assertInterpolation({
+  property: '--percentage-list',
+  from: 'initial',
+  to: '20% 200%',
+}, [
+  {at: -0.3, is: '46% 460%'},
+  {at: 0, is: '40% 400%'},
+  {at: 0.5, is: '30% 300%'},
+  {at: 1, is: '20% 200%'},
+  {at: 1.5, is: '10% 100%'},
+]);
+
+assertInterpolation({
+  property: '--percentage-list',
+  from: 'inherit',
+  to: '20% 200%',
+}, [
+  {at: -0.3, is: '33% 330%'},
+  {at: 0, is: '30% 300%'},
+  {at: 0.5, is: '25% 250%'},
+  {at: 1, is: '20% 200%'},
+  {at: 1.5, is: '15% 150%'},
+]);
+
+assertInterpolation({
+  property: '--percentage-list',
+  from: 'unset',
+  to: '20% 200%',
+}, [
+  {at: -0.3, is: '46% 460%'},
+  {at: 0, is: '40% 400%'},
+  {at: 0.5, is: '30% 300%'},
+  {at: 1, is: '20% 200%'},
+  {at: 1.5, is: '10% 100%'},
+]);
+
+assertInterpolation({
+  property: '--percentage-list',
+  from: '-10% -100%',
+  to: '10% 100%',
+}, [
+  {at: -0.3, is: '-16% -160%'},
+  {at: 0, is: '-10% -100%'},
+  {at: 0.5, is: '0% 0%'},
+  {at: 1, is: '10% 100%'},
+  {at: 1.5, is: '20% 200%'}
+]); 
+
+assertInterpolation({
+  property: '--percentage-list',
+  from: '10%',
+  to: '100%',
+}, [
+  {at: -0.3, is: '-17%'},
+  {at: 0, is: '10%'},
+  {at: 0.5, is: '55%'},
+  {at: 1, is: '100%'},
+  {at: 1.5, is: '145%'}
+]);
+
+// Composition and neutralKeyframe tests assume that composite:add means
+// component-wise addition, which may or may not be the behavior we want.
+// https://github.com/w3c/css-houdini-drafts/issues/799
+
+assertInterpolation({
+  property: '--percentage-list',
+  from: neutralKeyframe,
+  to: '20% 200%',
+}, [
+  {at: -0.3, is: '7% 70%'},
+  {at: 0, is: '10% 100%'},
+  {at: 0.5, is: '15% 150%'},
+  {at: 1, is: '20% 200%'},
+  {at: 1.5, is: '25% 250%'},
+]);
+
+assertComposition({
+  property: '--percentage-list',
+  underlying: '50% 60%',
+  addFrom: '10% 140%',
+  addTo: '110% 40%',
+}, [
+  {at: -0.3, is: '30% 230%'},
+  {at: 0, is: '60% 200%'},
+  {at: 0.5, is: '110% 150%'},
+  {at: 1, is: '160% 100%'},
+  {at: 1.5, is: '210% 50%'},
+]);
+
+assertComposition({
+  property: '--percentage-list',
+  underlying: '50% 60%',
+  addFrom: '10% 140%',
+  replaceTo: '110% 40%',
+}, [
+  {at: -0.3, is: '45% 248%'},
+  {at: 0, is: '60% 200%'},
+  {at: 0.5, is: '85% 120%'},
+  {at: 1, is: '110% 40%'},
+  {at: 1.5, is: '135% -40%'},
+]);
+
+</script>
+</body>
diff --git a/third_party/WebKit/LayoutTests/animations/custom-properties/custom-resolution-list-type-interpolation-expected.txt b/third_party/WebKit/LayoutTests/animations/custom-properties/custom-resolution-list-type-interpolation-expected.txt
new file mode 100644
index 0000000..bca3c5abc
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/animations/custom-properties/custom-resolution-list-type-interpolation-expected.txt
@@ -0,0 +1,119 @@
+This is a testharness.js-based test.
+PASS This test uses interpolation-test.js.
+PASS CSS Transitions: property <--resolution-list> from [initial] to [20dppx 200dppx] at (-0.3) is [46dppx 460dppx]
+PASS CSS Transitions: property <--resolution-list> from [initial] to [20dppx 200dppx] at (0) is [40dppx 400dppx]
+PASS CSS Transitions: property <--resolution-list> from [initial] to [20dppx 200dppx] at (0.5) is [30dppx 300dppx]
+PASS CSS Transitions: property <--resolution-list> from [initial] to [20dppx 200dppx] at (1) is [20dppx 200dppx]
+PASS CSS Transitions: property <--resolution-list> from [initial] to [20dppx 200dppx] at (1.5) is [10dppx 100dppx]
+PASS CSS Transitions: property <--resolution-list> from [inherit] to [20dppx 200dppx] at (-0.3) is [33dppx 330dppx]
+PASS CSS Transitions: property <--resolution-list> from [inherit] to [20dppx 200dppx] at (0) is [30dppx 300dppx]
+PASS CSS Transitions: property <--resolution-list> from [inherit] to [20dppx 200dppx] at (0.5) is [25dppx 250dppx]
+PASS CSS Transitions: property <--resolution-list> from [inherit] to [20dppx 200dppx] at (1) is [20dppx 200dppx]
+PASS CSS Transitions: property <--resolution-list> from [inherit] to [20dppx 200dppx] at (1.5) is [15dppx 150dppx]
+PASS CSS Transitions: property <--resolution-list> from [unset] to [20dppx 200dppx] at (-0.3) is [46dppx 460dppx]
+PASS CSS Transitions: property <--resolution-list> from [unset] to [20dppx 200dppx] at (0) is [40dppx 400dppx]
+PASS CSS Transitions: property <--resolution-list> from [unset] to [20dppx 200dppx] at (0.5) is [30dppx 300dppx]
+PASS CSS Transitions: property <--resolution-list> from [unset] to [20dppx 200dppx] at (1) is [20dppx 200dppx]
+PASS CSS Transitions: property <--resolution-list> from [unset] to [20dppx 200dppx] at (1.5) is [10dppx 100dppx]
+PASS CSS Transitions: property <--resolution-list> from [-10dppx -100dppx] to [10dppx 100dppx] at (-0.3) is [-16dppx -160dppx]
+PASS CSS Transitions: property <--resolution-list> from [-10dppx -100dppx] to [10dppx 100dppx] at (0) is [-10dppx -100dppx]
+PASS CSS Transitions: property <--resolution-list> from [-10dppx -100dppx] to [10dppx 100dppx] at (0.5) is [0dppx 0dppx]
+PASS CSS Transitions: property <--resolution-list> from [-10dppx -100dppx] to [10dppx 100dppx] at (1) is [10dppx 100dppx]
+PASS CSS Transitions: property <--resolution-list> from [-10dppx -100dppx] to [10dppx 100dppx] at (1.5) is [20dppx 200dppx]
+PASS CSS Transitions: property <--resolution-list> from [10dppx] to [100dppx] at (-0.3) is [-17dppx]
+PASS CSS Transitions: property <--resolution-list> from [10dppx] to [100dppx] at (0) is [10dppx]
+PASS CSS Transitions: property <--resolution-list> from [10dppx] to [100dppx] at (0.5) is [55dppx]
+PASS CSS Transitions: property <--resolution-list> from [10dppx] to [100dppx] at (1) is [100dppx]
+PASS CSS Transitions: property <--resolution-list> from [10dppx] to [100dppx] at (1.5) is [145dppx]
+PASS CSS Transitions: property <--resolution-list> from [96dpi 192dpi] to [2dppx 2dppx] at (-0.3) is [0.7dppx 2dppx]
+PASS CSS Transitions: property <--resolution-list> from [96dpi 192dpi] to [2dppx 2dppx] at (0) is [1dppx 2dppx]
+PASS CSS Transitions: property <--resolution-list> from [96dpi 192dpi] to [2dppx 2dppx] at (0.5) is [1.5dppx 2dppx]
+PASS CSS Transitions: property <--resolution-list> from [96dpi 192dpi] to [2dppx 2dppx] at (1) is [2dppx 2dppx]
+PASS CSS Transitions: property <--resolution-list> from [96dpi 192dpi] to [2dppx 2dppx] at (1.5) is [2.5dppx 2dppx]
+PASS CSS Transitions: property <--resolution-list> from neutral to [20dppx 200dppx] at (-0.3) is [7dppx 70dppx]
+PASS CSS Transitions: property <--resolution-list> from neutral to [20dppx 200dppx] at (0) is [10dppx 100dppx]
+PASS CSS Transitions: property <--resolution-list> from neutral to [20dppx 200dppx] at (0.5) is [15dppx 150dppx]
+PASS CSS Transitions: property <--resolution-list> from neutral to [20dppx 200dppx] at (1) is [20dppx 200dppx]
+PASS CSS Transitions: property <--resolution-list> from neutral to [20dppx 200dppx] at (1.5) is [25dppx 250dppx]
+PASS CSS Animations: property <--resolution-list> from [initial] to [20dppx 200dppx] at (-0.3) is [46dppx 460dppx]
+PASS CSS Animations: property <--resolution-list> from [initial] to [20dppx 200dppx] at (0) is [40dppx 400dppx]
+PASS CSS Animations: property <--resolution-list> from [initial] to [20dppx 200dppx] at (0.5) is [30dppx 300dppx]
+PASS CSS Animations: property <--resolution-list> from [initial] to [20dppx 200dppx] at (1) is [20dppx 200dppx]
+PASS CSS Animations: property <--resolution-list> from [initial] to [20dppx 200dppx] at (1.5) is [10dppx 100dppx]
+PASS CSS Animations: property <--resolution-list> from [inherit] to [20dppx 200dppx] at (-0.3) is [33dppx 330dppx]
+PASS CSS Animations: property <--resolution-list> from [inherit] to [20dppx 200dppx] at (0) is [30dppx 300dppx]
+PASS CSS Animations: property <--resolution-list> from [inherit] to [20dppx 200dppx] at (0.5) is [25dppx 250dppx]
+PASS CSS Animations: property <--resolution-list> from [inherit] to [20dppx 200dppx] at (1) is [20dppx 200dppx]
+PASS CSS Animations: property <--resolution-list> from [inherit] to [20dppx 200dppx] at (1.5) is [15dppx 150dppx]
+PASS CSS Animations: property <--resolution-list> from [unset] to [20dppx 200dppx] at (-0.3) is [46dppx 460dppx]
+PASS CSS Animations: property <--resolution-list> from [unset] to [20dppx 200dppx] at (0) is [40dppx 400dppx]
+PASS CSS Animations: property <--resolution-list> from [unset] to [20dppx 200dppx] at (0.5) is [30dppx 300dppx]
+PASS CSS Animations: property <--resolution-list> from [unset] to [20dppx 200dppx] at (1) is [20dppx 200dppx]
+PASS CSS Animations: property <--resolution-list> from [unset] to [20dppx 200dppx] at (1.5) is [10dppx 100dppx]
+PASS CSS Animations: property <--resolution-list> from [-10dppx -100dppx] to [10dppx 100dppx] at (-0.3) is [-16dppx -160dppx]
+PASS CSS Animations: property <--resolution-list> from [-10dppx -100dppx] to [10dppx 100dppx] at (0) is [-10dppx -100dppx]
+PASS CSS Animations: property <--resolution-list> from [-10dppx -100dppx] to [10dppx 100dppx] at (0.5) is [0dppx 0dppx]
+PASS CSS Animations: property <--resolution-list> from [-10dppx -100dppx] to [10dppx 100dppx] at (1) is [10dppx 100dppx]
+PASS CSS Animations: property <--resolution-list> from [-10dppx -100dppx] to [10dppx 100dppx] at (1.5) is [20dppx 200dppx]
+PASS CSS Animations: property <--resolution-list> from [10dppx] to [100dppx] at (-0.3) is [-17dppx]
+PASS CSS Animations: property <--resolution-list> from [10dppx] to [100dppx] at (0) is [10dppx]
+PASS CSS Animations: property <--resolution-list> from [10dppx] to [100dppx] at (0.5) is [55dppx]
+PASS CSS Animations: property <--resolution-list> from [10dppx] to [100dppx] at (1) is [100dppx]
+PASS CSS Animations: property <--resolution-list> from [10dppx] to [100dppx] at (1.5) is [145dppx]
+PASS CSS Animations: property <--resolution-list> from [96dpi 192dpi] to [2dppx 2dppx] at (-0.3) is [0.7dppx 2dppx]
+PASS CSS Animations: property <--resolution-list> from [96dpi 192dpi] to [2dppx 2dppx] at (0) is [1dppx 2dppx]
+PASS CSS Animations: property <--resolution-list> from [96dpi 192dpi] to [2dppx 2dppx] at (0.5) is [1.5dppx 2dppx]
+PASS CSS Animations: property <--resolution-list> from [96dpi 192dpi] to [2dppx 2dppx] at (1) is [2dppx 2dppx]
+PASS CSS Animations: property <--resolution-list> from [96dpi 192dpi] to [2dppx 2dppx] at (1.5) is [2.5dppx 2dppx]
+FAIL CSS Animations: property <--resolution-list> from neutral to [20dppx 200dppx] at (-0.3) is [-6dppx -60dppx] assert_equals: expected "7dppx 70dppx " but got "- 6dppx - 60dppx "
+FAIL CSS Animations: property <--resolution-list> from neutral to [20dppx 200dppx] at (0) is [0dppx 0dppx] assert_equals: expected "10dppx 100dppx " but got "0dppx 0dppx "
+FAIL CSS Animations: property <--resolution-list> from neutral to [20dppx 200dppx] at (0.5) is [10dppx 100dppx] assert_equals: expected "15dppx 150dppx " but got "10dppx 100dppx "
+PASS CSS Animations: property <--resolution-list> from neutral to [20dppx 200dppx] at (1) is [20dppx 200dppx]
+FAIL CSS Animations: property <--resolution-list> from neutral to [20dppx 200dppx] at (1.5) is [30dppx 300dppx] assert_equals: expected "25dppx 250dppx " but got "30dppx 300dppx "
+PASS Web Animations: property <--resolution-list> from [initial] to [20dppx 200dppx] at (-0.3) is [46dppx 460dppx]
+PASS Web Animations: property <--resolution-list> from [initial] to [20dppx 200dppx] at (0) is [40dppx 400dppx]
+PASS Web Animations: property <--resolution-list> from [initial] to [20dppx 200dppx] at (0.5) is [30dppx 300dppx]
+PASS Web Animations: property <--resolution-list> from [initial] to [20dppx 200dppx] at (1) is [20dppx 200dppx]
+PASS Web Animations: property <--resolution-list> from [initial] to [20dppx 200dppx] at (1.5) is [10dppx 100dppx]
+PASS Web Animations: property <--resolution-list> from [inherit] to [20dppx 200dppx] at (-0.3) is [33dppx 330dppx]
+PASS Web Animations: property <--resolution-list> from [inherit] to [20dppx 200dppx] at (0) is [30dppx 300dppx]
+PASS Web Animations: property <--resolution-list> from [inherit] to [20dppx 200dppx] at (0.5) is [25dppx 250dppx]
+PASS Web Animations: property <--resolution-list> from [inherit] to [20dppx 200dppx] at (1) is [20dppx 200dppx]
+PASS Web Animations: property <--resolution-list> from [inherit] to [20dppx 200dppx] at (1.5) is [15dppx 150dppx]
+PASS Web Animations: property <--resolution-list> from [unset] to [20dppx 200dppx] at (-0.3) is [46dppx 460dppx]
+PASS Web Animations: property <--resolution-list> from [unset] to [20dppx 200dppx] at (0) is [40dppx 400dppx]
+PASS Web Animations: property <--resolution-list> from [unset] to [20dppx 200dppx] at (0.5) is [30dppx 300dppx]
+PASS Web Animations: property <--resolution-list> from [unset] to [20dppx 200dppx] at (1) is [20dppx 200dppx]
+PASS Web Animations: property <--resolution-list> from [unset] to [20dppx 200dppx] at (1.5) is [10dppx 100dppx]
+PASS Web Animations: property <--resolution-list> from [-10dppx -100dppx] to [10dppx 100dppx] at (-0.3) is [-16dppx -160dppx]
+PASS Web Animations: property <--resolution-list> from [-10dppx -100dppx] to [10dppx 100dppx] at (0) is [-10dppx -100dppx]
+PASS Web Animations: property <--resolution-list> from [-10dppx -100dppx] to [10dppx 100dppx] at (0.5) is [0dppx 0dppx]
+PASS Web Animations: property <--resolution-list> from [-10dppx -100dppx] to [10dppx 100dppx] at (1) is [10dppx 100dppx]
+PASS Web Animations: property <--resolution-list> from [-10dppx -100dppx] to [10dppx 100dppx] at (1.5) is [20dppx 200dppx]
+PASS Web Animations: property <--resolution-list> from [10dppx] to [100dppx] at (-0.3) is [-17dppx]
+PASS Web Animations: property <--resolution-list> from [10dppx] to [100dppx] at (0) is [10dppx]
+PASS Web Animations: property <--resolution-list> from [10dppx] to [100dppx] at (0.5) is [55dppx]
+PASS Web Animations: property <--resolution-list> from [10dppx] to [100dppx] at (1) is [100dppx]
+PASS Web Animations: property <--resolution-list> from [10dppx] to [100dppx] at (1.5) is [145dppx]
+PASS Web Animations: property <--resolution-list> from [96dpi 192dpi] to [2dppx 2dppx] at (-0.3) is [0.7dppx 2dppx]
+PASS Web Animations: property <--resolution-list> from [96dpi 192dpi] to [2dppx 2dppx] at (0) is [1dppx 2dppx]
+PASS Web Animations: property <--resolution-list> from [96dpi 192dpi] to [2dppx 2dppx] at (0.5) is [1.5dppx 2dppx]
+PASS Web Animations: property <--resolution-list> from [96dpi 192dpi] to [2dppx 2dppx] at (1) is [2dppx 2dppx]
+PASS Web Animations: property <--resolution-list> from [96dpi 192dpi] to [2dppx 2dppx] at (1.5) is [2.5dppx 2dppx]
+FAIL Web Animations: property <--resolution-list> from neutral to [20dppx 200dppx] at (-0.3) is [-6dppx -60dppx] assert_equals: expected "7dppx 70dppx " but got "- 6dppx - 60dppx "
+FAIL Web Animations: property <--resolution-list> from neutral to [20dppx 200dppx] at (0) is [0dppx 0dppx] assert_equals: expected "10dppx 100dppx " but got "0dppx 0dppx "
+FAIL Web Animations: property <--resolution-list> from neutral to [20dppx 200dppx] at (0.5) is [10dppx 100dppx] assert_equals: expected "15dppx 150dppx " but got "10dppx 100dppx "
+PASS Web Animations: property <--resolution-list> from neutral to [20dppx 200dppx] at (1) is [20dppx 200dppx]
+FAIL Web Animations: property <--resolution-list> from neutral to [20dppx 200dppx] at (1.5) is [30dppx 300dppx] assert_equals: expected "25dppx 250dppx " but got "30dppx 300dppx "
+FAIL Compositing: property <--resolution-list> underlying [50dppx 60dppx] from add [10dppx 140dppx] to add [110dppx 40dppx] at (-0.3) is [-20dppx 170dppx] assert_equals: expected "30dppx 230dppx " but got "- 20dppx 170dppx "
+FAIL Compositing: property <--resolution-list> underlying [50dppx 60dppx] from add [10dppx 140dppx] to add [110dppx 40dppx] at (0) is [10dppx 140dppx] assert_equals: expected "60dppx 200dppx " but got "10dppx 140dppx "
+FAIL Compositing: property <--resolution-list> underlying [50dppx 60dppx] from add [10dppx 140dppx] to add [110dppx 40dppx] at (0.5) is [60dppx 90dppx] assert_equals: expected "110dppx 150dppx " but got "60dppx 90dppx "
+FAIL Compositing: property <--resolution-list> underlying [50dppx 60dppx] from add [10dppx 140dppx] to add [110dppx 40dppx] at (1) is [110dppx 40dppx] assert_equals: expected "160dppx 100dppx " but got "110dppx 40dppx "
+FAIL Compositing: property <--resolution-list> underlying [50dppx 60dppx] from add [10dppx 140dppx] to add [110dppx 40dppx] at (1.5) is [160dppx -10dppx] assert_equals: expected "210dppx 50dppx " but got "160dppx - 10dppx "
+FAIL Compositing: property <--resolution-list> underlying [50dppx 60dppx] from add [10dppx 140dppx] to replace [110dppx 40dppx] at (-0.3) is [-20dppx 170dppx] assert_equals: expected "45dppx 248dppx " but got "- 20dppx 170dppx "
+FAIL Compositing: property <--resolution-list> underlying [50dppx 60dppx] from add [10dppx 140dppx] to replace [110dppx 40dppx] at (0) is [10dppx 140dppx] assert_equals: expected "60dppx 200dppx " but got "10dppx 140dppx "
+FAIL Compositing: property <--resolution-list> underlying [50dppx 60dppx] from add [10dppx 140dppx] to replace [110dppx 40dppx] at (0.5) is [60dppx 90dppx] assert_equals: expected "85dppx 120dppx " but got "60dppx 90dppx "
+PASS Compositing: property <--resolution-list> underlying [50dppx 60dppx] from add [10dppx 140dppx] to replace [110dppx 40dppx] at (1) is [110dppx 40dppx]
+FAIL Compositing: property <--resolution-list> underlying [50dppx 60dppx] from add [10dppx 140dppx] to replace [110dppx 40dppx] at (1.5) is [160dppx -10dppx] assert_equals: expected "135dppx - 40dppx " but got "160dppx - 10dppx "
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/animations/custom-properties/custom-resolution-list-type-interpolation.html b/third_party/WebKit/LayoutTests/animations/custom-properties/custom-resolution-list-type-interpolation.html
new file mode 100644
index 0000000..42c3504
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/animations/custom-properties/custom-resolution-list-type-interpolation.html
@@ -0,0 +1,136 @@
+<!DOCTYPE html>
+<meta charset="UTF-8">
+<style>
+.parent {
+  --resolution-list: 30dppx 300dppx;
+}
+.target {
+  --resolution-list: 10dppx 100dppx;
+}
+</style>
+<body>
+<script src="../interpolation/resources/interpolation-test.js"></script>
+<script>
+CSS.registerProperty({
+  name: '--resolution-list',
+  syntax: '<resolution>+',
+  initialValue: '40dppx 400dppx',
+  inherits: false,
+});
+
+assertInterpolation({
+  property: '--resolution-list',
+  from: 'initial',
+  to: '20dppx 200dppx',
+}, [
+  {at: -0.3, is: '46dppx 460dppx'},
+  {at: 0, is: '40dppx 400dppx'},
+  {at: 0.5, is: '30dppx 300dppx'},
+  {at: 1, is: '20dppx 200dppx'},
+  {at: 1.5, is: '10dppx 100dppx'},
+]);
+
+assertInterpolation({
+  property: '--resolution-list',
+  from: 'inherit',
+  to: '20dppx 200dppx',
+}, [
+  {at: -0.3, is: '33dppx 330dppx'},
+  {at: 0, is: '30dppx 300dppx'},
+  {at: 0.5, is: '25dppx 250dppx'},
+  {at: 1, is: '20dppx 200dppx'},
+  {at: 1.5, is: '15dppx 150dppx'},
+]);
+
+assertInterpolation({
+  property: '--resolution-list',
+  from: 'unset',
+  to: '20dppx 200dppx',
+}, [
+  {at: -0.3, is: '46dppx 460dppx'},
+  {at: 0, is: '40dppx 400dppx'},
+  {at: 0.5, is: '30dppx 300dppx'},
+  {at: 1, is: '20dppx 200dppx'},
+  {at: 1.5, is: '10dppx 100dppx'},
+]);
+
+assertInterpolation({
+  property: '--resolution-list',
+  from: '-10dppx -100dppx',
+  to: '10dppx 100dppx',
+}, [
+  {at: -0.3, is: '-16dppx -160dppx'},
+  {at: 0, is: '-10dppx -100dppx'},
+  {at: 0.5, is: '0dppx 0dppx'},
+  {at: 1, is: '10dppx 100dppx'},
+  {at: 1.5, is: '20dppx 200dppx'}
+]); 
+
+assertInterpolation({
+  property: '--resolution-list',
+  from: '10dppx',
+  to: '100dppx',
+}, [
+  {at: -0.3, is: '-17dppx'},
+  {at: 0, is: '10dppx'},
+  {at: 0.5, is: '55dppx'},
+  {at: 1, is: '100dppx'},
+  {at: 1.5, is: '145dppx'}
+]);
+
+assertInterpolation({
+  property: '--resolution-list',
+  from: '96dpi 192dpi',
+  to: '2dppx 2dppx',
+}, [
+  {at: -0.3, is: '0.7dppx 2dppx'},
+  {at: 0, is: '1dppx 2dppx'},
+  {at: 0.5, is: '1.5dppx 2dppx'},
+  {at: 1, is: '2dppx 2dppx'},
+  {at: 1.5, is: '2.5dppx 2dppx'}
+]);
+
+// Composition and neutralKeyframe tests assume that composite:add means
+// component-wise addition, which may or may not be the behavior we want.
+// https://github.com/w3c/css-houdini-drafts/issues/799
+
+assertInterpolation({
+  property: '--resolution-list',
+  from: neutralKeyframe,
+  to: '20dppx 200dppx',
+}, [
+  {at: -0.3, is: '7dppx 70dppx'},
+  {at: 0, is: '10dppx 100dppx'},
+  {at: 0.5, is: '15dppx 150dppx'},
+  {at: 1, is: '20dppx 200dppx'},
+  {at: 1.5, is: '25dppx 250dppx'},
+]);
+
+assertComposition({
+  property: '--resolution-list',
+  underlying: '50dppx 60dppx',
+  addFrom: '10dppx 140dppx',
+  addTo: '110dppx 40dppx',
+}, [
+  {at: -0.3, is: '30dppx 230dppx'},
+  {at: 0, is: '60dppx 200dppx'},
+  {at: 0.5, is: '110dppx 150dppx'},
+  {at: 1, is: '160dppx 100dppx'},
+  {at: 1.5, is: '210dppx 50dppx'},
+]);
+
+assertComposition({
+  property: '--resolution-list',
+  underlying: '50dppx 60dppx',
+  addFrom: '10dppx 140dppx',
+  replaceTo: '110dppx 40dppx',
+}, [
+  {at: -0.3, is: '45dppx 248dppx'},
+  {at: 0, is: '60dppx 200dppx'},
+  {at: 0.5, is: '85dppx 120dppx'},
+  {at: 1, is: '110dppx 40dppx'},
+  {at: 1.5, is: '135dppx -40dppx'},
+]);
+
+</script>
+</body>
diff --git a/third_party/WebKit/LayoutTests/animations/custom-properties/custom-time-list-type-interpolation-expected.txt b/third_party/WebKit/LayoutTests/animations/custom-properties/custom-time-list-type-interpolation-expected.txt
new file mode 100644
index 0000000..08cf030f
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/animations/custom-properties/custom-time-list-type-interpolation-expected.txt
@@ -0,0 +1,119 @@
+This is a testharness.js-based test.
+PASS This test uses interpolation-test.js.
+PASS CSS Transitions: property <--time-list> from [initial] to [20s 200s] at (-0.3) is [46s 460s]
+PASS CSS Transitions: property <--time-list> from [initial] to [20s 200s] at (0) is [40s 400s]
+PASS CSS Transitions: property <--time-list> from [initial] to [20s 200s] at (0.5) is [30s 300s]
+PASS CSS Transitions: property <--time-list> from [initial] to [20s 200s] at (1) is [20s 200s]
+PASS CSS Transitions: property <--time-list> from [initial] to [20s 200s] at (1.5) is [10s 100s]
+PASS CSS Transitions: property <--time-list> from [inherit] to [20s 200s] at (-0.3) is [33s 330s]
+PASS CSS Transitions: property <--time-list> from [inherit] to [20s 200s] at (0) is [30s 300s]
+PASS CSS Transitions: property <--time-list> from [inherit] to [20s 200s] at (0.5) is [25s 250s]
+PASS CSS Transitions: property <--time-list> from [inherit] to [20s 200s] at (1) is [20s 200s]
+PASS CSS Transitions: property <--time-list> from [inherit] to [20s 200s] at (1.5) is [15s 150s]
+PASS CSS Transitions: property <--time-list> from [unset] to [20s 200s] at (-0.3) is [46s 460s]
+PASS CSS Transitions: property <--time-list> from [unset] to [20s 200s] at (0) is [40s 400s]
+PASS CSS Transitions: property <--time-list> from [unset] to [20s 200s] at (0.5) is [30s 300s]
+PASS CSS Transitions: property <--time-list> from [unset] to [20s 200s] at (1) is [20s 200s]
+PASS CSS Transitions: property <--time-list> from [unset] to [20s 200s] at (1.5) is [10s 100s]
+PASS CSS Transitions: property <--time-list> from [-10s -100s] to [10s 100s] at (-0.3) is [-16s -160s]
+PASS CSS Transitions: property <--time-list> from [-10s -100s] to [10s 100s] at (0) is [-10s -100s]
+PASS CSS Transitions: property <--time-list> from [-10s -100s] to [10s 100s] at (0.5) is [0s 0s]
+PASS CSS Transitions: property <--time-list> from [-10s -100s] to [10s 100s] at (1) is [10s 100s]
+PASS CSS Transitions: property <--time-list> from [-10s -100s] to [10s 100s] at (1.5) is [20s 200s]
+PASS CSS Transitions: property <--time-list> from [10s] to [100s] at (-0.3) is [-17s]
+PASS CSS Transitions: property <--time-list> from [10s] to [100s] at (0) is [10s]
+PASS CSS Transitions: property <--time-list> from [10s] to [100s] at (0.5) is [55s]
+PASS CSS Transitions: property <--time-list> from [10s] to [100s] at (1) is [100s]
+PASS CSS Transitions: property <--time-list> from [10s] to [100s] at (1.5) is [145s]
+PASS CSS Transitions: property <--time-list> from [0s 1000ms] to [1000ms 0s] at (-0.3) is [-0.3s 1.3s]
+PASS CSS Transitions: property <--time-list> from [0s 1000ms] to [1000ms 0s] at (0) is [0s 1s]
+PASS CSS Transitions: property <--time-list> from [0s 1000ms] to [1000ms 0s] at (0.5) is [0.5s 0.5s]
+PASS CSS Transitions: property <--time-list> from [0s 1000ms] to [1000ms 0s] at (1) is [1s 0s]
+PASS CSS Transitions: property <--time-list> from [0s 1000ms] to [1000ms 0s] at (1.5) is [1.5s -0.5s]
+PASS CSS Transitions: property <--time-list> from neutral to [20s 200s] at (-0.3) is [7s 70s]
+PASS CSS Transitions: property <--time-list> from neutral to [20s 200s] at (0) is [10s 100s]
+PASS CSS Transitions: property <--time-list> from neutral to [20s 200s] at (0.5) is [15s 150s]
+PASS CSS Transitions: property <--time-list> from neutral to [20s 200s] at (1) is [20s 200s]
+PASS CSS Transitions: property <--time-list> from neutral to [20s 200s] at (1.5) is [25s 250s]
+PASS CSS Animations: property <--time-list> from [initial] to [20s 200s] at (-0.3) is [46s 460s]
+PASS CSS Animations: property <--time-list> from [initial] to [20s 200s] at (0) is [40s 400s]
+PASS CSS Animations: property <--time-list> from [initial] to [20s 200s] at (0.5) is [30s 300s]
+PASS CSS Animations: property <--time-list> from [initial] to [20s 200s] at (1) is [20s 200s]
+PASS CSS Animations: property <--time-list> from [initial] to [20s 200s] at (1.5) is [10s 100s]
+PASS CSS Animations: property <--time-list> from [inherit] to [20s 200s] at (-0.3) is [33s 330s]
+PASS CSS Animations: property <--time-list> from [inherit] to [20s 200s] at (0) is [30s 300s]
+PASS CSS Animations: property <--time-list> from [inherit] to [20s 200s] at (0.5) is [25s 250s]
+PASS CSS Animations: property <--time-list> from [inherit] to [20s 200s] at (1) is [20s 200s]
+PASS CSS Animations: property <--time-list> from [inherit] to [20s 200s] at (1.5) is [15s 150s]
+PASS CSS Animations: property <--time-list> from [unset] to [20s 200s] at (-0.3) is [46s 460s]
+PASS CSS Animations: property <--time-list> from [unset] to [20s 200s] at (0) is [40s 400s]
+PASS CSS Animations: property <--time-list> from [unset] to [20s 200s] at (0.5) is [30s 300s]
+PASS CSS Animations: property <--time-list> from [unset] to [20s 200s] at (1) is [20s 200s]
+PASS CSS Animations: property <--time-list> from [unset] to [20s 200s] at (1.5) is [10s 100s]
+PASS CSS Animations: property <--time-list> from [-10s -100s] to [10s 100s] at (-0.3) is [-16s -160s]
+PASS CSS Animations: property <--time-list> from [-10s -100s] to [10s 100s] at (0) is [-10s -100s]
+PASS CSS Animations: property <--time-list> from [-10s -100s] to [10s 100s] at (0.5) is [0s 0s]
+PASS CSS Animations: property <--time-list> from [-10s -100s] to [10s 100s] at (1) is [10s 100s]
+PASS CSS Animations: property <--time-list> from [-10s -100s] to [10s 100s] at (1.5) is [20s 200s]
+PASS CSS Animations: property <--time-list> from [10s] to [100s] at (-0.3) is [-17s]
+PASS CSS Animations: property <--time-list> from [10s] to [100s] at (0) is [10s]
+PASS CSS Animations: property <--time-list> from [10s] to [100s] at (0.5) is [55s]
+PASS CSS Animations: property <--time-list> from [10s] to [100s] at (1) is [100s]
+PASS CSS Animations: property <--time-list> from [10s] to [100s] at (1.5) is [145s]
+PASS CSS Animations: property <--time-list> from [0s 1000ms] to [1000ms 0s] at (-0.3) is [-0.3s 1.3s]
+PASS CSS Animations: property <--time-list> from [0s 1000ms] to [1000ms 0s] at (0) is [0s 1s]
+PASS CSS Animations: property <--time-list> from [0s 1000ms] to [1000ms 0s] at (0.5) is [0.5s 0.5s]
+PASS CSS Animations: property <--time-list> from [0s 1000ms] to [1000ms 0s] at (1) is [1s 0s]
+PASS CSS Animations: property <--time-list> from [0s 1000ms] to [1000ms 0s] at (1.5) is [1.5s -0.5s]
+FAIL CSS Animations: property <--time-list> from neutral to [20s 200s] at (-0.3) is [-6s -60s] assert_equals: expected "7s 70s " but got "- 6s - 60s "
+FAIL CSS Animations: property <--time-list> from neutral to [20s 200s] at (0) is [0s 0s] assert_equals: expected "10s 100s " but got "0s 0s "
+FAIL CSS Animations: property <--time-list> from neutral to [20s 200s] at (0.5) is [10s 100s] assert_equals: expected "15s 150s " but got "10s 100s "
+PASS CSS Animations: property <--time-list> from neutral to [20s 200s] at (1) is [20s 200s]
+FAIL CSS Animations: property <--time-list> from neutral to [20s 200s] at (1.5) is [30s 300s] assert_equals: expected "25s 250s " but got "30s 300s "
+PASS Web Animations: property <--time-list> from [initial] to [20s 200s] at (-0.3) is [46s 460s]
+PASS Web Animations: property <--time-list> from [initial] to [20s 200s] at (0) is [40s 400s]
+PASS Web Animations: property <--time-list> from [initial] to [20s 200s] at (0.5) is [30s 300s]
+PASS Web Animations: property <--time-list> from [initial] to [20s 200s] at (1) is [20s 200s]
+PASS Web Animations: property <--time-list> from [initial] to [20s 200s] at (1.5) is [10s 100s]
+PASS Web Animations: property <--time-list> from [inherit] to [20s 200s] at (-0.3) is [33s 330s]
+PASS Web Animations: property <--time-list> from [inherit] to [20s 200s] at (0) is [30s 300s]
+PASS Web Animations: property <--time-list> from [inherit] to [20s 200s] at (0.5) is [25s 250s]
+PASS Web Animations: property <--time-list> from [inherit] to [20s 200s] at (1) is [20s 200s]
+PASS Web Animations: property <--time-list> from [inherit] to [20s 200s] at (1.5) is [15s 150s]
+PASS Web Animations: property <--time-list> from [unset] to [20s 200s] at (-0.3) is [46s 460s]
+PASS Web Animations: property <--time-list> from [unset] to [20s 200s] at (0) is [40s 400s]
+PASS Web Animations: property <--time-list> from [unset] to [20s 200s] at (0.5) is [30s 300s]
+PASS Web Animations: property <--time-list> from [unset] to [20s 200s] at (1) is [20s 200s]
+PASS Web Animations: property <--time-list> from [unset] to [20s 200s] at (1.5) is [10s 100s]
+PASS Web Animations: property <--time-list> from [-10s -100s] to [10s 100s] at (-0.3) is [-16s -160s]
+PASS Web Animations: property <--time-list> from [-10s -100s] to [10s 100s] at (0) is [-10s -100s]
+PASS Web Animations: property <--time-list> from [-10s -100s] to [10s 100s] at (0.5) is [0s 0s]
+PASS Web Animations: property <--time-list> from [-10s -100s] to [10s 100s] at (1) is [10s 100s]
+PASS Web Animations: property <--time-list> from [-10s -100s] to [10s 100s] at (1.5) is [20s 200s]
+PASS Web Animations: property <--time-list> from [10s] to [100s] at (-0.3) is [-17s]
+PASS Web Animations: property <--time-list> from [10s] to [100s] at (0) is [10s]
+PASS Web Animations: property <--time-list> from [10s] to [100s] at (0.5) is [55s]
+PASS Web Animations: property <--time-list> from [10s] to [100s] at (1) is [100s]
+PASS Web Animations: property <--time-list> from [10s] to [100s] at (1.5) is [145s]
+PASS Web Animations: property <--time-list> from [0s 1000ms] to [1000ms 0s] at (-0.3) is [-0.3s 1.3s]
+PASS Web Animations: property <--time-list> from [0s 1000ms] to [1000ms 0s] at (0) is [0s 1s]
+PASS Web Animations: property <--time-list> from [0s 1000ms] to [1000ms 0s] at (0.5) is [0.5s 0.5s]
+PASS Web Animations: property <--time-list> from [0s 1000ms] to [1000ms 0s] at (1) is [1s 0s]
+PASS Web Animations: property <--time-list> from [0s 1000ms] to [1000ms 0s] at (1.5) is [1.5s -0.5s]
+FAIL Web Animations: property <--time-list> from neutral to [20s 200s] at (-0.3) is [-6s -60s] assert_equals: expected "7s 70s " but got "- 6s - 60s "
+FAIL Web Animations: property <--time-list> from neutral to [20s 200s] at (0) is [0s 0s] assert_equals: expected "10s 100s " but got "0s 0s "
+FAIL Web Animations: property <--time-list> from neutral to [20s 200s] at (0.5) is [10s 100s] assert_equals: expected "15s 150s " but got "10s 100s "
+PASS Web Animations: property <--time-list> from neutral to [20s 200s] at (1) is [20s 200s]
+FAIL Web Animations: property <--time-list> from neutral to [20s 200s] at (1.5) is [30s 300s] assert_equals: expected "25s 250s " but got "30s 300s "
+FAIL Compositing: property <--time-list> underlying [50s 60s] from add [10s 140s] to add [110s 40s] at (-0.3) is [-20s 170s] assert_equals: expected "30s 230s " but got "- 20s 170s "
+FAIL Compositing: property <--time-list> underlying [50s 60s] from add [10s 140s] to add [110s 40s] at (0) is [10s 140s] assert_equals: expected "60s 200s " but got "10s 140s "
+FAIL Compositing: property <--time-list> underlying [50s 60s] from add [10s 140s] to add [110s 40s] at (0.5) is [60s 90s] assert_equals: expected "110s 150s " but got "60s 90s "
+FAIL Compositing: property <--time-list> underlying [50s 60s] from add [10s 140s] to add [110s 40s] at (1) is [110s 40s] assert_equals: expected "160s 100s " but got "110s 40s "
+FAIL Compositing: property <--time-list> underlying [50s 60s] from add [10s 140s] to add [110s 40s] at (1.5) is [160s -10s] assert_equals: expected "210s 50s " but got "160s - 10s "
+FAIL Compositing: property <--time-list> underlying [50s 60s] from add [10s 140s] to replace [110s 40s] at (-0.3) is [-20s 170s] assert_equals: expected "45s 248s " but got "- 20s 170s "
+FAIL Compositing: property <--time-list> underlying [50s 60s] from add [10s 140s] to replace [110s 40s] at (0) is [10s 140s] assert_equals: expected "60s 200s " but got "10s 140s "
+FAIL Compositing: property <--time-list> underlying [50s 60s] from add [10s 140s] to replace [110s 40s] at (0.5) is [60s 90s] assert_equals: expected "85s 120s " but got "60s 90s "
+PASS Compositing: property <--time-list> underlying [50s 60s] from add [10s 140s] to replace [110s 40s] at (1) is [110s 40s]
+FAIL Compositing: property <--time-list> underlying [50s 60s] from add [10s 140s] to replace [110s 40s] at (1.5) is [160s -10s] assert_equals: expected "135s - 40s " but got "160s - 10s "
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/animations/custom-properties/custom-time-list-type-interpolation.html b/third_party/WebKit/LayoutTests/animations/custom-properties/custom-time-list-type-interpolation.html
new file mode 100644
index 0000000..5eb7b1d7
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/animations/custom-properties/custom-time-list-type-interpolation.html
@@ -0,0 +1,136 @@
+<!DOCTYPE html>
+<meta charset="UTF-8">
+<style>
+.parent {
+  --time-list: 30s 300s;
+}
+.target {
+  --time-list: 10s 100s;
+}
+</style>
+<body>
+<script src="../interpolation/resources/interpolation-test.js"></script>
+<script>
+CSS.registerProperty({
+  name: '--time-list',
+  syntax: '<time>+',
+  initialValue: '40s 400s',
+  inherits: false,
+});
+
+assertInterpolation({
+  property: '--time-list',
+  from: 'initial',
+  to: '20s 200s',
+}, [
+  {at: -0.3, is: '46s 460s'},
+  {at: 0, is: '40s 400s'},
+  {at: 0.5, is: '30s 300s'},
+  {at: 1, is: '20s 200s'},
+  {at: 1.5, is: '10s 100s'},
+]);
+
+assertInterpolation({
+  property: '--time-list',
+  from: 'inherit',
+  to: '20s 200s',
+}, [
+  {at: -0.3, is: '33s 330s'},
+  {at: 0, is: '30s 300s'},
+  {at: 0.5, is: '25s 250s'},
+  {at: 1, is: '20s 200s'},
+  {at: 1.5, is: '15s 150s'},
+]);
+
+assertInterpolation({
+  property: '--time-list',
+  from: 'unset',
+  to: '20s 200s',
+}, [
+  {at: -0.3, is: '46s 460s'},
+  {at: 0, is: '40s 400s'},
+  {at: 0.5, is: '30s 300s'},
+  {at: 1, is: '20s 200s'},
+  {at: 1.5, is: '10s 100s'},
+]);
+
+assertInterpolation({
+  property: '--time-list',
+  from: '-10s -100s',
+  to: '10s 100s',
+}, [
+  {at: -0.3, is: '-16s -160s'},
+  {at: 0, is: '-10s -100s'},
+  {at: 0.5, is: '0s 0s'},
+  {at: 1, is: '10s 100s'},
+  {at: 1.5, is: '20s 200s'}
+]); 
+
+assertInterpolation({
+  property: '--time-list',
+  from: '10s',
+  to: '100s',
+}, [
+  {at: -0.3, is: '-17s'},
+  {at: 0, is: '10s'},
+  {at: 0.5, is: '55s'},
+  {at: 1, is: '100s'},
+  {at: 1.5, is: '145s'}
+]);
+
+assertInterpolation({
+  property: '--time-list',
+  from: '0s 1000ms',
+  to: '1000ms 0s',
+}, [
+  {at: -0.3, is: '-0.3s 1.3s'},
+  {at: 0, is: '0s 1s'},
+  {at: 0.5, is: '0.5s 0.5s'},
+  {at: 1, is: '1s 0s'},
+  {at: 1.5, is: '1.5s -0.5s'}
+]);
+
+// Composition and neutralKeyframe tests assume that composite:add means
+// component-wise addition, which may or may not be the behavior we want.
+// https://github.com/w3c/css-houdini-drafts/issues/799
+
+assertInterpolation({
+  property: '--time-list',
+  from: neutralKeyframe,
+  to: '20s 200s',
+}, [
+  {at: -0.3, is: '7s 70s'},
+  {at: 0, is: '10s 100s'},
+  {at: 0.5, is: '15s 150s'},
+  {at: 1, is: '20s 200s'},
+  {at: 1.5, is: '25s 250s'},
+]);
+
+assertComposition({
+  property: '--time-list',
+  underlying: '50s 60s',
+  addFrom: '10s 140s',
+  addTo: '110s 40s',
+}, [
+  {at: -0.3, is: '30s 230s'},
+  {at: 0, is: '60s 200s'},
+  {at: 0.5, is: '110s 150s'},
+  {at: 1, is: '160s 100s'},
+  {at: 1.5, is: '210s 50s'},
+]);
+
+assertComposition({
+  property: '--time-list',
+  underlying: '50s 60s',
+  addFrom: '10s 140s',
+  replaceTo: '110s 40s',
+}, [
+  {at: -0.3, is: '45s 248s'},
+  {at: 0, is: '60s 200s'},
+  {at: 0.5, is: '85s 120s'},
+  {at: 1, is: '110s 40s'},
+  {at: 1.5, is: '135s -40s'},
+]);
+
+</script>
+</body>
diff --git a/third_party/WebKit/LayoutTests/fast/masking/clip-path-inset-large-radii.html b/third_party/WebKit/LayoutTests/css3/masking/clip-path-inset-large-radii.html
similarity index 100%
rename from third_party/WebKit/LayoutTests/fast/masking/clip-path-inset-large-radii.html
rename to third_party/WebKit/LayoutTests/css3/masking/clip-path-inset-large-radii.html
diff --git a/third_party/WebKit/LayoutTests/css3/masking/clip-path-selection.html b/third_party/WebKit/LayoutTests/css3/masking/clip-path-selection.html
new file mode 100644
index 0000000..b08677d
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/css3/masking/clip-path-selection.html
@@ -0,0 +1,61 @@
+<!DOCTYPE html>
+<title>Selection on element with clip-path</title>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<style>
+body {
+    width: 600px;
+    font: 18px/1 Ahem;
+}
+
+#triangleLeft {
+    position: absolute;
+    top: 0px;
+    left: 0px;
+    width: 400px;
+    height: 400px;
+    background: green;
+    clip-path: polygon(0 0, 100% 50%, 0 100%);
+}
+</style>
+
+<div id="triangleLeft">Although the Culture was originated by humanoid species, subsequent interactions with other civilizations have introduced many non-humanoid species into the Culture (including some former enemy civilizations), though the majority of the biological Culture is still pan-human. Little uniformity exists in the Culture, and its citizens are such by choice, free to change physical form and even species (though some stranger biological conversions are irreversible, and conversion from biological to artificial sentience is considered to be what is known as an Unusual Life Choice). All members are also free to join, leave, and rejoin, or indeed declare themselves to be, say, 80% Culture.</div>
+
+<script>
+function drag(from, to) {
+  assert_exists(window, 'chrome');
+  assert_exists(window.chrome, 'gpuBenchmarking');
+  return new Promise((resolve) => {
+    chrome.gpuBenchmarking.pointerActionSequence(
+      [{
+        source: 'mouse',
+        actions: [
+          {name: 'pointerMove', x: from.x, y: from.y},
+          {name: 'pointerDown', x: from.x, y: from.y, button: 'left'},
+          {name: 'pointerMove', x: to.x, y: to.y},
+          {name: 'pointerUp', button: 'left'}
+        ]
+      }],
+      resolve);
+  });
+}
+
+promise_test(function() {
+  var sel = window.getSelection();
+  sel.removeAllRanges();
+  // select part of the polygon, not the text
+  return drag({ x: 180, y: 40 }, { x: 200, y: 40 }).then(() => {
+    assert_equals(sel.toString(), "");
+  });
+}, document.title + ', outside clip / inside unclipped element');
+
+promise_test(function() {
+  var sel = window.getSelection();
+  sel.removeAllRanges();
+  // select 'Little' using select drag
+  return drag({ x: 0, y: 260 }, { x: 100, y: 260 }).then(() => {
+    // verify that 'Little' was selected
+    assert_equals(sel.toString(), "Little");
+  });
+}, document.title + ', inside clip / inside unclipped element');
+</script>
diff --git a/third_party/WebKit/LayoutTests/fast/masking/mask-serializing-expected.txt b/third_party/WebKit/LayoutTests/css3/masking/mask-serializing-expected.txt
similarity index 100%
rename from third_party/WebKit/LayoutTests/fast/masking/mask-serializing-expected.txt
rename to third_party/WebKit/LayoutTests/css3/masking/mask-serializing-expected.txt
diff --git a/third_party/WebKit/LayoutTests/fast/masking/mask-serializing.html b/third_party/WebKit/LayoutTests/css3/masking/mask-serializing.html
similarity index 100%
rename from third_party/WebKit/LayoutTests/fast/masking/mask-serializing.html
rename to third_party/WebKit/LayoutTests/css3/masking/mask-serializing.html
diff --git a/third_party/WebKit/LayoutTests/fast/masking/parsing-clip-path-iri.html b/third_party/WebKit/LayoutTests/css3/masking/parsing-clip-path-iri.html
similarity index 100%
rename from third_party/WebKit/LayoutTests/fast/masking/parsing-clip-path-iri.html
rename to third_party/WebKit/LayoutTests/css3/masking/parsing-clip-path-iri.html
diff --git a/third_party/WebKit/LayoutTests/fast/masking/parsing-clip-path-shape.html b/third_party/WebKit/LayoutTests/css3/masking/parsing-clip-path-shape.html
similarity index 87%
rename from third_party/WebKit/LayoutTests/fast/masking/parsing-clip-path-shape.html
rename to third_party/WebKit/LayoutTests/css3/masking/parsing-clip-path-shape.html
index a175eeb..dea5c58ce 100644
--- a/third_party/WebKit/LayoutTests/fast/masking/parsing-clip-path-shape.html
+++ b/third_party/WebKit/LayoutTests/css3/masking/parsing-clip-path-shape.html
@@ -1,14 +1,13 @@
 <!DOCTYPE html>
-<html>
+<title>clip-path shapes accept different length units</title>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
 <style>
 * { font-size: 16px; }
 div { font-size: 8px; }
 </style>
 <body>
-<script src="../../resources/js-test.js"></script>
 <script>
-description('Test that clip-path shapes accept different length units');
-
 function computedStyle(property, value) {
     var div = document.createElement("div");
     document.body.appendChild(div);
@@ -18,21 +17,22 @@
     return computedValue;
 }
 
-function innerStyle(property, value) {
+function specifiedStyle(property, value) {
     var div = document.createElement("div");
     div.style.setProperty(property, value);
     return div.style.getPropertyValue(property);
 }
 
 function testComputed(property, value, expected) {
-    shouldBeEqualToString('computedStyle("' + property + '", "' + value + '")', expected);
+    test(function() {
+        assert_equals(computedStyle(property, value), expected);
+    }, document.title + ', ' + property + ': ' + value + ' (computed)');
 }
 
 function testInner(property, value, expected) {
-    if (expected === null)
-        shouldBeNull('innerStyle("' + property + '", "' + value + '")');
-    else
-        shouldBeEqualToString('innerStyle("' + property + '", "' + value + '")', expected);
+    test(function() {
+        assert_equals(specifiedStyle(property, value), expected);
+    }, document.title + ', ' + property + ': ' + value + ' (specified)');
 }
 
 function negativeTest(property, value) {
@@ -93,7 +93,5 @@
 negativeTest("-webkit-clip-path", "polygon()");
 negativeTest("-webkit-clip-path", "polygon(evenodd)");
 negativeTest("-webkit-clip-path", "polygon(nonzero)");
-
 </script>
 </body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/css3/masking/parsing-mask-source-type.html b/third_party/WebKit/LayoutTests/css3/masking/parsing-mask-source-type.html
new file mode 100644
index 0000000..041e2894
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/css3/masking/parsing-mask-source-type.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<title>Parsing/serializing 'mask-source-type'</title>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<style>
+  * { font-size: 16px; }
+  div { font-size: 8px; }
+</style>
+<body>
+<script>
+function computedValue(value) {
+    var div = document.createElement('div');
+    document.body.appendChild(div);
+    div.style.setProperty("-webkit-mask-image", "none, none, none, none");
+    div.style.setProperty("mask-source-type", value);
+    var computedValue = getComputedStyle(div).getPropertyValue("mask-source-type");
+    document.body.removeChild(div);
+    return computedValue;
+}
+
+function makeTest(value, expected) {
+    test(function() {
+        assert_equals(computedValue(value), expected);
+    }, document.title + ', ' + value);
+}
+
+makeTest("alpha, alpha, alpha, alpha", "alpha, alpha, alpha, alpha");
+makeTest("luminance, alpha", "luminance, alpha, alpha, alpha");
+makeTest("luminance, luminance, luminance, luminance",
+         "luminance, luminance, luminance, luminance");
+makeTest("auto, alpha, luminance, luminance",
+         "alpha, alpha, luminance, luminance");
+</script>
+</body>
diff --git a/third_party/WebKit/LayoutTests/fast/masking/parsing-mask.html b/third_party/WebKit/LayoutTests/css3/masking/parsing-mask.html
similarity index 92%
rename from third_party/WebKit/LayoutTests/fast/masking/parsing-mask.html
rename to third_party/WebKit/LayoutTests/css3/masking/parsing-mask.html
index c865a51..3a96056 100644
--- a/third_party/WebKit/LayoutTests/fast/masking/parsing-mask.html
+++ b/third_party/WebKit/LayoutTests/css3/masking/parsing-mask.html
@@ -1,14 +1,13 @@
 <!DOCTYPE html>
-<html>
+<title>Parsing/serializing the '(-webkit-)mask property</title>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
 <style>
 * { font-size: 16px; }
 div { font-size: 8px; }
 </style>
 <body>
-<script src="../../resources/js-test.js"></script>
 <script>
-description('Test that clip-path shapes accept different length units');
-
 function computedStyle(property, value) {
     var div = document.createElement("div");
     document.body.appendChild(div);
@@ -18,21 +17,22 @@
     return computedValue;
 }
 
-function innerStyle(property, value) {
+function specifiedStyle(property, value) {
     var div = document.createElement("div");
     div.style.setProperty(property, value);
     return div.style.getPropertyValue(property);
 }
 
 function testComputed(property, value, expected) {
-    shouldBeEqualToString('computedStyle("' + property + '", "' + value + '")', expected);
+    test(function() {
+        assert_equals(computedStyle(property, value), expected);
+    }, document.title + ', ' + property + ': ' + value + ' (computed)');
 }
 
 function testInner(property, value, expected) {
-    if (expected === null)
-        shouldBeNull('innerStyle("' + property + '", "' + value + '")');
-    else
-        shouldBeEqualToString('innerStyle("' + property + '", "' + value + '")', expected);
+    test(function() {
+        assert_equals(specifiedStyle(property, value), expected);
+    }, document.title + ', ' + property + ': ' + value + ' (specified)');
 }
 
 function negativeTest(property, value) {
@@ -60,7 +60,6 @@
 testInner("-webkit-mask", "none right", "none right center");
 testInner("-webkit-mask", "none top right", "none right top");
 testInner("-webkit-mask", "none bottom left", "none left bottom");
-testInner("-webkit-mask", "none right", "none right center");
 testInner("-webkit-mask", "none left", "none left center");
 testInner("-webkit-mask", "center 50%", "center 50%");
 testInner("-webkit-mask", "50px 50%", "50px 50%");
@@ -131,8 +130,6 @@
 
 // combinations
 testInner("-webkit-mask", "none padding-box content-box", "none padding-box content-box");
-testInner("-webkit-mask", "none padding-box", "none padding-box padding-box");
-testInner("-webkit-mask", "none top", "none center top");
 testInner("-webkit-mask", "none center right 20px", "none right 20px center");
 testInner("-webkit-mask", "none border-box left top", "none left top border-box border-box");
 testInner("-webkit-mask", "none border-box left top 20px", "none left top 20px border-box border-box");
@@ -181,7 +178,5 @@
 negativeTest("mask-source-type", "");
 negativeTest("mask-source-type", "center");
 negativeTest("mask-source-type", "repeat");
-
 </script>
 </body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/css3/motion-path/offset.html b/third_party/WebKit/LayoutTests/css3/motion-path/offset.html
index 1125250..5a732db4 100644
--- a/third_party/WebKit/LayoutTests/css3/motion-path/offset.html
+++ b/third_party/WebKit/LayoutTests/css3/motion-path/offset.html
@@ -41,10 +41,10 @@
 }, 'offset supports various angle units');
 
 test(function() {
-    assert_equals(getComputedStyle(div3, null).offsetPath, 'path("M 10 20 h 30 v 150")');
+    assert_equals(getComputedStyle(div3, null).offsetPath, 'path("M 10 20 H 40 V 170")');
     assert_equals(getComputedStyle(div3, null).offsetDistance, '70px');
     assert_equals(getComputedStyle(div3, null).offsetRotate, '0deg');
-    assert_equals(getComputedStyle(div3, null).offset, 'path("M 10 20 h 30 v 150") 70px 0deg');
+    assert_equals(getComputedStyle(div3, null).offset, 'path("M 10 20 H 40 V 170") 70px 0deg');
     assert_equals(getComputedStyle(div3, null).transform, 'matrix(1, 0, 0, 1, 0, 0)');
 }, 'offset supports SVG path data');
 
diff --git a/third_party/WebKit/LayoutTests/external/wpt/background-fetch/dangling-markup.https.window.js b/third_party/WebKit/LayoutTests/external/wpt/background-fetch/dangling-markup.https.window.js
deleted file mode 100644
index 764257d..0000000
--- a/third_party/WebKit/LayoutTests/external/wpt/background-fetch/dangling-markup.https.window.js
+++ /dev/null
@@ -1,17 +0,0 @@
-// META: script=/service-workers/service-worker/resources/test-helpers.sub.js
-// META: script=resources/utils.js
-'use strict';
-
-// "If request's url's potentially-dangling-markup flag is set, and request's
-// url's scheme is an HTTP(S) scheme, then set response to a network error."
-// https://github.com/whatwg/fetch/pull/519
-// https://github.com/whatwg/fetch/issues/546
-
-// This is not a comprehensive test of dangling markup detection - it is just
-// intended to check that detection is enabled.
-
-backgroundFetchTest((t, bgFetch) => {
-  return promise_rejects(
-      t, new TypeError(),
-      bgFetch.fetch(uniqueId(), 'https://example.com/?\n<'));
-}, 'fetch to URL containing \\n and < should reject');
diff --git a/third_party/WebKit/LayoutTests/external/wpt/background-fetch/fetch.https.window.js b/third_party/WebKit/LayoutTests/external/wpt/background-fetch/fetch.https.window.js
index 70dacd7..95990e0 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/background-fetch/fetch.https.window.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/background-fetch/fetch.https.window.js
@@ -139,7 +139,6 @@
   assert_equals(results.length, 1);
 
   assert_equals(eventRegistration.id, registration.id);
-  assert_equals(eventRegistration.state, 'success');
   assert_equals(eventRegistration.failureReason, '');
 
   for (const result of results) {
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-grid/abspos/absolute-positioning-grid-container-parent-001-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/css/css-grid/abspos/absolute-positioning-grid-container-parent-001-expected.txt
deleted file mode 100644
index 9b3a658..0000000
--- a/third_party/WebKit/LayoutTests/external/wpt/css/css-grid/abspos/absolute-positioning-grid-container-parent-001-expected.txt
+++ /dev/null
@@ -1,51 +0,0 @@
-This is a testharness.js-based test.
-FAIL .container 1 assert_equals: 
-<div class="container relative">
-  <div class="grid">
-    <div class="sizedToGridArea absolute autoRowAutoColumn" data-offset-x="50" data-offset-y="10" data-expected-width="500" data-expected-height="400"></div>
-  </div>
-</div>
-offsetLeft expected 50 but got 35
-FAIL .container 2 assert_equals: 
-<div class="container relative">
-  <div class="grid">
-    <div class="sizedToGridArea absolute firstRowFirstColumn" data-offset-x="50" data-offset-y="10" data-expected-width="500" data-expected-height="400"></div>
-  </div>
-</div>
-offsetLeft expected 50 but got 35
-FAIL .container 3 assert_equals: 
-<div class="container relative">
-  <div class="grid">
-    <div class="sizedToGridArea absolute secondRowSecondColumn" data-offset-x="50" data-offset-y="10" data-expected-width="500" data-expected-height="400"></div>
-  </div>
-</div>
-offsetLeft expected 50 but got 35
-PASS .container 4
-PASS .container 5
-PASS .container 6
-FAIL .container 7 assert_equals: 
-<div class="container relative">
-  <div class="grid directionRTL">
-    <div class="sizedToGridArea absolute autoRowAutoColumn" data-offset-x="-150" data-offset-y="10" data-expected-width="500" data-expected-height="400"></div>
-  </div>
-</div>
-offsetLeft expected -150 but got -135
-FAIL .container 8 assert_equals: 
-<div class="container relative">
-  <div class="grid directionRTL">
-    <div class="sizedToGridArea absolute firstRowFirstColumn" data-offset-x="-150" data-offset-y="10" data-expected-width="500" data-expected-height="400"></div>
-  </div>
-</div>
-offsetLeft expected -150 but got -135
-FAIL .container 9 assert_equals: 
-<div class="container relative">
-  <div class="grid directionRTL">
-    <div class="sizedToGridArea absolute secondRowSecondColumn" data-offset-x="-150" data-offset-y="10" data-expected-width="500" data-expected-height="400"></div>
-  </div>
-</div>
-offsetLeft expected -150 but got -135
-PASS .container 10
-PASS .container 11
-PASS .container 12
-Harness: the test ran to completion.
-
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-tables/width-distribution/computing-column-measure-2.html b/third_party/WebKit/LayoutTests/external/wpt/css/css-tables/width-distribution/computing-column-measure-2.html
new file mode 100644
index 0000000..b9840187
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/css-tables/width-distribution/computing-column-measure-2.html
@@ -0,0 +1,81 @@
+<!doctype html>
+<script src='/resources/testharness.js'></script>
+<script src='/resources/testharnessreport.js'></script>
+<link rel="author" title="Richard Townsend" href="Richard.Townsend@arm.com" />
+<link rel="help" href="https://drafts.csswg.org/css-tables-3/#computing-column-measures" />
+<style type="text/css">
+td {
+    padding: 0;
+}
+div {
+    /* Bug does not happen when the table's containing block is less
+       than (100+200+300)=600px, so make sure sure that it's larger. */
+    width: 750px;
+}
+</style>
+<div>
+    <h1>Width Distribution</h1>
+    <h4>"Computing column measures"</h4>
+    <p>This is testing that the table's auto columns are correctly recalculated after removing and adding a <code>col</code> element.
+         <a href="https://www.w3.org/TR/CSS2/tables.html#auto-table-layout">Spec Text</a></p>
+
+    <hr/>
+
+    <table id="one" style="border: 1px solid orange">
+        <colgroup>
+            <col style="width: 100px" />
+            <col style="width: 200px" />
+            <col style="width: 300px;" id="hideable" />
+        </colgroup>
+        <tr>
+            <td>100</td>
+            <td>200</td>
+            <td>300</td>
+        </tr>
+    </table>
+
+    <table id="two" style="border: 1px solid orange">
+        <colgroup>
+            <col style="width: 100px; display: none;" id="displayable" />
+            <col style="width: 200px;" />
+            <col style="width: auto;" />
+        </colgroup>
+        <tr>
+            <td>100</td>
+            <td>200</td>
+            <td>300</td>
+        </tr>
+    </table>
+
+    <table id="ref" style="border: 1px solid orange">
+        <colgroup>
+            <col style="width: 100px;" />
+            <col style="width: 200px;" />
+            <col style="width: auto;" />
+        </colgroup>
+        <tr>
+            <td>100</td>
+            <td>200</td>
+            <td>300</td>
+        </tr>
+    </table>
+
+</div>
+<script>
+    test(function() {
+        var one = document.getElementById('one');
+        var two = document.getElementById('two');
+        var ref = document.getElementById('ref');
+
+        // Force initial layout.
+        assert_greater_than(one.offsetWidth, ref.offsetWidth);
+
+        // Display two's colgroup and hide one's.
+        document.getElementById('displayable').style.display = 'table-column';
+        document.getElementById('hideable').style.display = 'none';
+
+        // Both tables should now match the reference.
+        assert_equals(one.offsetWidth, ref.offsetWidth);
+        assert_equals(two.offsetWidth, ref.offsetWidth);
+    }, "Table recalculations should match the reference");
+</script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/motion/offset-path-serialization.html b/third_party/WebKit/LayoutTests/external/wpt/css/motion/offset-path-serialization.html
new file mode 100644
index 0000000..1c00091
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/motion/offset-path-serialization.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Motion Path Module Level 1: path serialization</title>
+<link rel="author" title="Eric Willigers" href="mailto:ericwilligers@chromium.org">
+<link rel="help" href="https://drafts.fxtf.org/motion-1/">
+<h:meta name="assert" content="computed offset-path is serialized using absolute commands"/>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+<div id="target"></div>
+<script>
+'use strict';
+
+test(function() {
+  var target = document.getElementById('target');
+
+  target.style.offsetPath = 'path("m 10 20 q 30 60 40 50 q 100 70 90 80")';
+  assert_equals(target.style.offsetPath, 'path("m 10 20 q 30 60 40 50 q 100 70 90 80")');
+  assert_equals(getComputedStyle(target).offsetPath, 'path("M 10 20 Q 40 80 50 70 Q 150 140 140 150")');
+
+  target.style.offsetPath = "path('M 0 0 L 100 100 m 0 100 l 100 0 Z l 160 20 Z')";
+  assert_equals(target.style.offsetPath, 'path("M 0 0 L 100 100 m 0 100 l 100 0 Z l 160 20 Z")');
+  assert_equals(getComputedStyle(target).offsetPath, 'path("M 0 0 L 100 100 M 100 200 L 200 200 Z L 260 220 Z")');
+
+  target.style.offsetPath = 'path("m 10 20   l 20 30   Z   l 50 60   Z   m 70 80   l 90 60   Z   t 70 120")';
+  assert_equals(target.style.offsetPath, 'path("m 10 20 l 20 30 Z l 50 60 Z m 70 80 l 90 60 Z t 70 120")');
+  assert_equals(getComputedStyle(target).offsetPath, 'path("M 10 20 L 30 50 Z L 60 80 Z M 80 100 L 170 160 Z T 150 220")');
+
+  target.style.offsetPath = 'path("m 10.0 170.0 h 90.00 v 30.00 m 0 0 s 1 2 3 4 z c 9 8 7 6 5 4")';
+  assert_equals(target.style.offsetPath, 'path("m 10 170 h 90 v 30 m 0 0 s 1 2 3 4 Z c 9 8 7 6 5 4")');
+  assert_equals(getComputedStyle(target).offsetPath, 'path("M 10 170 H 100 V 200 M 100 200 S 101 202 103 204 Z C 109 208 107 206 105 204")');
+
+  target.style.offsetPath = '  path(  "m 10 20 a 10 20 30 1 0 40 50 a 110 120 30 1 1 140 50"  )  ';
+  assert_equals(target.style.offsetPath, 'path("m 10 20 a 10 20 30 1 0 40 50 a 110 120 30 1 1 140 50")');
+  assert_equals(getComputedStyle(target).offsetPath, 'path("M 10 20 A 10 20 30 1 0 50 70 A 110 120 30 1 1 190 120")');
+});
+</script>
+</body>
+</html>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/motion/parsing/offset-parsing-valid.html b/third_party/WebKit/LayoutTests/external/wpt/css/motion/parsing/offset-parsing-valid.html
index b93091b2..3fe8a5b 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/css/motion/parsing/offset-parsing-valid.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/motion/parsing/offset-parsing-valid.html
@@ -30,11 +30,11 @@
 test_valid_value("offset", "path(\"M 0 0 H 1\") auto");
 test_valid_value("offset", "path('M 0 0 H 1') reverse 30deg 50px", "path(\"M 0 0 H 1\") 50px reverse 30deg");
 test_valid_value("offset", "path(\"M 0 0 H 1\")");
-test_valid_value("offset", "path('m 0 0 h 100') -7rad 8px / auto", "path(\"m 0 0 h 100\") 8px -7rad / auto");
-test_valid_value("offset", "path('m 0 0 h 100') -7rad 8px / left top", "path(\"m 0 0 h 100\") 8px -7rad / left top");
+test_valid_value("offset", "path('m 20 0 h 100') -7rad 8px / auto", "path(\"m 20 0 h 100\") 8px -7rad / auto");
+test_valid_value("offset", "path('m 0 30 v 100') -7rad 8px / left top", "path(\"m 0 30 v 100\") 8px -7rad / left top");
 test_valid_value("offset", "path('m 0 0 h 100') -7rad 8px", "path(\"m 0 0 h 100\") 8px -7rad");
-test_valid_value("offset", "path(\"m 0 0 h 100\") 100px 0deg");
-test_valid_value("offset", "path('m 1 2 v 3 Z')", "path(\"m 1 2 v 3 Z\")");
+test_valid_value("offset", "path(\"M 0 0 H 100\") 100px 0deg");
+test_valid_value("offset", "path(  'm 1 2   v 3.00 z')", "path(\"m 1 2 v 3 Z\")");
 test_valid_value("offset", "ray(farthest-corner 90deg) 1%", "ray(90deg farthest-corner) 1%");
 test_valid_value("offset", "ray(sides 0deg) 50% 90deg auto", "ray(0deg sides) 50% auto 90deg");
 test_valid_value("offset", "right bottom / left top");
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/motion/parsing/offset-path-parsing-valid-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/css/motion/parsing/offset-path-parsing-valid-expected.txt
index 8a2d22a..ab96dc8 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/css/motion/parsing/offset-path-parsing-valid-expected.txt
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/motion/parsing/offset-path-parsing-valid-expected.txt
@@ -6,8 +6,13 @@
 PASS e.style['offset-path'] = "ray(270deg farthest-corner contain)" should set the property value
 PASS e.style['offset-path'] = "ray(-720deg sides)" should set the property value
 PASS e.style['offset-path'] = "ray(calc(180deg - 45deg) farthest-side)" should set the property value
-PASS e.style['offset-path'] = "path(\"m 0 0 h -100\")" should set the property value
+PASS e.style['offset-path'] = "path(\"m 20 0 h -100\")" should set the property value
 PASS e.style['offset-path'] = "path(\"M 0 0 L 100 100 M 100 200 L 200 200 Z L 300 300 Z\")" should set the property value
+PASS e.style['offset-path'] = "path(\"m 10 20 q 30 60 40 50 q 100 70 90 80\")" should set the property value
+PASS e.style['offset-path'] = "path(\"M 0 0 L 100 100 m 0 100 l 100 0 Z l 160 20 Z\")" should set the property value
+PASS e.style['offset-path'] = "path(\"m 10 20 l 20 30 Z l 50 60 Z m 70 80 l 90 60 Z t 70 120\")" should set the property value
+PASS e.style['offset-path'] = "path(\"m 10 170 h 90 v 30 m 0 0 s 1 2 3 4 z c 9 8 7 6 5 4\")" should set the property value
+PASS e.style['offset-path'] = "path(\"m 10 20 a 10 20 30 1 0 40 50 a 110 120 30 1 1 140 50\")" should set the property value
 FAIL e.style['offset-path'] = "url(\"http://www.example.com/index.html#polyline1\")" should set the property value assert_not_equals: property should be set got disallowed value ""
 FAIL e.style['offset-path'] = "circle(100px)" should set the property value assert_not_equals: property should be set got disallowed value ""
 FAIL e.style['offset-path'] = "margin-box" should set the property value assert_not_equals: property should be set got disallowed value ""
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/motion/parsing/offset-path-parsing-valid.html b/third_party/WebKit/LayoutTests/external/wpt/css/motion/parsing/offset-path-parsing-valid.html
index bda8292..0ed360f 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/css/motion/parsing/offset-path-parsing-valid.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/motion/parsing/offset-path-parsing-valid.html
@@ -21,8 +21,13 @@
 test_valid_value("offset-path", "ray(-720deg sides)");
 test_valid_value("offset-path", "ray(calc(180deg - 45deg) farthest-side)", "ray(calc(135deg) farthest-side)");
 
-test_valid_value("offset-path", 'path("m 0 0 h -100")');
+test_valid_value("offset-path", 'path("m 20 0 h -100")');
 test_valid_value("offset-path", 'path("M 0 0 L 100 100 M 100 200 L 200 200 Z L 300 300 Z")');
+test_valid_value("offset-path", 'path("m 10 20 q 30 60 40 50 q 100 70 90 80")');
+test_valid_value("offset-path", 'path("M 0 0 L 100 100 m 0 100 l 100 0 Z l 160 20 Z")');
+test_valid_value("offset-path", 'path("m 10 20 l 20 30 Z l 50 60 Z m 70 80 l 90 60 Z t 70 120")');
+test_valid_value("offset-path", 'path("m 10 170 h 90 v 30 m 0 0 s 1 2 3 4 z c 9 8 7 6 5 4")', 'path("m 10 170 h 90 v 30 m 0 0 s 1 2 3 4 Z c 9 8 7 6 5 4")');
+test_valid_value("offset-path", 'path("m 10 20 a 10 20 30 1 0 40 50 a 110 120 30 1 1 140 50")');
 
 test_valid_value("offset-path", 'url("http://www.example.com/index.html#polyline1")');
 
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/OWNERS b/third_party/WebKit/LayoutTests/external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/OWNERS
new file mode 100644
index 0000000..5f43f911
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/OWNERS
@@ -0,0 +1,4 @@
+# TEAM: layout-dev@chromium.org
+# COMPONENT: Blink>Layout
+# WPT-NOTIFY: true
+rego@igalia.com
diff --git a/third_party/WebKit/LayoutTests/external/wpt/svg/path/property/getComputedStyle.svg b/third_party/WebKit/LayoutTests/external/wpt/svg/path/property/getComputedStyle.svg
index 58301919..392c570 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/svg/path/property/getComputedStyle.svg
+++ b/third_party/WebKit/LayoutTests/external/wpt/svg/path/property/getComputedStyle.svg
@@ -10,7 +10,7 @@
       d: path('M 10 3 H 30');
     }
     .g5 {
-      d: path('M 10 5 H 50');
+      d: path('m 10 5 h 40');
     }
     .p6 {
       d: inherit;
diff --git a/third_party/WebKit/LayoutTests/external/wpt/svg/path/property/serialization.svg b/third_party/WebKit/LayoutTests/external/wpt/svg/path/property/serialization.svg
new file mode 100644
index 0000000..297f8ede68
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/svg/path/property/serialization.svg
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg xmlns="http://www.w3.org/2000/svg"
+     xmlns:h="http://www.w3.org/1999/xhtml">
+  <metadata>
+    <h:link rel="help" href="https://svgwg.org/svg2-draft/paths.html#TheDProperty"/>
+    <h:meta name="assert" content="computed d is serialized using absolute commands"/>
+  </metadata>
+  <path id="target"></path>
+  <h:script src="/resources/testharness.js"/>
+  <h:script src="/resources/testharnessreport.js"/>
+  <script><![CDATA[
+  test(function() {
+    var target = document.getElementById('target');
+
+    target.style.d = 'path("m 10 20 q 30 60 40 50 q 100 70 90 80")';
+    assert_equals(target.style.d, 'path("m 10 20 q 30 60 40 50 q 100 70 90 80")');
+    assert_equals(getComputedStyle(target).d, 'path("M 10 20 Q 40 80 50 70 Q 150 140 140 150")');
+
+    target.style.d = "path('M 0 0 L 100 100 m 0 100 l 100 0 Z l 160 20 Z')";
+    assert_equals(target.style.d, 'path("M 0 0 L 100 100 m 0 100 l 100 0 Z l 160 20 Z")');
+    assert_equals(getComputedStyle(target).d, 'path("M 0 0 L 100 100 M 100 200 L 200 200 Z L 260 220 Z")');
+
+    target.style.d = 'path("m 10 20   l 20 30   Z   l 50 60   Z   m 70 80   l 90 60   Z   t 70 120")';
+    assert_equals(target.style.d, 'path("m 10 20 l 20 30 Z l 50 60 Z m 70 80 l 90 60 Z t 70 120")');
+    assert_equals(getComputedStyle(target).d, 'path("M 10 20 L 30 50 Z L 60 80 Z M 80 100 L 170 160 Z T 150 220")');
+
+    target.style.d = 'path("m 10.0 170.0 h 90.00 v 30.00 m 0 0 s 1 2 3 4 z c 9 8 7 6 5 4")';
+    assert_equals(target.style.d, 'path("m 10 170 h 90 v 30 m 0 0 s 1 2 3 4 Z c 9 8 7 6 5 4")');
+    assert_equals(getComputedStyle(target).d, 'path("M 10 170 H 100 V 200 M 100 200 S 101 202 103 204 Z C 109 208 107 206 105 204")');
+
+    target.style.d = '  path(  "m 10 20 a 10 20 30 1 0 40 50 a 110 120 30 1 1 140 50"  )  ';
+    assert_equals(target.style.d, 'path("m 10 20 a 10 20 30 1 0 40 50 a 110 120 30 1 1 140 50")');
+    assert_equals(getComputedStyle(target).d, 'path("M 10 20 A 10 20 30 1 0 50 70 A 110 120 30 1 1 190 120")');
+  });
+  ]]></script>
+</svg>
diff --git a/third_party/WebKit/LayoutTests/fast/animation/scroll-animations/scrolltimeline-currenttime.html b/third_party/WebKit/LayoutTests/fast/animation/scroll-animations/scrolltimeline-currenttime.html
index b262457f..fff1559 100644
--- a/third_party/WebKit/LayoutTests/fast/animation/scroll-animations/scrolltimeline-currenttime.html
+++ b/third_party/WebKit/LayoutTests/fast/animation/scroll-animations/scrolltimeline-currenttime.html
@@ -23,17 +23,23 @@
   const scroller = document.querySelector('#scroller1');
   // For simplicity, we set the timeRange such that currentTime maps directly to
   // the value scrolled. We have a square scroller/contents, so can just compute
-  // one edge and use it for inline and block.
+  // one edge and use it for all the timelines;
   const scrollerSize = scroller.scrollHeight - scroller.clientHeight;
 
   const blockScrollTimeline = new ScrollTimeline(
       { scrollSource: scroller, timeRange: scrollerSize, orientation: 'block' });
   const inlineScrollTimeline = new ScrollTimeline(
       { scrollSource: scroller, timeRange: scrollerSize, orientation: 'inline' });
+  const horizontalScrollTimeline = new ScrollTimeline(
+      { scrollSource: scroller, timeRange: scrollerSize, orientation: 'horizontal' });
+  const verticalScrollTimeline = new ScrollTimeline(
+      { scrollSource: scroller, timeRange: scrollerSize, orientation: 'vertical' });
 
-  // Unscrolled, both timelines should read a currentTime of 0.
+  // Unscrolled, all timelines should read a currentTime of 0.
   assert_equals(blockScrollTimeline.currentTime, 0);
   assert_equals(inlineScrollTimeline.currentTime, 0);
+  assert_equals(horizontalScrollTimeline.currentTime, 0);
+  assert_equals(verticalScrollTimeline.currentTime, 0);
 
   // Now do some scrolling and make sure that the ScrollTimelines update.
   scroller.scrollTop = 50;
@@ -43,6 +49,8 @@
   // be the scroll offset.
   assert_equals(blockScrollTimeline.currentTime, 50);
   assert_equals(inlineScrollTimeline.currentTime, 75);
+  assert_equals(horizontalScrollTimeline.currentTime, 75);
+  assert_equals(verticalScrollTimeline.currentTime, 50);
 }, 'currentTime calculates the correct time based on scrolled amount');
 </script>
 
@@ -82,26 +90,34 @@
 
   // For simplicity, we set the timeRange such that currentTime maps directly to
   // the value scrolled. We have a square scroller/contents, so can just compute
-  // one edge and use it for inline and block.
+  // one edge and use it for all the timelines.
   const scrollerSize = scroller.scrollHeight - scroller.clientHeight;
 
   const blockScrollTimeline = new ScrollTimeline(
       { scrollSource: scroller, timeRange: scrollerSize, orientation: 'block' });
   const inlineScrollTimeline = new ScrollTimeline(
       { scrollSource: scroller, timeRange: scrollerSize, orientation: 'inline' });
+  const horizontalScrollTimeline = new ScrollTimeline(
+      { scrollSource: scroller, timeRange: scrollerSize, orientation: 'horizontal' });
+  const verticalScrollTimeline = new ScrollTimeline(
+      { scrollSource: scroller, timeRange: scrollerSize, orientation: 'vertical' });
 
-  // Unscrolled, both timelines should read a current time of 0 even though the
+  // Unscrolled, all timelines should read a current time of 0 even though the
   // X-axis will have started at the right hand side for rtl.
   assert_equals(blockScrollTimeline.currentTime, 0);
   assert_equals(inlineScrollTimeline.currentTime, 0);
+  assert_equals(horizontalScrollTimeline.currentTime, 0);
+  assert_equals(verticalScrollTimeline.currentTime, 0);
 
-  // The offset in the inline direction should be inverted. The block direction
-  // should be unaffected.
+  // The offset in the inline/horizontal direction should be inverted. The
+  // block/vertical direction should be unaffected.
   scroller.scrollTop = 50;
   scroller.scrollLeft = 75;
 
   assert_equals(blockScrollTimeline.currentTime, 50);
   assert_equals(inlineScrollTimeline.currentTime, scrollerSize - 75);
+  assert_equals(horizontalScrollTimeline.currentTime, scrollerSize - 75);
+  assert_equals(verticalScrollTimeline.currentTime, 50);
 }, 'currentTime handles direction: rtl correctly');
 </script>
 
@@ -114,26 +130,35 @@
 
   // For simplicity, we set the timeRange such that currentTime maps directly to
   // the value scrolled. We have a square scroller/contents, so can just compute
-  // one edge and use it for inline and block.
+  // one edge and use it for all the timelines.
   const scrollerSize = scroller.scrollHeight - scroller.clientHeight;
 
   const blockScrollTimeline = new ScrollTimeline(
       { scrollSource: scroller, timeRange: scrollerSize, orientation: 'block' });
   const inlineScrollTimeline = new ScrollTimeline(
       { scrollSource: scroller, timeRange: scrollerSize, orientation: 'inline' });
+  const horizontalScrollTimeline = new ScrollTimeline(
+      { scrollSource: scroller, timeRange: scrollerSize, orientation: 'horizontal' });
+  const verticalScrollTimeline = new ScrollTimeline(
+      { scrollSource: scroller, timeRange: scrollerSize, orientation: 'vertical' });
 
-  // Unscrolled, both timelines should read a current time of 0 even though the
+  // Unscrolled, all timelines should read a current time of 0 even though the
   // X-axis will have started at the right hand side for vertical-rl.
   assert_equals(blockScrollTimeline.currentTime, 0);
   assert_equals(inlineScrollTimeline.currentTime, 0);
+  assert_equals(horizontalScrollTimeline.currentTime, 0);
+  assert_equals(verticalScrollTimeline.currentTime, 0);
 
   // For vertical-rl, the X-axis starts on the right-hand-side and is the block
-  // axis. The Y-axis is normal but is the inline axis.
+  // axis. The Y-axis is normal but is the inline axis. For the horizontal/vertical
+  // cases, horizontal starts on the right-hand-side and vertical is normal.
   scroller.scrollTop = 50;
   scroller.scrollLeft = 75;
 
   assert_equals(blockScrollTimeline.currentTime, scrollerSize - 75);
   assert_equals(inlineScrollTimeline.currentTime, 50);
+  assert_equals(horizontalScrollTimeline.currentTime, scrollerSize - 75);
+  assert_equals(verticalScrollTimeline.currentTime, 50);
 }, 'currentTime handles writing-mode: vertical-rl correctly');
 </script>
 
@@ -146,24 +171,33 @@
 
   // For simplicity, we set the timeRange such that currentTime maps directly to
   // the value scrolled. We have a square scroller/contents, so can just compute
-  // one edge and use it for inline and block.
+  // one edge and use it for all the timelines.
   const scrollerSize = scroller.scrollHeight - scroller.clientHeight;
 
   const blockScrollTimeline = new ScrollTimeline(
       { scrollSource: scroller, timeRange: scrollerSize, orientation: 'block' });
   const inlineScrollTimeline = new ScrollTimeline(
       { scrollSource: scroller, timeRange: scrollerSize, orientation: 'inline' });
+  const horizontalScrollTimeline = new ScrollTimeline(
+      { scrollSource: scroller, timeRange: scrollerSize, orientation: 'horizontal' });
+  const verticalScrollTimeline = new ScrollTimeline(
+      { scrollSource: scroller, timeRange: scrollerSize, orientation: 'vertical' });
 
-  // Unscrolled, both timelines should read a current time of 0.
+  // Unscrolled, all timelines should read a current time of 0.
   assert_equals(blockScrollTimeline.currentTime, 0);
   assert_equals(inlineScrollTimeline.currentTime, 0);
+  assert_equals(horizontalScrollTimeline.currentTime, 0);
+  assert_equals(verticalScrollTimeline.currentTime, 0);
 
   // For vertical-lr, both axes start at their 'normal' positions but the X-axis
-  // is the block direction and the Y-axis is the inline direction.
+  // is the block direction and the Y-axis is the inline direction. This does not
+  // affect horizontal/vertical.
   scroller.scrollTop = 50;
   scroller.scrollLeft = 75;
 
   assert_equals(blockScrollTimeline.currentTime, 75);
   assert_equals(inlineScrollTimeline.currentTime, 50);
+  assert_equals(horizontalScrollTimeline.currentTime, 75);
+  assert_equals(verticalScrollTimeline.currentTime, 50);
 }, 'currentTime handles writing-mode: vertical-lr correctly');
 </script>
diff --git a/third_party/WebKit/LayoutTests/fast/masking/clip-path-selection-expected.txt b/third_party/WebKit/LayoutTests/fast/masking/clip-path-selection-expected.txt
deleted file mode 100644
index b005ed2..0000000
--- a/third_party/WebKit/LayoutTests/fast/masking/clip-path-selection-expected.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-PASS sel.toString() is ""
-PASS sel.toString() is "Little"
-PASS successfullyParsed is true
-
-TEST COMPLETE
-Although the Culture was originated by humanoid species, subsequent interactions with other civilizations have introduced many non-humanoid species into the Culture (including some former enemy civilizations), though the majority of the biological Culture is still pan-human. Little uniformity exists in the Culture, and its citizens are such by choice, free to change physical form and even species (though some stranger biological conversions are irreversible, and conversion from biological to artificial sentience is considered to be what is known as an Unusual Life Choice). All members are also free to join, leave, and rejoin, or indeed declare themselves to be, say, 80% Culture.
diff --git a/third_party/WebKit/LayoutTests/fast/masking/clip-path-selection.html b/third_party/WebKit/LayoutTests/fast/masking/clip-path-selection.html
deleted file mode 100644
index 10545d4f..0000000
--- a/third_party/WebKit/LayoutTests/fast/masking/clip-path-selection.html
+++ /dev/null
@@ -1,39 +0,0 @@
-<style>
-body {
-    width: 600px;
-    font: 18px/1 Ahem;
-}
-
-#triangleLeft {
-    position: absolute;
-    top: 0px;
-    left: 0px;
-    width: 400px;
-    height: 400px;
-    background: green;
-    -webkit-clip-path: polygon(0 0, 100% 50%, 0 100%);
-}
-</style>
-
-<div id="triangleLeft">Although the Culture was originated by humanoid species, subsequent interactions with other civilizations have introduced many non-humanoid species into the Culture (including some former enemy civilizations), though the majority of the biological Culture is still pan-human. Little uniformity exists in the Culture, and its citizens are such by choice, free to change physical form and even species (though some stranger biological conversions are irreversible, and conversion from biological to artificial sentience is considered to be what is known as an Unusual Life Choice). All members are also free to join, leave, and rejoin, or indeed declare themselves to be, say, 80% Culture.</div>
-
-<script src="../../resources/js-test.js"></script>
-<script>
-    var sel = window.getSelection();
-    sel.removeAllRanges();
-    // select part of the polygon, not the text
-    eventSender.mouseMoveTo(180, 40);
-    eventSender.mouseDown();
-    eventSender.mouseMoveTo(200, 40);
-    eventSender.mouseUp();
-    shouldBe('sel.toString()', '""');
-
-    sel.removeAllRanges();
-    // select 'Little' using select drag
-    eventSender.mouseMoveTo(0, 260);
-    eventSender.mouseDown();
-    eventSender.mouseMoveTo(100, 260);
-    eventSender.mouseUp();
-    // verify that 'Little' was selected
-    shouldBe('sel.toString()', '"Little"');
-</script>
diff --git a/third_party/WebKit/LayoutTests/fast/masking/parsing-clip-path-shape-expected.txt b/third_party/WebKit/LayoutTests/fast/masking/parsing-clip-path-shape-expected.txt
deleted file mode 100644
index 3988324..0000000
--- a/third_party/WebKit/LayoutTests/fast/masking/parsing-clip-path-shape-expected.txt
+++ /dev/null
@@ -1,60 +0,0 @@
-Test that clip-path shapes accept different length units
-
-On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
-
-
-PASS innerStyle("-webkit-clip-path", "circle(0 at 0 0)") is "circle(0px at 0% 0%)"
-PASS innerStyle("-webkit-clip-path", "circle(1px at -1px +1px)") is "circle(1px at -1px 1px)"
-PASS innerStyle("-webkit-clip-path", "circle(1.5px at -1.5px +1.5px)") is "circle(1.5px at -1.5px 1.5px)"
-PASS innerStyle("-webkit-clip-path", "circle(.5px at -.5px +.5px)") is "circle(0.5px at -0.5px 0.5px)"
-PASS innerStyle("-webkit-clip-path", "circle(1em at -1em +1em)") is "circle(1em at -1em 1em)"
-PASS innerStyle("-webkit-clip-path", "circle(1.5em at -1.5em +1.5em)") is "circle(1.5em at -1.5em 1.5em)"
-PASS innerStyle("-webkit-clip-path", "circle(.5em at -.5em +.5em)") is "circle(0.5em at -0.5em 0.5em)"
-PASS innerStyle("-webkit-clip-path", "circle(1ex at 1ex 1ex)") is "circle(1ex at 1ex 1ex)"
-PASS innerStyle("-webkit-clip-path", "circle(1rem at 1rem 1rem)") is "circle(1rem at 1rem 1rem)"
-PASS computedStyle("-webkit-clip-path", "circle(1.5em at .5em 1em)") is "circle(12px at 4px 8px)"
-PASS computedStyle("-webkit-clip-path", "circle(1.5rem at .5rem 1rem)") is "circle(24px at 8px 16px)"
-PASS innerStyle("-webkit-clip-path", "circle(1vw at 1vw 1vw)") is "circle(1vw at 1vw 1vw)"
-PASS innerStyle("-webkit-clip-path", "circle(1vh at 1vh 1vh)") is "circle(1vh at 1vh 1vh)"
-PASS innerStyle("-webkit-clip-path", "circle(1vmin at 1vmin 1vmin)") is "circle(1vmin at 1vmin 1vmin)"
-PASS computedStyle("-webkit-clip-path", "circle(1.5vw at .5vw 1vw)") is "circle(12px at 4px 8px)"
-PASS computedStyle("-webkit-clip-path", "circle(1.5vh at .5vh 1vh)") is "circle(9px at 3px 6px)"
-PASS computedStyle("-webkit-clip-path", "circle(1.5vmin at .5vmin 1vmin)") is "circle(9px at 3px 6px)"
-PASS computedStyle("-webkit-clip-path", "circle(150% at 50% 100%)") is "circle(150% at 50% 100%)"
-PASS computedStyle("-webkit-clip-path", "inset(45% 45% 90% 60% round 25% 10%)") is "inset(45% 45% 90% 60% round 25% 10%)"
-PASS computedStyle("-webkit-clip-path", "ellipse(100% 100% at 100% 100%)") is "ellipse(100% 100% at 100% 100%)"
-PASS computedStyle("-webkit-clip-path", "polygon(10% 20%, 30% 40%, 40% 50%)") is "polygon(10% 20%, 30% 40%, 40% 50%)"
-PASS innerStyle("-webkit-clip-path", "circle(1 at 1px 1px)") is ""
-PASS computedStyle("-webkit-clip-path", "circle(1 at 1px 1px)") is "none"
-PASS innerStyle("-webkit-clip-path", "circle(px at 1px 1px)") is ""
-PASS computedStyle("-webkit-clip-path", "circle(px at 1px 1px)") is "none"
-PASS innerStyle("-webkit-clip-path", "circle(1p at 1px 1px)") is ""
-PASS computedStyle("-webkit-clip-path", "circle(1p at 1px 1px)") is "none"
-PASS innerStyle("-webkit-clip-path", "circle(calc() at 1px 1px)") is ""
-PASS computedStyle("-webkit-clip-path", "circle(calc() at 1px 1px)") is "none"
-PASS innerStyle("-webkit-clip-path", "circle(-1.5px at -1.5px +1.5px)") is ""
-PASS computedStyle("-webkit-clip-path", "circle(-1.5px at -1.5px +1.5px)") is "none"
-PASS innerStyle("-webkit-clip-path", "inset(1cm 1mm 1in 1px round -1pt, 1pc)") is ""
-PASS computedStyle("-webkit-clip-path", "inset(1cm 1mm 1in 1px round -1pt, 1pc)") is "none"
-PASS innerStyle("-webkit-clip-path", "inset(1cm 1mm 1in 1px round 1pt -1pc)") is ""
-PASS computedStyle("-webkit-clip-path", "inset(1cm 1mm 1in 1px round 1pt -1pc)") is "none"
-PASS innerStyle("-webkit-clip-path", "ellipse(-1em 1em at 1em 1em)") is ""
-PASS computedStyle("-webkit-clip-path", "ellipse(-1em 1em at 1em 1em)") is "none"
-PASS innerStyle("-webkit-clip-path", "ellipse(1em -1em at 1em 1em)") is ""
-PASS computedStyle("-webkit-clip-path", "ellipse(1em -1em at 1em 1em)") is "none"
-PASS innerStyle("-webkit-clip-path", "polygon(0, 0)") is ""
-PASS computedStyle("-webkit-clip-path", "polygon(0, 0)") is "none"
-PASS innerStyle("-webkit-clip-path", "polygon(0 0, 0)") is ""
-PASS computedStyle("-webkit-clip-path", "polygon(0 0, 0)") is "none"
-PASS innerStyle("-webkit-clip-path", "polygon(0)") is ""
-PASS computedStyle("-webkit-clip-path", "polygon(0)") is "none"
-PASS innerStyle("-webkit-clip-path", "polygon()") is ""
-PASS computedStyle("-webkit-clip-path", "polygon()") is "none"
-PASS innerStyle("-webkit-clip-path", "polygon(evenodd)") is ""
-PASS computedStyle("-webkit-clip-path", "polygon(evenodd)") is "none"
-PASS innerStyle("-webkit-clip-path", "polygon(nonzero)") is ""
-PASS computedStyle("-webkit-clip-path", "polygon(nonzero)") is "none"
-PASS successfullyParsed is true
-
-TEST COMPLETE
-
diff --git a/third_party/WebKit/LayoutTests/fast/masking/parsing-mask-expected.txt b/third_party/WebKit/LayoutTests/fast/masking/parsing-mask-expected.txt
deleted file mode 100644
index 31a03d67..0000000
--- a/third_party/WebKit/LayoutTests/fast/masking/parsing-mask-expected.txt
+++ /dev/null
@@ -1,127 +0,0 @@
-Test that clip-path shapes accept different length units
-
-On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
-
-
-PASS innerStyle("-webkit-mask", "none") is "none"
-PASS innerStyle("-webkit-mask", "none, none") is "none, none"
-PASS innerStyle("-webkit-mask", "none, none, none") is "none, none, none"
-PASS innerStyle("-webkit-mask", "url(file:///image.png), none") is "url(\"file:///image.png\"), none"
-PASS innerStyle("-webkit-mask", "none, url(file:///image.png)") is "none, url(\"file:///image.png\")"
-PASS innerStyle("-webkit-mask", "top left") is "left top"
-PASS innerStyle("-webkit-mask", "bottom right") is "right bottom"
-PASS innerStyle("-webkit-mask", "left bottom") is "left bottom"
-PASS innerStyle("-webkit-mask", "right top") is "right top"
-PASS innerStyle("-webkit-mask", "center") is "center center"
-PASS innerStyle("-webkit-mask", "none top") is "none center top"
-PASS innerStyle("-webkit-mask", "none bottom") is "none center bottom"
-PASS innerStyle("-webkit-mask", "none right") is "none right center"
-PASS innerStyle("-webkit-mask", "none top right") is "none right top"
-PASS innerStyle("-webkit-mask", "none bottom left") is "none left bottom"
-PASS innerStyle("-webkit-mask", "none right") is "none right center"
-PASS innerStyle("-webkit-mask", "none left") is "none left center"
-PASS innerStyle("-webkit-mask", "center 50%") is "center 50%"
-PASS innerStyle("-webkit-mask", "50px 50%") is "50px 50%"
-PASS innerStyle("-webkit-mask", "center left") is "left center"
-PASS innerStyle("-webkit-mask", "top center") is "center top"
-PASS innerStyle("-webkit-mask", "left 10px top 15px") is "left 10px top 15px"
-PASS innerStyle("-webkit-mask", "left 10% top 30%") is "left 10% top 30%"
-PASS innerStyle("-webkit-mask", "right top 15px") is "right top 15px"
-PASS innerStyle("-webkit-mask", "left 10px center") is "left 10px center"
-PASS innerStyle("-webkit-mask", "center top 20px") is "center top 20px"
-PASS innerStyle("-webkit-mask", "center left 30px") is "left 30px center"
-PASS innerStyle("-webkit-mask", "left 20% top") is "left 20% top"
-PASS innerStyle("-webkit-mask", "center center") is "center center"
-PASS innerStyle("-webkit-mask-position", "left 10px top 15px") is "left 10px top 15px"
-PASS innerStyle("-webkit-mask-position", "left 10% top 30%") is "left 10% top 30%"
-PASS innerStyle("-webkit-mask-position", "right top 15px") is "right top 15px"
-PASS innerStyle("-webkit-mask-position", "left 10px center") is "left 10px center"
-PASS innerStyle("-webkit-mask-position", "center top 20px") is "center top 20px"
-PASS innerStyle("-webkit-mask-position", "center left 30px") is "left 30px center"
-PASS innerStyle("-webkit-mask-position", "left 20% top") is "left 20% top"
-PASS innerStyle("mask-source-type", "alpha") is "alpha"
-PASS innerStyle("mask-source-type", "luminance") is "luminance"
-PASS innerStyle("mask-source-type", "auto") is "auto"
-PASS computedStyle("mask-source-type", "alpha") is "alpha"
-PASS computedStyle("mask-source-type", "auto") is "alpha"
-PASS computedStyle("mask-source-type", "luminance") is "luminance"
-PASS computedStyle("mask-source-type", "") is "alpha"
-PASS innerStyle("-webkit-mask", "repeat-x") is "repeat-x"
-PASS innerStyle("-webkit-mask", "repeat-y") is "repeat-y"
-PASS innerStyle("-webkit-mask", "repeat") is "repeat"
-PASS innerStyle("-webkit-mask", "space") is "space"
-PASS innerStyle("-webkit-mask", "no-repeat") is "no-repeat"
-PASS innerStyle("-webkit-mask", "repeat space") is "repeat space"
-PASS innerStyle("-webkit-mask", "repeat round") is "repeat round"
-PASS innerStyle("-webkit-mask", "repeat no-repeat") is "repeat-x"
-PASS innerStyle("-webkit-mask", "repeat space, repeat-x") is "repeat space, repeat-x"
-PASS innerStyle("-webkit-mask", "repeat none") is "none repeat"
-PASS innerStyle("-webkit-mask", "none repeat") is "none repeat"
-PASS innerStyle("-webkit-mask", "padding-box") is "padding-box padding-box"
-PASS innerStyle("-webkit-mask", "border-box") is "border-box border-box"
-PASS innerStyle("-webkit-mask", "content-box") is "content-box content-box"
-PASS innerStyle("-webkit-mask", "padding-box none") is "none padding-box padding-box"
-PASS innerStyle("-webkit-mask", "none padding-box") is "none padding-box padding-box"
-PASS innerStyle("-webkit-mask", "padding-box content-box") is "padding-box content-box"
-PASS innerStyle("-webkit-mask", "content-box content-box") is "content-box content-box"
-PASS innerStyle("-webkit-mask", "padding-box border-box") is "padding-box border-box"
-PASS innerStyle("-webkit-mask", "padding-box border-box none") is "none padding-box border-box"
-PASS innerStyle("-webkit-mask", "none padding-box border-box") is "none padding-box border-box"
-PASS innerStyle("-webkit-mask", "none left top / auto") is "none left top / auto"
-PASS innerStyle("-webkit-mask", "none left top / auto auto") is "none left top / auto"
-PASS innerStyle("-webkit-mask", "none left top / 100%") is "none left top / 100%"
-PASS innerStyle("-webkit-mask", "none left top / 100% 100%") is "none left top / 100% 100%"
-PASS innerStyle("-webkit-mask", "none left top / 0%") is "none left top / 0%"
-PASS innerStyle("-webkit-mask", "none left top / auto 0%") is "none left top / auto 0%"
-PASS innerStyle("-webkit-mask", "none left top / cover") is "none left top / cover"
-PASS innerStyle("-webkit-mask", "none left top / contain") is "none left top / contain"
-PASS innerStyle("-webkit-mask", "none left 20px top 10px / contain") is "none left 20px top 10px / contain"
-PASS innerStyle("-webkit-mask", "none left 20px top / contain") is "none left 20px top / contain"
-PASS innerStyle("-webkit-mask", "none padding-box content-box") is "none padding-box content-box"
-PASS innerStyle("-webkit-mask", "none padding-box") is "none padding-box padding-box"
-PASS innerStyle("-webkit-mask", "none top") is "none center top"
-PASS innerStyle("-webkit-mask", "none center right 20px") is "none right 20px center"
-PASS innerStyle("-webkit-mask", "none border-box left top") is "none left top border-box border-box"
-PASS innerStyle("-webkit-mask", "none border-box left top 20px") is "none left top 20px border-box border-box"
-PASS innerStyle("-webkit-mask", "none border-box content-box left top repeat-x") is "none left top repeat-x border-box content-box"
-PASS innerStyle("-webkit-mask", "none border-box content-box left top / auto repeat-x") is "none left top / auto repeat-x border-box content-box"
-PASS innerStyle("-webkit-mask", "none border-box content-box right 0px center / auto repeat-x") is "none right 0px center / auto repeat-x border-box content-box"
-PASS innerStyle("-webkit-mask", "top none left") is ""
-PASS innerStyle("-webkit-mask", "right none bottom") is ""
-PASS innerStyle("-webkit-mask", "right right") is ""
-PASS innerStyle("-webkit-mask", "left left") is ""
-PASS innerStyle("-webkit-mask", "top top") is ""
-PASS innerStyle("-webkit-mask", "bottom bottom") is ""
-PASS innerStyle("-webkit-mask", "50% none 50%") is ""
-PASS innerStyle("-webkit-mask", "repeat-x repeat-x") is ""
-PASS innerStyle("-webkit-mask", "space repeat-y") is ""
-PASS innerStyle("-webkit-mask", "repeat space space") is ""
-PASS innerStyle("-webkit-mask", "padding-box border-box content-box") is ""
-PASS innerStyle("-webkit-mask", "none / auto") is ""
-PASS innerStyle("-webkit-mask", "none repeat-x / auto") is ""
-PASS innerStyle("-webkit-mask", "none border-box / auto") is ""
-PASS innerStyle("-webkit-mask", "none top left / cover 100%") is ""
-PASS innerStyle("-webkit-mask", "scroll") is ""
-PASS innerStyle("-webkit-mask", "fixed") is ""
-PASS innerStyle("-webkit-mask", "local") is ""
-PASS innerStyle("-webkit-mask", "space scroll") is ""
-PASS innerStyle("-webkit-mask", "none scroll") is ""
-PASS innerStyle("-webkit-mask", "none top left / auto repeat-x scroll border-box border-box") is ""
-PASS innerStyle("-webkit-mask", "right top left") is ""
-PASS innerStyle("-webkit-mask", "center left center") is ""
-PASS innerStyle("-webkit-mask", "center top center") is ""
-PASS innerStyle("-webkit-mask", "center right bottom") is ""
-PASS innerStyle("-webkit-mask", "top solid bottom") is ""
-PASS innerStyle("-webkit-mask", "none top left right center top / auto repeat-x scroll border-box border-box") is ""
-PASS innerStyle("-webkit-mask", "none center center 20px / auto repeat-x scroll border-box border-box") is ""
-PASS innerStyle("-webkit-mask", "none top 20px right 30px center / auto repeat-x scroll border-box border-box") is ""
-PASS innerStyle("-webkit-mask", "none top 20px top 30px / auto repeat-x scroll border-box border-box") is ""
-PASS innerStyle("-webkit-mask", "none top 20px bottom / auto repeat-x scroll border-box border-box") is ""
-PASS innerStyle("mask-source-type", "rubbish") is ""
-PASS innerStyle("mask-source-type", "") is ""
-PASS innerStyle("mask-source-type", "center") is ""
-PASS innerStyle("mask-source-type", "repeat") is ""
-PASS successfullyParsed is true
-
-TEST COMPLETE
-
diff --git a/third_party/WebKit/LayoutTests/fast/masking/parsing-mask-source-type-expected.txt b/third_party/WebKit/LayoutTests/fast/masking/parsing-mask-source-type-expected.txt
deleted file mode 100644
index c540f5f..0000000
--- a/third_party/WebKit/LayoutTests/fast/masking/parsing-mask-source-type-expected.txt
+++ /dev/null
@@ -1,8 +0,0 @@
-PASS setProperty("alpha, alpha, alpha, alpha") is "alpha, alpha, alpha, alpha"
-PASS setProperty("luminance, alpha") is "luminance, alpha, alpha, alpha"
-PASS setProperty("luminance, luminance, luminance, luminance") is "luminance, luminance, luminance, luminance"
-PASS setProperty("auto, alpha, luminance, luminance") is "alpha, alpha, luminance, luminance"
-PASS successfullyParsed is true
-
-TEST COMPLETE
-
diff --git a/third_party/WebKit/LayoutTests/fast/masking/parsing-mask-source-type.html b/third_party/WebKit/LayoutTests/fast/masking/parsing-mask-source-type.html
deleted file mode 100644
index 75bf865..0000000
--- a/third_party/WebKit/LayoutTests/fast/masking/parsing-mask-source-type.html
+++ /dev/null
@@ -1,32 +0,0 @@
-<!DOCTYPE html>
-<html>
-    <style>
-        * { font-size: 16px; }
-        div { font-size: 8px; }
-    </style>
-    <body>
-        <script src="../../resources/js-test.js"></script>
-        <script>
-            function setProperty(value) {
-                var div = document.createElement('div');
-                document.body.appendChild(div);
-                div.style.setProperty("-webkit-mask-image", "none, none, none, none");
-                div.style.setProperty("mask-source-type", value);
-
-                var computedValue = getComputedStyle(div).getPropertyValue("mask-source-type");
-                document.body.removeChild(div);
-
-                return computedValue;
-            }
-
-            function test(value, expected) {
-                shouldBeEqualToString('setProperty("' + value + '")', expected);
-            }
-
-            test("alpha, alpha, alpha, alpha", "alpha, alpha, alpha, alpha");
-            test("luminance, alpha", "luminance, alpha, alpha, alpha");
-            test("luminance, luminance, luminance, luminance", "luminance, luminance, luminance, luminance");
-            test("auto, alpha, luminance, luminance", "alpha, alpha, luminance, luminance");
-        </script>
-    </body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/masking/clip-path-inset-large-radii-expected.png b/third_party/WebKit/LayoutTests/platform/linux/css3/masking/clip-path-inset-large-radii-expected.png
similarity index 100%
rename from third_party/WebKit/LayoutTests/platform/linux/fast/masking/clip-path-inset-large-radii-expected.png
rename to third_party/WebKit/LayoutTests/platform/linux/css3/masking/clip-path-inset-large-radii-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/masking/clip-path-inset-large-radii-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/css3/masking/clip-path-inset-large-radii-expected.txt
similarity index 100%
rename from third_party/WebKit/LayoutTests/platform/linux/fast/masking/clip-path-inset-large-radii-expected.txt
rename to third_party/WebKit/LayoutTests/platform/linux/css3/masking/clip-path-inset-large-radii-expected.txt
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/masking/clip-path-inset-large-radii-expected.png b/third_party/WebKit/LayoutTests/platform/mac/css3/masking/clip-path-inset-large-radii-expected.png
similarity index 100%
rename from third_party/WebKit/LayoutTests/platform/mac/fast/masking/clip-path-inset-large-radii-expected.png
rename to third_party/WebKit/LayoutTests/platform/mac/css3/masking/clip-path-inset-large-radii-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/masking/clip-path-inset-large-radii-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/css3/masking/clip-path-inset-large-radii-expected.txt
similarity index 100%
rename from third_party/WebKit/LayoutTests/platform/mac/fast/masking/clip-path-inset-large-radii-expected.txt
rename to third_party/WebKit/LayoutTests/platform/mac/css3/masking/clip-path-inset-large-radii-expected.txt
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/masking/clip-path-inset-large-radii-expected.png b/third_party/WebKit/LayoutTests/platform/win/css3/masking/clip-path-inset-large-radii-expected.png
similarity index 100%
rename from third_party/WebKit/LayoutTests/platform/win/fast/masking/clip-path-inset-large-radii-expected.png
rename to third_party/WebKit/LayoutTests/platform/win/css3/masking/clip-path-inset-large-radii-expected.png
Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/masking/clip-path-inset-large-radii-expected.txt b/third_party/WebKit/LayoutTests/platform/win/css3/masking/clip-path-inset-large-radii-expected.txt
similarity index 100%
rename from third_party/WebKit/LayoutTests/platform/win/fast/masking/clip-path-inset-large-radii-expected.txt
rename to third_party/WebKit/LayoutTests/platform/win/css3/masking/clip-path-inset-large-radii-expected.txt
diff --git a/third_party/blink/public/platform/web_runtime_features.h b/third_party/blink/public/platform/web_runtime_features.h
index 8817a61..a40742c 100644
--- a/third_party/blink/public/platform/web_runtime_features.h
+++ b/third_party/blink/public/platform/web_runtime_features.h
@@ -102,7 +102,6 @@
   BLINK_PLATFORM_EXPORT static void EnableLazyFrameVisibleLoadTimeMetrics(bool);
   BLINK_PLATFORM_EXPORT static void EnableLazyImageLoading(bool);
   BLINK_PLATFORM_EXPORT static void EnableLazyImageVisibleLoadTimeMetrics(bool);
-  BLINK_PLATFORM_EXPORT static void EnableLazyParseCSS(bool);
   BLINK_PLATFORM_EXPORT static void EnableMediaCapture(bool);
   BLINK_PLATFORM_EXPORT static void EnableMediaSession(bool);
   BLINK_PLATFORM_EXPORT static void EnableMiddleClickAutoscroll(bool);
diff --git a/third_party/blink/public/web/web_widget_client.h b/third_party/blink/public/web/web_widget_client.h
index 5baca13e..9e8c537 100644
--- a/third_party/blink/public/web/web_widget_client.h
+++ b/third_party/blink/public/web/web_widget_client.h
@@ -64,10 +64,11 @@
   // Called when a region of the WebWidget needs to be re-painted.
   virtual void DidInvalidateRect(const WebRect&) {}
 
-  // Attempt to initialize compositing view for this widget. If successful,
-  // returns a valid WebLayerTreeView which is owned by the
-  // WebWidgetClient.
-  virtual WebLayerTreeView* InitializeLayerTreeView() { return nullptr; }
+  // Initializes the layer compositor for the widget, returning a pointer
+  // to the newly constructed LayerTreeView that provides access to the
+  // compositor, which is owned by the WebWidgetClient. Will return null if
+  // AllowsBrokenNullLayerTreeView() is true.
+  virtual WebLayerTreeView* InitializeLayerTreeView() = 0;
 
   // FIXME: Remove all overrides of this.
   virtual bool AllowsBrokenNullLayerTreeView() const { return false; }
diff --git a/third_party/blink/renderer/core/animation/BUILD.gn b/third_party/blink/renderer/core/animation/BUILD.gn
index 53c2402..b96fc0b 100644
--- a/third_party/blink/renderer/core/animation/BUILD.gn
+++ b/third_party/blink/renderer/core/animation/BUILD.gn
@@ -56,6 +56,8 @@
     "css_clip_interpolation_type.h",
     "css_color_interpolation_type.cc",
     "css_color_interpolation_type.h",
+    "css_custom_list_interpolation_type.cc",
+    "css_custom_list_interpolation_type.h",
     "css_default_interpolation_type.cc",
     "css_default_interpolation_type.h",
     "css_filter_list_interpolation_type.cc",
diff --git a/third_party/blink/renderer/core/animation/css/css_animations_test.cc b/third_party/blink/renderer/core/animation/css/css_animations_test.cc
index 59d5c1a6..ceaa7b74 100644
--- a/third_party/blink/renderer/core/animation/css/css_animations_test.cc
+++ b/third_party/blink/renderer/core/animation/css/css_animations_test.cc
@@ -7,6 +7,7 @@
 #include "third_party/blink/renderer/core/animation/element_animations.h"
 #include "third_party/blink/renderer/core/dom/node_computed_style.h"
 #include "third_party/blink/renderer/core/layout/layout_object.h"
+#include "third_party/blink/renderer/core/paint/stub_chrome_client_for_spv2.h"
 #include "third_party/blink/renderer/core/testing/core_unit_test_helper.h"
 #include "third_party/blink/renderer/platform/animation/compositor_animation_delegate.h"
 
@@ -14,7 +15,7 @@
 
 class CSSAnimationsTest : public RenderingTest {
  public:
-  CSSAnimationsTest() {
+  CSSAnimationsTest() : chrome_client_(new StubChromeClientForSPv2()) {
     EnablePlatform();
     platform()->SetThreadedAnimationEnabled(true);
   }
@@ -32,6 +33,8 @@
     platform()->RunUntilIdle();
   }
 
+  ChromeClient& GetChromeClient() const override { return *chrome_client_; }
+
   void StartAnimationOnCompositor(Animation* animation) {
     static_cast<CompositorAnimationDelegate*>(animation)
         ->NotifyAnimationStarted(
@@ -52,6 +55,9 @@
     return static_cast<const BasicComponentTransferFilterOperation*>(filter)
         ->Amount();
   }
+
+ private:
+  Persistent<StubChromeClientForSPv2> chrome_client_;
 };
 
 // Verify that a composited animation is retargeted according to its composited
diff --git a/third_party/blink/renderer/core/animation/css_custom_list_interpolation_type.cc b/third_party/blink/renderer/core/animation/css_custom_list_interpolation_type.cc
new file mode 100644
index 0000000..34b353959
--- /dev/null
+++ b/third_party/blink/renderer/core/animation/css_custom_list_interpolation_type.cc
@@ -0,0 +1,111 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/core/animation/css_custom_list_interpolation_type.h"
+
+#include "third_party/blink/renderer/core/animation/length_interpolation_functions.h"
+#include "third_party/blink/renderer/core/animation/underlying_length_checker.h"
+#include "third_party/blink/renderer/core/css/css_primitive_value.h"
+#include "third_party/blink/renderer/core/css/css_value_list.h"
+
+namespace blink {
+
+InterpolationValue CSSCustomListInterpolationType::MaybeConvertNeutral(
+    const InterpolationValue& underlying,
+    ConversionCheckers& conversion_checkers) const {
+  size_t underlying_length =
+      UnderlyingLengthChecker::GetUnderlyingLength(underlying);
+  conversion_checkers.push_back(
+      UnderlyingLengthChecker::Create(underlying_length));
+
+  if (underlying_length == 0)
+    return nullptr;
+
+  InterpolationValue null_underlying(nullptr);
+  ConversionCheckers null_checkers;
+
+  auto convert_inner = [this, &null_underlying, &null_checkers](size_t) {
+    return this->inner_interpolation_type_->MaybeConvertNeutral(null_underlying,
+                                                                null_checkers);
+  };
+
+  return ListInterpolationFunctions::CreateList(underlying_length,
+                                                convert_inner);
+}
+
+InterpolationValue CSSCustomListInterpolationType::MaybeConvertValue(
+    const CSSValue& value,
+    const StyleResolverState* state,
+    ConversionCheckers&) const {
+  if (!value.IsValueList())
+    return nullptr;
+
+  ConversionCheckers null_checkers;
+
+  const CSSValueList& list = ToCSSValueList(value);
+  return ListInterpolationFunctions::CreateList(
+      list.length(), [this, &list, state, &null_checkers](size_t index) {
+        return this->inner_interpolation_type_->MaybeConvertValue(
+            list.Item(index), state, null_checkers);
+      });
+}
+
+const CSSValue* CSSCustomListInterpolationType::CreateCSSValue(
+    const InterpolableValue& interpolable_value,
+    const NonInterpolableValue* non_interpolable_value,
+    const StyleResolverState& state) const {
+  const InterpolableList& interpolable_list =
+      ToInterpolableList(interpolable_value);
+  const NonInterpolableList* non_interpolable_list =
+      non_interpolable_value ? &ToNonInterpolableList(*non_interpolable_value)
+                             : nullptr;
+
+  CSSValueList* list = nullptr;
+
+  switch (syntax_repeat_) {
+    default:
+      NOTREACHED();
+      FALLTHROUGH;
+    case CSSSyntaxRepeat::kSpaceSeparated:
+      list = CSSValueList::CreateSpaceSeparated();
+      break;
+    case CSSSyntaxRepeat::kCommaSeparated:
+      list = CSSValueList::CreateCommaSeparated();
+      break;
+  }
+
+  DCHECK(!non_interpolable_list ||
+         interpolable_list.length() == non_interpolable_list->length());
+
+  for (size_t i = 0; i < interpolable_list.length(); ++i) {
+    const NonInterpolableValue* non_interpolable_single_value =
+        non_interpolable_list ? non_interpolable_list->Get(i) : nullptr;
+    list->Append(*inner_interpolation_type_->CreateCSSValue(
+        *interpolable_list.Get(i), non_interpolable_single_value, state));
+  }
+
+  return list;
+}
+
+void CSSCustomListInterpolationType::Composite(
+    UnderlyingValueOwner& underlying_value_owner,
+    double underlying_fraction,
+    const InterpolationValue& value,
+    double interpolation_fraction) const {
+  // TODO(andruud): Properly support composition once behavior is defined.
+  // https://github.com/w3c/css-houdini-drafts/issues/799
+  underlying_value_owner.Set(*this, value);
+}
+
+PairwiseInterpolationValue CSSCustomListInterpolationType::MaybeMergeSingles(
+    InterpolationValue&& start,
+    InterpolationValue&& end) const {
+  return ListInterpolationFunctions::MaybeMergeSingles(
+      std::move(start), std::move(end),
+      ListInterpolationFunctions::LengthMatchingStrategy::kEqual,
+      WTF::BindRepeating(&CSSInterpolationType::MaybeMergeSingles,
+                         WTF::Unretained(inner_interpolation_type_.get())));
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/core/animation/css_custom_list_interpolation_type.h b/third_party/blink/renderer/core/animation/css_custom_list_interpolation_type.h
new file mode 100644
index 0000000..345f87f
--- /dev/null
+++ b/third_party/blink/renderer/core/animation/css_custom_list_interpolation_type.h
@@ -0,0 +1,85 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_ANIMATION_CSS_CUSTOM_LIST_INTERPOLATION_TYPE_H_
+#define THIRD_PARTY_BLINK_RENDERER_CORE_ANIMATION_CSS_CUSTOM_LIST_INTERPOLATION_TYPE_H_
+
+#include "third_party/blink/renderer/core/animation/css_interpolation_type.h"
+
+#include "third_party/blink/renderer/core/animation/list_interpolation_functions.h"
+#include "third_party/blink/renderer/core/css/css_syntax_descriptor.h"
+
+namespace blink {
+
+class CSSCustomListInterpolationType : public CSSInterpolationType {
+ public:
+  CSSCustomListInterpolationType(
+      PropertyHandle property,
+      const PropertyRegistration* registration,
+      std::unique_ptr<CSSInterpolationType> inner_interpolation_type,
+      CSSSyntaxRepeat syntax_repeat)
+      : CSSInterpolationType(property, registration),
+        inner_interpolation_type_(std::move(inner_interpolation_type)),
+        syntax_repeat_(syntax_repeat) {
+    DCHECK(property.IsCSSCustomProperty());
+  }
+
+  InterpolationValue MaybeConvertNeutral(const InterpolationValue& underlying,
+                                         ConversionCheckers&) const final;
+  InterpolationValue MaybeConvertValue(const CSSValue&,
+                                       const StyleResolverState*,
+                                       ConversionCheckers&) const final;
+  const CSSValue* CreateCSSValue(const InterpolableValue&,
+                                 const NonInterpolableValue*,
+                                 const StyleResolverState&) const final;
+  void Composite(UnderlyingValueOwner&,
+                 double underlying_fraction,
+                 const InterpolationValue&,
+                 double interpolation_fraction) const final;
+  PairwiseInterpolationValue MaybeMergeSingles(
+      InterpolationValue&& start,
+      InterpolationValue&& end) const final;
+
+ private:
+  // These methods only apply to CSSInterpolationTypes used by standard CSS
+  // properties. CSSCustomListInterpolationType is only accessible via
+  // registered custom CSS properties.
+  InterpolationValue MaybeConvertStandardPropertyUnderlyingValue(
+      const ComputedStyle&) const final {
+    NOTREACHED();
+    return nullptr;
+  }
+  void ApplyStandardPropertyValue(const InterpolableValue&,
+                                  const NonInterpolableValue*,
+                                  StyleResolverState&) const final {
+    NOTREACHED();
+  }
+  InterpolationValue MaybeConvertInitial(const StyleResolverState&,
+                                         ConversionCheckers&) const final {
+    NOTREACHED();
+    return nullptr;
+  }
+  InterpolationValue MaybeConvertInherit(const StyleResolverState&,
+                                         ConversionCheckers&) const final {
+    NOTREACHED();
+    return nullptr;
+  }
+
+  // This InterpolationType represents the interpolation of elements inside
+  // the list.
+  //
+  // TODO(andruud): Disallow applying inner interpolation type:
+  //                https://crbug.com/882379
+  //
+  // Currently, InterpolationTypes are not designed to "nest" in this way due to
+  // their mandatory association with specific properties, so please do not call
+  // InterpolationType::Apply on inner_interpolation_type_.
+  std::unique_ptr<CSSInterpolationType> inner_interpolation_type_;
+
+  const CSSSyntaxRepeat syntax_repeat_;
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_CORE_ANIMATION_CSS_CUSTOM_LIST_INTERPOLATION_TYPE_H_
diff --git a/third_party/blink/renderer/core/animation/css_interpolation_type.h b/third_party/blink/renderer/core/animation/css_interpolation_type.h
index 38b44c9..ae8df46e7 100644
--- a/third_party/blink/renderer/core/animation/css_interpolation_type.h
+++ b/third_party/blink/renderer/core/animation/css_interpolation_type.h
@@ -32,6 +32,26 @@
                          const InterpolationValue& underlying) const = 0;
   };
 
+  virtual InterpolationValue MaybeConvertNeutral(
+      const InterpolationValue& underlying,
+      ConversionCheckers&) const = 0;
+  virtual InterpolationValue MaybeConvertInitial(const StyleResolverState&,
+                                                 ConversionCheckers&) const = 0;
+  virtual InterpolationValue MaybeConvertInherit(const StyleResolverState&,
+                                                 ConversionCheckers&) const = 0;
+  virtual InterpolationValue MaybeConvertValue(const CSSValue&,
+                                               const StyleResolverState*,
+                                               ConversionCheckers&) const = 0;
+  virtual const CSSValue* CreateCSSValue(const InterpolableValue&,
+                                         const NonInterpolableValue*,
+                                         const StyleResolverState&) const {
+    // TODO(alancutter): Implement this for all subclasses and make this an
+    // abstract declaration so the return type can be changed to
+    // const CSSValue&.
+    NOTREACHED();
+    return nullptr;
+  }
+
  protected:
   CSSInterpolationType(PropertyHandle, const PropertyRegistration* = nullptr);
 
@@ -43,16 +63,7 @@
                                         const InterpolationEnvironment&,
                                         const InterpolationValue& underlying,
                                         ConversionCheckers&) const final;
-  virtual InterpolationValue MaybeConvertNeutral(
-      const InterpolationValue& underlying,
-      ConversionCheckers&) const = 0;
-  virtual InterpolationValue MaybeConvertInitial(const StyleResolverState&,
-                                                 ConversionCheckers&) const = 0;
-  virtual InterpolationValue MaybeConvertInherit(const StyleResolverState&,
-                                                 ConversionCheckers&) const = 0;
-  virtual InterpolationValue MaybeConvertValue(const CSSValue&,
-                                               const StyleResolverState*,
-                                               ConversionCheckers&) const = 0;
+
   virtual void AdditiveKeyframeHook(InterpolationValue&) const {}
 
   InterpolationValue MaybeConvertUnderlyingValue(
@@ -80,16 +91,6 @@
       CSSVariableResolver&,
       ConversionCheckers&) const;
 
-  virtual const CSSValue* CreateCSSValue(const InterpolableValue&,
-                                         const NonInterpolableValue*,
-                                         const StyleResolverState&) const {
-    // TODO(alancutter): Implement this for all subclasses and make this an
-    // abstract declaration so the return type can be changed to
-    // const CSSValue&.
-    NOTREACHED();
-    return nullptr;
-  }
-
   const PropertyRegistration& Registration() const {
     DCHECK(GetProperty().IsCSSCustomProperty());
     return *registration_;
diff --git a/third_party/blink/renderer/core/animation/css_interpolation_types_map.cc b/third_party/blink/renderer/core/animation/css_interpolation_types_map.cc
index 33e62a8d..9dc15cb 100644
--- a/third_party/blink/renderer/core/animation/css_interpolation_types_map.cc
+++ b/third_party/blink/renderer/core/animation/css_interpolation_types_map.cc
@@ -12,6 +12,7 @@
 #include "third_party/blink/renderer/core/animation/css_border_image_length_box_interpolation_type.h"
 #include "third_party/blink/renderer/core/animation/css_clip_interpolation_type.h"
 #include "third_party/blink/renderer/core/animation/css_color_interpolation_type.h"
+#include "third_party/blink/renderer/core/animation/css_custom_list_interpolation_type.h"
 #include "third_party/blink/renderer/core/animation/css_default_interpolation_type.h"
 #include "third_party/blink/renderer/core/animation/css_filter_list_interpolation_type.h"
 #include "third_party/blink/renderer/core/animation/css_font_size_interpolation_type.h"
@@ -374,6 +375,53 @@
   return registry_ ? registry_->RegistrationCount() : 0;
 }
 
+static std::unique_ptr<CSSInterpolationType>
+CreateInterpolationTypeForCSSSyntax(CSSSyntaxType syntax,
+                                    PropertyHandle property,
+                                    const PropertyRegistration& registration) {
+  switch (syntax) {
+    case CSSSyntaxType::kAngle:
+      return std::make_unique<CSSAngleInterpolationType>(property,
+                                                         &registration);
+    case CSSSyntaxType::kColor:
+      return std::make_unique<CSSColorInterpolationType>(property,
+                                                         &registration);
+    case CSSSyntaxType::kLength:
+    case CSSSyntaxType::kLengthPercentage:
+    case CSSSyntaxType::kPercentage:
+      return std::make_unique<CSSLengthInterpolationType>(property,
+                                                          &registration);
+    case CSSSyntaxType::kNumber:
+      return std::make_unique<CSSNumberInterpolationType>(property,
+                                                          &registration);
+    case CSSSyntaxType::kResolution:
+      return std::make_unique<CSSResolutionInterpolationType>(property,
+                                                              &registration);
+    case CSSSyntaxType::kTime:
+      return std::make_unique<CSSTimeInterpolationType>(property,
+                                                        &registration);
+    case CSSSyntaxType::kImage:
+      // TODO(andruud): Implement smooth interpolation for gradients.
+      return nullptr;
+    case CSSSyntaxType::kInteger:
+      return std::make_unique<CSSNumberInterpolationType>(property,
+                                                          &registration, true);
+    case CSSSyntaxType::kTransformFunction:
+    case CSSSyntaxType::kTransformList:
+      // TODO(alancutter): Support smooth interpolation of these types.
+      return nullptr;
+    case CSSSyntaxType::kCustomIdent:
+    case CSSSyntaxType::kIdent:
+    case CSSSyntaxType::kTokenStream:
+    case CSSSyntaxType::kUrl:
+      // Smooth interpolation not supported for these types.
+      return nullptr;
+    default:
+      NOTREACHED();
+      return nullptr;
+  }
+}
+
 InterpolationTypes
 CSSInterpolationTypesMap::CreateInterpolationTypesForCSSSyntax(
     const AtomicString& property_name,
@@ -387,60 +435,20 @@
       std::make_unique<CSSVarCycleInterpolationType>(property, registration));
 
   for (const CSSSyntaxComponent& component : descriptor.Components()) {
-    if (component.IsRepeatable()) {
-      // TODO(alancutter): Support animation of repeatable types.
+    std::unique_ptr<CSSInterpolationType> interpolation_type =
+        CreateInterpolationTypeForCSSSyntax(component.type_, property,
+                                            registration);
+
+    if (!interpolation_type)
       continue;
+
+    if (component.IsRepeatable()) {
+      interpolation_type = std::make_unique<CSSCustomListInterpolationType>(
+          property, &registration, std::move(interpolation_type),
+          component.repeat_);
     }
 
-    switch (component.type_) {
-      case CSSSyntaxType::kAngle:
-        result.push_back(std::make_unique<CSSAngleInterpolationType>(
-            property, &registration));
-        break;
-      case CSSSyntaxType::kColor:
-        result.push_back(std::make_unique<CSSColorInterpolationType>(
-            property, &registration));
-        break;
-      case CSSSyntaxType::kLength:
-      case CSSSyntaxType::kLengthPercentage:
-      case CSSSyntaxType::kPercentage:
-        result.push_back(std::make_unique<CSSLengthInterpolationType>(
-            property, &registration));
-        break;
-      case CSSSyntaxType::kNumber:
-        result.push_back(std::make_unique<CSSNumberInterpolationType>(
-            property, &registration));
-        break;
-      case CSSSyntaxType::kResolution:
-        result.push_back(std::make_unique<CSSResolutionInterpolationType>(
-            property, &registration));
-        break;
-      case CSSSyntaxType::kTime:
-        result.push_back(std::make_unique<CSSTimeInterpolationType>(
-            property, &registration));
-        break;
-      case CSSSyntaxType::kImage:
-        // TODO(andruud): Implement smooth interpolation for gradients.
-        break;
-      case CSSSyntaxType::kInteger:
-        result.push_back(std::make_unique<CSSNumberInterpolationType>(
-            property, &registration, true));
-        break;
-      case CSSSyntaxType::kTransformFunction:
-      case CSSSyntaxType::kTransformList:
-        // TODO(alancutter): Support smooth interpolation of these types.
-        break;
-      case CSSSyntaxType::kCustomIdent:
-      case CSSSyntaxType::kIdent:
-      case CSSSyntaxType::kTokenStream:
-      case CSSSyntaxType::kUrl:
-        // No interpolation behaviour defined, uses the
-        // CSSDefaultInterpolationType added below.
-        break;
-      default:
-        NOTREACHED();
-        break;
-    }
+    result.push_back(std::move(interpolation_type));
   }
   result.push_back(std::make_unique<CSSDefaultInterpolationType>(property));
   return result;
diff --git a/third_party/blink/renderer/core/animation/list_interpolation_functions_test.cc b/third_party/blink/renderer/core/animation/list_interpolation_functions_test.cc
index 772f05da..527c9e8 100644
--- a/third_party/blink/renderer/core/animation/list_interpolation_functions_test.cc
+++ b/third_party/blink/renderer/core/animation/list_interpolation_functions_test.cc
@@ -66,8 +66,6 @@
 
 bool NonInterpolableValuesAreCompatible(const NonInterpolableValue* a,
                                         const NonInterpolableValue* b) {
-  // Note that '0' may never be held by TestNonInterpolableValues. See
-  // DCHECK in TestNonInterpolableValue::Create.
   return (a ? ToTestNonInterpolableValue(*a).GetValue() : 0) ==
          (b ? ToTestNonInterpolableValue(*b).GetValue() : 0);
 }
diff --git a/third_party/blink/renderer/core/animation/scroll_timeline.cc b/third_party/blink/renderer/core/animation/scroll_timeline.cc
index 8103b2b4..c4b62ae1 100644
--- a/third_party/blink/renderer/core/animation/scroll_timeline.cc
+++ b/third_party/blink/renderer/core/animation/scroll_timeline.cc
@@ -30,6 +30,14 @@
     result = ScrollTimeline::Inline;
     return true;
   }
+  if (scroll_direction == "horizontal") {
+    result = ScrollTimeline::Horizontal;
+    return true;
+  }
+  if (scroll_direction == "vertical") {
+    result = ScrollTimeline::Vertical;
+    return true;
+  }
   return false;
 }
 }  // namespace
@@ -104,12 +112,18 @@
         is_horizontal ? scroll_offset.Height() : scroll_offset.Width();
     max_offset =
         is_horizontal ? scroll_dimensions.Height() : scroll_dimensions.Width();
-  } else {
-    DCHECK(orientation_ == Inline);
+  } else if (orientation_ == Inline) {
     current_offset =
         is_horizontal ? scroll_offset.Width() : scroll_offset.Height();
     max_offset =
         is_horizontal ? scroll_dimensions.Width() : scroll_dimensions.Height();
+  } else if (orientation_ == Horizontal) {
+    current_offset = scroll_offset.Width();
+    max_offset = scroll_dimensions.Width();
+  } else {
+    DCHECK(orientation_ == Vertical);
+    current_offset = scroll_offset.Height();
+    max_offset = scroll_dimensions.Height();
   }
 
   // 3. If current scroll offset is less than startScrollOffset, return an
@@ -139,6 +153,10 @@
       return "block";
     case Inline:
       return "inline";
+    case Horizontal:
+      return "horizontal";
+    case Vertical:
+      return "vertical";
     default:
       NOTREACHED();
       return "";
diff --git a/third_party/blink/renderer/core/animation/scroll_timeline.h b/third_party/blink/renderer/core/animation/scroll_timeline.h
index ea60fd8..dba7810 100644
--- a/third_party/blink/renderer/core/animation/scroll_timeline.h
+++ b/third_party/blink/renderer/core/animation/scroll_timeline.h
@@ -30,6 +30,8 @@
   enum ScrollDirection {
     Block,
     Inline,
+    Horizontal,
+    Vertical,
   };
 
   static ScrollTimeline* Create(Document&,
diff --git a/third_party/blink/renderer/core/animation/scroll_timeline_options.idl b/third_party/blink/renderer/core/animation/scroll_timeline_options.idl
index aaaae39..ebc4a74 100644
--- a/third_party/blink/renderer/core/animation/scroll_timeline_options.idl
+++ b/third_party/blink/renderer/core/animation/scroll_timeline_options.idl
@@ -7,6 +7,8 @@
 enum ScrollDirection {
     "block",
     "inline",
+    "horizontal",
+    "vertical"
 };
 
 enum ScrollTimelineAutoKeyword { "auto" };
diff --git a/third_party/blink/renderer/core/css/css_path_value.cc b/third_party/blink/renderer/core/css/css_path_value.cc
index ad54f0f..97d2b38 100644
--- a/third_party/blink/renderer/core/css/css_path_value.cc
+++ b/third_party/blink/renderer/core/css/css_path_value.cc
@@ -13,17 +13,24 @@
 
 namespace cssvalue {
 
-CSSPathValue* CSSPathValue::Create(scoped_refptr<StylePath> style_path) {
-  return new CSSPathValue(std::move(style_path));
+CSSPathValue* CSSPathValue::Create(
+    scoped_refptr<StylePath> style_path,
+    PathSerializationFormat serialization_format) {
+  return new CSSPathValue(std::move(style_path), serialization_format);
 }
 
 CSSPathValue* CSSPathValue::Create(
-    std::unique_ptr<SVGPathByteStream> path_byte_stream) {
-  return CSSPathValue::Create(StylePath::Create(std::move(path_byte_stream)));
+    std::unique_ptr<SVGPathByteStream> path_byte_stream,
+    PathSerializationFormat serialization_format) {
+  return CSSPathValue::Create(StylePath::Create(std::move(path_byte_stream)),
+                              serialization_format);
 }
 
-CSSPathValue::CSSPathValue(scoped_refptr<StylePath> style_path)
-    : CSSValue(kPathClass), style_path_(std::move(style_path)) {
+CSSPathValue::CSSPathValue(scoped_refptr<StylePath> style_path,
+                           PathSerializationFormat serialization_format)
+    : CSSValue(kPathClass),
+      style_path_(std::move(style_path)),
+      serialization_format_(serialization_format) {
   DCHECK(style_path_);
 }
 
@@ -46,7 +53,8 @@
 }
 
 String CSSPathValue::CustomCSSText() const {
-  return "path(\"" + BuildStringFromByteStream(ByteStream()) + "\")";
+  return "path(\"" +
+         BuildStringFromByteStream(ByteStream(), serialization_format_) + "\")";
 }
 
 bool CSSPathValue::Equals(const CSSPathValue& other) const {
diff --git a/third_party/blink/renderer/core/css/css_path_value.h b/third_party/blink/renderer/core/css/css_path_value.h
index 7d5c863f..9352a74d 100644
--- a/third_party/blink/renderer/core/css/css_path_value.h
+++ b/third_party/blink/renderer/core/css/css_path_value.h
@@ -10,6 +10,7 @@
 #include "third_party/blink/renderer/core/css/css_value.h"
 #include "third_party/blink/renderer/core/style/style_path.h"
 #include "third_party/blink/renderer/core/svg/svg_path_byte_stream.h"
+#include "third_party/blink/renderer/core/svg/svg_path_utilities.h"
 
 namespace blink {
 
@@ -19,8 +20,10 @@
 
 class CSSPathValue : public CSSValue {
  public:
-  static CSSPathValue* Create(scoped_refptr<StylePath>);
-  static CSSPathValue* Create(std::unique_ptr<SVGPathByteStream>);
+  static CSSPathValue* Create(scoped_refptr<StylePath>,
+                              PathSerializationFormat = kNoTransformation);
+  static CSSPathValue* Create(std::unique_ptr<SVGPathByteStream>,
+                              PathSerializationFormat = kNoTransformation);
 
   static CSSPathValue& EmptyPathValue();
 
@@ -36,9 +39,10 @@
   }
 
  private:
-  CSSPathValue(scoped_refptr<StylePath>);
+  CSSPathValue(scoped_refptr<StylePath>, PathSerializationFormat);
 
   scoped_refptr<StylePath> style_path_;
+  const PathSerializationFormat serialization_format_;
 };
 
 DEFINE_CSS_VALUE_TYPE_CASTS(CSSPathValue, IsPathValue());
diff --git a/third_party/blink/renderer/core/css/parser/css_lazy_parsing_test.cc b/third_party/blink/renderer/core/css/parser/css_lazy_parsing_test.cc
index 0770334..73f942f 100644
--- a/third_party/blink/renderer/core/css/parser/css_lazy_parsing_test.cc
+++ b/third_party/blink/renderer/core/css/parser/css_lazy_parsing_test.cc
@@ -38,7 +38,7 @@
 
   String sheet_text = "body { background-color: red; }";
   CSSParser::ParseSheet(context, style_sheet, sheet_text,
-                        true /* lazy parse */);
+                        CSSDeferPropertyParsing::kYes);
   StyleRule* rule = RuleAt(style_sheet, 0);
   EXPECT_FALSE(HasParsedProperties(rule));
   rule->Properties();
@@ -55,7 +55,7 @@
   String sheet_text =
       "p::before { content: 'foo' } p .class::after { content: 'bar' } ";
   CSSParser::ParseSheet(context, style_sheet, sheet_text,
-                        true /* lazy parse */);
+                        CSSDeferPropertyParsing::kYes);
 
   EXPECT_TRUE(HasParsedProperties(RuleAt(style_sheet, 0)));
   EXPECT_TRUE(HasParsedProperties(RuleAt(style_sheet, 1)));
@@ -72,7 +72,7 @@
 
   String sheet_text = "p::first-letter { ,badness, } ";
   CSSParser::ParseSheet(context, style_sheet, sheet_text,
-                        true /* lazy parse */);
+                        CSSDeferPropertyParsing::kYes);
 
   StyleRule* rule = RuleAt(style_sheet, 0);
   EXPECT_FALSE(HasParsedProperties(rule));
@@ -97,7 +97,7 @@
 
   String sheet_text = "p::before { ,badness, } ";
   CSSParser::ParseSheet(context, style_sheet, sheet_text,
-                        true /* lazy parse */);
+                        CSSDeferPropertyParsing::kYes);
 
   StyleRule* rule = RuleAt(style_sheet, 0);
   EXPECT_TRUE(HasParsedProperties(rule));
@@ -124,7 +124,7 @@
 
     String sheet_text = "body { background-color: red; } p { color: orange;  }";
     CSSParser::ParseSheet(context, cached_contents_, sheet_text,
-                          true /* lazy parse */);
+                          CSSDeferPropertyParsing::kYes);
 
     // Parse the first property set with the first document as owner.
     StyleRule* rule = RuleAt(cached_contents_, 0);
diff --git a/third_party/blink/renderer/core/css/parser/css_parser.cc b/third_party/blink/renderer/core/css/parser/css_parser.cc
index 8757b4e..a753f3f 100644
--- a/third_party/blink/renderer/core/css/parser/css_parser.cc
+++ b/third_party/blink/renderer/core/css/parser/css_parser.cc
@@ -68,7 +68,7 @@
 void CSSParser::ParseSheet(const CSSParserContext* context,
                            StyleSheetContents* style_sheet,
                            const String& text,
-                           bool defer_property_parsing) {
+                           CSSDeferPropertyParsing defer_property_parsing) {
   return CSSParserImpl::ParseStyleSheet(text, context, style_sheet,
                                         defer_property_parsing);
 }
diff --git a/third_party/blink/renderer/core/css/parser/css_parser.h b/third_party/blink/renderer/core/css/parser/css_parser.h
index 690487b..40471cc4 100644
--- a/third_party/blink/renderer/core/css/parser/css_parser.h
+++ b/third_party/blink/renderer/core/css/parser/css_parser.h
@@ -33,10 +33,12 @@
   static StyleRuleBase* ParseRule(const CSSParserContext*,
                                   StyleSheetContents*,
                                   const String&);
+
   static void ParseSheet(const CSSParserContext*,
                          StyleSheetContents*,
                          const String&,
-                         bool defer_property_parsing = false);
+                         CSSDeferPropertyParsing defer_property_parsing =
+                             CSSDeferPropertyParsing::kNo);
   static CSSSelectorList ParseSelector(const CSSParserContext*,
                                        StyleSheetContents*,
                                        const String&);
diff --git a/third_party/blink/renderer/core/css/parser/css_parser_impl.cc b/third_party/blink/renderer/core/css/parser/css_parser_impl.cc
index abce963..1aab233 100644
--- a/third_party/blink/renderer/core/css/parser/css_parser_impl.cc
+++ b/third_party/blink/renderer/core/css/parser/css_parser_impl.cc
@@ -242,10 +242,11 @@
   return rule;
 }
 
-void CSSParserImpl::ParseStyleSheet(const String& string,
-                                    const CSSParserContext* context,
-                                    StyleSheetContents* style_sheet,
-                                    bool defer_property_parsing) {
+void CSSParserImpl::ParseStyleSheet(
+    const String& string,
+    const CSSParserContext* context,
+    StyleSheetContents* style_sheet,
+    CSSDeferPropertyParsing defer_property_parsing) {
   TRACE_EVENT_BEGIN2("blink,blink_style", "CSSParserImpl::parseStyleSheet",
                      "baseUrl", context->BaseURL().GetString().Utf8(), "mode",
                      context->Mode());
@@ -255,7 +256,7 @@
   CSSTokenizer tokenizer(string);
   CSSParserTokenStream stream(tokenizer);
   CSSParserImpl parser(context, style_sheet);
-  if (defer_property_parsing) {
+  if (defer_property_parsing == CSSDeferPropertyParsing::kYes) {
     parser.lazy_state_ =
         new CSSLazyParsingState(context, string, parser.style_sheet_);
   }
diff --git a/third_party/blink/renderer/core/css/parser/css_parser_impl.h b/third_party/blink/renderer/core/css/parser/css_parser_impl.h
index 867ea413..b67f9aa6 100644
--- a/third_party/blink/renderer/core/css/parser/css_parser_impl.h
+++ b/third_party/blink/renderer/core/css/parser/css_parser_impl.h
@@ -95,10 +95,11 @@
                                   const CSSParserContext*,
                                   StyleSheetContents*,
                                   AllowedRulesType);
-  static void ParseStyleSheet(const String&,
-                              const CSSParserContext*,
-                              StyleSheetContents*,
-                              bool defer_property_parsing = false);
+  static void ParseStyleSheet(
+      const String&,
+      const CSSParserContext*,
+      StyleSheetContents*,
+      CSSDeferPropertyParsing = CSSDeferPropertyParsing::kNo);
   static CSSSelectorList ParsePageSelector(CSSParserTokenRange,
                                            StyleSheetContents*);
 
diff --git a/third_party/blink/renderer/core/css/parser/css_parser_mode.h b/third_party/blink/renderer/core/css/parser/css_parser_mode.h
index 05e761c..f2d9bde 100644
--- a/third_party/blink/renderer/core/css/parser/css_parser_mode.h
+++ b/third_party/blink/renderer/core/css/parser/css_parser_mode.h
@@ -67,6 +67,11 @@
   return mode != kUASheetMode;
 }
 
+// Used in CSSParser APIs to say if we should defer parsing of declaration lists
+// in style rules until we need them for CSSOM access, or for applying matched
+// rules to computed style.
+enum class CSSDeferPropertyParsing { kNo, kYes };
+
 }  // namespace blink
 
 #endif  // THIRD_PARTY_BLINK_RENDERER_CORE_CSS_PARSER_CSS_PARSER_MODE_H_
diff --git a/third_party/blink/renderer/core/css/parser/css_parser_proto_fuzzer.cc b/third_party/blink/renderer/core/css/parser/css_parser_proto_fuzzer.cc
index 4b5debeb..0b3fbab65 100644
--- a/third_party/blink/renderer/core/css/parser/css_parser_proto_fuzzer.cc
+++ b/third_party/blink/renderer/core/css/parser/css_parser_proto_fuzzer.cc
@@ -45,6 +45,11 @@
     selector_profile = blink::CSSParserContext::kLiveProfile;
   else
     selector_profile = blink::CSSParserContext::kSnapshotProfile;
+  blink::CSSDeferPropertyParsing defer_property_parsing;
+  if (input.defer_property_parsing())
+    defer_property_parsing = blink::CSSDeferPropertyParsing::kYes;
+  else
+    defer_property_parsing = blink::CSSDeferPropertyParsing::kNo;
   blink::CSSParserContext* context = blink::CSSParserContext::Create(
       mode, secure_context_mode, selector_profile);
 
@@ -55,5 +60,5 @@
       converter.Convert(input.style_sheet()).c_str());
 
   blink::CSSParser::ParseSheet(context, style_sheet, style_sheet_string,
-                               input.defer_property_parsing());
+                               defer_property_parsing);
 }
diff --git a/third_party/blink/renderer/core/css/parser/css_property_parser_helpers.cc b/third_party/blink/renderer/core/css/parser/css_property_parser_helpers.cc
index ba42b924..497d1a5 100644
--- a/third_party/blink/renderer/core/css/parser/css_property_parser_helpers.cc
+++ b/third_party/blink/renderer/core/css/parser/css_property_parser_helpers.cc
@@ -1170,16 +1170,14 @@
       return false;
     gradient->AddStop(stop);
 
-    if (RuntimeEnabledFeatures::MultipleColorStopPositionsEnabled()) {
-      if (!stop.color_ || !stop.offset_)
-        continue;
+    if (!stop.color_ || !stop.offset_)
+      continue;
 
-      // Optional second position.
-      stop.offset_ = consume_position_func(range, context, kValueRangeAll,
-                                           UnitlessQuirk::kForbid);
-      if (stop.offset_)
-        gradient->AddStop(stop);
-    }
+    // Optional second position.
+    stop.offset_ = consume_position_func(range, context, kValueRangeAll,
+                                         UnitlessQuirk::kForbid);
+    if (stop.offset_)
+      gradient->AddStop(stop);
   } while (ConsumeCommaIncludingWhitespace(range));
 
   // The last color stop cannot be a color hint.
diff --git a/third_party/blink/renderer/core/css/style_engine.cc b/third_party/blink/renderer/core/css/style_engine.cc
index 53414d7d..45b8040 100644
--- a/third_party/blink/renderer/core/css/style_engine.cc
+++ b/third_party/blink/renderer/core/css/style_engine.cc
@@ -1512,7 +1512,8 @@
   }
   style_invalidation_root_.ChildrenRemoved(parent);
   style_recalc_root_.ChildrenRemoved(parent);
-  style_invalidation_root_.ChildrenRemoved(parent);
+  DCHECK(!layout_tree_rebuild_root_.GetRootNode());
+  layout_tree_rebuild_root_.ChildrenRemoved(parent);
 }
 
 void StyleEngine::CollectMatchingUserRules(
diff --git a/third_party/blink/renderer/core/css/style_sheet_contents.cc b/third_party/blink/renderer/core/css/style_sheet_contents.cc
index 9c7769fc..2ec89e30 100644
--- a/third_party/blink/renderer/core/css/style_sheet_contents.cc
+++ b/third_party/blink/renderer/core/css/style_sheet_contents.cc
@@ -108,7 +108,7 @@
         static_cast<StyleRuleNamespace*>(o.namespace_rules_[i]->Copy());
   }
 
-  // LazyParseCSS: Copying child rules is a strict point for lazy parsing, so
+  // Copying child rules is a strict point for deferred property parsing, so
   // there is no need to copy lazy parsing state here.
   for (unsigned i = 0; i < child_rules_.size(); ++i)
     child_rules_[i] = o.child_rules_[i]->Copy();
@@ -369,7 +369,7 @@
   const CSSParserContext* context =
       CSSParserContext::CreateWithStyleSheetContents(ParserContext(), this);
   CSSParser::ParseSheet(context, this, sheet_text,
-                        RuntimeEnabledFeatures::LazyParseCSSEnabled());
+                        CSSDeferPropertyParsing::kYes);
 
   DEFINE_STATIC_LOCAL(CustomCountHistogram, parse_histogram,
                       ("Style.AuthorStyleSheet.ParseTime", 0, 10000000, 50));
diff --git a/third_party/blink/renderer/core/dom/attribute_collection.h b/third_party/blink/renderer/core/dom/attribute_collection.h
index 17332803..d7ec066d 100644
--- a/third_party/blink/renderer/core/dom/attribute_collection.h
+++ b/third_party/blink/renderer/core/dom/attribute_collection.h
@@ -64,11 +64,11 @@
   // Find() returns nullptr if the specified name is not found.
   iterator Find(const QualifiedName&) const;
   iterator Find(const AtomicString& name) const;
-  size_t FindIndex(const QualifiedName&) const;
-  size_t FindIndex(const AtomicString& name) const;
+  wtf_size_t FindIndex(const QualifiedName&) const;
+  wtf_size_t FindIndex(const AtomicString& name) const;
 
  protected:
-  size_t FindSlowCase(const AtomicString& name) const;
+  wtf_size_t FindSlowCase(const AtomicString& name) const;
 
   ContainerMemberType attributes_;
 };
@@ -129,16 +129,16 @@
                                            ContainerMemberType>::iterator
 AttributeCollectionGeneric<Container, ContainerMemberType>::Find(
     const AtomicString& name) const {
-  size_t index = FindIndex(name);
+  wtf_size_t index = FindIndex(name);
   return index != kNotFound ? &at(index) : nullptr;
 }
 
 template <typename Container, typename ContainerMemberType>
-inline size_t
+inline wtf_size_t
 AttributeCollectionGeneric<Container, ContainerMemberType>::FindIndex(
     const QualifiedName& name) const {
   iterator end = this->end();
-  unsigned index = 0;
+  wtf_size_t index = 0;
   for (iterator it = begin(); it != end; ++it, ++index) {
     if (it->GetName().Matches(name))
       return index;
@@ -147,7 +147,7 @@
 }
 
 template <typename Container, typename ContainerMemberType>
-inline size_t
+inline wtf_size_t
 AttributeCollectionGeneric<Container, ContainerMemberType>::FindIndex(
     const AtomicString& name) const {
   bool do_slow_check = false;
@@ -155,7 +155,7 @@
   // Optimize for the case where the attribute exists and its name exactly
   // matches.
   iterator end = this->end();
-  unsigned index = 0;
+  wtf_size_t index = 0;
   for (iterator it = begin(); it != end; ++it, ++index) {
     // FIXME: Why check the prefix? Namespaces should be all that matter.
     // Most attributes (all of HTML and CSS) have no namespace.
@@ -186,12 +186,13 @@
 }
 
 template <typename Container, typename ContainerMemberType>
-size_t AttributeCollectionGeneric<Container, ContainerMemberType>::FindSlowCase(
+wtf_size_t
+AttributeCollectionGeneric<Container, ContainerMemberType>::FindSlowCase(
     const AtomicString& name) const {
   // Continue to checking case-insensitively and/or full namespaced names if
   // necessary:
   iterator end = this->end();
-  unsigned index = 0;
+  wtf_size_t index = 0;
   for (iterator it = begin(); it != end; ++it, ++index) {
     if (!it->GetName().HasPrefix()) {
       // Skip attributes with no prefixes because they must be checked in
diff --git a/third_party/blink/renderer/core/dom/distributed_nodes.cc b/third_party/blink/renderer/core/dom/distributed_nodes.cc
index 8d645b3..9ef449dc 100644
--- a/third_party/blink/renderer/core/dom/distributed_nodes.cc
+++ b/third_party/blink/renderer/core/dom/distributed_nodes.cc
@@ -38,13 +38,13 @@
 void DistributedNodes::Append(Node* node) {
   DCHECK(node);
   DCHECK(node->CanParticipateInFlatTree());
-  size_t size = nodes_.size();
+  wtf_size_t size = nodes_.size();
   indices_.Set(node, size);
   nodes_.push_back(node);
 }
 
-size_t DistributedNodes::Find(const Node* node) const {
-  HeapHashMap<Member<const Node>, size_t>::const_iterator it =
+wtf_size_t DistributedNodes::Find(const Node* node) const {
+  HeapHashMap<Member<const Node>, wtf_size_t>::const_iterator it =
       indices_.find(node);
   if (it == indices_.end())
     return kNotFound;
@@ -53,14 +53,14 @@
 }
 
 Node* DistributedNodes::NextTo(const Node* node) const {
-  size_t index = Find(node);
+  wtf_size_t index = Find(node);
   if (index == kNotFound || index + 1 == size())
     return nullptr;
   return at(index + 1);
 }
 
 Node* DistributedNodes::PreviousTo(const Node* node) const {
-  size_t index = Find(node);
+  wtf_size_t index = Find(node);
   if (index == kNotFound || !index)
     return nullptr;
   return at(index - 1);
diff --git a/third_party/blink/renderer/core/dom/distributed_nodes.h b/third_party/blink/renderer/core/dom/distributed_nodes.h
index 16e7743..003bcbc 100644
--- a/third_party/blink/renderer/core/dom/distributed_nodes.h
+++ b/third_party/blink/renderer/core/dom/distributed_nodes.h
@@ -45,9 +45,9 @@
 
   Node* First() const { return nodes_.front(); }
   Node* Last() const { return nodes_.back(); }
-  Node* at(size_t index) const { return nodes_.at(index); }
+  Node* at(wtf_size_t index) const { return nodes_.at(index); }
 
-  size_t size() const { return nodes_.size(); }
+  wtf_size_t size() const { return nodes_.size(); }
   bool IsEmpty() const { return nodes_.IsEmpty(); }
 
   void Append(Node*);
@@ -58,7 +58,7 @@
   void ShrinkToFit() { nodes_.ShrinkToFit(); }
 
   bool Contains(const Node* node) const { return indices_.Contains(node); }
-  size_t Find(const Node*) const;
+  wtf_size_t Find(const Node*) const;
   Node* NextTo(const Node*) const;
   Node* PreviousTo(const Node*) const;
 
@@ -68,7 +68,7 @@
 
  private:
   HeapVector<Member<Node>> nodes_;
-  HeapHashMap<Member<const Node>, size_t> indices_;
+  HeapHashMap<Member<const Node>, wtf_size_t> indices_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/dom/document.cc b/third_party/blink/renderer/core/dom/document.cc
index 0c4160d..f8e2b53 100644
--- a/third_party/blink/renderer/core/dom/document.cc
+++ b/third_party/blink/renderer/core/dom/document.cc
@@ -6694,7 +6694,7 @@
   DCHECK(!top_layer_elements_.Contains(element));
   DCHECK(!before || top_layer_elements_.Contains(before));
   if (before) {
-    size_t before_position = top_layer_elements_.Find(before);
+    wtf_size_t before_position = top_layer_elements_.Find(before);
     top_layer_elements_.insert(before_position, element);
   } else {
     top_layer_elements_.push_back(element);
@@ -6705,7 +6705,7 @@
 void Document::RemoveFromTopLayer(Element* element) {
   if (!element->IsInTopLayer())
     return;
-  size_t position = top_layer_elements_.Find(element);
+  wtf_size_t position = top_layer_elements_.Find(element);
   DCHECK_NE(position, kNotFound);
   top_layer_elements_.EraseAt(position);
   element->SetIsInTopLayer(false);
@@ -6875,7 +6875,7 @@
   if (!View())
     return;
 
-  for (size_t i = 0; i < quads.size(); ++i) {
+  for (wtf_size_t i = 0; i < quads.size(); ++i) {
     AdjustForAbsoluteZoom::AdjustFloatQuad(quads[i], layout_object);
   }
 }
diff --git a/third_party/blink/renderer/core/dom/dom_token_list.cc b/third_party/blink/renderer/core/dom/dom_token_list.cc
index 02337667..b4b6a439 100644
--- a/third_party/blink/renderer/core/dom/dom_token_list.cc
+++ b/third_party/blink/renderer/core/dom/dom_token_list.cc
@@ -193,7 +193,7 @@
   bool found_old_token = false;
   bool found_new_token = false;
   bool did_update = false;
-  for (size_t i = 0; i < token_set_.size(); ++i) {
+  for (wtf_size_t i = 0; i < token_set_.size(); ++i) {
     const AtomicString& existing_token = token_set_[i];
     if (found_old_token) {
       if (existing_token == new_token) {
diff --git a/third_party/blink/renderer/core/dom/element.cc b/third_party/blink/renderer/core/dom/element.cc
index 7273c8c2..767bbbd 100644
--- a/third_party/blink/renderer/core/dom/element.cc
+++ b/third_party/blink/renderer/core/dom/element.cc
@@ -298,7 +298,7 @@
                                IsValue());
 }
 
-Attr* Element::DetachAttribute(size_t index) {
+Attr* Element::DetachAttribute(wtf_size_t index) {
   DCHECK(GetElementData());
   const Attribute& attribute = GetElementData()->Attributes().at(index);
   Attr* attr_node = AttrIfExists(attribute.GetName());
@@ -312,7 +312,7 @@
   return attr_node;
 }
 
-void Element::DetachAttrNodeAtIndex(Attr* attr, size_t index) {
+void Element::DetachAttrNodeAtIndex(Attr* attr, wtf_size_t index) {
   DCHECK(attr);
   DCHECK(GetElementData());
 
@@ -326,7 +326,7 @@
   if (!GetElementData())
     return;
 
-  size_t index = GetElementData()->Attributes().FindIndex(name);
+  wtf_size_t index = GetElementData()->Attributes().FindIndex(name);
   if (index == kNotFound)
     return;
 
@@ -1279,7 +1279,7 @@
     return IntRect();
 
   IntRect result = quads[0].EnclosingBoundingBox();
-  for (size_t i = 1; i < quads.size(); ++i)
+  for (wtf_size_t i = 1; i < quads.size(); ++i)
     result.Unite(quads[i].EnclosingBoundingBox());
 
   return view->FrameToViewport(result);
@@ -1370,7 +1370,7 @@
     return DOMRect::Create();
 
   FloatRect result = quads[0].BoundingBox();
-  for (size_t i = 1; i < quads.size(); ++i)
+  for (wtf_size_t i = 1; i < quads.size(); ++i)
     result.Unite(quads[i].BoundingBox());
 
   LayoutObject* element_layout_object = GetLayoutObject();
@@ -1599,7 +1599,7 @@
   }
 
   AttributeCollection attributes = GetElementData()->Attributes();
-  size_t index = attributes.FindIndex(case_adjusted_local_name);
+  wtf_size_t index = attributes.FindIndex(case_adjusted_local_name);
   const QualifiedName& q_name =
       index != kNotFound
           ? attributes[index].GetName()
@@ -1616,18 +1616,18 @@
 void Element::setAttribute(const QualifiedName& name,
                            const AtomicString& value) {
   SynchronizeAttribute(name);
-  size_t index = GetElementData()
-                     ? GetElementData()->Attributes().FindIndex(name)
-                     : kNotFound;
+  wtf_size_t index = GetElementData()
+                         ? GetElementData()->Attributes().FindIndex(name)
+                         : kNotFound;
   SetAttributeInternal(index, name, value,
                        kNotInSynchronizationOfLazyAttribute);
 }
 
 void Element::SetSynchronizedLazyAttribute(const QualifiedName& name,
                                            const AtomicString& value) {
-  size_t index = GetElementData()
-                     ? GetElementData()->Attributes().FindIndex(name)
-                     : kNotFound;
+  wtf_size_t index = GetElementData()
+                         ? GetElementData()->Attributes().FindIndex(name)
+                         : kNotFound;
   SetAttributeInternal(index, name, value, kInSynchronizationOfLazyAttribute);
 }
 
@@ -1694,7 +1694,7 @@
 }
 
 ALWAYS_INLINE void Element::SetAttributeInternal(
-    size_t index,
+    wtf_size_t index,
     const QualifiedName& name,
     const AtomicString& new_value,
     SynchronizationOfLazyAttribute in_synchronization_of_lazy_attribute) {
@@ -1941,8 +1941,8 @@
 
 void Element::StripScriptingAttributes(
     Vector<Attribute>& attribute_vector) const {
-  size_t destination = 0;
-  for (size_t source = 0; source < attribute_vector.size(); ++source) {
+  wtf_size_t destination = 0;
+  for (wtf_size_t source = 0; source < attribute_vector.size(); ++source) {
     if (IsScriptingAttribute(attribute_vector[source]))
       continue;
 
@@ -3084,7 +3084,7 @@
   const UniqueElementData& element_data = EnsureUniqueElementData();
 
   AttributeCollection attributes = element_data.Attributes();
-  size_t index = attributes.FindIndex(attr_node->GetQualifiedName());
+  wtf_size_t index = attributes.FindIndex(attr_node->GetQualifiedName());
   AtomicString local_name;
   if (index != kNotFound) {
     const Attribute& attr = attributes[index];
@@ -3135,7 +3135,7 @@
 
   SynchronizeAttribute(attr->GetQualifiedName());
 
-  size_t index =
+  wtf_size_t index =
       GetElementData()->Attributes().FindIndex(attr->GetQualifiedName());
   if (index == kNotFound) {
     exception_state.ThrowDOMException(
@@ -3204,7 +3204,7 @@
 }
 
 void Element::RemoveAttributeInternal(
-    size_t index,
+    wtf_size_t index,
     SynchronizationOfLazyAttribute in_synchronization_of_lazy_attribute) {
   MutableAttributeCollection attributes =
       EnsureUniqueElementData().Attributes();
@@ -3248,7 +3248,7 @@
     return;
 
   AtomicString local_name = LowercaseIfNecessary(name);
-  size_t index = GetElementData()->Attributes().FindIndex(local_name);
+  wtf_size_t index = GetElementData()->Attributes().FindIndex(local_name);
   if (index == kNotFound) {
     if (UNLIKELY(local_name == styleAttr) &&
         GetElementData()->style_attribute_is_dirty_ && IsStyledElement())
@@ -4751,7 +4751,7 @@
   attr_node->DetachFromElementWithValue(value);
 
   AttrNodeList* list = GetAttrNodeList();
-  size_t index = list->Find(attr_node);
+  wtf_size_t index = list->Find(attr_node);
   DCHECK_NE(index, kNotFound);
   list->EraseAt(index);
   if (list->IsEmpty())
diff --git a/third_party/blink/renderer/core/dom/element.h b/third_party/blink/renderer/core/dom/element.h
index 3c2e627..54973be 100644
--- a/third_party/blink/renderer/core/dom/element.h
+++ b/third_party/blink/renderer/core/dom/element.h
@@ -353,7 +353,7 @@
   void removeAttributeNS(const AtomicString& namespace_uri,
                          const AtomicString& local_name);
 
-  Attr* DetachAttribute(size_t index);
+  Attr* DetachAttribute(wtf_size_t index);
 
   Attr* getAttributeNode(const AtomicString& name);
   Attr* getAttributeNodeNS(const AtomicString& namespace_uri,
@@ -1074,14 +1074,15 @@
   NodeType getNodeType() const final;
   bool ChildTypeAllowed(NodeType) const final;
 
-  void SetAttributeInternal(size_t index,
+  void SetAttributeInternal(wtf_size_t index,
                             const QualifiedName&,
                             const AtomicString& value,
                             SynchronizationOfLazyAttribute);
   void AppendAttributeInternal(const QualifiedName&,
                                const AtomicString& value,
                                SynchronizationOfLazyAttribute);
-  void RemoveAttributeInternal(size_t index, SynchronizationOfLazyAttribute);
+  void RemoveAttributeInternal(wtf_size_t index,
+                               SynchronizationOfLazyAttribute);
 
   void CancelFocusAppearanceUpdate();
 
@@ -1121,7 +1122,7 @@
   void RemoveAttrNodeList();
   void DetachAllAttrNodesFromElement();
   void DetachAttrNodeFromElementWithValue(Attr*, const AtomicString& value);
-  void DetachAttrNodeAtIndex(Attr*, size_t index);
+  void DetachAttrNodeAtIndex(Attr*, wtf_size_t index);
 
   Member<ElementData> element_data_;
 };
diff --git a/third_party/blink/renderer/core/dom/events/event_dispatcher.cc b/third_party/blink/renderer/core/dom/events/event_dispatcher.cc
index c5f1cce..1fbddc6 100644
--- a/third_party/blink/renderer/core/dom/events/event_dispatcher.cc
+++ b/third_party/blink/renderer/core/dom/events/event_dispatcher.cc
@@ -165,8 +165,8 @@
 
   // A part of step 9 loop.
   if (is_activation_event && !activation_target && event_->bubbles()) {
-    size_t size = event_->GetEventPath().size();
-    for (size_t i = 1; i < size; ++i) {
+    wtf_size_t size = event_->GetEventPath().size();
+    for (wtf_size_t i = 1; i < size; ++i) {
       Node* target = event_->GetEventPath()[i].GetNode();
       if (target->HasActivationBehavior()) {
         activation_target = target;
@@ -224,7 +224,7 @@
       event_->PropagationStopped())
     return kDoneDispatching;
 
-  for (size_t i = event_->GetEventPath().size() - 1; i > 0; --i) {
+  for (wtf_size_t i = event_->GetEventPath().size() - 1; i > 0; --i) {
     const NodeEventContext& event_context = event_->GetEventPath()[i];
     if (event_context.CurrentTargetSameAsTarget()) {
       if (!RuntimeEnabledFeatures::
@@ -254,8 +254,8 @@
 inline void EventDispatcher::DispatchEventAtBubbling() {
   // Trigger bubbling event handlers, starting at the bottom and working our way
   // up.
-  size_t size = event_->GetEventPath().size();
-  for (size_t i = 1; i < size; ++i) {
+  wtf_size_t size = event_->GetEventPath().size();
+  for (wtf_size_t i = 1; i < size; ++i) {
     const NodeEventContext& event_context = event_->GetEventPath()[i];
     if (event_context.CurrentTargetSameAsTarget()) {
       // TODO(hayato): Need to check cancelBubble() also here?
@@ -348,8 +348,8 @@
     // For bubbling events, call default event handlers on the same targets in
     // the same order as the bubbling phase.
     if (!event_->DefaultHandled() && event_->bubbles()) {
-      size_t size = event_->GetEventPath().size();
-      for (size_t i = 1; i < size; ++i) {
+      wtf_size_t size = event_->GetEventPath().size();
+      for (wtf_size_t i = 1; i < size; ++i) {
         event_->GetEventPath()[i].GetNode()->WillCallDefaultEventHandler(
             *event_);
         event_->GetEventPath()[i].GetNode()->DefaultEventHandler(*event_);
diff --git a/third_party/blink/renderer/core/dom/events/event_listener_map.cc b/third_party/blink/renderer/core/dom/events/event_listener_map.cc
index d4c1474..dd0644c 100644
--- a/third_party/blink/renderer/core/dom/events/event_listener_map.cc
+++ b/third_party/blink/renderer/core/dom/events/event_listener_map.cc
@@ -129,7 +129,7 @@
     EventListenerVector* listener_vector,
     const EventListener* listener,
     const EventListenerOptions& options,
-    size_t* index_of_removed_listener,
+    wtf_size_t* index_of_removed_listener,
     RegisteredEventListener* registered_listener) {
   auto* const begin = listener_vector->data();
   auto* const end = begin + listener_vector->size();
@@ -147,7 +147,7 @@
     return false;
   }
   *registered_listener = *it;
-  *index_of_removed_listener = it - begin;
+  *index_of_removed_listener = static_cast<wtf_size_t>(it - begin);
   listener_vector->EraseAt(*index_of_removed_listener);
   return true;
 }
@@ -155,7 +155,7 @@
 bool EventListenerMap::Remove(const AtomicString& event_type,
                               const EventListener* listener,
                               const EventListenerOptions& options,
-                              size_t* index_of_removed_listener,
+                              wtf_size_t* index_of_removed_listener,
                               RegisteredEventListener* registered_listener) {
   CheckNoActiveIterators();
 
diff --git a/third_party/blink/renderer/core/dom/events/event_listener_map.h b/third_party/blink/renderer/core/dom/events/event_listener_map.h
index fc603fd..35e4fa57 100644
--- a/third_party/blink/renderer/core/dom/events/event_listener_map.h
+++ b/third_party/blink/renderer/core/dom/events/event_listener_map.h
@@ -65,7 +65,7 @@
   bool Remove(const AtomicString& event_type,
               const EventListener*,
               const EventListenerOptions&,
-              size_t* index_of_removed_listener,
+              wtf_size_t* index_of_removed_listener,
               RegisteredEventListener* registered_listener);
   EventListenerVector* Find(const AtomicString& event_type);
   Vector<AtomicString> EventTypes() const;
diff --git a/third_party/blink/renderer/core/dom/events/event_path.cc b/third_party/blink/renderer/core/dom/events/event_path.cc
index fecad3a..7aa7fc8 100644
--- a/third_party/blink/renderer/core/dom/events/event_path.cc
+++ b/third_party/blink/renderer/core/dom/events/event_path.cc
@@ -306,7 +306,7 @@
 
 void EventPath::ShrinkForRelatedTarget(const Node& event_target_node,
                                        const Node& event_related_target_node) {
-  for (size_t i = 0; i < size(); ++i) {
+  for (wtf_size_t i = 0; i < size(); ++i) {
     if (ShouldStopEventPath(*at(i).Target(), *at(i).RelatedTarget(),
                             event_target_node, event_related_target_node)) {
       Shrink(i);
@@ -358,7 +358,7 @@
     const HeapVector<Member<TreeScope>>& tree_scopes) {
   if (!touch_list)
     return;
-  for (size_t i = 0; i < touch_list->length(); ++i) {
+  for (wtf_size_t i = 0; i < touch_list->length(); ++i) {
     const Touch& touch = *touch_list->item(i);
     if (!touch.target())
       continue;
@@ -369,7 +369,7 @@
 
     RelatedTargetMap related_node_map;
     BuildRelatedNodeMap(*target_node, related_node_map);
-    for (size_t j = 0; j < tree_scopes.size(); ++j) {
+    for (wtf_size_t j = 0; j < tree_scopes.size(); ++j) {
       adjusted_touch_list[j]->Append(touch.CloneWithNewTarget(
           FindRelatedNode(*tree_scopes[j], related_node_map)));
     }
@@ -409,7 +409,7 @@
 #if DCHECK_IS_ON()
 void EventPath::CheckReachability(TreeScope& tree_scope,
                                   TouchList& touch_list) {
-  for (size_t i = 0; i < touch_list.length(); ++i)
+  for (wtf_size_t i = 0; i < touch_list.length(); ++i)
     DCHECK(touch_list.item(i)
                ->target()
                ->ToNode()
diff --git a/third_party/blink/renderer/core/dom/events/event_path.h b/third_party/blink/renderer/core/dom/events/event_path.h
index 27754e73..8dc3a5a 100644
--- a/third_party/blink/renderer/core/dom/events/event_path.h
+++ b/third_party/blink/renderer/core/dom/events/event_path.h
@@ -56,13 +56,13 @@
   HeapVector<NodeEventContext>& NodeEventContexts() {
     return node_event_contexts_;
   }
-  NodeEventContext& operator[](size_t index) {
+  NodeEventContext& operator[](wtf_size_t index) {
     return node_event_contexts_[index];
   }
-  const NodeEventContext& operator[](size_t index) const {
+  const NodeEventContext& operator[](wtf_size_t index) const {
     return node_event_contexts_[index];
   }
-  NodeEventContext& at(size_t index) { return node_event_contexts_[index]; }
+  NodeEventContext& at(wtf_size_t index) { return node_event_contexts_[index]; }
   NodeEventContext& Last() { return node_event_contexts_[size() - 1]; }
 
   WindowEventContext& GetWindowEventContext() {
@@ -72,7 +72,7 @@
   void EnsureWindowEventContext();
 
   bool IsEmpty() const { return node_event_contexts_.IsEmpty(); }
-  size_t size() const { return node_event_contexts_.size(); }
+  wtf_size_t size() const { return node_event_contexts_.size(); }
 
   void AdjustForRelatedTarget(Node&, EventTarget* related_target);
   void AdjustForTouchEvent(const TouchEvent&);
@@ -98,7 +98,7 @@
   void CalculateAdjustedTargets();
   void CalculateTreeOrderAndSetNearestAncestorClosedTree();
 
-  void Shrink(size_t new_size) {
+  void Shrink(wtf_size_t new_size) {
     DCHECK(!window_event_context_);
     node_event_contexts_.Shrink(new_size);
   }
diff --git a/third_party/blink/renderer/core/dom/events/event_target.cc b/third_party/blink/renderer/core/dom/events/event_target.cc
index 1a27dfe60..95de7fc 100644
--- a/third_party/blink/renderer/core/dom/events/event_target.cc
+++ b/third_party/blink/renderer/core/dom/events/event_target.cc
@@ -481,7 +481,7 @@
   if (!d)
     return false;
 
-  size_t index_of_removed_listener;
+  wtf_size_t index_of_removed_listener;
   RegisteredEventListener registered_listener;
 
   if (!d->event_listener_map.Remove(event_type, listener, options,
@@ -790,8 +790,8 @@
   if (!context)
     return false;
 
-  size_t i = 0;
-  size_t size = entry.size();
+  wtf_size_t i = 0;
+  wtf_size_t size = entry.size();
   if (!d->firing_event_iterators)
     d->firing_event_iterators = std::make_unique<FiringEventIteratorVector>();
   d->firing_event_iterators->push_back(
diff --git a/third_party/blink/renderer/core/dom/events/event_target.h b/third_party/blink/renderer/core/dom/events/event_target.h
index f8f2d5f..c7fb38a 100644
--- a/third_party/blink/renderer/core/dom/events/event_target.h
+++ b/third_party/blink/renderer/core/dom/events/event_target.h
@@ -65,13 +65,13 @@
 struct FiringEventIterator {
   DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
   FiringEventIterator(const AtomicString& event_type,
-                      size_t& iterator,
-                      size_t& end)
+                      wtf_size_t& iterator,
+                      wtf_size_t& end)
       : event_type(event_type), iterator(iterator), end(end) {}
 
   const AtomicString& event_type;
-  size_t& iterator;
-  size_t& end;
+  wtf_size_t& iterator;
+  wtf_size_t& end;
 };
 using FiringEventIteratorVector = Vector<FiringEventIterator, 1>;
 
diff --git a/third_party/blink/renderer/core/dom/frame_request_callback_collection.cc b/third_party/blink/renderer/core/dom/frame_request_callback_collection.cc
index 3923e0b2..bb03fea1 100644
--- a/third_party/blink/renderer/core/dom/frame_request_callback_collection.cc
+++ b/third_party/blink/renderer/core/dom/frame_request_callback_collection.cc
@@ -29,7 +29,7 @@
 }
 
 void FrameRequestCallbackCollection::CancelCallback(CallbackId id) {
-  for (size_t i = 0; i < callbacks_.size(); ++i) {
+  for (wtf_size_t i = 0; i < callbacks_.size(); ++i) {
     if (callbacks_[i]->Id() == id) {
       probe::AsyncTaskCanceledBreakable(context_, "cancelAnimationFrame",
                                         callbacks_[i]);
diff --git a/third_party/blink/renderer/core/dom/layout_tree_builder.cc b/third_party/blink/renderer/core/dom/layout_tree_builder.cc
index 0c2925b..ba586f17 100644
--- a/third_party/blink/renderer/core/dom/layout_tree_builder.cc
+++ b/third_party/blink/renderer/core/dom/layout_tree_builder.cc
@@ -277,7 +277,7 @@
       blocks_.begin(), blocks_.end(), [bfc](const LayoutObject* object) {
         return object->GetNode()->IsDescendantOf(bfc->GetNode());
       });
-  blocks_.resize(std::distance(blocks_.begin(), itr));
+  blocks_.resize(static_cast<wtf_size_t>(std::distance(blocks_.begin(), itr)));
   // Mark BFC root is added into the list.
   bfc->MutableStyle()->SetForceLegacyLayout(true);
   blocks_.push_back(bfc);
diff --git a/third_party/blink/renderer/core/dom/layout_tree_builder_traversal.cc b/third_party/blink/renderer/core/dom/layout_tree_builder_traversal.cc
index 8e00d68..11da8b7 100644
--- a/third_party/blink/renderer/core/dom/layout_tree_builder_traversal.cc
+++ b/third_party/blink/renderer/core/dom/layout_tree_builder_traversal.cc
@@ -301,9 +301,9 @@
     return nullptr;
   const HeapVector<Member<Element>>& top_layer_elements =
       element.GetDocument().TopLayerElements();
-  size_t position = top_layer_elements.Find(&element);
+  wtf_size_t position = top_layer_elements.Find(&element);
   DCHECK_NE(position, kNotFound);
-  for (size_t i = position + 1; i < top_layer_elements.size(); ++i) {
+  for (wtf_size_t i = position + 1; i < top_layer_elements.size(); ++i) {
     if (LayoutObject* layout_object = top_layer_elements[i]->GetLayoutObject())
       return layout_object;
   }
diff --git a/third_party/blink/renderer/core/dom/live_node_list_registry.cc b/third_party/blink/renderer/core/dom/live_node_list_registry.cc
index 45ff11c..7250fafc 100644
--- a/third_party/blink/renderer/core/dom/live_node_list_registry.cc
+++ b/third_party/blink/renderer/core/dom/live_node_list_registry.cc
@@ -49,7 +49,7 @@
   if (it == data_.end())
     return;
 
-  data_.Shrink(it - data_.begin());
+  data_.Shrink(static_cast<wtf_size_t>(it - data_.begin()));
   data_.ShrinkToReasonableCapacity();
   RecomputeMask();
 }
diff --git a/third_party/blink/renderer/core/dom/named_node_map.cc b/third_party/blink/renderer/core/dom/named_node_map.cc
index c611049..691c73b8 100644
--- a/third_party/blink/renderer/core/dom/named_node_map.cc
+++ b/third_party/blink/renderer/core/dom/named_node_map.cc
@@ -46,7 +46,7 @@
 
 Attr* NamedNodeMap::removeNamedItem(const AtomicString& name,
                                     ExceptionState& exception_state) {
-  size_t index =
+  wtf_size_t index =
       element_->Attributes().FindIndex(element_->LowercaseIfNecessary(name));
   if (index == kNotFound) {
     exception_state.ThrowDOMException(
@@ -60,7 +60,7 @@
 Attr* NamedNodeMap::removeNamedItemNS(const AtomicString& namespace_uri,
                                       const AtomicString& local_name,
                                       ExceptionState& exception_state) {
-  size_t index = element_->Attributes().FindIndex(
+  wtf_size_t index = element_->Attributes().FindIndex(
       QualifiedName(g_null_atom, local_name, namespace_uri));
   if (index == kNotFound) {
     exception_state.ThrowDOMException(DOMExceptionCode::kNotFoundError,
diff --git a/third_party/blink/renderer/core/dom/node.cc b/third_party/blink/renderer/core/dom/node.cc
index fb15b44..a2eb134c 100644
--- a/third_party/blink/renderer/core/dom/node.cc
+++ b/third_party/blink/renderer/core/dom/node.cc
@@ -1718,7 +1718,7 @@
 
     if (this_element.HasClass()) {
       name.Append(" class=\'");
-      for (size_t i = 0; i < this_element.ClassNames().size(); ++i) {
+      for (wtf_size_t i = 0; i < this_element.ClassNames().size(); ++i) {
         if (i > 0)
           name.Append(' ');
         AppendUnsafe(name, this_element.ClassNames()[i]);
diff --git a/third_party/blink/renderer/core/dom/shadow_root_v0.cc b/third_party/blink/renderer/core/dom/shadow_root_v0.cc
index b90fb4b3..78a9cfd 100644
--- a/third_party/blink/renderer/core/dom/shadow_root_v0.cc
+++ b/third_party/blink/renderer/core/dom/shadow_root_v0.cc
@@ -70,7 +70,7 @@
 
     if (IsActiveV0InsertionPoint(*child)) {
       V0InsertionPoint* insertion_point = ToV0InsertionPoint(child);
-      for (size_t i = 0; i < insertion_point->DistributedNodesSize(); ++i)
+      for (wtf_size_t i = 0; i < insertion_point->DistributedNodesSize(); ++i)
         nodes_.push_back(insertion_point->DistributedNodeAt(i));
     } else {
       nodes_.push_back(child);
@@ -84,7 +84,7 @@
                                     ShadowRoot* shadow_root) {
   DistributedNodes distributed_nodes;
 
-  for (size_t i = 0; i < nodes_.size(); ++i) {
+  for (wtf_size_t i = 0; i < nodes_.size(); ++i) {
     if (distributed_[i])
       continue;
 
@@ -115,7 +115,7 @@
 }
 
 inline void DistributionPool::DetachNonDistributedNodes() {
-  for (size_t i = 0; i < nodes_.size(); ++i) {
+  for (wtf_size_t i = 0; i < nodes_.size(); ++i) {
     if (distributed_[i])
       continue;
     if (nodes_[i]->GetLayoutObject())
diff --git a/third_party/blink/renderer/core/dom/space_split_string.cc b/third_party/blink/renderer/core/dom/space_split_string.cc
index c41e1b7..38730b3 100644
--- a/third_party/blink/renderer/core/dom/space_split_string.cc
+++ b/third_party/blink/renderer/core/dom/space_split_string.cc
@@ -86,11 +86,11 @@
   if (this == &other)
     return true;
 
-  size_t this_size = vector_.size();
-  size_t other_size = other.vector_.size();
-  for (size_t i = 0; i < other_size; ++i) {
+  wtf_size_t this_size = vector_.size();
+  wtf_size_t other_size = other.vector_.size();
+  for (wtf_size_t i = 0; i < other_size; ++i) {
     const AtomicString& name = other.vector_[i];
-    size_t j;
+    wtf_size_t j;
     for (j = 0; j < this_size; ++j) {
       if (vector_[j] == name)
         break;
@@ -140,27 +140,27 @@
   return changed;
 }
 
-void SpaceSplitString::Remove(size_t index) {
+void SpaceSplitString::Remove(wtf_size_t index) {
   DCHECK_LT(index, size());
   EnsureUnique();
   data_->Remove(index);
 }
 
-void SpaceSplitString::ReplaceAt(size_t index, const AtomicString& token) {
+void SpaceSplitString::ReplaceAt(wtf_size_t index, const AtomicString& token) {
   DCHECK_LT(index, data_->size());
   EnsureUnique();
   (*data_)[index] = token;
 }
 
 AtomicString SpaceSplitString::SerializeToString() const {
-  size_t size = this->size();
+  wtf_size_t size = this->size();
   if (size == 0)
     return g_empty_atom;
   if (size == 1)
     return (*data_)[0];
   StringBuilder builder;
   builder.Append((*data_)[0]);
-  for (size_t i = 1; i < size; ++i) {
+  for (wtf_size_t i = 1; i < size; ++i) {
     builder.Append(' ');
     builder.Append((*data_)[i]);
   }
diff --git a/third_party/blink/renderer/core/dom/space_split_string.h b/third_party/blink/renderer/core/dom/space_split_string.h
index be96e2f..1e1afbf 100644
--- a/third_party/blink/renderer/core/dom/space_split_string.h
+++ b/third_party/blink/renderer/core/dom/space_split_string.h
@@ -51,8 +51,8 @@
   }
   void Add(const AtomicString&);
   bool Remove(const AtomicString&);
-  void Remove(size_t index);
-  void ReplaceAt(size_t index, const AtomicString&);
+  void Remove(wtf_size_t index);
+  void ReplaceAt(wtf_size_t index, const AtomicString&);
 
   // https://dom.spec.whatwg.org/#concept-ordered-set-serializer
   // The ordered set serializer takes a set and returns the concatenation of the
@@ -60,9 +60,9 @@
   // and the empty string otherwise.
   AtomicString SerializeToString() const;
 
-  size_t size() const { return data_ ? data_->size() : 0; }
+  wtf_size_t size() const { return data_ ? data_->size() : 0; }
   bool IsNull() const { return !data_; }
-  const AtomicString& operator[](size_t i) const { return (*data_)[i]; }
+  const AtomicString& operator[](wtf_size_t i) const { return (*data_)[i]; }
 
  private:
   class Data : public RefCounted<Data> {
@@ -86,9 +86,9 @@
     void Remove(unsigned index);
 
     bool IsUnique() const { return key_string_.IsNull(); }
-    size_t size() const { return vector_.size(); }
-    const AtomicString& operator[](size_t i) const { return vector_[i]; }
-    AtomicString& operator[](size_t i) { return vector_[i]; }
+    wtf_size_t size() const { return vector_.size(); }
+    const AtomicString& operator[](wtf_size_t i) const { return vector_[i]; }
+    AtomicString& operator[](wtf_size_t i) { return vector_[i]; }
 
    private:
     explicit Data(const AtomicString&);
diff --git a/third_party/blink/renderer/core/dom/tree_ordered_list.h b/third_party/blink/renderer/core/dom/tree_ordered_list.h
index 3cc7778..19fb759 100644
--- a/third_party/blink/renderer/core/dom/tree_ordered_list.h
+++ b/third_party/blink/renderer/core/dom/tree_ordered_list.h
@@ -48,7 +48,7 @@
   void Remove(const Node*);
   bool IsEmpty() const { return nodes_.IsEmpty(); }
   void Clear() { nodes_.clear(); }
-  size_t size() const { return nodes_.size(); }
+  wtf_size_t size() const { return nodes_.size(); }
 
   using iterator = HeapListHashSet<Member<Node>, 32>::iterator;
   using const_iterator = HeapListHashSet<Member<Node>, 32>::const_iterator;
diff --git a/third_party/blink/renderer/core/dom/tree_scope.cc b/third_party/blink/renderer/core/dom/tree_scope.cc
index d1ef510..f1dfd1e 100644
--- a/third_party/blink/renderer/core/dom/tree_scope.cc
+++ b/third_party/blink/renderer/core/dom/tree_scope.cc
@@ -188,7 +188,7 @@
     return nullptr;
   if (!image_maps_by_name_)
     return nullptr;
-  size_t hash_pos = url.find('#');
+  wtf_size_t hash_pos = url.find('#');
   String name = hash_pos == kNotFound ? url : url.Substring(hash_pos + 1);
   return ToHTMLMapElement(
       image_maps_by_name_->GetElementByMapName(AtomicString(name), *this));
diff --git a/third_party/blink/renderer/core/dom/v0_insertion_point.cc b/third_party/blink/renderer/core/dom/v0_insertion_point.cc
index aef3967..5d5304b 100644
--- a/third_party/blink/renderer/core/dom/v0_insertion_point.cc
+++ b/third_party/blink/renderer/core/dom/v0_insertion_point.cc
@@ -57,8 +57,8 @@
   // Attempt not to reattach nodes that would be distributed to the exact same
   // location by comparing the old and new distributions.
 
-  size_t i = 0;
-  size_t j = 0;
+  wtf_size_t i = 0;
+  wtf_size_t j = 0;
 
   for (; i < distributed_nodes_.size() && j < distributed_nodes.size();
        ++i, ++j) {
@@ -110,7 +110,7 @@
   // distributed nodes benefit from the n^2 protection.
   AttachContext children_context(context);
 
-  for (size_t i = 0; i < distributed_nodes_.size(); ++i) {
+  for (wtf_size_t i = 0; i < distributed_nodes_.size(); ++i) {
     Node* child = distributed_nodes_.at(i);
     if (child->NeedsAttach())
       child->AttachLayoutTree(children_context);
@@ -122,7 +122,7 @@
 }
 
 void V0InsertionPoint::DetachLayoutTree(const AttachContext& context) {
-  for (size_t i = 0; i < distributed_nodes_.size(); ++i)
+  for (wtf_size_t i = 0; i < distributed_nodes_.size(); ++i)
     distributed_nodes_.at(i)->LazyReattachIfAttached();
 
   HTMLElement::DetachLayoutTree(context);
@@ -132,7 +132,7 @@
     WhitespaceAttacher& whitespace_attacher) {
   // This loop traverses the nodes from right to left for the same reason as the
   // one described in ContainerNode::RebuildChildrenLayoutTrees().
-  for (size_t i = distributed_nodes_.size(); i > 0; --i) {
+  for (wtf_size_t i = distributed_nodes_.size(); i > 0; --i) {
     RebuildLayoutTreeForChild(distributed_nodes_.at(i - 1),
                               whitespace_attacher);
   }
@@ -149,7 +149,7 @@
   StyleChangeType style_change_type =
       change == kForce ? kSubtreeStyleChange : kLocalStyleChange;
 
-  for (size_t i = 0; i < distributed_nodes_.size(); ++i) {
+  for (wtf_size_t i = 0; i < distributed_nodes_.size(); ++i) {
     Node* node = distributed_nodes_.at(i);
     if (change == kReattach && node->IsElementNode()) {
       if (node->ShouldCallRecalcStyle(kReattach))
@@ -199,7 +199,7 @@
 
   HeapVector<Member<Node>> nodes;
   nodes.ReserveInitialCapacity(distributed_nodes_.size());
-  for (size_t i = 0; i < distributed_nodes_.size(); ++i)
+  for (wtf_size_t i = 0; i < distributed_nodes_.size(); ++i)
     nodes.UncheckedAppend(distributed_nodes_.at(i));
 
   return StaticNodeList::Adopt(nodes);
@@ -310,7 +310,7 @@
         shadow_root->V0().DestinationInsertionPointsFor(&node);
     if (!insertion_points)
       return;
-    for (size_t i = 0; i < insertion_points->size(); ++i)
+    for (wtf_size_t i = 0; i < insertion_points->size(); ++i)
       results.push_back(insertion_points->at(i).Get());
     DCHECK_NE(current, insertion_points->back().Get());
     current = insertion_points->back().Get();
diff --git a/third_party/blink/renderer/core/dom/v0_insertion_point.h b/third_party/blink/renderer/core/dom/v0_insertion_point.h
index fde33dc5..e50c8f4 100644
--- a/third_party/blink/renderer/core/dom/v0_insertion_point.h
+++ b/third_party/blink/renderer/core/dom/v0_insertion_point.h
@@ -59,7 +59,7 @@
   void RebuildDistributedChildrenLayoutTrees(WhitespaceAttacher&);
 
   size_t DistributedNodesSize() const { return distributed_nodes_.size(); }
-  Node* DistributedNodeAt(size_t index) const {
+  Node* DistributedNodeAt(wtf_size_t index) const {
     return distributed_nodes_.at(index);
   }
   Node* FirstDistributedNode() const {
diff --git a/third_party/blink/renderer/core/exported/web_page_popup_impl.cc b/third_party/blink/renderer/core/exported/web_page_popup_impl.cc
index a60244e..2bd3f31 100644
--- a/third_party/blink/renderer/core/exported/web_page_popup_impl.cc
+++ b/third_party/blink/renderer/core/exported/web_page_popup_impl.cc
@@ -266,22 +266,13 @@
   DCHECK(!page_);
 }
 
-bool WebPagePopupImpl::Initialize(WebViewImpl* web_view,
+void WebPagePopupImpl::Initialize(WebViewImpl* web_view,
                                   PagePopupClient* popup_client) {
   DCHECK(web_view);
   DCHECK(popup_client);
   web_view_ = web_view;
   popup_client_ = popup_client;
 
-  if (!widget_client_ || !InitializePage())
-    return false;
-  widget_client_->Show(WebNavigationPolicy());
-  SetFocus(true);
-
-  return true;
-}
-
-bool WebPagePopupImpl::InitializePage() {
   Page::PageClients page_clients;
   FillWithEmptyClients(page_clients);
   chrome_client_ = PagePopupChromeClient::Create(this);
@@ -327,8 +318,10 @@
   scoped_refptr<SharedBuffer> data = SharedBuffer::Create();
   popup_client_->WriteDocument(data.get());
   frame->SetPageZoomFactor(popup_client_->ZoomFactor());
-  frame->ForceSynchronousDocumentInstall("text/html", data);
-  return true;
+  frame->ForceSynchronousDocumentInstall("text/html", std::move(data));
+
+  widget_client_->Show(WebNavigationPolicy());
+  SetFocus(true);
 }
 
 void WebPagePopupImpl::PostMessageToPopup(const String& message) {
@@ -379,16 +372,12 @@
 }
 
 void WebPagePopupImpl::InitializeLayerTreeView() {
-  TRACE_EVENT0("blink", "WebPagePopupImpl::initializeLayerTreeView");
+  TRACE_EVENT0("blink", "WebPagePopupImpl::InitializeLayerTreeView");
   layer_tree_view_ = widget_client_->InitializeLayerTreeView();
-  if (layer_tree_view_) {
-    layer_tree_view_->SetVisible(true);
-    animation_host_ = std::make_unique<CompositorAnimationHost>(
-        layer_tree_view_->CompositorAnimationHost());
-    page_->LayerTreeViewInitialized(*layer_tree_view_, nullptr);
-  } else {
-    animation_host_ = nullptr;
-  }
+  layer_tree_view_->SetVisible(true);
+  animation_host_ = std::make_unique<CompositorAnimationHost>(
+      layer_tree_view_->CompositorAnimationHost());
+  page_->LayerTreeViewInitialized(*layer_tree_view_, nullptr);
 }
 
 void WebPagePopupImpl::SetSuppressFrameRequestsWorkaroundFor704763Only(
diff --git a/third_party/blink/renderer/core/exported/web_page_popup_impl.h b/third_party/blink/renderer/core/exported/web_page_popup_impl.h
index c34ff129..38dbb53d 100644
--- a/third_party/blink/renderer/core/exported/web_page_popup_impl.h
+++ b/third_party/blink/renderer/core/exported/web_page_popup_impl.h
@@ -61,7 +61,7 @@
 
  public:
   ~WebPagePopupImpl() override;
-  bool Initialize(WebViewImpl*, PagePopupClient*);
+  void Initialize(WebViewImpl*, PagePopupClient*);
   void ClosePopup();
   WebWidgetClient* WidgetClient() const { return widget_client_; }
   bool HasSamePopupClient(WebPagePopupImpl* other) {
@@ -112,7 +112,6 @@
   void SetWindowRect(const IntRect&) override;
 
   explicit WebPagePopupImpl(WebWidgetClient*);
-  bool InitializePage();
   void DestroyPage();
   void InitializeLayerTreeView();
   void SetRootLayer(cc::Layer*);
diff --git a/third_party/blink/renderer/core/exported/web_view_impl.cc b/third_party/blink/renderer/core/exported/web_view_impl.cc
index 80fbad0..8f7ebcf 100644
--- a/third_party/blink/renderer/core/exported/web_view_impl.cc
+++ b/third_party/blink/renderer/core/exported/web_view_impl.cc
@@ -343,7 +343,21 @@
   CoreInitializer::GetInstance().ProvideModulesToPage(*page_, client_);
   SetVisibilityState(visibility_state, true);
 
-  InitializeLayerTreeView();
+  // TODO(dcheng): All WebViewImpls should have an associated LayerTreeView,
+  // but for various reasons, that's not the case... WebView plugin, printing,
+  // workers, and tests don't use a compositor in their WebViews. Sometimes
+  // they avoid the compositor by using a null client, and sometimes by having
+  // the client return a null compositor. We should make things more consistent
+  // and clear.
+  if (WidgetClient()) {
+    if (WidgetClient()->AllowsBrokenNullLayerTreeView()) {
+      // For some reason this was not set when WidgetClient() is not provided,
+      // even though there will be no LayerTreeView in that case either.
+      page_->GetSettings().SetAcceleratedCompositingEnabled(false);
+    } else {
+      InitializeLayerTreeView();
+    }
+  }
 
   dev_tools_emulator_ = DevToolsEmulator::Create(this);
 
@@ -1248,10 +1262,7 @@
   if (!popup_widget)
     return nullptr;
   page_popup_ = ToWebPagePopupImpl(popup_widget);
-  if (!page_popup_->Initialize(this, client)) {
-    page_popup_->ClosePopup();
-    page_popup_ = nullptr;
-  }
+  page_popup_->Initialize(this, client);
   EnablePopupMouseWheelEventListener(frame);
   return page_popup_.get();
 }
@@ -3251,30 +3262,17 @@
 }
 
 void WebViewImpl::InitializeLayerTreeView() {
-  if (WidgetClient()) {
-    layer_tree_view_ = WidgetClient()->InitializeLayerTreeView();
-    // TODO(dcheng): All WebViewImpls should have an associated LayerTreeView,
-    // but for various reasons, that's not the case...
-    page_->GetSettings().SetAcceleratedCompositingEnabled(layer_tree_view_);
-    if (layer_tree_view_) {
-      if (Platform::Current()->IsThreadedAnimationEnabled()) {
-        animation_host_ = std::make_unique<CompositorAnimationHost>(
-            layer_tree_view_->CompositorAnimationHost());
-      }
-
-      page_->LayerTreeViewInitialized(*layer_tree_view_, nullptr);
-      // We don't yet have a page loaded at this point of the initialization of
-      // WebViewImpl, so don't allow cc to commit any frames Blink might
-      // try to create in the meantime.
-      scoped_defer_commits_ = layer_tree_view_->DeferCommits();
-    }
+  layer_tree_view_ = WidgetClient()->InitializeLayerTreeView();
+  if (Platform::Current()->IsThreadedAnimationEnabled()) {
+    animation_host_ = std::make_unique<CompositorAnimationHost>(
+        layer_tree_view_->CompositorAnimationHost());
   }
 
-  // FIXME: only unittests, click to play, Android printing, and printing (for
-  // headers and footers) make this assert necessary. We should make them not
-  // hit this code and then delete allowsBrokenNullLayerTreeView.
-  DCHECK(layer_tree_view_ || !client_ ||
-         client_->WidgetClient()->AllowsBrokenNullLayerTreeView());
+  page_->LayerTreeViewInitialized(*layer_tree_view_, nullptr);
+  // We don't yet have a page loaded at this point of the initialization of
+  // WebViewImpl, so don't allow cc to commit any frames Blink might
+  // try to create in the meantime.
+  scoped_defer_commits_ = layer_tree_view_->DeferCommits();
 }
 
 void WebViewImpl::ApplyViewportDeltas(
diff --git a/third_party/blink/renderer/core/frame/device_single_window_event_controller.cc b/third_party/blink/renderer/core/frame/device_single_window_event_controller.cc
index e804905..3138b17 100644
--- a/third_party/blink/renderer/core/frame/device_single_window_event_controller.cc
+++ b/third_party/blink/renderer/core/frame/device_single_window_event_controller.cc
@@ -7,6 +7,7 @@
 #include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/dom/events/event.h"
 #include "third_party/blink/renderer/core/page/page.h"
+#include "third_party/blink/renderer/platform/weborigin/security_origin.h"
 
 namespace blink {
 
diff --git a/third_party/blink/renderer/core/frame/local_frame.cc b/third_party/blink/renderer/core/frame/local_frame.cc
index 6b033aa..795a02a 100644
--- a/third_party/blink/renderer/core/frame/local_frame.cc
+++ b/third_party/blink/renderer/core/frame/local_frame.cc
@@ -645,7 +645,7 @@
     }
   }
 
-  View()->SetSubtreeNeedsPaintPropertyUpdate();
+  View()->SetSubtreeNeedsForcedPaintPropertyUpdate();
 
   if (!printing)
     GetDocument()->SetPrinting(Document::kNotPrinting);
diff --git a/third_party/blink/renderer/core/frame/local_frame.h b/third_party/blink/renderer/core/frame/local_frame.h
index 327d1b8..6a53c3dd 100644
--- a/third_party/blink/renderer/core/frame/local_frame.h
+++ b/third_party/blink/renderer/core/frame/local_frame.h
@@ -67,6 +67,7 @@
 class AssociatedInterfaceProvider;
 class Color;
 class ComputedAccessibleNode;
+class ContentSecurityPolicy;
 class ContentSettingsClient;
 class Document;
 class Editor;
diff --git a/third_party/blink/renderer/core/frame/local_frame_view.cc b/third_party/blink/renderer/core/frame/local_frame_view.cc
index b206036e..de1453e 100644
--- a/third_party/blink/renderer/core/frame/local_frame_view.cc
+++ b/third_party/blink/renderer/core/frame/local_frame_view.cc
@@ -109,6 +109,7 @@
 #include "third_party/blink/renderer/core/paint/paint_layer.h"
 #include "third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h"
 #include "third_party/blink/renderer/core/paint/paint_timing.h"
+#include "third_party/blink/renderer/core/paint/paint_tracker.h"
 #include "third_party/blink/renderer/core/paint/pre_paint_tree_walk.h"
 #include "third_party/blink/renderer/core/probe/core_probes.h"
 #include "third_party/blink/renderer/core/resize_observer/resize_observer_controller.h"
@@ -989,9 +990,9 @@
     layout_view->SetNeedsPaintPropertyUpdate();
 }
 
-void LocalFrameView::SetSubtreeNeedsPaintPropertyUpdate() {
+void LocalFrameView::SetSubtreeNeedsForcedPaintPropertyUpdate() {
   if (auto* layout_view = GetLayoutView())
-    layout_view->SetSubtreeNeedsPaintPropertyUpdate();
+    layout_view->SetSubtreeNeedsForcedPaintPropertyUpdate();
 }
 
 FloatSize LocalFrameView::ViewportSizeForViewportUnits() const {
@@ -3515,7 +3516,7 @@
   // We may have updated paint properties in detached frame subtree for
   // printing (see UpdateLifecyclePhasesForPrinting()). The paint properties
   // may change after the frame is attached.
-  SetSubtreeNeedsPaintPropertyUpdate();
+  SetSubtreeNeedsForcedPaintPropertyUpdate();
 }
 
 void LocalFrameView::DetachFromLayout() {
@@ -3534,7 +3535,7 @@
 
   // We may need update paint properties in detached frame subtree for printing.
   // See UpdateLifecyclePhasesForPrinting().
-  SetSubtreeNeedsPaintPropertyUpdate();
+  SetSubtreeNeedsForcedPaintPropertyUpdate();
 }
 
 void LocalFrameView::AddPlugin(WebPluginContainerImpl* plugin) {
@@ -4049,7 +4050,7 @@
       layout_view->InvalidatePaintForViewAndCompositedLayers();
     // Also need to update all paint properties that might be skipped while
     // the frame was throttled.
-    SetSubtreeNeedsPaintPropertyUpdate();
+    SetSubtreeNeedsForcedPaintPropertyUpdate();
   }
 
   EventHandlerRegistry& registry = frame_->GetEventHandlerRegistry();
diff --git a/third_party/blink/renderer/core/frame/local_frame_view.h b/third_party/blink/renderer/core/frame/local_frame_view.h
index 7add0823..7f2339ef 100644
--- a/third_party/blink/renderer/core/frame/local_frame_view.h
+++ b/third_party/blink/renderer/core/frame/local_frame_view.h
@@ -37,7 +37,6 @@
 #include "third_party/blink/renderer/core/layout/depth_ordered_layout_object_list.h"
 #include "third_party/blink/renderer/core/paint/compositing/paint_layer_compositor.h"
 #include "third_party/blink/renderer/core/paint/layout_object_counter.h"
-#include "third_party/blink/renderer/core/paint/paint_tracker.h"
 #include "third_party/blink/renderer/platform/geometry/int_rect.h"
 #include "third_party/blink/renderer/platform/geometry/layout_size.h"
 #include "third_party/blink/renderer/platform/graphics/color.h"
@@ -78,6 +77,7 @@
 class PaintArtifactCompositor;
 class PaintController;
 class PaintLayerScrollableArea;
+class PaintTracker;
 class PrintContext;
 class RootFrameViewport;
 class ScrollableArea;
@@ -579,7 +579,7 @@
 
   // Shorthands of LayoutView's corresponding methods.
   void SetNeedsPaintPropertyUpdate();
-  void SetSubtreeNeedsPaintPropertyUpdate();
+  void SetSubtreeNeedsForcedPaintPropertyUpdate();
 
   // Viewport size that should be used for viewport units (i.e. 'vh'/'vw').
   // May include the size of browser controls. See implementation for further
diff --git a/third_party/blink/renderer/core/html/forms/html_text_area_element.cc b/third_party/blink/renderer/core/html/forms/html_text_area_element.cc
index 47172cd..0cf9a4bb 100644
--- a/third_party/blink/renderer/core/html/forms/html_text_area_element.cc
+++ b/third_party/blink/renderer/core/html/forms/html_text_area_element.cc
@@ -394,7 +394,7 @@
     TextControlSetValueSelection selection) {
   // Code elsewhere normalizes line endings added by the user via the keyboard
   // or pasting.  We normalize line endings coming from JavaScript here.
-  String normalized_value = new_value.IsNull() ? "" : new_value;
+  String normalized_value = new_value;
   normalized_value.Replace("\r\n", "\n");
   normalized_value.Replace('\r', '\n');
 
diff --git a/third_party/blink/renderer/core/html/forms/html_text_area_element.idl b/third_party/blink/renderer/core/html/forms/html_text_area_element.idl
index 5fc9a25e..edeecbf 100644
--- a/third_party/blink/renderer/core/html/forms/html_text_area_element.idl
+++ b/third_party/blink/renderer/core/html/forms/html_text_area_element.idl
@@ -39,7 +39,7 @@
 
     readonly attribute DOMString type;
     [CEReactions] attribute DOMString defaultValue;
-    [CEReactions] attribute [TreatNullAs=NullString] DOMString value;
+    [CEReactions] attribute [TreatNullAs=EmptyString] DOMString value;
     readonly attribute unsigned long textLength;
 
     readonly attribute boolean willValidate;
diff --git a/third_party/blink/renderer/core/imagebitmap/image_bitmap.cc b/third_party/blink/renderer/core/imagebitmap/image_bitmap.cc
index 022ddf7..027e5feb 100644
--- a/third_party/blink/renderer/core/imagebitmap/image_bitmap.cc
+++ b/third_party/blink/renderer/core/imagebitmap/image_bitmap.cc
@@ -31,6 +31,7 @@
 constexpr const char* kImageOrientationFlipY = "flipY";
 constexpr const char* kImageBitmapOptionNone = "none";
 constexpr const char* kImageBitmapOptionDefault = "default";
+constexpr const char* kImageBitmapPixelFormat8888 = "8-8-8-8";
 constexpr const char* kImageBitmapOptionPremultiply = "premultiply";
 constexpr const char* kImageBitmapOptionResizeQualityHigh = "high";
 constexpr const char* kImageBitmapOptionResizeQualityMedium = "medium";
@@ -69,6 +70,10 @@
     parsed_options.flip_y = false;
     DCHECK(options.imageOrientation() == kImageBitmapOptionNone);
   }
+
+  if (options.imagePixelFormat() == kImageBitmapPixelFormat8888)
+    parsed_options.pixel_format = kImageBitmapPixelFormat_8888;
+
   if (options.premultiplyAlpha() == kImageBitmapOptionNone) {
     parsed_options.premultiply_alpha = false;
   } else {
@@ -420,17 +425,24 @@
   return StaticBitmapImage::Create(surface->makeImageSnapshot());
 }
 
-}  // namespace
+scoped_refptr<StaticBitmapImage> GetImageWithPixelFormat(
+    scoped_refptr<StaticBitmapImage>&& image,
+    ImageBitmapPixelFormat pixel_format) {
+  if (pixel_format == kImageBitmapPixelFormat_Default)
+    return std::move(image);
+  // If the the image is not half float backed, default and 8-8-8-8 image bitmap
+  // pixel formats result in the same 8-8-8-8 backed image bitmap.
+  sk_sp<SkImage> skia_image = image->PaintImageForCurrentFrame().GetSkImage();
+  if (skia_image->colorType() != kRGBA_F16_SkColorType)
+    return std::move(image);
 
-sk_sp<SkImage> ImageBitmap::GetSkImageFromDecoder(
-    std::unique_ptr<ImageDecoder> decoder) {
-  if (!decoder->FrameCount())
-    return nullptr;
-  ImageFrame* frame = decoder->DecodeFrameBufferAtIndex(0);
-  if (!frame || frame->GetStatus() != ImageFrame::kFrameComplete)
-    return nullptr;
-  DCHECK(!frame->Bitmap().isNull() && !frame->Bitmap().empty());
-  return frame->FinalizePixelsAndGetImage();
+  SkPixmap pixmap;
+  skia_image->peekPixels(&pixmap);
+  SkImageInfo target_info = pixmap.info().makeColorType(kN32_SkColorType);
+  SkBitmap target_bitmap;
+  target_bitmap.allocPixels(target_info);
+  pixmap.readPixels(target_bitmap.pixmap(), 0, 0);
+  return StaticBitmapImage::Create(SkImage::MakeFromBitmap(target_bitmap));
 }
 
 static scoped_refptr<StaticBitmapImage> CropImageAndApplyColorSpaceConversion(
@@ -517,6 +529,11 @@
                                         parsed_options.premultiply_alpha
                                             ? kPremultiplyAlpha
                                             : kUnpremultiplyAlpha);
+
+  // convert pixel format if needed
+  result =
+      GetImageWithPixelFormat(std::move(result), parsed_options.pixel_format);
+
   // resize if up-scaling
   if (up_scaling) {
     result = ScaleImage(std::move(result), parsed_options);
@@ -526,6 +543,18 @@
 
   return result;
 }
+}  // namespace
+
+sk_sp<SkImage> ImageBitmap::GetSkImageFromDecoder(
+    std::unique_ptr<ImageDecoder> decoder) {
+  if (!decoder->FrameCount())
+    return nullptr;
+  ImageFrame* frame = decoder->DecodeFrameBufferAtIndex(0);
+  if (!frame || frame->GetStatus() != ImageFrame::kFrameComplete)
+    return nullptr;
+  DCHECK(!frame->Bitmap().isNull() && !frame->Bitmap().empty());
+  return frame->FinalizePixelsAndGetImage();
+}
 
 ImageBitmap::ImageBitmap(ImageElementBase* image,
                          base::Optional<IntRect> crop_rect,
diff --git a/third_party/blink/renderer/core/imagebitmap/image_bitmap.h b/third_party/blink/renderer/core/imagebitmap/image_bitmap.h
index b98ee61..1410bfe2 100644
--- a/third_party/blink/renderer/core/imagebitmap/image_bitmap.h
+++ b/third_party/blink/renderer/core/imagebitmap/image_bitmap.h
@@ -27,6 +27,11 @@
 class ImageDecoder;
 class OffscreenCanvas;
 
+enum ImageBitmapPixelFormat {
+  kImageBitmapPixelFormat_Default,
+  kImageBitmapPixelFormat_8888,
+};
+
 class CORE_EXPORT ImageBitmap final : public ScriptWrappable,
                                       public CanvasImageSource,
                                       public ImageBitmapSource {
@@ -126,6 +131,7 @@
     unsigned resize_width = 0;
     unsigned resize_height = 0;
     IntRect crop_rect;
+    ImageBitmapPixelFormat pixel_format = kImageBitmapPixelFormat_Default;
     SkFilterQuality resize_quality = kLow_SkFilterQuality;
     CanvasColorParams color_params;
   };
diff --git a/third_party/blink/renderer/core/imagebitmap/image_bitmap_options.idl b/third_party/blink/renderer/core/imagebitmap/image_bitmap_options.idl
index 9fcdca0..f855b7d 100644
--- a/third_party/blink/renderer/core/imagebitmap/image_bitmap_options.idl
+++ b/third_party/blink/renderer/core/imagebitmap/image_bitmap_options.idl
@@ -5,11 +5,13 @@
 // https://html.spec.whatwg.org/#imagebitmapoptions
 
 enum ImageOrientation { "none", "flipY" };
+enum ImageBitmapPixelFormat { "default", "8-8-8-8"};
 enum PremultiplyAlpha { "none", "premultiply", "default" };
 enum ColorSpaceConversion { "none", "default", "srgb", "linear-rgb", "rec2020", "p3" };
 enum ResizeQuality { "pixelated", "low", "medium", "high" };
 dictionary ImageBitmapOptions {
     ImageOrientation imageOrientation = "none";
+    [RuntimeEnabled=CanvasColorManagement] ImageBitmapPixelFormat imagePixelFormat = "default";
     PremultiplyAlpha premultiplyAlpha = "default";
     ColorSpaceConversion colorSpaceConversion = "default";
     [EnforceRange] unsigned long resizeWidth;
diff --git a/third_party/blink/renderer/core/imagebitmap/image_bitmap_test.cc b/third_party/blink/renderer/core/imagebitmap/image_bitmap_test.cc
index f3accea3..03ce26f5 100644
--- a/third_party/blink/renderer/core/imagebitmap/image_bitmap_test.cc
+++ b/third_party/blink/renderer/core/imagebitmap/image_bitmap_test.cc
@@ -759,6 +759,68 @@
   }
 }
 
+TEST_F(ImageBitmapTest, ImageBitmapPixelFormat) {
+  SkImageInfo info = SkImageInfo::MakeS32(10, 10, kPremul_SkAlphaType);
+  sk_sp<SkSurface> surface(SkSurface::MakeRaster(info));
+  sk_sp<SkImage> sk_image = surface->makeImageSnapshot();
+  scoped_refptr<StaticBitmapImage> bitmap_image =
+      StaticBitmapImage::Create(sk_image);
+
+  // source: 8888, bitmap pixel format: default
+  ImageBitmapOptions options;
+  ImageBitmap* image_bitmap =
+      ImageBitmap::Create(bitmap_image, bitmap_image->Rect(), options);
+
+  ASSERT_TRUE(image_bitmap);
+  sk_sp<SkImage> sk_image_internal =
+      image_bitmap->BitmapImage()->PaintImageForCurrentFrame().GetSkImage();
+  ASSERT_EQ(kN32_SkColorType, sk_image_internal->colorType());
+
+  // source: 8888, bitmap pixel format: 8888
+  options.setImagePixelFormat("8-8-8-8");
+  ImageBitmap* image_bitmap_8888 =
+      ImageBitmap::Create(bitmap_image, bitmap_image->Rect(), options);
+  ASSERT_TRUE(image_bitmap_8888);
+  sk_sp<SkImage> sk_image_internal_8888 = image_bitmap_8888->BitmapImage()
+                                              ->PaintImageForCurrentFrame()
+                                              .GetSkImage();
+  ASSERT_EQ(kN32_SkColorType, sk_image_internal_8888->colorType());
+
+  // Since there is no conversion from 8888 to default for image bitmap pixel
+  // format option, we expect the two image bitmaps to refer to the same
+  // internal SkImage back storage.
+  ASSERT_EQ(sk_image_internal, sk_image_internal_8888);
+
+  sk_sp<SkColorSpace> p3_color_space = SkColorSpace::MakeRGB(
+      SkColorSpace::kLinear_RenderTargetGamma, SkColorSpace::kDCIP3_D65_Gamut);
+  SkImageInfo info_f16 = SkImageInfo::Make(10, 10, kRGBA_F16_SkColorType,
+                                           kPremul_SkAlphaType, p3_color_space);
+  sk_sp<SkSurface> surface_f16(SkSurface::MakeRaster(info_f16));
+  sk_sp<SkImage> sk_image_f16 = surface_f16->makeImageSnapshot();
+  scoped_refptr<StaticBitmapImage> bitmap_image_f16 =
+      StaticBitmapImage::Create(sk_image_f16);
+
+  // source: f16, bitmap pixel format: default
+  ImageBitmapOptions options_f16;
+  ImageBitmap* image_bitmap_f16 = ImageBitmap::Create(
+      bitmap_image_f16, bitmap_image_f16->Rect(), options_f16);
+  ASSERT_TRUE(image_bitmap_f16);
+  sk_sp<SkImage> sk_image_internal_f16 =
+      image_bitmap_f16->BitmapImage()->PaintImageForCurrentFrame().GetSkImage();
+  ASSERT_EQ(kRGBA_F16_SkColorType, sk_image_internal_f16->colorType());
+
+  // source: f16, bitmap pixel format: 8888
+  options_f16.setImagePixelFormat("8-8-8-8");
+  ImageBitmap* image_bitmap_f16_8888 = ImageBitmap::Create(
+      bitmap_image_f16, bitmap_image_f16->Rect(), options_f16);
+  ASSERT_TRUE(image_bitmap_f16_8888);
+  sk_sp<SkImage> sk_image_internal_f16_8888 =
+      image_bitmap_f16_8888->BitmapImage()
+          ->PaintImageForCurrentFrame()
+          .GetSkImage();
+  ASSERT_EQ(kN32_SkColorType, sk_image_internal_f16_8888->colorType());
+}
+
 // This test is failing on asan-clang-phone because memory allocation is
 // declined. See <http://crbug.com/782286>.
 #if defined(OS_ANDROID)
diff --git a/third_party/blink/renderer/core/inspector/inspected_frames.cc b/third_party/blink/renderer/core/inspector/inspected_frames.cc
index c5dd408d..51b9336 100644
--- a/third_party/blink/renderer/core/inspector/inspected_frames.cc
+++ b/third_party/blink/renderer/core/inspector/inspected_frames.cc
@@ -6,6 +6,7 @@
 
 #include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/frame/local_frame.h"
+#include "third_party/blink/renderer/platform/weborigin/security_origin.h"
 
 namespace blink {
 
diff --git a/third_party/blink/renderer/core/inspector/inspector_emulation_agent.cc b/third_party/blink/renderer/core/inspector/inspector_emulation_agent.cc
index abb1bdc0..479cdb0 100644
--- a/third_party/blink/renderer/core/inspector/inspector_emulation_agent.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_emulation_agent.cc
@@ -19,6 +19,7 @@
 #include "third_party/blink/renderer/platform/geometry/double_rect.h"
 #include "third_party/blink/renderer/platform/graphics/color.h"
 #include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_request.h"
 #include "third_party/blink/renderer/platform/network/network_utils.h"
 #include "third_party/blink/renderer/platform/scheduler/util/thread_cpu_throttler.h"
 #include "third_party/blink/renderer/platform/wtf/time.h"
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 c7664b4..10068f2 100644
--- a/third_party/blink/renderer/core/inspector/inspector_network_agent.h
+++ b/third_party/blink/renderer/core/inspector/inspector_network_agent.h
@@ -38,6 +38,7 @@
 #include "third_party/blink/renderer/core/inspector/inspector_page_agent.h"
 #include "third_party/blink/renderer/core/inspector/protocol/Network.h"
 #include "third_party/blink/renderer/platform/heap/handle.h"
+#include "third_party/blink/renderer/platform/loader/fetch/resource_load_priority.h"
 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
 
 namespace network {
@@ -51,6 +52,7 @@
 
 namespace blink {
 
+class BlobDataHandle;
 class Document;
 class DocumentLoader;
 class ExecutionContext;
@@ -66,6 +68,7 @@
 class XHRReplayData;
 class XMLHttpRequest;
 class WorkerGlobalScope;
+enum class ResourceRequestBlockedReason;
 enum class ResourceType : uint8_t;
 
 class CORE_EXPORT InspectorNetworkAgent final
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 4bcb13bb..910b913 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
@@ -314,7 +314,7 @@
     // This may affect paint properties of the current object, and descendants
     // even if paint properties of the current object won't change. E.g. the
     // stacking context and/or containing block of descendants may change.
-    SetSubtreeNeedsPaintPropertyUpdate();
+    SetSubtreeNeedsForcedPaintPropertyUpdate();
   } else if (had_transform_related_property != HasTransformRelatedProperty()) {
     // This affects whether to create transform node.
     SetNeedsPaintPropertyUpdate();
diff --git a/third_party/blink/renderer/core/layout/layout_grid.cc b/third_party/blink/renderer/core/layout/layout_grid.cc
index 351edfd..591b871 100644
--- a/third_party/blink/renderer/core/layout/layout_grid.cc
+++ b/third_party/blink/renderer/core/layout/layout_grid.cc
@@ -1291,8 +1291,10 @@
   child.ContainingBlock()->InsertPositionedObject(&child);
 
   PaintLayer* child_layer = child.Layer();
-  child_layer->SetStaticInlinePosition(LayoutUnit(BorderStart()));
-  child_layer->SetStaticBlockPosition(LayoutUnit(BorderBefore()));
+  // Static position of a positioned child should use the content-box
+  // (https://drafts.csswg.org/css-grid/#static-position).
+  child_layer->SetStaticInlinePosition(BorderAndPaddingStart());
+  child_layer->SetStaticBlockPosition(BorderAndPaddingBefore());
 }
 
 bool LayoutGrid::HasStaticPositionForChild(
diff --git a/third_party/blink/renderer/core/layout/layout_object.cc b/third_party/blink/renderer/core/layout/layout_object.cc
index 93f3387..7614f28 100644
--- a/third_party/blink/renderer/core/layout/layout_object.cc
+++ b/third_party/blink/renderer/core/layout/layout_object.cc
@@ -93,6 +93,7 @@
 #include "third_party/blink/renderer/core/paint/object_paint_invalidator.h"
 #include "third_party/blink/renderer/core/paint/paint_layer.h"
 #include "third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h"
+#include "third_party/blink/renderer/core/paint/paint_tracker.h"
 #include "third_party/blink/renderer/core/scroll/smooth_scroll_sequencer.h"
 #include "third_party/blink/renderer/core/style/content_data.h"
 #include "third_party/blink/renderer/core/style/cursor_data.h"
@@ -2296,7 +2297,7 @@
   if (old_style &&
       old_style->UsedTransformStyle3D() != StyleRef().UsedTransformStyle3D()) {
     // Change of transform-style may affect descendant transform property nodes.
-    SetSubtreeNeedsPaintPropertyUpdate();
+    SetSubtreeNeedsForcedPaintPropertyUpdate();
   }
 }
 
diff --git a/third_party/blink/renderer/core/layout/layout_object.h b/third_party/blink/renderer/core/layout/layout_object.h
index 341f995b..1629e56 100644
--- a/third_party/blink/renderer/core/layout/layout_object.h
+++ b/third_party/blink/renderer/core/layout/layout_object.h
@@ -1893,7 +1893,7 @@
                 DocumentLifecycle::kInPrePaint);
       layout_object_.ClearPaintInvalidationFlags();
       layout_object_.bitfields_.SetNeedsPaintPropertyUpdate(false);
-      layout_object_.bitfields_.SetSubtreeNeedsPaintPropertyUpdate(false);
+      layout_object_.bitfields_.SetSubtreeNeedsForcedPaintPropertyUpdate(false);
       layout_object_.bitfields_.SetDescendantNeedsPaintPropertyUpdate(false);
       layout_object_.bitfields_.SetEffectiveWhitelistedTouchActionChanged(
           false);
@@ -1945,8 +1945,8 @@
     void SetNeedsPaintPropertyUpdate() {
       layout_object_.SetNeedsPaintPropertyUpdate();
     }
-    void SetSubtreeNeedsPaintPropertyUpdate() {
-      layout_object_.SetSubtreeNeedsPaintPropertyUpdate();
+    void SetSubtreeNeedsForcedPaintPropertyUpdate() {
+      layout_object_.SetSubtreeNeedsForcedPaintPropertyUpdate();
     }
 
     void SetPartialInvalidationVisualRect(const LayoutRect& r) {
@@ -2011,12 +2011,12 @@
   bool NeedsPaintPropertyUpdate() const {
     return bitfields_.NeedsPaintPropertyUpdate();
   }
-  void SetSubtreeNeedsPaintPropertyUpdate() {
-    bitfields_.SetSubtreeNeedsPaintPropertyUpdate(true);
+  void SetSubtreeNeedsForcedPaintPropertyUpdate() {
+    bitfields_.SetSubtreeNeedsForcedPaintPropertyUpdate(true);
     SetNeedsPaintPropertyUpdate();
   }
-  bool SubtreeNeedsPaintPropertyUpdate() const {
-    return bitfields_.SubtreeNeedsPaintPropertyUpdate();
+  bool SubtreeNeedsForcedPaintPropertyUpdate() const {
+    return bitfields_.SubtreeNeedsForcedPaintPropertyUpdate();
   }
   bool DescendantNeedsPaintPropertyUpdate() const {
     return bitfields_.DescendantNeedsPaintPropertyUpdate();
@@ -2463,7 +2463,7 @@
           scroll_anchor_disabling_style_changed_(false),
           has_box_decoration_background_(false),
           needs_paint_property_update_(true),
-          subtree_needs_paint_property_update_(true),
+          subtree_needs_forced_paint_property_update_(true),
           descendant_needs_paint_property_update_(true),
           background_changed_since_last_paint_invalidation_(true),
           outline_may_be_affected_by_descendants_(false),
@@ -2666,8 +2666,8 @@
     ADD_BOOLEAN_BITFIELD(needs_paint_property_update_,
                          NeedsPaintPropertyUpdate);
     // Whether paint properties of the whole subtree need to be updated.
-    ADD_BOOLEAN_BITFIELD(subtree_needs_paint_property_update_,
-                         SubtreeNeedsPaintPropertyUpdate)
+    ADD_BOOLEAN_BITFIELD(subtree_needs_forced_paint_property_update_,
+                         SubtreeNeedsForcedPaintPropertyUpdate)
     // Whether the paint properties of a descendant need to be updated. For more
     // details, see LayoutObject::descendantNeedsPaintPropertyUpdate().
     ADD_BOOLEAN_BITFIELD(descendant_needs_paint_property_update_,
diff --git a/third_party/blink/renderer/core/layout/layout_object_child_list.cc b/third_party/blink/renderer/core/layout/layout_object_child_list.cc
index 3be7ef9d..8b670299 100644
--- a/third_party/blink/renderer/core/layout/layout_object_child_list.cc
+++ b/third_party/blink/renderer/core/layout/layout_object_child_list.cc
@@ -215,7 +215,7 @@
       LayoutInvalidationReason::kAddedToLayout);
   new_child->SetShouldDoFullPaintInvalidation(
       PaintInvalidationReason::kAppeared);
-  new_child->SetSubtreeNeedsPaintPropertyUpdate();
+  new_child->SetSubtreeNeedsForcedPaintPropertyUpdate();
   if (!owner->NormalChildNeedsLayout()) {
     owner->SetChildNeedsLayout();  // We may supply the static position for an
                                    // absolute positioned child.
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 a40375c..a9154a3 100644
--- a/third_party/blink/renderer/core/layout/layout_object_test.cc
+++ b/third_party/blink/renderer/core/layout/layout_object_test.cc
@@ -348,17 +348,17 @@
   EXPECT_FALSE(object->DescendantNeedsPaintPropertyUpdate());
 }
 
-TEST_F(LayoutObjectTest, SubtreeNeedsPaintPropertyUpdate) {
+TEST_F(LayoutObjectTest, SubtreeNeedsForcedPaintPropertyUpdate) {
   LayoutObject* object = GetDocument().body()->GetLayoutObject();
-  object->SetSubtreeNeedsPaintPropertyUpdate();
-  EXPECT_TRUE(object->SubtreeNeedsPaintPropertyUpdate());
+  object->SetSubtreeNeedsForcedPaintPropertyUpdate();
+  EXPECT_TRUE(object->SubtreeNeedsForcedPaintPropertyUpdate());
   EXPECT_TRUE(object->NeedsPaintPropertyUpdate());
   EXPECT_TRUE(object->Parent()->DescendantNeedsPaintPropertyUpdate());
 
   GetDocument().Lifecycle().AdvanceTo(DocumentLifecycle::kInPrePaint);
   object->GetMutableForPainting().ClearPaintFlags();
 
-  EXPECT_FALSE(object->SubtreeNeedsPaintPropertyUpdate());
+  EXPECT_FALSE(object->SubtreeNeedsForcedPaintPropertyUpdate());
   EXPECT_FALSE(object->NeedsPaintPropertyUpdate());
 }
 
diff --git a/third_party/blink/renderer/core/layout/layout_table.cc b/third_party/blink/renderer/core/layout/layout_table.cc
index f2497d62..ab67179c 100644
--- a/third_party/blink/renderer/core/layout/layout_table.cc
+++ b/third_party/blink/renderer/core/layout/layout_table.cc
@@ -254,16 +254,21 @@
   column_layout_objects_.resize(0);
 }
 
-void LayoutTable::AddColumn(const LayoutTableCol*) {
+void LayoutTable::ColumnStructureChanged() {
+  column_structure_changed_ = true;
   InvalidateCachedColumns();
+  // We don't really need to recompute our sections, but we do need to update
+  // our column count, whether we have a column, and possibly the logical width
+  // distribution too.
+  SetNeedsSectionRecalc();
+}
+
+void LayoutTable::AddColumn(const LayoutTableCol*) {
+  ColumnStructureChanged();
 }
 
 void LayoutTable::RemoveColumn(const LayoutTableCol*) {
-  InvalidateCachedColumns();
-  // We don't really need to recompute our sections, but we need to update our
-  // column count and whether we have a column. Currently, we only have one
-  // size-fit-all flag but we may have to consider splitting it.
-  SetNeedsSectionRecalc();
+  ColumnStructureChanged();
 }
 
 bool LayoutTable::IsLogicalWidthAuto() const {
@@ -1250,11 +1255,16 @@
        child = child->NextSibling()) {
     if (child->IsTableSection()) {
       LayoutTableSection* section = ToLayoutTableSection(child);
+      if (column_structure_changed_) {
+        section->MarkAllCellsWidthsDirtyAndOrNeedsLayout(
+            LayoutTable::kMarkDirtyAndNeedsLayout);
+      }
       unsigned section_cols = section->NumEffectiveColumns();
       if (section_cols > max_cols)
         max_cols = section_cols;
     }
   }
+  column_structure_changed_ = false;
 
   effective_columns_.resize(max_cols);
   effective_column_positions_.resize(max_cols + 1);
diff --git a/third_party/blink/renderer/core/layout/layout_table.h b/third_party/blink/renderer/core/layout/layout_table.h
index 045eff0..9edfc57 100644
--- a/third_party/blink/renderer/core/layout/layout_table.h
+++ b/third_party/blink/renderer/core/layout/layout_table.h
@@ -430,6 +430,7 @@
   void EnsureIsReadyForPaintInvalidation() override;
   void InvalidatePaint(const PaintInvalidatorContext&) const override;
   bool PaintedOutputOfObjectHasNoEffectRegardlessOfSize() const override;
+  void ColumnStructureChanged();
 
  private:
   bool IsOfType(LayoutObjectType type) const override {
@@ -561,6 +562,12 @@
   mutable bool needs_section_recalc_ : 1;
 
   bool column_logical_width_changed_ : 1;
+  // This flag indicates whether any columns (with or without fixed widths) have
+  // been added or removed since the last layout. If they have, then the true
+  // size of the cell contents needs to be determined with a full layout before
+  // the layout cache is updated. The layout cache can be invalid when layout is
+  // valid (e.g. if the table is being painted for the first time).
+  mutable bool column_structure_changed_ : 1;
   mutable bool column_layout_objects_valid_ : 1;
   mutable unsigned no_cell_colspan_at_least_;
   unsigned CalcNoCellColspanAtLeast() const {
diff --git a/third_party/blink/renderer/core/layout/svg/svg_layout_tree_as_text.cc b/third_party/blink/renderer/core/layout/svg/svg_layout_tree_as_text.cc
index 2cd2bea2..6b685d8 100644
--- a/third_party/blink/renderer/core/layout/svg/svg_layout_tree_as_text.cc
+++ b/third_party/blink/renderer/core/layout/svg/svg_layout_tree_as_text.cc
@@ -412,8 +412,9 @@
   } else if (IsSVGPathElement(*svg_element)) {
     const StylePath& path =
         svg_style.D() ? *svg_style.D() : *StylePath::EmptyPath();
-    WriteNameAndQuotedValue(ts, "data",
-                            BuildStringFromByteStream(path.ByteStream()));
+    WriteNameAndQuotedValue(
+        ts, "data",
+        BuildStringFromByteStream(path.ByteStream(), kNoTransformation));
   } else {
     NOTREACHED();
   }
diff --git a/third_party/blink/renderer/core/loader/frame_loader.cc b/third_party/blink/renderer/core/loader/frame_loader.cc
index 51d2bd7..00f2aca 100644
--- a/third_party/blink/renderer/core/loader/frame_loader.cc
+++ b/third_party/blink/renderer/core/loader/frame_loader.cc
@@ -44,6 +44,7 @@
 #include "third_party/blink/public/platform/modules/service_worker/web_service_worker_network_provider.h"
 #include "third_party/blink/public/platform/task_type.h"
 #include "third_party/blink/public/platform/web_url_request.h"
+#include "third_party/blink/public/web/commit_result.mojom-shared.h"
 #include "third_party/blink/public/web/web_frame_load_type.h"
 #include "third_party/blink/public/web/web_history_item.h"
 #include "third_party/blink/public/web/web_navigation_params.h"
diff --git a/third_party/blink/renderer/core/loader/frame_loader.h b/third_party/blink/renderer/core/loader/frame_loader.h
index 2771023..d184fba1 100644
--- a/third_party/blink/renderer/core/loader/frame_loader.h
+++ b/third_party/blink/renderer/core/loader/frame_loader.h
@@ -34,17 +34,10 @@
 #define THIRD_PARTY_BLINK_RENDERER_CORE_LOADER_FRAME_LOADER_H_
 
 #include "base/macros.h"
-#include "third_party/blink/public/mojom/blob/blob_url_store.mojom-blink.h"
-#include "third_party/blink/public/platform/web_insecure_request_policy.h"
-#include "third_party/blink/public/web/commit_result.mojom-shared.h"
 #include "third_party/blink/public/web/web_document_loader.h"
 #include "third_party/blink/public/web/web_frame_load_type.h"
-#include "third_party/blink/public/web/web_navigation_params.h"
 #include "third_party/blink/public/web/web_navigation_type.h"
-#include "third_party/blink/public/web/web_triggering_event_info.h"
 #include "third_party/blink/renderer/core/core_export.h"
-#include "third_party/blink/renderer/core/dom/icon_url.h"
-#include "third_party/blink/renderer/core/execution_context/security_context.h"
 #include "third_party/blink/renderer/core/frame/frame_types.h"
 #include "third_party/blink/renderer/core/frame/sandbox_flags.h"
 #include "third_party/blink/renderer/core/loader/frame_loader_state_machine.h"
@@ -52,9 +45,6 @@
 #include "third_party/blink/renderer/core/loader/history_item.h"
 #include "third_party/blink/renderer/core/loader/navigation_policy.h"
 #include "third_party/blink/renderer/platform/heap/handle.h"
-#include "third_party/blink/renderer/platform/instrumentation/tracing/traced_value.h"
-#include "third_party/blink/renderer/platform/loader/fetch/resource_loader_options.h"
-#include "third_party/blink/renderer/platform/loader/fetch/resource_request.h"
 #include "third_party/blink/renderer/platform/wtf/forward.h"
 #include "third_party/blink/renderer/platform/wtf/hash_set.h"
 
@@ -70,11 +60,17 @@
 class LocalFrameClient;
 class ProgressTracker;
 class ResourceError;
+class ResourceRequest;
 class SerializedScriptValue;
 class SubstituteData;
+class TracedValue;
 struct FrameLoadRequest;
 struct WebNavigationParams;
 
+namespace mojom {
+enum class CommitResult : int32_t;
+}
+
 CORE_EXPORT bool IsBackForwardLoadType(WebFrameLoadType);
 CORE_EXPORT bool IsReloadLoadType(WebFrameLoadType);
 
diff --git a/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc b/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc
index 1918064d9..0451973 100644
--- a/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc
+++ b/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc
@@ -888,7 +888,9 @@
 static bool NeedsFilter(const LayoutObject& object) {
   // TODO(trchen): SVG caches filters in SVGResources. Implement it.
   if (object.IsBoxModelObject() && ToLayoutBoxModelObject(object).Layer() &&
-      (object.StyleRef().HasFilter() || object.HasReflection()))
+      (object.StyleRef().HasFilter() || object.HasReflection() ||
+       CompositingReasonFinder::RequiresCompositingForFilterAnimation(
+           object.StyleRef())))
     return true;
   if (object.IsLayoutImage() && ToLayoutImage(object).ShouldInvertColor())
     return true;
@@ -2578,7 +2580,7 @@
 
   // Need to update subtree paint properties for the changed fragments.
   if (fragments_changed)
-    object_.GetMutableForPainting().SetSubtreeNeedsPaintPropertyUpdate();
+    object_.GetMutableForPainting().SetSubtreeNeedsForcedPaintPropertyUpdate();
 }
 
 bool PaintPropertyTreeBuilder::ObjectIsRepeatingTableSectionInPagedMedia()
@@ -2804,7 +2806,8 @@
     builder.UpdateForChildren();
     property_changed |= builder.PropertyChanged();
     property_added_or_removed |= builder.PropertyAddedOrRemoved();
-    context_.force_subtree_update |= object_.SubtreeNeedsPaintPropertyUpdate();
+    context_.force_subtree_update |=
+        object_.SubtreeNeedsForcedPaintPropertyUpdate();
     fragment_data = fragment_data->NextFragment();
   }
   DCHECK(!fragment_data);
diff --git a/third_party/blink/renderer/core/paint/paint_tracker.cc b/third_party/blink/renderer/core/paint/paint_tracker.cc
index 45807d05..76aa975 100644
--- a/third_party/blink/renderer/core/paint/paint_tracker.cc
+++ b/third_party/blink/renderer/core/paint/paint_tracker.cc
@@ -32,10 +32,10 @@
 }
 
 void PaintTracker::NotifyNodeRemoved(const LayoutObject& object) {
-  if (object.GetNode()) {
-    text_paint_timing_detector_->NotifyNodeRemoved(
-        DOMNodeIds::IdForNode(object.GetNode()));
-  }
+  if (!object.GetNode())
+    return;
+  text_paint_timing_detector_->NotifyNodeRemoved(
+      DOMNodeIds::IdForNode(object.GetNode()));
 }
 
 void PaintTracker::Dispose() {
diff --git a/third_party/blink/renderer/core/paint/paint_tracker.h b/third_party/blink/renderer/core/paint/paint_tracker.h
index ed0aa081..23fcac2 100644
--- a/third_party/blink/renderer/core/paint/paint_tracker.h
+++ b/third_party/blink/renderer/core/paint/paint_tracker.h
@@ -18,6 +18,8 @@
 // PaintTracker contains some of paint metric detectors, providing common
 // infrastructure for these detectors.
 //
+// Users has to enable 'loading' trace category to enable the metrics.
+//
 // See also:
 // https://docs.google.com/document/d/1DRVd4a2VU8-yyWftgOparZF-sf16daf0vfbsHuz2rws/edit
 class CORE_EXPORT PaintTracker : public GarbageCollected<PaintTracker> {
diff --git a/third_party/blink/renderer/core/paint/pre_paint_tree_walk.cc b/third_party/blink/renderer/core/paint/pre_paint_tree_walk.cc
index f00f098..1636421 100644
--- a/third_party/blink/renderer/core/paint/pre_paint_tree_walk.cc
+++ b/third_party/blink/renderer/core/paint/pre_paint_tree_walk.cc
@@ -21,6 +21,7 @@
 #include "third_party/blink/renderer/core/paint/ng/ng_paint_fragment.h"
 #include "third_party/blink/renderer/core/paint/paint_layer.h"
 #include "third_party/blink/renderer/core/paint/paint_property_tree_printer.h"
+#include "third_party/blink/renderer/core/paint/paint_tracker.h"
 #include "third_party/blink/renderer/platform/graphics/paint/geometry_mapper.h"
 
 namespace blink {
diff --git a/third_party/blink/renderer/core/paint/text_paint_timing_detector.cc b/third_party/blink/renderer/core/paint/text_paint_timing_detector.cc
index a961ad33..32f12199 100644
--- a/third_party/blink/renderer/core/paint/text_paint_timing_detector.cc
+++ b/third_party/blink/renderer/core/paint/text_paint_timing_detector.cc
@@ -75,8 +75,7 @@
 }
 
 void TextPaintTimingDetector::TimerFired(TimerBase* timer) {
-  TextRecord* largest_text_first_paint = FindLargestPaintCandidate();
-  if (largest_text_first_paint) {
+  if (TextRecord* largest_text_first_paint = FindLargestPaintCandidate()) {
     std::unique_ptr<TracedValue> value = TracedValue::Create();
     PopulateTraceValue(value, largest_text_first_paint,
                        largest_text_report_count_++);
@@ -84,8 +83,7 @@
         "loading", "LargestTextPaint::Candidate", TRACE_EVENT_SCOPE_THREAD,
         largest_text_first_paint->first_paint_time, "data", std::move(value));
   }
-  TextRecord* last_text_first_paint = FindLastPaintCandidate();
-  if (last_text_first_paint) {
+  if (TextRecord* last_text_first_paint = FindLastPaintCandidate()) {
     std::unique_ptr<TracedValue> value = TracedValue::Create();
     PopulateTraceValue(value, last_text_first_paint, last_text_report_count_++);
     TRACE_EVENT_INSTANT_WITH_TIMESTAMP1(
@@ -95,24 +93,25 @@
 }
 
 void TextPaintTimingDetector::OnPrePaintFinished() {
-  if (texts_to_record_swap_time.size() > 0) {
+  if (texts_to_record_swap_time_.size() > 0) {
     // Start repeating timer only once after the first text prepaint.
     if (!timer_.IsActive()) {
       timer_.StartRepeating(kTimerDelay, FROM_HERE);
     }
-    RegisterNotifySwapTime(
-        CrossThreadBind(&TextPaintTimingDetector::ReportSwapTime,
-                        WrapCrossThreadWeakPersistent(this)));
+    if (!awaiting_swap_promise_) {
+      RegisterNotifySwapTime(
+          CrossThreadBind(&TextPaintTimingDetector::ReportSwapTime,
+                          WrapCrossThreadWeakPersistent(this)));
+    }
   }
 }
 
 void TextPaintTimingDetector::NotifyNodeRemoved(DOMNodeId node_id) {
-  auto it = recorded_text_node_ids.find(node_id);
-  if (it != recorded_text_node_ids.end()) {
+  if (recorded_text_node_ids_.find(node_id) != recorded_text_node_ids_.end()) {
     // We assume that the removed node's id wouldn't be recycled, so we don't
     // bother to remove these records from largest_text_heap_ or
     // latest_text_heap_, to reduce computation.
-    recorded_text_node_ids.erase(node_id);
+    recorded_text_node_ids_.erase(node_id);
   }
 }
 
@@ -126,23 +125,25 @@
   if (WebLayerTreeView* layerTreeView =
           frame.GetPage()->GetChromeClient().GetWebLayerTreeView(&frame)) {
     layerTreeView->NotifySwapTime(ConvertToBaseCallback(std::move(callback)));
+    awaiting_swap_promise_ = true;
   }
 }
 
 void TextPaintTimingDetector::ReportSwapTime(
     WebLayerTreeView::SwapResult result,
     base::TimeTicks timestamp) {
-  // If texts_to_record_swap_time.size == 0, it means the array has been
+  // If texts_to_record_swap_time_.size == 0, it means the array has been
   // consumed in a callback earlier than this one. That violates the assumption
   // that only one or zero callback will be called after one OnPrePaintFinished.
-  DCHECK_GT(texts_to_record_swap_time.size(), 0UL);
-  for (TextRecord& record : texts_to_record_swap_time) {
+  DCHECK_GT(texts_to_record_swap_time_.size(), 0UL);
+  for (TextRecord& record : texts_to_record_swap_time_) {
     record.first_paint_time = timestamp;
-    recorded_text_node_ids.insert(record.node_id);
+    recorded_text_node_ids_.insert(record.node_id);
     largest_text_heap_.push(std::make_unique<TextRecord>(record));
     latest_text_heap_.push(std::make_unique<TextRecord>(record));
   }
-  texts_to_record_swap_time.clear();
+  texts_to_record_swap_time_.clear();
+  awaiting_swap_promise_ = false;
 }
 
 void TextPaintTimingDetector::RecordText(const LayoutObject& object,
@@ -152,12 +153,12 @@
     return;
   DOMNodeId node_id = DOMNodeIds::IdForNode(node);
 
-  if (size_zero_node_ids.find(node_id) != size_zero_node_ids.end())
+  if (size_zero_node_ids_.find(node_id) != size_zero_node_ids_.end())
     return;
-  if (recorded_text_node_ids.find(node_id) != recorded_text_node_ids.end())
+  if (recorded_text_node_ids_.find(node_id) != recorded_text_node_ids_.end())
     return;
 
-  // When node_id is not found in recorded_text_node_ids, this invalidation is
+  // When node_id is not found in recorded_text_node_ids_, this invalidation is
   // the text's first invalidation.
   LayoutRect invalidated_rect = object.FirstFragment().VisualRect();
   int rect_size = 0;
@@ -171,41 +172,35 @@
   // the text is size 0 or the text is out of viewport. Either way, we don't
   // record their time, to reduce computation.
   if (rect_size == 0) {
-    size_zero_node_ids.insert(node_id);
+    size_zero_node_ids_.insert(node_id);
   } else {
     TextRecord record = {node_id, rect_size, base::TimeTicks(),
                          ToLayoutText(&object)->GetText()};
-    texts_to_record_swap_time.push_back(record);
+    texts_to_record_swap_time_.push_back(record);
   }
 }
 
 TextRecord* TextPaintTimingDetector::FindLargestPaintCandidate() {
-  while (!largest_text_heap_.empty()) {
-    auto&& record = largest_text_heap_.top();
-    if (recorded_text_node_ids.find(record->node_id) ==
-        recorded_text_node_ids.end()) {
-      // If recorded_text_node_ids doesn't have record.node_id, the node has
-      // been deleted. We discard the records of deleted node.
-      largest_text_heap_.pop();
-    } else {
-      return largest_text_heap_.top().get();
-    }
+  while (!largest_text_heap_.empty() &&
+         !recorded_text_node_ids_.Contains(largest_text_heap_.top()->node_id)) {
+    // If recorded_text_node_ids_ doesn't have record.node_id, the node has
+    // been deleted. We discard the records of deleted node.
+    largest_text_heap_.pop();
   }
+  if (!largest_text_heap_.empty())
+    return largest_text_heap_.top().get();
   return nullptr;
 }
 
 TextRecord* TextPaintTimingDetector::FindLastPaintCandidate() {
-  while (!latest_text_heap_.empty()) {
-    auto&& record = latest_text_heap_.top();
-    if (recorded_text_node_ids.find(record->node_id) ==
-        recorded_text_node_ids.end()) {
-      // If recorded_text_node_ids doesn't have record.node_id, the node has
-      // been deleted. We discard the records of deleted node.
-      latest_text_heap_.pop();
-    } else {
-      return latest_text_heap_.top().get();
-    }
+  while (!latest_text_heap_.empty() &&
+         !recorded_text_node_ids_.Contains(latest_text_heap_.top()->node_id)) {
+    // If recorded_text_node_ids_ doesn't have record.node_id, the node has
+    // been deleted. We discard the records of deleted node.
+    latest_text_heap_.pop();
   }
+  if (!latest_text_heap_.empty())
+    return latest_text_heap_.top().get();
   return nullptr;
 }
 
diff --git a/third_party/blink/renderer/core/paint/text_paint_timing_detector.h b/third_party/blink/renderer/core/paint/text_paint_timing_detector.h
index a59a9f11..78c04288 100644
--- a/third_party/blink/renderer/core/paint/text_paint_timing_detector.h
+++ b/third_party/blink/renderer/core/paint/text_paint_timing_detector.h
@@ -20,17 +20,12 @@
 class LocalFrameView;
 
 struct TextRecord {
-  DOMNodeId node_id;
+  DOMNodeId node_id = kInvalidDOMNodeId;
   double first_size = 0.0;
-  base::TimeTicks first_paint_time;
+  base::TimeTicks first_paint_time = base::TimeTicks();
   String text = "";
 };
 
-struct TextRect {
-  LayoutRect invalidated_rect;
-  IntRect transformed_rect_in_viewport;
-};
-
 // TextPaintTimingDetector contains Largest Text Paint and Last Text Paint.
 //
 // Largest Text Paint timing measures when the largest text element gets painted
@@ -81,8 +76,8 @@
                       base::TimeTicks timestamp);
   void RegisterNotifySwapTime(ReportTimeCallback callback);
 
-  HashSet<DOMNodeId> recorded_text_node_ids;
-  HashSet<DOMNodeId> size_zero_node_ids;
+  HashSet<DOMNodeId> recorded_text_node_ids_;
+  HashSet<DOMNodeId> size_zero_node_ids_;
   std::priority_queue<std::unique_ptr<TextRecord>,
                       std::vector<std::unique_ptr<TextRecord>>,
                       std::function<bool(std::unique_ptr<TextRecord>&,
@@ -93,8 +88,10 @@
                       std::function<bool(std::unique_ptr<TextRecord>&,
                                          std::unique_ptr<TextRecord>&)>>
       latest_text_heap_;
-  std::vector<TextRecord> texts_to_record_swap_time;
+  std::vector<TextRecord> texts_to_record_swap_time_;
 
+  // Make sure that at most one swap promise is ongoing.
+  bool awaiting_swap_promise_ = false;
   unsigned largest_text_report_count_ = 0;
   unsigned last_text_report_count_ = 0;
   TaskRunnerTimer<TextPaintTimingDetector> timer_;
diff --git a/third_party/blink/renderer/core/paint/text_paint_timing_detector_test.cc b/third_party/blink/renderer/core/paint/text_paint_timing_detector_test.cc
index 857d945..88241475 100644
--- a/third_party/blink/renderer/core/paint/text_paint_timing_detector_test.cc
+++ b/third_party/blink/renderer/core/paint/text_paint_timing_detector_test.cc
@@ -23,7 +23,7 @@
     GetFrameView().UpdateAllLifecyclePhases();
     TextPaintTimingDetector& detector =
         GetPaintTracker().GetTextPaintTimingDetector();
-    if (detector.texts_to_record_swap_time.size() > 0) {
+    if (detector.texts_to_record_swap_time_.size() > 0) {
       detector.ReportSwapTime(WebLayerTreeView::SwapResult::kDidSwap,
                               CurrentTimeTicks());
     }
@@ -97,8 +97,8 @@
                            .FindLargestPaintCandidate();
   EXPECT_EQ(record->text, "a long-long-long-long moving text");
   TimeTicks firing_time = record->first_paint_time;
-  EXPECT_GT(firing_time, time1);
-  EXPECT_GT(time2, firing_time);
+  EXPECT_GE(firing_time, time1);
+  EXPECT_GE(time2, firing_time);
 }
 
 TEST_F(TextPaintTimingDetectorTest,
@@ -238,8 +238,8 @@
       GetPaintTracker().GetTextPaintTimingDetector().FindLastPaintCandidate();
   EXPECT_EQ(record->text, "latest text");
   TimeTicks firing_time = record->first_paint_time;
-  EXPECT_GT(firing_time, time1);
-  EXPECT_GT(time2, firing_time);
+  EXPECT_GE(firing_time, time1);
+  EXPECT_GE(time2, firing_time);
 }
 
 TEST_F(TextPaintTimingDetectorTest, LastTextPaint_IgnoreRemovedText) {
diff --git a/third_party/blink/renderer/core/style/style_path.cc b/third_party/blink/renderer/core/style/style_path.cc
index b32d43cb..4f4a33f6 100644
--- a/third_party/blink/renderer/core/style/style_path.cc
+++ b/third_party/blink/renderer/core/style/style_path.cc
@@ -53,7 +53,8 @@
 }
 
 CSSValue* StylePath::ComputedCSSValue() const {
-  return cssvalue::CSSPathValue::Create(const_cast<StylePath*>(this));
+  return cssvalue::CSSPathValue::Create(const_cast<StylePath*>(this),
+                                        kTransformToAbsolute);
 }
 
 bool StylePath::operator==(const BasicShape& o) const {
diff --git a/third_party/blink/renderer/core/svg/svg_path.cc b/third_party/blink/renderer/core/svg/svg_path.cc
index 64fb3b64..79e67b6 100644
--- a/third_party/blink/renderer/core/svg/svg_path.cc
+++ b/third_party/blink/renderer/core/svg/svg_path.cc
@@ -86,7 +86,7 @@
 SVGPath::~SVGPath() = default;
 
 String SVGPath::ValueAsString() const {
-  return BuildStringFromByteStream(ByteStream());
+  return BuildStringFromByteStream(ByteStream(), kNoTransformation);
 }
 
 SVGPath* SVGPath::Clone() const {
diff --git a/third_party/blink/renderer/core/svg/svg_path_parser.cc b/third_party/blink/renderer/core/svg/svg_path_parser.cc
index 447adfb..4385c43 100644
--- a/third_party/blink/renderer/core/svg/svg_path_parser.cc
+++ b/third_party/blink/renderer/core/svg/svg_path_parser.cc
@@ -277,4 +277,26 @@
   return true;
 }
 
+void SVGPathAbsolutizer::EmitSegment(const PathSegmentData& segment) {
+  PathSegmentData absolute_segment = segment;
+  if (!IsAbsolutePathSegType(segment.command)) {
+    absolute_segment.command = ToAbsolutePathSegType(segment.command);
+    if (segment.command != kPathSegArcRel) {
+      absolute_segment.point1 += current_point_;
+      absolute_segment.point2 += current_point_;
+    }
+    absolute_segment.target_point += current_point_;
+  }
+  consumer_->EmitSegment(absolute_segment);
+
+  if (absolute_segment.command == kPathSegClosePath) {
+    current_point_ = sub_path_point_;
+  } else {
+    current_point_ = absolute_segment.target_point;
+    if (absolute_segment.command == kPathSegMoveToAbs) {
+      sub_path_point_ = current_point_;
+    }
+  }
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/svg/svg_path_parser.h b/third_party/blink/renderer/core/svg/svg_path_parser.h
index 3f2f750..b8cc121 100644
--- a/third_party/blink/renderer/core/svg/svg_path_parser.h
+++ b/third_party/blink/renderer/core/svg/svg_path_parser.h
@@ -26,6 +26,7 @@
 
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/svg/svg_path_data.h"
+#include "third_party/blink/renderer/platform/geometry/float_point.h"
 #include "third_party/blink/renderer/platform/heap/handle.h"
 
 namespace blink {
@@ -70,6 +71,22 @@
   SVGPathSegType last_command_;
 };
 
+class SVGPathAbsolutizer {
+  STACK_ALLOCATED();
+
+ public:
+  SVGPathAbsolutizer(SVGPathConsumer* consumer) : consumer_(consumer) {
+    DCHECK(consumer_);
+  }
+
+  void EmitSegment(const PathSegmentData&);
+
+ private:
+  SVGPathConsumer* consumer_;
+  FloatPoint sub_path_point_;
+  FloatPoint current_point_;
+};
+
 }  // namespace blink
 
 #endif  // THIRD_PARTY_BLINK_RENDERER_CORE_SVG_SVG_PATH_PARSER_H_
diff --git a/third_party/blink/renderer/core/svg/svg_path_utilities.cc b/third_party/blink/renderer/core/svg/svg_path_utilities.cc
index ea454f6f..4fb92cd 100644
--- a/third_party/blink/renderer/core/svg/svg_path_utilities.cc
+++ b/third_party/blink/renderer/core/svg/svg_path_utilities.cc
@@ -46,13 +46,19 @@
   return SVGPathParser::ParsePath(source, builder);
 }
 
-String BuildStringFromByteStream(const SVGPathByteStream& stream) {
+String BuildStringFromByteStream(const SVGPathByteStream& stream,
+                                 PathSerializationFormat format) {
   if (stream.IsEmpty())
     return String();
 
   SVGPathStringBuilder builder;
   SVGPathByteStreamSource source(stream);
-  SVGPathParser::ParsePath(source, builder);
+  if (format == kTransformToAbsolute) {
+    SVGPathAbsolutizer absolutizer(&builder);
+    SVGPathParser::ParsePath(source, absolutizer);
+  } else {
+    SVGPathParser::ParsePath(source, builder);
+  }
   return builder.Result();
 }
 
diff --git a/third_party/blink/renderer/core/svg/svg_path_utilities.h b/third_party/blink/renderer/core/svg/svg_path_utilities.h
index eca72747..cafa0430c 100644
--- a/third_party/blink/renderer/core/svg/svg_path_utilities.h
+++ b/third_party/blink/renderer/core/svg/svg_path_utilities.h
@@ -37,7 +37,9 @@
 SVGParsingError BuildByteStreamFromString(const String&, SVGPathByteStream&);
 
 // SVGPathByteStream -> String
-String BuildStringFromByteStream(const SVGPathByteStream&);
+enum PathSerializationFormat { kNoTransformation, kTransformToAbsolute };
+String BuildStringFromByteStream(const SVGPathByteStream&,
+                                 PathSerializationFormat);
 
 }  // namespace blink
 
diff --git a/third_party/blink/renderer/core/timing/memory_info.cc b/third_party/blink/renderer/core/timing/memory_info.cc
index a944e613..3c83c44 100644
--- a/third_party/blink/renderer/core/timing/memory_info.cc
+++ b/third_party/blink/renderer/core/timing/memory_info.cc
@@ -81,7 +81,8 @@
     TimeDelta delta_allowed = precision == MemoryInfo::Precision::Bucketized
                                   ? kTwentyMinutes
                                   : kFiftyMs;
-    if (now - last_update_time_ >= delta_allowed) {
+    if (!last_update_time_.has_value() ||
+        now - last_update_time_.value() >= delta_allowed) {
       Update(precision);
       last_update_time_ = now;
     }
@@ -97,7 +98,7 @@
     info_.js_heap_size_limit = QuantizeMemorySize(info_.js_heap_size_limit);
   }
 
-  TimeTicks last_update_time_;
+  base::Optional<TimeTicks> last_update_time_;
 
   HeapInfo info_;
   DISALLOW_COPY_AND_ASSIGN(HeapSizeCache);
@@ -164,6 +165,9 @@
     GetHeapSize(info_);
   else
     HeapSizeCache::ForCurrentThread().GetCachedHeapSize(info_, precision);
+  // The values must have been computed, so totalJSHeapSize must be greater than
+  // 0.
+  DCHECK_GT(totalJSHeapSize(), 0u);
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/timing/memory_info_test.cc b/third_party/blink/renderer/core/timing/memory_info_test.cc
index bb3115a..5cebb2f 100644
--- a/third_party/blink/renderer/core/timing/memory_info_test.cc
+++ b/third_party/blink/renderer/core/timing/memory_info_test.cc
@@ -231,4 +231,24 @@
             precise_memory->usedJSHeapSize());
 }
 
+TEST_F(MemoryInfoTest, ZeroTime) {
+  // In this test, we make sure that even if the CurrentTimeTicks() value is
+  // very close to 0, we still obtain memory information from the first call to
+  // MemoryInfo::Create. We cannot just subtract CurrentTimeTicks() here
+  // because many places have DCHECKs for !time.is_null(), which would be hit if
+  // we set the clock to be exactly 0.
+  AdvanceClock(-CurrentTimeTicksInSeconds() + 0.0001);
+  V8TestingScope scope;
+  v8::Isolate* isolate = scope.GetIsolate();
+  std::vector<v8::Local<v8::ArrayBuffer>> objects;
+  objects.push_back(v8::ArrayBuffer::New(isolate, 100));
+
+  MemoryInfo* precise_memory =
+      MemoryInfo::Create(MemoryInfo::Precision::Precise);
+  CheckValues(precise_memory, MemoryInfo::Precision::Precise);
+  EXPECT_LT(0u, precise_memory->usedJSHeapSize());
+  EXPECT_LT(0u, precise_memory->totalJSHeapSize());
+  EXPECT_LT(0u, precise_memory->jsHeapSizeLimit());
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/workers/worker_thread.cc b/third_party/blink/renderer/core/workers/worker_thread.cc
index 745fa5b..9899633 100644
--- a/third_party/blink/renderer/core/workers/worker_thread.cc
+++ b/third_party/blink/renderer/core/workers/worker_thread.cc
@@ -72,7 +72,7 @@
 
 }  // namespace
 
-static Mutex& ThreadSetMutex() {
+Mutex& WorkerThread::ThreadSetMutex() {
   DEFINE_THREAD_SAFE_STATIC_LOCAL(Mutex, mutex, ());
   return mutex;
 }
diff --git a/third_party/blink/renderer/core/workers/worker_thread.h b/third_party/blink/renderer/core/workers/worker_thread.h
index 41c1ce3..26c3f58 100644
--- a/third_party/blink/renderer/core/workers/worker_thread.h
+++ b/third_party/blink/renderer/core/workers/worker_thread.h
@@ -136,6 +136,7 @@
   // workers are shut down. Please be careful when using this function, because
   // after the synchronous termination any V8 APIs may suddenly start to return
   // empty handles and it may cause crashes.
+  // WARNING: This is not safe if a nested worker is running.
   static void TerminateAllWorkersForTesting();
 
   // WebThread::TaskObserver.
@@ -182,9 +183,19 @@
   // Number of active worker threads.
   static unsigned WorkerThreadCount();
 
-  // Returns a set of all worker threads. This must be called only on the main
-  // thread and the returned set must not be stored for future use.
-  static HashSet<WorkerThread*>& WorkerThreads();
+  // Runs |function| with |parameters| on each worker thread, and
+  // adds the current WorkerThread* as the first parameter |function|.
+  template <typename FunctionType, typename... Parameters>
+  static void CallOnAllWorkerThreads(FunctionType function,
+                                     Parameters&&... parameters) {
+    MutexLocker lock(ThreadSetMutex());
+    for (WorkerThread* thread : WorkerThreads()) {
+      PostCrossThreadTask(
+          *thread->GetTaskRunner(TaskType::kInternalWorker), FROM_HERE,
+          CrossThreadBind(function, WTF::CrossThreadUnretained(thread),
+                          parameters...));
+    }
+  }
 
   int GetWorkerThreadId() const { return worker_thread_id_; }
 
@@ -240,6 +251,9 @@
   FRIEND_TEST_ALL_PREFIXES(WorkerThreadTest,
                            Terminate_WhileDebuggerTaskIsRunning);
 
+  static HashSet<WorkerThread*>& WorkerThreads();
+  static Mutex& ThreadSetMutex();
+
   // Represents the state of this worker thread.
   enum class ThreadState {
     kNotStarted,
diff --git a/third_party/blink/renderer/devtools/front_end/host/InspectorFrontendHost.js b/third_party/blink/renderer/devtools/front_end/host/InspectorFrontendHost.js
index 2dd463657..3a5bc6c 100644
--- a/third_party/blink/renderer/devtools/front_end/host/InspectorFrontendHost.js
+++ b/third_party/blink/renderer/devtools/front_end/host/InspectorFrontendHost.js
@@ -556,8 +556,7 @@
 /**
  * @type {!InspectorFrontendHostAPI}
  */
-let InspectorFrontendHost = window.InspectorFrontendHost || null;
-window.InspectorFrontendHost = InspectorFrontendHost;
+let InspectorFrontendHost = window.InspectorFrontendHost;
 (function() {
 
   function initializeInspectorFrontendHost() {
@@ -568,25 +567,17 @@
     } else {
       // Otherwise add stubs for missing methods that are declared in the interface.
       proto = Host.InspectorFrontendHostStub.prototype;
-      for (const name in proto) {
-        const value = proto[name];
-        if (typeof value !== 'function' || InspectorFrontendHost[name])
+      for (const name of Object.getOwnPropertyNames(proto)) {
+        const stub = proto[name];
+        if (typeof stub !== 'function' || InspectorFrontendHost[name])
           continue;
 
-        InspectorFrontendHost[name] = stub.bind(null, name);
+        console.error(
+            'Incompatible embedder: method InspectorFrontendHost.' + name + ' is missing. Using stub instead.');
+        InspectorFrontendHost[name] = stub;
       }
     }
 
-    /**
-     * @param {string} name
-     * @return {?}
-     */
-    function stub(name) {
-      console.error('Incompatible embedder: method InspectorFrontendHost.' + name + ' is missing. Using stub instead.');
-      const args = Array.prototype.slice.call(arguments, 1);
-      return proto[name].apply(InspectorFrontendHost, args);
-    }
-
     // Attach the events object.
     InspectorFrontendHost.events = new Common.Object();
   }
diff --git a/third_party/blink/renderer/modules/BUILD.gn b/third_party/blink/renderer/modules/BUILD.gn
index 773c81a..6b00a25f 100644
--- a/third_party/blink/renderer/modules/BUILD.gn
+++ b/third_party/blink/renderer/modules/BUILD.gn
@@ -265,10 +265,10 @@
     "indexeddb/mock_web_idb_database.cc",
     "indexeddb/mock_web_idb_database.h",
     "manifest/image_resource_type_converters_test.cc",
+    "media_controls/elements/media_control_animated_arrow_container_element_test.cc",
     "media_controls/elements/media_control_display_cutout_fullscreen_button_element_test.cc",
     "media_controls/elements/media_control_input_element_test.cc",
     "media_controls/elements/media_control_loading_panel_element_test.cc",
-    "media_controls/elements/media_control_overlay_play_button_element_test.cc",
     "media_controls/elements/media_control_panel_element_test.cc",
     "media_controls/elements/media_control_scrubbing_message_element_test.cc",
     "media_controls/elements/media_control_timeline_element_test.cc",
diff --git a/third_party/blink/renderer/modules/accessibility/ax_media_controls.cc b/third_party/blink/renderer/modules/accessibility/ax_media_controls.cc
index 66bef28..8fbf352 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_media_controls.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_media_controls.cc
@@ -93,6 +93,7 @@
     case kMediaEnterPictureInPictureButton:
     case kMediaExitPictureInPictureButton:
     case kMediaDisplayCutoutFullscreenButton:
+    case kMediaAnimatedArrowContainer:
       return new AccessibilityMediaControl(layout_object, ax_object_cache);
   }
 
@@ -173,6 +174,7 @@
     case kMediaVolumeSliderThumb:
     case kMediaOverflowList:
     case kMediaScrubbingMessage:
+    case kMediaAnimatedArrowContainer:
       return QueryString(WebLocalizedString::kAXMediaDefault);
     case kMediaEnterPictureInPictureButton:
       return QueryString(
@@ -247,6 +249,7 @@
     case kMediaOverflowList:
     case kMediaDownloadButton:
     case kMediaScrubbingMessage:
+    case kMediaAnimatedArrowContainer:
       return QueryString(WebLocalizedString::kAXMediaDefault);
     case kMediaSlider:
       NOTREACHED();
@@ -302,6 +305,7 @@
     case kMediaVolumeSlider:
     case kMediaVolumeSliderThumb:
     case kMediaScrubbingMessage:
+    case kMediaAnimatedArrowContainer:
       return kUnknownRole;
 
     case kMediaSlider:
diff --git a/third_party/blink/renderer/modules/animationworklet/worklet_animation.cc b/third_party/blink/renderer/modules/animationworklet/worklet_animation.cc
index f5fd53a1..b68cb325a 100644
--- a/third_party/blink/renderer/modules/animationworklet/worklet_animation.cc
+++ b/third_party/blink/renderer/modules/animationworklet/worklet_animation.cc
@@ -148,6 +148,10 @@
     case ScrollTimeline::Inline:
       return is_horizontal_writing_mode ? CompositorScrollTimeline::Horizontal
                                         : CompositorScrollTimeline::Vertical;
+    case ScrollTimeline::Horizontal:
+      return CompositorScrollTimeline::Horizontal;
+    case ScrollTimeline::Vertical:
+      return CompositorScrollTimeline::Vertical;
     default:
       NOTREACHED();
       return CompositorScrollTimeline::Vertical;
diff --git a/third_party/blink/renderer/modules/background_fetch/background_fetch_icon_loader.cc b/third_party/blink/renderer/modules/background_fetch/background_fetch_icon_loader.cc
index e35e486..9813aba 100644
--- a/third_party/blink/renderer/modules/background_fetch/background_fetch_icon_loader.cc
+++ b/third_party/blink/renderer/modules/background_fetch/background_fetch_icon_loader.cc
@@ -94,6 +94,11 @@
   resource_request.SetRequestContext(WebURLRequest::kRequestContextImage);
   resource_request.SetPriority(ResourceLoadPriority::kMedium);
   resource_request.SetRequestorOrigin(execution_context->GetSecurityOrigin());
+  resource_request.SetKeepalive(true);
+  resource_request.SetFetchRequestMode(
+      network::mojom::FetchRequestMode::kNoCORS);
+  resource_request.SetFetchCredentialsMode(
+      network::mojom::FetchCredentialsMode::kInclude);
 
   threadable_loader_ =
       new ThreadableLoader(*execution_context, this, resource_loader_options);
diff --git a/third_party/blink/renderer/modules/background_fetch/background_fetch_registration.cc b/third_party/blink/renderer/modules/background_fetch/background_fetch_registration.cc
index 3d24f9a..86d0135 100644
--- a/third_party/blink/renderer/modules/background_fetch/background_fetch_registration.cc
+++ b/third_party/blink/renderer/modules/background_fetch/background_fetch_registration.cc
@@ -175,6 +175,26 @@
     mojom::blink::QueryParamsPtr cache_query_params,
     ExceptionState& exception_state,
     bool match_all) {
+  // TODO(crbug.com/875201): Update this check once we support access to active
+  // fetches.
+  if (result_ == mojom::BackgroundFetchResult::UNSET) {
+    return ScriptPromise::RejectWithDOMException(
+        script_state,
+        DOMException::Create(
+            DOMExceptionCode::kInvalidStateError,
+            "Access to records for in-progress background fetches is not yet "
+            "implemented. Please see crbug.com/875201 for more details."));
+  }
+
+  if (!records_available_) {
+    return ScriptPromise::RejectWithDOMException(
+        script_state,
+        DOMException::Create(
+            DOMExceptionCode::kInvalidStateError,
+            "The records associated with this background fetch are no longer "
+            "available."));
+  }
+
   ScriptPromiseResolver* resolver = ScriptPromiseResolver::Create(script_state);
   ScriptPromise promise = resolver->Promise();
 
diff --git a/third_party/blink/renderer/modules/device_orientation/device_orientation_event_pump.cc b/third_party/blink/renderer/modules/device_orientation/device_orientation_event_pump.cc
index 362f5c3..145c861 100644
--- a/third_party/blink/renderer/modules/device_orientation/device_orientation_event_pump.cc
+++ b/third_party/blink/renderer/modules/device_orientation/device_orientation_event_pump.cc
@@ -9,6 +9,7 @@
 #include "third_party/blink/renderer/core/frame/local_frame.h"
 #include "third_party/blink/renderer/modules/device_orientation/device_orientation_data.h"
 #include "third_party/blink/renderer/modules/device_orientation/device_orientation_event_pump.h"
+#include "third_party/blink/renderer/platform/heap/persistent.h"
 
 namespace {
 
diff --git a/third_party/blink/renderer/modules/media_controls/BUILD.gn b/third_party/blink/renderer/modules/media_controls/BUILD.gn
index f15b852..5c8e0de 100644
--- a/third_party/blink/renderer/modules/media_controls/BUILD.gn
+++ b/third_party/blink/renderer/modules/media_controls/BUILD.gn
@@ -7,6 +7,8 @@
 
 blink_modules_sources("media_controls") {
   sources = [
+    "elements/media_control_animated_arrow_container_element.cc",
+    "elements/media_control_animated_arrow_container_element.h",
     "elements/media_control_animation_event_listener.cc",
     "elements/media_control_animation_event_listener.h",
     "elements/media_control_button_panel_element.cc",
diff --git a/third_party/blink/renderer/modules/media_controls/elements/media_control_animated_arrow_container_element.cc b/third_party/blink/renderer/modules/media_controls/elements/media_control_animated_arrow_container_element.cc
new file mode 100644
index 0000000..0848f2d8
--- /dev/null
+++ b/third_party/blink/renderer/modules/media_controls/elements/media_control_animated_arrow_container_element.cc
@@ -0,0 +1,124 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/modules/media_controls/elements/media_control_animated_arrow_container_element.h"
+
+#include "third_party/blink/renderer/core/dom/shadow_root.h"
+#include "third_party/blink/renderer/core/html/html_style_element.h"
+#include "third_party/blink/renderer/modules/media_controls/media_controls_resource_loader.h"
+
+namespace blink {
+
+MediaControlAnimatedArrowContainerElement::AnimatedArrow::AnimatedArrow(
+    const AtomicString& id,
+    Document& document)
+    : HTMLDivElement(document) {
+  setAttribute("id", id);
+}
+
+void MediaControlAnimatedArrowContainerElement::AnimatedArrow::HideInternal() {
+  DCHECK(!hidden_);
+  svg_container_->SetInlineStyleProperty(CSSPropertyDisplay, CSSValueNone);
+  hidden_ = true;
+}
+
+void MediaControlAnimatedArrowContainerElement::AnimatedArrow::ShowInternal() {
+  DCHECK(hidden_);
+  hidden_ = false;
+
+  if (svg_container_) {
+    svg_container_->RemoveInlineStyleProperty(CSSPropertyDisplay);
+    return;
+  }
+
+  SetInnerHTMLFromString(MediaControlsResourceLoader::GetJumpSVGImage());
+
+  last_arrow_ = getElementById("arrow-3");
+  svg_container_ = getElementById("jump");
+
+  event_listener_ = new MediaControlAnimationEventListener(this);
+}
+
+void MediaControlAnimatedArrowContainerElement::AnimatedArrow::
+    OnAnimationIteration() {
+  counter_--;
+
+  if (counter_ == 0)
+    HideInternal();
+}
+
+void MediaControlAnimatedArrowContainerElement::AnimatedArrow::Show() {
+  if (hidden_)
+    ShowInternal();
+
+  counter_++;
+}
+
+Element& MediaControlAnimatedArrowContainerElement::AnimatedArrow::
+    WatchedAnimationElement() const {
+  return *last_arrow_;
+}
+
+void MediaControlAnimatedArrowContainerElement::AnimatedArrow::Trace(
+    Visitor* visitor) {
+  MediaControlAnimationEventListener::Observer::Trace(visitor);
+  HTMLDivElement::Trace(visitor);
+  visitor->Trace(last_arrow_);
+  visitor->Trace(svg_container_);
+  visitor->Trace(event_listener_);
+}
+
+MediaControlAnimatedArrowContainerElement::
+    MediaControlAnimatedArrowContainerElement(MediaControlsImpl& media_controls)
+    : MediaControlDivElement(media_controls, kMediaAnimatedArrowContainer),
+      left_jump_arrow_(nullptr),
+      right_jump_arrow_(nullptr) {
+  EnsureUserAgentShadowRoot();
+  SetShadowPseudoId(
+      AtomicString("-internal-media-controls-animated-arrow-container"));
+}
+
+void MediaControlAnimatedArrowContainerElement::ShowArrowAnimation(
+    MediaControlAnimatedArrowContainerElement::ArrowDirection direction) {
+  // Load the arrow icons and associate CSS the first time we jump.
+  if (!left_jump_arrow_) {
+    DCHECK(!right_jump_arrow_);
+    ShadowRoot* shadow_root = GetShadowRoot();
+
+    // This stylesheet element and will contain rules that are specific to the
+    // jump arrows. The shadow DOM protects these rules from the parent DOM
+    // from bleeding across the shadow DOM boundary.
+    auto* style = HTMLStyleElement::Create(GetDocument(), CreateElementFlags());
+    style->setTextContent(
+        MediaControlsResourceLoader::GetAnimatedArrowStyleSheet());
+    shadow_root->ParserAppendChild(style);
+
+    left_jump_arrow_ =
+        new MediaControlAnimatedArrowContainerElement::AnimatedArrow(
+            "left-arrow", GetDocument());
+    shadow_root->ParserAppendChild(left_jump_arrow_);
+
+    right_jump_arrow_ =
+        new MediaControlAnimatedArrowContainerElement::AnimatedArrow(
+            "right-arrow", GetDocument());
+    shadow_root->ParserAppendChild(right_jump_arrow_);
+  }
+
+  DCHECK(left_jump_arrow_ && right_jump_arrow_);
+
+  if (direction ==
+      MediaControlAnimatedArrowContainerElement::ArrowDirection::kLeft) {
+    left_jump_arrow_->Show();
+  } else {
+    right_jump_arrow_->Show();
+  }
+}
+
+void MediaControlAnimatedArrowContainerElement::Trace(blink::Visitor* visitor) {
+  MediaControlDivElement::Trace(visitor);
+  visitor->Trace(left_jump_arrow_);
+  visitor->Trace(right_jump_arrow_);
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/modules/media_controls/elements/media_control_animated_arrow_container_element.h b/third_party/blink/renderer/modules/media_controls/elements/media_control_animated_arrow_container_element.h
new file mode 100644
index 0000000..ea60c72e
--- /dev/null
+++ b/third_party/blink/renderer/modules/media_controls/elements/media_control_animated_arrow_container_element.h
@@ -0,0 +1,75 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_MEDIA_CONTROLS_ELEMENTS_MEDIA_CONTROL_ANIMATED_ARROW_CONTAINER_ELEMENT_H_
+#define THIRD_PARTY_BLINK_RENDERER_MODULES_MEDIA_CONTROLS_ELEMENTS_MEDIA_CONTROL_ANIMATED_ARROW_CONTAINER_ELEMENT_H_
+
+#include "third_party/blink/renderer/core/html/html_div_element.h"
+#include "third_party/blink/renderer/modules/media_controls/elements/media_control_animation_event_listener.h"
+#include "third_party/blink/renderer/modules/media_controls/elements/media_control_div_element.h"
+#include "third_party/blink/renderer/modules/modules_export.h"
+
+namespace WTF {
+class AtomicString;
+}
+
+namespace blink {
+
+class MediaControlsImpl;
+
+class MODULES_EXPORT MediaControlAnimatedArrowContainerElement final
+    : public MediaControlDivElement {
+ public:
+  enum class ArrowDirection { kLeft, kRight };
+
+  explicit MediaControlAnimatedArrowContainerElement(MediaControlsImpl&);
+
+  void ShowArrowAnimation(ArrowDirection);
+
+  void Trace(blink::Visitor*) override;
+
+ private:
+  friend class MediaControlAnimatedArrowContainerElementTest;
+
+  // This class is responible for displaying the arrow animation when a jump is
+  // triggered by the user.
+  class MODULES_EXPORT AnimatedArrow final
+      : public HTMLDivElement,
+        public MediaControlAnimationEventListener::Observer {
+    USING_GARBAGE_COLLECTED_MIXIN(AnimatedArrow);
+
+   public:
+    AnimatedArrow(const AtomicString& id, Document& document);
+
+    // MediaControlAnimationEventListener::Observer overrides
+    void OnAnimationIteration() override;
+    void OnAnimationEnd() override{};
+    Element& WatchedAnimationElement() const override;
+
+    // Shows the animated arrows for a single animation iteration. If the
+    // arrows are already shown it will show them for another animation
+    // iteration.
+    void Show();
+
+    void Trace(Visitor*) override;
+
+   private:
+    void HideInternal();
+    void ShowInternal();
+
+    int counter_ = 0;
+    bool hidden_ = true;
+
+    Member<Element> last_arrow_;
+    Member<Element> svg_container_;
+    Member<MediaControlAnimationEventListener> event_listener_;
+  };
+
+  Member<AnimatedArrow> left_jump_arrow_;
+  Member<AnimatedArrow> right_jump_arrow_;
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_MEDIA_CONTROLS_ELEMENTS_MEDIA_CONTROL_ANIMATED_ARROW_CONTAINER_ELEMENT_H_
diff --git a/third_party/blink/renderer/modules/media_controls/elements/media_control_overlay_play_button_element_test.cc b/third_party/blink/renderer/modules/media_controls/elements/media_control_animated_arrow_container_element_test.cc
similarity index 80%
rename from third_party/blink/renderer/modules/media_controls/elements/media_control_overlay_play_button_element_test.cc
rename to third_party/blink/renderer/modules/media_controls/elements/media_control_animated_arrow_container_element_test.cc
index 9ca6ab5..584e6c1 100644
--- a/third_party/blink/renderer/modules/media_controls/elements/media_control_overlay_play_button_element_test.cc
+++ b/third_party/blink/renderer/modules/media_controls/elements/media_control_animated_arrow_container_element_test.cc
@@ -1,8 +1,8 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
+// Copyright 2018 The Chromium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "third_party/blink/renderer/modules/media_controls/elements/media_control_overlay_play_button_element.h"
+#include "third_party/blink/renderer/modules/media_controls/elements/media_control_animated_arrow_container_element.h"
 
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/renderer/core/css/css_property_value_set.h"
@@ -12,13 +12,14 @@
 
 namespace blink {
 
-class MediaControlOverlayPlayButtonElementTest : public PageTestBase {
+class MediaControlAnimatedArrowContainerElementTest : public PageTestBase {
  public:
   void SetUp() final {
     // Create page and instance of AnimatedArrow to run tests on.
     PageTestBase::SetUp();
-    arrow_element_ = new MediaControlOverlayPlayButtonElement::AnimatedArrow(
-        "test", GetDocument());
+    arrow_element_ =
+        new MediaControlAnimatedArrowContainerElement::AnimatedArrow(
+            "test", GetDocument());
     GetDocument().body()->AppendChild(arrow_element_);
   }
 
@@ -54,11 +55,11 @@
     return GetDocument().body()->getElementById(id);
   }
 
-  Persistent<MediaControlOverlayPlayButtonElement::AnimatedArrow>
+  Persistent<MediaControlAnimatedArrowContainerElement::AnimatedArrow>
       arrow_element_;
 };
 
-TEST_F(MediaControlOverlayPlayButtonElementTest, ShowIncrementsCounter) {
+TEST_F(MediaControlAnimatedArrowContainerElementTest, ShowIncrementsCounter) {
   ExpectNotPresent();
 
   // Start a new show.
diff --git a/third_party/blink/renderer/modules/media_controls/elements/media_control_element_type.h b/third_party/blink/renderer/modules/media_controls/elements/media_control_element_type.h
index d5a73cc..06df6aa 100644
--- a/third_party/blink/renderer/modules/media_controls/elements/media_control_element_type.h
+++ b/third_party/blink/renderer/modules/media_controls/elements/media_control_element_type.h
@@ -39,6 +39,7 @@
   kMediaEnterPictureInPictureButton,
   kMediaExitPictureInPictureButton,
   kMediaDisplayCutoutFullscreenButton,
+  kMediaAnimatedArrowContainer,
 };
 
 #endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_MEDIA_CONTROLS_ELEMENTS_MEDIA_CONTROL_ELEMENT_TYPE_H_
diff --git a/third_party/blink/renderer/modules/media_controls/elements/media_control_overlay_play_button_element.cc b/third_party/blink/renderer/modules/media_controls/elements/media_control_overlay_play_button_element.cc
index eaa2592d..e0a514d 100644
--- a/third_party/blink/renderer/modules/media_controls/elements/media_control_overlay_play_button_element.cc
+++ b/third_party/blink/renderer/modules/media_controls/elements/media_control_overlay_play_button_element.cc
@@ -12,14 +12,12 @@
 #include "third_party/blink/renderer/core/dom/shadow_root.h"
 #include "third_party/blink/renderer/core/events/mouse_event.h"
 #include "third_party/blink/renderer/core/geometry/dom_rect.h"
-#include "third_party/blink/renderer/core/html/html_style_element.h"
 #include "third_party/blink/renderer/core/html/media/html_media_element.h"
 #include "third_party/blink/renderer/core/html/media/html_media_source.h"
 #include "third_party/blink/renderer/core/input_type_names.h"
 #include "third_party/blink/renderer/core/layout/layout_view.h"
 #include "third_party/blink/renderer/modules/media_controls/elements/media_control_elements_helper.h"
 #include "third_party/blink/renderer/modules/media_controls/media_controls_impl.h"
-#include "third_party/blink/renderer/modules/media_controls/media_controls_resource_loader.h"
 #include "third_party/blink/renderer/platform/runtime_enabled_features.h"
 #include "third_party/blink/renderer/platform/wtf/time.h"
 
@@ -50,66 +48,6 @@
 
 namespace blink {
 
-MediaControlOverlayPlayButtonElement::AnimatedArrow::AnimatedArrow(
-    const AtomicString& id,
-    Document& document)
-    : HTMLDivElement(document) {
-  setAttribute("id", id);
-}
-
-void MediaControlOverlayPlayButtonElement::AnimatedArrow::HideInternal() {
-  DCHECK(!hidden_);
-  svg_container_->SetInlineStyleProperty(CSSPropertyDisplay, CSSValueNone);
-  hidden_ = true;
-}
-
-void MediaControlOverlayPlayButtonElement::AnimatedArrow::ShowInternal() {
-  DCHECK(hidden_);
-  hidden_ = false;
-
-  if (svg_container_) {
-    svg_container_->RemoveInlineStyleProperty(CSSPropertyDisplay);
-    return;
-  }
-
-  SetInnerHTMLFromString(MediaControlsResourceLoader::GetJumpSVGImage());
-
-  last_arrow_ = getElementById("arrow-3");
-  svg_container_ = getElementById("jump");
-
-  event_listener_ = new MediaControlAnimationEventListener(this);
-}
-
-void MediaControlOverlayPlayButtonElement::AnimatedArrow::
-    OnAnimationIteration() {
-  counter_--;
-
-  if (counter_ == 0)
-    HideInternal();
-}
-
-void MediaControlOverlayPlayButtonElement::AnimatedArrow::Show() {
-  if (hidden_)
-    ShowInternal();
-
-  counter_++;
-}
-
-Element&
-MediaControlOverlayPlayButtonElement::AnimatedArrow::WatchedAnimationElement()
-    const {
-  return *last_arrow_;
-}
-
-void MediaControlOverlayPlayButtonElement::AnimatedArrow::Trace(
-    Visitor* visitor) {
-  MediaControlAnimationEventListener::Observer::Trace(visitor);
-  HTMLDivElement::Trace(visitor);
-  visitor->Trace(last_arrow_);
-  visitor->Trace(svg_container_);
-  visitor->Trace(event_listener_);
-}
-
 // The DOM structure looks like:
 //
 // MediaControlOverlayPlayButtonElement
@@ -123,9 +61,7 @@
       tap_timer_(GetDocument().GetTaskRunner(TaskType::kMediaElementEvent),
                  this,
                  &MediaControlOverlayPlayButtonElement::TapTimerFired),
-      internal_button_(nullptr),
-      left_jump_arrow_(nullptr),
-      right_jump_arrow_(nullptr) {
+      internal_button_(nullptr) {
   EnsureUserAgentShadowRoot();
   setType(InputTypeNames::button);
   SetShadowPseudoId(AtomicString("-webkit-media-controls-overlay-play-button"));
@@ -180,40 +116,11 @@
 }
 
 void MediaControlOverlayPlayButtonElement::MaybeJump(int seconds) {
-  // Load the arrow icons and associate CSS the first time we jump.
-  if (!left_jump_arrow_) {
-    DCHECK(!right_jump_arrow_);
-    ShadowRoot* shadow_root = GetShadowRoot();
-
-    // This stylesheet element and will contain rules that are specific to the
-    // jump arrows. The shadow DOM protects these rules from the parent DOM
-    // from bleeding across the shadow DOM boundary.
-    auto* style = HTMLStyleElement::Create(GetDocument(), CreateElementFlags());
-    style->setTextContent(
-        MediaControlsResourceLoader::GetOverlayPlayStyleSheet());
-    shadow_root->ParserAppendChild(style);
-
-    // Insert the left jump arrow to the left of the play button.
-    left_jump_arrow_ = new MediaControlOverlayPlayButtonElement::AnimatedArrow(
-        "left-arrow", GetDocument());
-    shadow_root->ParserInsertBefore(left_jump_arrow_,
-                                    *shadow_root->firstChild());
-
-    // Insert the right jump arrow to the right of the play button.
-    right_jump_arrow_ = new MediaControlOverlayPlayButtonElement::AnimatedArrow(
-        "right-arrow", GetDocument());
-    shadow_root->ParserAppendChild(right_jump_arrow_);
-  }
-
-  DCHECK(left_jump_arrow_ && right_jump_arrow_);
   double new_time = std::max(0.0, MediaElement().currentTime() + seconds);
   new_time = std::min(new_time, MediaElement().duration());
   MediaElement().setCurrentTime(new_time);
 
-  if (seconds > 0)
-    right_jump_arrow_->Show();
-  else
-    left_jump_arrow_->Show();
+  GetMediaControls().ShowArrowAnimation(seconds > 0);
 }
 
 void MediaControlOverlayPlayButtonElement::DefaultEventHandler(Event& event) {
@@ -341,8 +248,6 @@
 void MediaControlOverlayPlayButtonElement::Trace(blink::Visitor* visitor) {
   MediaControlInputElement::Trace(visitor);
   visitor->Trace(internal_button_);
-  visitor->Trace(left_jump_arrow_);
-  visitor->Trace(right_jump_arrow_);
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/media_controls/elements/media_control_overlay_play_button_element.h b/third_party/blink/renderer/modules/media_controls/elements/media_control_overlay_play_button_element.h
index d2eec86..0891dd6 100644
--- a/third_party/blink/renderer/modules/media_controls/elements/media_control_overlay_play_button_element.h
+++ b/third_party/blink/renderer/modules/media_controls/elements/media_control_overlay_play_button_element.h
@@ -6,15 +6,10 @@
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_MEDIA_CONTROLS_ELEMENTS_MEDIA_CONTROL_OVERLAY_PLAY_BUTTON_ELEMENT_H_
 
 #include "third_party/blink/renderer/core/html/html_div_element.h"
-#include "third_party/blink/renderer/modules/media_controls/elements/media_control_animation_event_listener.h"
 #include "third_party/blink/renderer/modules/media_controls/elements/media_control_input_element.h"
 #include "third_party/blink/renderer/modules/modules_export.h"
 #include "third_party/blink/renderer/platform/timer.h"
 
-namespace WTF {
-class AtomicString;
-}
-
 namespace blink {
 
 class Event;
@@ -43,40 +38,6 @@
  private:
   friend class MediaControlOverlayPlayButtonElementTest;
 
-  // This class is responible for displaying the arrow animation when a jump is
-  // triggered by the user.
-  class MODULES_EXPORT AnimatedArrow final
-      : public HTMLDivElement,
-        public MediaControlAnimationEventListener::Observer {
-    USING_GARBAGE_COLLECTED_MIXIN(AnimatedArrow);
-
-   public:
-    AnimatedArrow(const AtomicString& id, Document& document);
-
-    // MediaControlAnimationEventListener::Observer overrides
-    void OnAnimationIteration() override;
-    void OnAnimationEnd() override{};
-    Element& WatchedAnimationElement() const override;
-
-    // Shows the animated arrows for a single animation iteration. If the
-    // arrows are already shown it will show them for another animation
-    // iteration.
-    void Show();
-
-    void Trace(Visitor*) override;
-
-   private:
-    void HideInternal();
-    void ShowInternal();
-
-    int counter_ = 0;
-    bool hidden_ = true;
-
-    Member<Element> last_arrow_;
-    Member<Element> svg_container_;
-    Member<MediaControlAnimationEventListener> event_listener_;
-  };
-
   void TapTimerFired(TimerBase*);
 
   void DefaultEventHandler(Event&) override;
@@ -91,8 +52,6 @@
   base::Optional<bool> tap_was_touch_event_;
 
   Member<HTMLDivElement> internal_button_;
-  Member<AnimatedArrow> left_jump_arrow_;
-  Member<AnimatedArrow> right_jump_arrow_;
 
   bool displayed_ = true;
 };
diff --git a/third_party/blink/renderer/modules/media_controls/media_controls_impl.cc b/third_party/blink/renderer/modules/media_controls/media_controls_impl.cc
index 335e4b98..18a7d94ee 100644
--- a/third_party/blink/renderer/modules/media_controls/media_controls_impl.cc
+++ b/third_party/blink/renderer/modules/media_controls/media_controls_impl.cc
@@ -51,6 +51,7 @@
 #include "third_party/blink/renderer/core/page/spatial_navigation.h"
 #include "third_party/blink/renderer/core/resize_observer/resize_observer.h"
 #include "third_party/blink/renderer/core/resize_observer/resize_observer_entry.h"
+#include "third_party/blink/renderer/modules/media_controls/elements/media_control_animated_arrow_container_element.h"
 #include "third_party/blink/renderer/modules/media_controls/elements/media_control_button_panel_element.h"
 #include "third_party/blink/renderer/modules/media_controls/elements/media_control_cast_button_element.h"
 #include "third_party/blink/renderer/modules/media_controls/elements/media_control_current_time_display_element.h"
@@ -368,6 +369,7 @@
       media_button_panel_(nullptr),
       loading_panel_(nullptr),
       picture_in_picture_button_(nullptr),
+      animated_arrow_container_element_(nullptr),
       cast_button_(nullptr),
       fullscreen_button_(nullptr),
       display_cutout_fullscreen_button_(nullptr),
@@ -1425,6 +1427,19 @@
   MaybeShow();
 }
 
+void MediaControlsImpl::ShowArrowAnimation(bool is_right) {
+  if (!animated_arrow_container_element_) {
+    animated_arrow_container_element_ =
+        new MediaControlAnimatedArrowContainerElement(*this);
+    ParserAppendChild(animated_arrow_container_element_);
+  }
+  MediaControlAnimatedArrowContainerElement::ArrowDirection direction =
+      (is_right)
+          ? MediaControlAnimatedArrowContainerElement::ArrowDirection::kRight
+          : MediaControlAnimatedArrowContainerElement::ArrowDirection::kLeft;
+  animated_arrow_container_element_->ShowArrowAnimation(direction);
+}
+
 void MediaControlsImpl::DefaultEventHandler(Event& event) {
   HTMLDivElement::DefaultEventHandler(event);
 
@@ -2068,6 +2083,7 @@
   visitor->Trace(mute_button_);
   visitor->Trace(volume_slider_);
   visitor->Trace(picture_in_picture_button_);
+  visitor->Trace(animated_arrow_container_element_);
   visitor->Trace(toggle_closed_captions_button_);
   visitor->Trace(fullscreen_button_);
   visitor->Trace(download_button_);
diff --git a/third_party/blink/renderer/modules/media_controls/media_controls_impl.h b/third_party/blink/renderer/modules/media_controls/media_controls_impl.h
index e87c133..4e13a5bf 100644
--- a/third_party/blink/renderer/modules/media_controls/media_controls_impl.h
+++ b/third_party/blink/renderer/modules/media_controls/media_controls_impl.h
@@ -42,6 +42,7 @@
 class MediaControlsOrientationLockDelegate;
 class MediaControlsRotateToFullscreenDelegate;
 class MediaControlsWindowEventListener;
+class MediaControlAnimatedArrowContainerElement;
 class MediaControlButtonPanelElement;
 class MediaControlCastButtonElement;
 class MediaControlCurrentTimeDisplayElement;
@@ -190,6 +191,10 @@
   // be exposed to the platform.
   void OnAccessibleFocus();
 
+  // TODO(884770): This should only be here until the double-tap-to-jump logic
+  // is moved to the controls from the overlay play button.
+  void ShowArrowAnimation(bool);
+
  private:
   // MediaControlsMediaEventListener is a component that is listening to events
   // and calling the appropriate callback on MediaControlsImpl. The object is
@@ -347,6 +352,8 @@
   Member<MediaControlButtonPanelElement> media_button_panel_;
   Member<MediaControlLoadingPanelElement> loading_panel_;
   Member<MediaControlPictureInPictureButtonElement> picture_in_picture_button_;
+  Member<MediaControlAnimatedArrowContainerElement>
+      animated_arrow_container_element_;
 
   Member<MediaControlCastButtonElement> cast_button_;
   Member<MediaControlFullscreenButtonElement> fullscreen_button_;
diff --git a/third_party/blink/renderer/modules/media_controls/media_controls_resource_loader.cc b/third_party/blink/renderer/modules/media_controls/media_controls_resource_loader.cc
index 7eef1d4..3b05144a 100644
--- a/third_party/blink/renderer/modules/media_controls/media_controls_resource_loader.cc
+++ b/third_party/blink/renderer/modules/media_controls/media_controls_resource_loader.cc
@@ -81,9 +81,9 @@
 };
 
 // static
-String MediaControlsResourceLoader::GetOverlayPlayStyleSheet() {
+String MediaControlsResourceLoader::GetAnimatedArrowStyleSheet() {
   return ResourceBundleHelper::UncompressResourceAsString(
-      IDR_SHADOWSTYLE_MODERN_MEDIA_CONTROLS_OVERLAY_PLAY_CSS);
+      IDR_SHADOWSTYLE_MODERN_MEDIA_CONTROLS_ANIMATED_ARROW_CSS);
 };
 
 // static
diff --git a/third_party/blink/renderer/modules/media_controls/media_controls_resource_loader.h b/third_party/blink/renderer/modules/media_controls/media_controls_resource_loader.h
index 2f5c249..40d99ac 100644
--- a/third_party/blink/renderer/modules/media_controls/media_controls_resource_loader.h
+++ b/third_party/blink/renderer/modules/media_controls/media_controls_resource_loader.h
@@ -37,8 +37,8 @@
   // Returns the scrubbing message stylesheet content as a string.
   static String GetScrubbingMessageStyleSheet();
 
-  // Returns the overlay play button stylesheet content as a string.
-  static String GetOverlayPlayStyleSheet();
+  // Returns the animated arrow stylesheet content as a string.
+  static String GetAnimatedArrowStyleSheet();
 
   // Returns the specific stylesheet used for media related interstitials.
   static String GetMediaInterstitialsStyleSheet();
diff --git a/third_party/blink/renderer/modules/media_controls/resources/media_controls_resources.grd b/third_party/blink/renderer/modules/media_controls/resources/media_controls_resources.grd
index 6da1783..ff832b58 100644
--- a/third_party/blink/renderer/modules/media_controls/resources/media_controls_resources.grd
+++ b/third_party/blink/renderer/modules/media_controls/resources/media_controls_resources.grd
@@ -17,7 +17,7 @@
     <includes>
       <include name="IDR_UASTYLE_LEGACY_MEDIA_CONTROLS_ANDROID_CSS" file="legacyMediaControlsAndroid.css" type="BINDATA" compress="gzip" />
       <include name="IDR_SHADOWSTYLE_MODERN_MEDIA_CONTROLS_TIMELINE_CSS" file="modernMediaControls_timeline.css" type="BINDATA" compress="gzip" />
-      <include name="IDR_SHADOWSTYLE_MODERN_MEDIA_CONTROLS_OVERLAY_PLAY_CSS" file="modernMediaControls_overlay_play.css" type="BINDATA" compress="gzip" />
+      <include name="IDR_SHADOWSTYLE_MODERN_MEDIA_CONTROLS_ANIMATED_ARROW_CSS" file="modernMediaControls_animated_arrow.css" type="BINDATA" compress="gzip" />
       <include name="IDR_SHADOWSTYLE_MODERN_MEDIA_CONTROLS_SCRUBBING_MESSAGE_CSS" file="modernMediaControls_scrubbing_message.css" type="BINDATA" compress="gzip" />
       <include name="IDR_MODERN_MEDIA_CONTROLS_JUMP_SVG" file="jump_image.svg" type="BINDATA" compress="gzip" />
       <include name="IDR_MODERN_MEDIA_CONTROLS_ARROW_RIGHT_SVG" file="ic_arrow_right.svg" type="BINDATA" compress="gzip" />
diff --git a/third_party/blink/renderer/modules/media_controls/resources/modernMediaControls.css b/third_party/blink/renderer/modules/media_controls/resources/modernMediaControls.css
index ba69e49f..85c6277 100644
--- a/third_party/blink/renderer/modules/media_controls/resources/modernMediaControls.css
+++ b/third_party/blink/renderer/modules/media_controls/resources/modernMediaControls.css
@@ -951,6 +951,28 @@
 }
 
 /**
+ * Animated Arrow Container
+ */
+
+video::-internal-media-controls-animated-arrow-container {
+  position: absolute;
+  display: flex;
+  align-items: center;
+  left: 0;
+  top: 0;
+  right: 0;
+  bottom: 0;
+  overflow: hidden;
+  z-index: 1;
+  pointer-events: none;
+}
+
+audio::-internal-media-controls-animated-arrow-container,
+video::-webkit-media-controls.audio-only [pseudo="-internal-media-controls-animated-arrow-container"] {
+  display: none;
+}
+
+/**
  * Text Tracks
  */
 video::-webkit-media-text-track-container {
diff --git a/third_party/blink/renderer/modules/media_controls/resources/modernMediaControls_overlay_play.css b/third_party/blink/renderer/modules/media_controls/resources/modernMediaControls_animated_arrow.css
similarity index 96%
rename from third_party/blink/renderer/modules/media_controls/resources/modernMediaControls_overlay_play.css
rename to third_party/blink/renderer/modules/media_controls/resources/modernMediaControls_animated_arrow.css
index b5dfd523..ef090b9 100644
--- a/third_party/blink/renderer/modules/media_controls/resources/modernMediaControls_overlay_play.css
+++ b/third_party/blink/renderer/modules/media_controls/resources/modernMediaControls_animated_arrow.css
@@ -5,6 +5,7 @@
 #left-arrow,
 #right-arrow {
   flex: 1;
+  text-align: center;
 }
 
 #left-arrow svg,
diff --git a/third_party/blink/renderer/modules/navigatorcontentutils/navigator_content_utils.cc b/third_party/blink/renderer/modules/navigatorcontentutils/navigator_content_utils.cc
index 587fae6..e62eb4a 100644
--- a/third_party/blink/renderer/modules/navigatorcontentutils/navigator_content_utils.cc
+++ b/third_party/blink/renderer/modules/navigatorcontentutils/navigator_content_utils.cc
@@ -30,6 +30,7 @@
 #include "third_party/blink/renderer/core/frame/local_frame.h"
 #include "third_party/blink/renderer/core/frame/use_counter.h"
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
+#include "third_party/blink/renderer/platform/weborigin/security_origin.h"
 #include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
 
 namespace blink {
diff --git a/third_party/blink/renderer/modules/nfc/nfc.cc b/third_party/blink/renderer/modules/nfc/nfc.cc
index e86dbe8..c4c5ea0 100644
--- a/third_party/blink/renderer/modules/nfc/nfc.cc
+++ b/third_party/blink/renderer/modules/nfc/nfc.cc
@@ -20,6 +20,7 @@
 #include "third_party/blink/renderer/modules/nfc/nfc_push_options.h"
 #include "third_party/blink/renderer/modules/nfc/nfc_watch_options.h"
 #include "third_party/blink/renderer/platform/mojo/mojo_helper.h"
+#include "third_party/blink/renderer/platform/weborigin/security_origin.h"
 
 namespace {
 const char kJsonMimePostfix[] = "+json";
diff --git a/third_party/blink/renderer/modules/sensor/sensor_proxy.cc b/third_party/blink/renderer/modules/sensor/sensor_proxy.cc
index 8da9db8..f652807 100644
--- a/third_party/blink/renderer/modules/sensor/sensor_proxy.cc
+++ b/third_party/blink/renderer/modules/sensor/sensor_proxy.cc
@@ -14,6 +14,7 @@
 #include "third_party/blink/renderer/modules/sensor/sensor_provider_proxy.h"
 #include "third_party/blink/renderer/modules/sensor/sensor_reading_remapper.h"
 #include "third_party/blink/renderer/platform/layout_test_support.h"
+#include "third_party/blink/renderer/platform/weborigin/security_origin.h"
 
 namespace blink {
 
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 848aebe7..9aa5d6f 100644
--- a/third_party/blink/renderer/modules/storage/dom_window_storage.cc
+++ b/third_party/blink/renderer/modules/storage/dom_window_storage.cc
@@ -14,6 +14,7 @@
 #include "third_party/blink/renderer/modules/storage/storage_area.h"
 #include "third_party/blink/renderer/modules/storage/storage_namespace.h"
 #include "third_party/blink/renderer/modules/storage/storage_namespace_controller.h"
+#include "third_party/blink/renderer/platform/weborigin/security_origin.h"
 
 namespace blink {
 
diff --git a/third_party/blink/renderer/modules/storage/storage_area.cc b/third_party/blink/renderer/modules/storage/storage_area.cc
index 15d3103..1a11fff8 100644
--- a/third_party/blink/renderer/modules/storage/storage_area.cc
+++ b/third_party/blink/renderer/modules/storage/storage_area.cc
@@ -35,6 +35,7 @@
 #include "third_party/blink/renderer/modules/storage/storage_namespace.h"
 #include "third_party/blink/renderer/modules/storage/storage_namespace_controller.h"
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
+#include "third_party/blink/renderer/platform/weborigin/security_origin.h"
 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
 
 namespace blink {
diff --git a/third_party/blink/renderer/modules/time_zone_monitor/time_zone_monitor_client.cc b/third_party/blink/renderer/modules/time_zone_monitor/time_zone_monitor_client.cc
index 4d8a972..e17c7d0 100644
--- a/third_party/blink/renderer/modules/time_zone_monitor/time_zone_monitor_client.cc
+++ b/third_party/blink/renderer/modules/time_zone_monitor/time_zone_monitor_client.cc
@@ -65,21 +65,7 @@
   }
 
   NotifyTimezoneChangeToV8(V8PerIsolateData::MainThreadIsolate());
-
-  HashSet<WorkerThread*>& threads = WorkerThread::WorkerThreads();
-  HashSet<WorkerBackingThread*> posted;
-  for (WorkerThread* thread : threads) {
-    // Ensure every WorkerBackingThread(holding one platform thread) only get
-    // the task posted once, because one WorkerBackingThread could be shared
-    // among multiple WorkerThreads.
-    if (posted.Contains(&thread->GetWorkerBackingThread()))
-      continue;
-    PostCrossThreadTask(*thread->GetTaskRunner(TaskType::kInternalDefault),
-                        FROM_HERE,
-                        CrossThreadBind(&NotifyTimezoneChangeOnWorkerThread,
-                                        WTF::CrossThreadUnretained(thread)));
-    posted.insert(&thread->GetWorkerBackingThread());
-  }
+  WorkerThread::CallOnAllWorkerThreads(&NotifyTimezoneChangeOnWorkerThread);
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/exported/web_runtime_features.cc b/third_party/blink/renderer/platform/exported/web_runtime_features.cc
index a6e88fa..60a7cbc 100644
--- a/third_party/blink/renderer/platform/exported/web_runtime_features.cc
+++ b/third_party/blink/renderer/platform/exported/web_runtime_features.cc
@@ -204,10 +204,6 @@
   RuntimeEnabledFeatures::SetLazyImageVisibleLoadTimeMetricsEnabled(enable);
 }
 
-void WebRuntimeFeatures::EnableLazyParseCSS(bool enable) {
-  RuntimeEnabledFeatures::SetLazyParseCSSEnabled(enable);
-}
-
 void WebRuntimeFeatures::EnableMediaCapture(bool enable) {
   RuntimeEnabledFeatures::SetMediaCaptureEnabled(enable);
 }
diff --git a/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor_test.cc b/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor_test.cc
index d4e7543..04bcabb 100644
--- a/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor_test.cc
+++ b/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor_test.cc
@@ -85,8 +85,7 @@
     // the compositor to run and submit frames.
     layer_tree_ = std::make_unique<LayerTreeHostEmbedder>(
         &layer_tree_host_client_,
-        /*single_thread_client=*/nullptr,
-        /*use_layer_lists=*/true);
+        /*single_thread_client=*/nullptr);
     layer_tree_host_client_.SetLayerTreeHost(layer_tree_->layer_tree_host());
     layer_tree_->layer_tree_host()->SetRootLayer(
         paint_artifact_compositor_->RootLayer());
diff --git a/third_party/blink/renderer/platform/graphics/static_bitmap_image.cc b/third_party/blink/renderer/platform/graphics/static_bitmap_image.cc
index e200c1f..b0301a4 100644
--- a/third_party/blink/renderer/platform/graphics/static_bitmap_image.cc
+++ b/third_party/blink/renderer/platform/graphics/static_bitmap_image.cc
@@ -15,6 +15,7 @@
 #include "third_party/skia/include/core/SkCanvas.h"
 #include "third_party/skia/include/core/SkImage.h"
 #include "third_party/skia/include/core/SkPaint.h"
+#include "third_party/skia/include/core/SkSurface.h"
 #include "third_party/skia/include/gpu/GrContext.h"
 
 namespace blink {
@@ -90,8 +91,31 @@
   if (SkColorSpace::Equals(src_color_space.get(), dst_color_space.get()))
     return this;
 
-  sk_sp<SkImage> converted_skia_image =
-      skia_image->makeColorSpace(dst_color_space);
+  // SkImage::makeColorSpace() converts all the color types to kN32_SkColorType.
+  // If the input color type is kRGBA_F16_SkColorType, we draw on a canvas to
+  // color convert the pixels. crbug.com/8847788: Fix this when skia:8382 is
+  // fixed.
+  sk_sp<SkImage> converted_skia_image = nullptr;
+  if (skia_image->colorType() != kRGBA_F16_SkColorType) {
+    converted_skia_image = skia_image->makeColorSpace(dst_color_space);
+  } else {
+    // Draw on a canvas to color convert.
+    SkImageInfo info = SkImageInfo::Make(
+        skia_image->width(), skia_image->height(), kRGBA_F16_SkColorType,
+        skia_image->alphaType(), dst_color_space);
+    sk_sp<SkSurface> surface = nullptr;
+    if (skia_image->isTextureBacked()) {
+      GrContext* gr =
+          ContextProviderWrapper()->ContextProvider()->GetGrContext();
+      surface = SkSurface::MakeRenderTarget(gr, SkBudgeted::kNo, info);
+    } else {
+      surface = SkSurface::MakeRaster(info);
+    }
+    SkPaint paint;
+    surface->getCanvas()->drawImage(skia_image, 0, 0, &paint);
+    converted_skia_image = surface->makeImageSnapshot();
+  }
+
   DCHECK(converted_skia_image.get());
   DCHECK(skia_image.get() != converted_skia_image.get());
 
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index 4f51bc9..7d3776b 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -669,10 +669,6 @@
       // This is enabled by features::kLazyInitializeMediaControls.
     },
     {
-      name: "LazyParseCSS",
-      status: "experimental",
-    },
-    {
       name: "LongTaskV2",
     },
     {
@@ -785,10 +781,6 @@
       status: "test",
     },
     {
-      name: "MultipleColorStopPositions",
-      status: "experimental",
-    },
-    {
       name: "NavigatorContentUtils",
     },
     {
diff --git a/third_party/blink/renderer/platform/testing/DEPS b/third_party/blink/renderer/platform/testing/DEPS
index ea9cdc9..a5014603 100644
--- a/third_party/blink/renderer/platform/testing/DEPS
+++ b/third_party/blink/renderer/platform/testing/DEPS
@@ -32,6 +32,7 @@
     "+third_party/blink/renderer/platform/network",
     "+third_party/blink/renderer/platform/platform_export.h",
     "+third_party/blink/renderer/platform/pod_arena.h",
+    "+third_party/blink/renderer/platform/runtime_enabled_features.h",
     "+third_party/blink/renderer/platform/scheduler",
     "+third_party/blink/renderer/platform/scroll",
     "+third_party/blink/renderer/platform/shared_buffer.h",
diff --git a/third_party/blink/renderer/platform/testing/layer_tree_host_embedder.cc b/third_party/blink/renderer/platform/testing/layer_tree_host_embedder.cc
index 792fa4c4..1c196523 100644
--- a/third_party/blink/renderer/platform/testing/layer_tree_host_embedder.cc
+++ b/third_party/blink/renderer/platform/testing/layer_tree_host_embedder.cc
@@ -5,22 +5,23 @@
 #include "third_party/blink/renderer/platform/testing/layer_tree_host_embedder.h"
 
 #include "base/threading/thread_task_runner_handle.h"
+#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
 
 namespace blink {
 
 LayerTreeHostEmbedder::LayerTreeHostEmbedder()
     : LayerTreeHostEmbedder(/*client=*/nullptr,
-                            /*single_thread_client=*/nullptr,
-                            /*use_layer_lists=*/false) {}
+                            /*single_thread_client=*/nullptr) {}
 
 LayerTreeHostEmbedder::LayerTreeHostEmbedder(
     cc::LayerTreeHostClient* client,
-    cc::LayerTreeHostSingleThreadClient* single_thread_client,
-    bool use_layer_lists) {
+    cc::LayerTreeHostSingleThreadClient* single_thread_client) {
   cc::LayerTreeSettings settings;
   settings.layer_transforms_should_scale_layer_contents = true;
   settings.single_thread_proxy_scheduler = false;
-  settings.use_layer_lists = use_layer_lists;
+  settings.use_layer_lists =
+      RuntimeEnabledFeatures::SlimmingPaintV2Enabled() ||
+      RuntimeEnabledFeatures::BlinkGenPropertyTreesEnabled();
 
   animation_host_ = cc::AnimationHost::CreateMainInstance();
   cc::LayerTreeHost::InitParams params;
diff --git a/third_party/blink/renderer/platform/testing/layer_tree_host_embedder.h b/third_party/blink/renderer/platform/testing/layer_tree_host_embedder.h
index 92be21c..59b713f0 100644
--- a/third_party/blink/renderer/platform/testing/layer_tree_host_embedder.h
+++ b/third_party/blink/renderer/platform/testing/layer_tree_host_embedder.h
@@ -26,8 +26,7 @@
   // overrides of LayerTreeSettings.
   LayerTreeHostEmbedder(
       cc::LayerTreeHostClient* client,
-      cc::LayerTreeHostSingleThreadClient* single_thread_client,
-      bool use_layer_lists);
+      cc::LayerTreeHostSingleThreadClient* single_thread_client);
 
   cc::LayerTreeHost* layer_tree_host() { return layer_tree_host_.get(); }
   cc::AnimationHost* animation_host() { return animation_host_.get(); }
diff --git a/third_party/blink/tools/blinkpy/web_tests/port/base.py b/third_party/blink/tools/blinkpy/web_tests/port/base.py
index 1e16870e80..3b4847c 100644
--- a/third_party/blink/tools/blinkpy/web_tests/port/base.py
+++ b/third_party/blink/tools/blinkpy/web_tests/port/base.py
@@ -383,37 +383,12 @@
     def check_sys_deps(self, needs_http):
         """Checks whether the system is properly configured.
 
-        If the port needs to do some runtime checks to ensure that the
-        tests can be run successfully, it should override this routine.
-        This step can be skipped with --nocheck-sys-deps.
+        Most checks happen during invocation of the driver prior to running
+        tests. This can be overridden to run custom checks.
 
         Returns:
             An exit status code.
         """
-        cmd = [self._path_to_driver(), '--check-layout-test-sys-deps']
-
-        additional_flags = self.get_option('additional_driver_flag', [])
-        if additional_flags:
-            cmd.append(additional_flags[0])
-
-        local_error = ScriptError()
-
-        def error_handler(script_error):
-            local_error.exit_code = script_error.exit_code
-
-        if self.host.platform.is_linux():
-            _log.debug('DISPLAY = %s', self.host.environ.get('DISPLAY', ''))
-        output = self._executive.run_command(cmd, error_handler=error_handler)
-        if local_error.exit_code:
-            _log.error('System dependencies check failed.')
-            _log.error('To override, invoke with --nocheck-sys-deps')
-            _log.error('')
-            _log.error(output)
-            if self.BUILD_REQUIREMENTS_URL is not '':
-                _log.error('')
-                _log.error('For complete build requirements, please see:')
-                _log.error(self.BUILD_REQUIREMENTS_URL)
-            return exit_codes.SYS_DEPS_EXIT_STATUS
         return exit_codes.OK_EXIT_STATUS
 
     def check_image_diff(self):
diff --git a/third_party/blink/tools/blinkpy/web_tests/port/browser_test_unittest.py b/third_party/blink/tools/blinkpy/web_tests/port/browser_test_unittest.py
index 6e21d61..71e9f80e 100644
--- a/third_party/blink/tools/blinkpy/web_tests/port/browser_test_unittest.py
+++ b/third_party/blink/tools/blinkpy/web_tests/port/browser_test_unittest.py
@@ -38,11 +38,6 @@
 
 class _BrowserTestTestCaseMixin(object):
 
-    def test_check_sys_deps(self):
-        port = self.make_port()
-        port._executive = MockExecutive(exit_code=0)  # pylint: disable=protected-access
-        self.assertEqual(port.check_sys_deps(needs_http=False), exit_codes.OK_EXIT_STATUS)
-
     def test_driver_name_option(self):
         self.assertTrue(self.make_port()._path_to_driver().endswith(self.driver_name_endswith))
 
diff --git a/third_party/blink/tools/blinkpy/web_tests/port/port_testcase.py b/third_party/blink/tools/blinkpy/web_tests/port/port_testcase.py
index a905f24..8628e69 100644
--- a/third_party/blink/tools/blinkpy/web_tests/port/port_testcase.py
+++ b/third_party/blink/tools/blinkpy/web_tests/port/port_testcase.py
@@ -254,13 +254,6 @@
             port.host.filesystem.join(port.layout_tests_dir(), 'SlowTests'),
         ])
 
-    def test_check_sys_deps(self):
-        port = self.make_port()
-        port._executive = MockExecutive(exit_code=0)  # pylint: disable=protected-access
-        self.assertEqual(port.check_sys_deps(needs_http=False), exit_codes.OK_EXIT_STATUS)
-        port._executive = MockExecutive(exit_code=1, output='testing output failure')  # pylint: disable=protected-access
-        self.assertEqual(port.check_sys_deps(needs_http=False), exit_codes.SYS_DEPS_EXIT_STATUS)
-
     def test_expectations_ordering(self):
         port = self.make_port()
         for path in port.expectations_files():
diff --git a/third_party/closure_compiler/externs/file_manager_private.js b/third_party/closure_compiler/externs/file_manager_private.js
index ddeeac9..1a7ab73 100644
--- a/third_party/closure_compiler/externs/file_manager_private.js
+++ b/third_party/closure_compiler/externs/file_manager_private.js
@@ -953,6 +953,18 @@
  */
 chrome.fileManagerPrivate.installLinuxPackage = function(entry, callback) {};
 
+/**
+ * For a file in DriveFS, retrieves its thumbnail. If |cropToSquare| is true,
+ * returns a thumbnail appropriate for file list or grid views; otherwise,
+ * returns a thumbnail appropriate for quickview.
+ * @param {Object} entry
+ * @param {boolean} cropToSquare
+ * @param {function(string):void} callback |thumbnailDataUrl| A data URL for the
+ *     thumbnail as a PNG; |thumbnailDataUrl| is empty if no thumbnail was
+ *     available.
+ */
+chrome.fileManagerPrivate.getThumbnail = function(entry, cropToSquare, callback) {};
+
 /** @type {!ChromeEvent} */
 chrome.fileManagerPrivate.onMountCompleted;
 
diff --git a/tools/accessibility/inspect/BUILD.gn b/tools/accessibility/inspect/BUILD.gn
index a28428b..cf05291 100644
--- a/tools/accessibility/inspect/BUILD.gn
+++ b/tools/accessibility/inspect/BUILD.gn
@@ -2,24 +2,22 @@
 # Copyright 2017 The Chromium Authors. All rights reserved.
 # found in the LICENSE file.
 
-if (is_win) {
-  executable("ax_dump_events") {
-    testonly = true
+executable("ax_dump_events") {
+  testonly = true
 
-    sources = [
-      "ax_dump_events.cc",
-      "ax_event_server.cc",
-    ]
+  sources = [
+    "ax_dump_events.cc",
+    "ax_event_server.cc",
+  ]
 
-    deps = [
-      "//base",
-      "//base/test:test_support",
-      "//content/test:test_support",
-    ]
+  deps = [
+    "//base",
+    "//base/test:test_support",
+    "//content/test:test_support",
+  ]
 
-    if (is_win) {
-      libs = [ "oleacc.lib" ]
-    }
+  if (is_win) {
+    libs = [ "oleacc.lib" ]
   }
 }
 
diff --git a/tools/accessibility/inspect/ax_dump_events.cc b/tools/accessibility/inspect/ax_dump_events.cc
index 823cec2..474aafde 100644
--- a/tools/accessibility/inspect/ax_dump_events.cc
+++ b/tools/accessibility/inspect/ax_dump_events.cc
@@ -16,6 +16,7 @@
 namespace {
 
 constexpr char kPidSwitch[] = "pid";
+constexpr char kPatternSwitch[] = "pattern";
 
 // Convert from string to int, whether in 0x hex format or decimal format.
 bool StringToInt(std::string str, int* result) {
@@ -35,25 +36,35 @@
   printf("%s", str.substr(message_start).c_str());
   return true;
 }
-
 }  // namespace
 
 int main(int argc, char** argv) {
   logging::SetLogMessageHandler(AXDumpEventsLogMessageHandler);
 
   base::CommandLine::Init(argc, argv);
+
   const std::string pid_str =
       base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(kPidSwitch);
-  int pid;
-  if (pid_str.empty() || !StringToInt(pid_str, &pid)) {
-    LOG(ERROR) << "* Error: No process id provided via --pid=[process-id].";
+  const std::string pattern_str =
+      base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
+          kPatternSwitch);
+  if (pid_str.empty() && pattern_str.empty()) {
+    LOG(ERROR) << "* Error: No process id provided via --pid=[process-id] or"
+                  " application name pattern via --pattern=[pattern].";
     return 1;
   }
 
+  int pid = 0;
+  if (!pid_str.empty()) {
+    if (!StringToInt(pid_str, &pid)) {
+      LOG(ERROR) << "* Error: Could not convert process id to integer.";
+      return 1;
+    }
+  }
+
   base::AtExitManager exit_manager;
   base::MessageLoopForUI message_loop;
-  const auto server = std::make_unique<tools::AXEventServer>(pid);
+  const auto server = std::make_unique<tools::AXEventServer>(pid, pattern_str);
   base::RunLoop().Run();
-
   return 0;
 }
diff --git a/tools/accessibility/inspect/ax_event_server.cc b/tools/accessibility/inspect/ax_event_server.cc
index 77fad511..64754d7e 100644
--- a/tools/accessibility/inspect/ax_event_server.cc
+++ b/tools/accessibility/inspect/ax_event_server.cc
@@ -11,9 +11,11 @@
 
 namespace tools {
 
-AXEventServer::AXEventServer(base::ProcessId pid)
-    : recorder_(
-          content::AccessibilityEventRecorder::GetInstance(nullptr, pid)) {
+AXEventServer::AXEventServer(base::ProcessId pid,
+                             const base::StringPiece& pattern)
+    : recorder_(content::AccessibilityEventRecorder::GetInstance(nullptr,
+                                                                 pid,
+                                                                 pattern)) {
   recorder_.ListenToEvents(
       base::BindRepeating(&AXEventServer::OnEvent, base::Unretained(this)));
 
diff --git a/tools/accessibility/inspect/ax_event_server.h b/tools/accessibility/inspect/ax_event_server.h
index 827d326..de081db 100644
--- a/tools/accessibility/inspect/ax_event_server.h
+++ b/tools/accessibility/inspect/ax_event_server.h
@@ -17,7 +17,13 @@
 
 class AXEventServer final {
  public:
-  explicit AXEventServer(base::ProcessId pid);
+  // `application_name_match_pattern` is a matching pattern, which may contain
+  // wildcard characters, to be matched against the accessibility application
+  // name. Only events that match the pattern will be shown. If the pattern is
+  // empty, it is unused.
+  explicit AXEventServer(
+      base::ProcessId pid,
+      const base::StringPiece& application_name_match_pattern);
   ~AXEventServer();
 
  private:
diff --git a/tools/binary_size/libsupersize/generate_milestone_report.py b/tools/binary_size/libsupersize/generate_milestone_report.py
index fd06ca41..9e972e2d 100755
--- a/tools/binary_size/libsupersize/generate_milestone_report.py
+++ b/tools/binary_size/libsupersize/generate_milestone_report.py
@@ -42,10 +42,10 @@
 REPORT_URL_TEMPLATE = '{cpu}/{apk}/report_{version1}_{version2}.ndjson'
 
 DESIRED_CPUS = ['arm', 'arm_64']
+# TODO: Add AndroidWebview.apk
 DESIRED_APKS = ['Monochrome.apk', 'ChromeModern.apk']
 # Versions are manually gathered from
-# https://omahaproxy.appspot.com/history?os=android&channel=beta
-# The latest version from each milestone was chosen, for the last 9 milestones.
+# https://omahaproxy.appspot.com/history?os=android&channel=stable
 DESIRED_VERSION = [
   '60.0.3112.116',
   '61.0.3163.98',
@@ -55,7 +55,9 @@
   '65.0.3325.85',
   '66.0.3359.158',
   '67.0.3396.87',
-  '68.0.3440.70',
+  '68.0.3440.85',
+  '69.0.3497.91',
+  '70.0.3538.17',  # Beta
 ]
 
 
@@ -178,11 +180,14 @@
 def main():
   parser = argparse.ArgumentParser(description=__doc__)
   parser.add_argument('directory',
-                      help='Directory to save report files to.')
+                      help='Directory to save report files to '
+                           '(must not exist).')
   parser.add_argument('--size-file-bucket', required=True,
-                      help='GCS bucket to find size files in.')
+                      help='GCS bucket to find size files in.'
+                           '(e.g. "gs://bucket/subdir")')
   parser.add_argument('--sync', action='store_true',
-                      help='Sync data files to GCS.')
+                      help='Sync data files to GCS '
+                           '(otherwise just prints out command to run).')
   parser.add_argument('-v',
                       '--verbose',
                       default=0,
@@ -203,7 +208,9 @@
   _BuildReports(args.directory, size_file_bucket)
   _SetPushedReports(args.directory)
   logging.warning('Reports saved to %s', args.directory)
-  cmd = ['gsutil.py', '-m', 'rsync', '-r', args.directory, PUSH_URL]
+  cmd = ['gsutil.py', '-m', 'rsync', '-J', '-a', 'publicRead', '-r',
+         args.directory, PUSH_URL]
+
   if args.sync:
     subprocess.check_call(cmd)
   else:
diff --git a/tools/binary_size/libsupersize/gsutil.py b/tools/binary_size/libsupersize/upload_html_viewer.py
similarity index 100%
rename from tools/binary_size/libsupersize/gsutil.py
rename to tools/binary_size/libsupersize/upload_html_viewer.py
diff --git a/tools/cygprofile/orderfile_generator_backend.py b/tools/cygprofile/orderfile_generator_backend.py
index 7da5668..c4af0b4a 100755
--- a/tools/cygprofile/orderfile_generator_backend.py
+++ b/tools/cygprofile/orderfile_generator_backend.py
@@ -812,11 +812,10 @@
       '--use-goma', action='store_true', help='Enable GOMA.', default=False)
   parser.add_argument('--adb-path', help='Path to the adb binary.')
 
-  parser.add_argument('--nosystem-health-orderfile', action='store_false',
-                      dest='system_health_orderfile', default=True,
-                      help=('Create an orderfile based on an about:blank '
-                            'startup benchmark instead of system health '
-                            'benchmarks.'))
+  parser.add_argument('--system-health-orderfile', action='store_true',
+                      help=('Create an orderfile based on system health '
+                            'benchmarks.'),
+                      default=False)
   parser.add_argument('--monochrome', action='store_true',
                       help=('Compile and instrument monochrome (for post-N '
                             'devices).'))
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index c4cd1f59..afd3252 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -11997,6 +11997,13 @@
   <int value="2" label="iOS mobile config">
     application/x-apple-aspen-config MIME type.
   </int>
+  <int value="3" label="Zip Archive">application/zip MIME type.</int>
+  <int value="4" label="Microsoft Application (.exe file)">
+    application/x-msdownload MIME type.
+  </int>
+  <int value="5" label="Android Package Archive (.apk file)">
+    application/vnd.android.package-archive MIME type.
+  </int>
 </enum>
 
 <enum name="DownloadNotificationForegroundLifecycle">
@@ -16756,6 +16763,7 @@
   <int value="1275" label="TABCAPTURE_GETMEDIASTREAMID"/>
   <int value="1276" label="WEBVIEWINTERNAL_SETSPATIALNAVIGATIONENABLED"/>
   <int value="1277" label="WEBVIEWINTERNAL_ISSPATIALNAVIGATIONENABLED"/>
+  <int value="1278" label="FILEMANAGERPRIVATEINTERNAL_GETTHUMBNAIL"/>
 </enum>
 
 <enum name="ExtensionIconState">
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index c3963a8..ac32801 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -642,6 +642,16 @@
   </summary>
 </histogram>
 
+<histogram name="Ads.Media.LoadType" enum="MediaLoadType"
+    expires_after="2019-09-18">
+  <owner>johnidel@chromium.org</owner>
+  <owner>jkarlin@chromium.org</owner>
+  <summary>
+    Load type of HTML5 media in ad subframes such as URL, MediaSource and
+    MediaStream.
+  </summary>
+</histogram>
+
 <histogram name="Ads.ResourceUsage.Size.Mainframe.AdResource" units="KB"
     expires_after="2019-09-05">
   <owner>jkarlin@chromium.org</owner>
@@ -107388,6 +107398,19 @@
   </summary>
 </histogram>
 
+<histogram base="true" name="TaskScheduler.NumTasksRunWhileQueuing"
+    units="tasks" expires_after="2019-10-01">
+  <owner>jessemckenna@google.com</owner>
+  <owner>fdoray@chromium.org</owner>
+  <summary>
+    Number of tasks run by TaskScheduler while task was queuing (from time task
+    was posted until time it was run). Recorded for dummy &quot;heartbeat&quot;
+    tasks posted with specific traits (see suffix). The heartbeat recording
+    avoids dependencies between this report and other work in the system.
+    Recorded every time the ServiceThread performs a heartbeat latency report.
+  </summary>
+</histogram>
+
 <histogram base="true" name="TaskScheduler.NumWorkers" units="workers">
   <owner>etiennep@chromium.org</owner>
   <owner>fdoray@chromium.org</owner>
@@ -131474,6 +131497,7 @@
   <affected-histogram name="TaskScheduler.HeartbeatLatencyMicroseconds"/>
   <affected-histogram name="TaskScheduler.NumTasksBeforeDetach"/>
   <affected-histogram name="TaskScheduler.NumTasksBetweenWaits"/>
+  <affected-histogram name="TaskScheduler.NumTasksRunWhileQueuing"/>
   <affected-histogram name="TaskScheduler.NumWorkers"/>
   <affected-histogram name="TaskScheduler.TaskLatencyMicroseconds"/>
 </histogram_suffixes>
@@ -131542,6 +131566,10 @@
       name="TaskScheduler.HeartbeatLatencyMicroseconds.ContentChild"/>
   <affected-histogram
       name="TaskScheduler.HeartbeatLatencyMicroseconds.Renderer"/>
+  <affected-histogram name="TaskScheduler.NumTasksRunWhileQueuing.Browser"/>
+  <affected-histogram
+      name="TaskScheduler.NumTasksRunWhileQueuing.ContentChild"/>
+  <affected-histogram name="TaskScheduler.NumTasksRunWhileQueuing.Renderer"/>
   <affected-histogram name="TaskScheduler.TaskLatencyMicroseconds.Browser"/>
   <affected-histogram
       name="TaskScheduler.TaskLatencyMicroseconds.ContentChild"/>
diff --git a/tools/perf/benchmarks/loading.py b/tools/perf/benchmarks/loading.py
index 61caa2c..9de446e 100644
--- a/tools/perf/benchmarks/loading.py
+++ b/tools/perf/benchmarks/loading.py
@@ -18,11 +18,6 @@
 
   options = {'pageset_repeat': 2}
 
-  def SetExtraBrowserOptions(self, options):
-    options.AppendExtraBrowserArgs([
-        '--enable-features=TracingPerfettoBackend',
-    ])
-
   def CreateCoreTimelineBasedMeasurementOptions(self):
     tbm_options = timeline_based_measurement.Options()
     loading_metrics_category.AugmentOptionsForLoadingMetrics(tbm_options)
diff --git a/tools/perf/benchmarks/v8_browsing.py b/tools/perf/benchmarks/v8_browsing.py
index dab04992..8922363 100644
--- a/tools/perf/benchmarks/v8_browsing.py
+++ b/tools/perf/benchmarks/v8_browsing.py
@@ -8,7 +8,6 @@
 from telemetry import benchmark
 from telemetry import story
 from telemetry.timeline import chrome_trace_config
-from telemetry.timeline import chrome_trace_category_filter
 from telemetry.web_perf import timeline_based_measurement
 import page_sets
 
@@ -30,6 +29,72 @@
     r'scavenger_|'
     r'total_)')
 
+def V8BrowsingShouldAddValue(name):
+  # TODO(crbug.com/775942): This is needed because of a race condition in
+  # the memory dump manager. Remove this once the bug is fixed.
+  if 'memory:unknown_browser' in name:
+    return ('renderer_processes' in name and
+            not _IGNORED_MEMORY_STATS_RE.search(name))
+  # TODO(crbug.com/610962): Remove this stopgap when the perf dashboard
+  # is able to cope with the data load generated by TBMv2 metrics.
+  if 'memory:chrome' in name:
+    return ('renderer_processes' in name and
+            not _IGNORED_MEMORY_STATS_RE.search(name))
+  if 'v8-gc' in name:
+    return (_V8_GC_HIGH_LEVEL_STATS_RE.search(name) and
+            not _IGNORED_V8_STATS_RE.search(name))
+  # Allow all other metrics.
+  return True
+
+
+def AugmentOptionsForV8BrowsingMetrics(options, enable_runtime_call_stats=True):
+  categories = [
+    # Disable all categories by default.
+    '-*',
+    # Memory categories.
+    'disabled-by-default-memory-infra',
+    # UE categories required by runtimeStatsTotalMetric to bucket
+    # runtimeStats by UE.
+    'rail',
+    # EQT categories.
+    'blink.user_timing',
+    'loading',
+    'navigation',
+    'toplevel',
+    # V8 categories.
+    'disabled-by-default-v8.gc',
+    'renderer.scheduler',
+    'v8',
+    'v8.console',
+    'webkit.console',
+    # TODO(crbug.com/616441, primiano): Remove this temporary workaround,
+    # which enables memory-infra V8 code stats in V8 code size benchmarks
+    # only (to not slow down detailed memory dumps in other benchmarks).
+    'disabled-by-default-memory-infra.v8.code_stats',
+    # Blink categories.
+    'blink_gc',
+  ]
+  options.ExtendTraceCategoryFilter(categories)
+  if enable_runtime_call_stats:
+    options.AddTraceCategoryFilter('disabled-by-default-v8.runtime_stats')
+
+  options.config.enable_android_graphics_memtrack = True
+  # Trigger periodic light memory dumps every 1000 ms.
+  memory_dump_config = chrome_trace_config.MemoryDumpConfig()
+  memory_dump_config.AddTrigger('light', 1000)
+  options.config.chrome_trace_config.SetMemoryDumpConfig(memory_dump_config)
+
+  metrics = [
+    'blinkGcMetric',
+    'consoleErrorMetric',
+    'expectedQueueingTimeMetric',
+    'gcMetric',
+    'memoryMetric',
+  ]
+  options.ExtendTimelineBasedMetric(metrics)
+  if enable_runtime_call_stats:
+    options.AddTimelineBasedMetric('runtimeStatsTotalMetric')
+  return options
 
 class _V8BrowsingBenchmark(perf_benchmark.PerfBenchmark):
   """Base class for V8 browsing benchmarks that measure RuntimeStats,
@@ -41,70 +106,14 @@
     return page_sets.SystemHealthStorySet(platform=self.PLATFORM, case='browse')
 
   def CreateCoreTimelineBasedMeasurementOptions(self):
-    categories = [
-      # Disable all categories by default.
-      '-*',
-      # Memory categories.
-      'disabled-by-default-memory-infra',
-      # UE categories requred by runtimeStatsTotalMetric to bucket
-      # runtimeStats by UE.
-      'rail',
-      # EQT categories.
-      'blink.user_timing',
-      'loading',
-      'navigation',
-      'toplevel',
-      # V8 categories.
-      'disabled-by-default-v8.gc',
-      'renderer.scheduler',
-      'v8',
-      'v8.console',
-      'webkit.console',
-      'disabled-by-default-v8.runtime_stats',
-      # TODO(crbug.com/616441, primiano): Remove this temporary workaround,
-      # which enables memory-infra V8 code stats in V8 code size benchmarks
-      # only (to not slow down detailed memory dumps in other benchmarks).
-      'disabled-by-default-memory-infra.v8.code_stats',
-      # Blink categories.
-      'blink_gc',
-    ]
-    options = timeline_based_measurement.Options(
-        chrome_trace_category_filter.ChromeTraceCategoryFilter(
-            ','.join(categories)))
-    options.config.enable_android_graphics_memtrack = True
-    # Trigger periodic light memory dumps every 1000 ms.
-    memory_dump_config = chrome_trace_config.MemoryDumpConfig()
-    memory_dump_config.AddTrigger('light', 1000)
-    options.config.chrome_trace_config.SetMemoryDumpConfig(memory_dump_config)
-
-    options.SetTimelineBasedMetrics([
-      'blinkGcMetric',
-      'consoleErrorMetric',
-      'expectedQueueingTimeMetric',
-      'gcMetric',
-      'memoryMetric',
-      'runtimeStatsTotalMetric'
-    ])
+    options = timeline_based_measurement.Options()
+    AugmentOptionsForV8BrowsingMetrics(options)
     return options
 
   @classmethod
   def ShouldAddValue(cls, name, from_first_story_run):
     del from_first_story_run  # unused
-    # TODO(crbug.com/775942): This is needed because of a race condition in
-    # the memory dump manager. Remove this once the bug is fixed.
-    if 'memory:unknown_browser' in name:
-      return ('renderer_processes' in name and
-              not _IGNORED_MEMORY_STATS_RE.search(name))
-    # TODO(crbug.com/610962): Remove this stopgap when the perf dashboard
-    # is able to cope with the data load generated by TBMv2 metrics.
-    if 'memory:chrome' in name:
-      return ('renderer_processes' in name and
-              not _IGNORED_MEMORY_STATS_RE.search(name))
-    if 'v8-gc' in name:
-      return (_V8_GC_HIGH_LEVEL_STATS_RE.search(name) and
-              not _IGNORED_V8_STATS_RE.search(name))
-    # Allow all other metrics.
-    return True
+    return V8BrowsingShouldAddValue(name)
 
 
 @benchmark.Info(emails=['mythria@chromium.org','ulan@chromium.org'])
diff --git a/tools/perf/contrib/cluster_telemetry/loading_base_ct.py b/tools/perf/contrib/cluster_telemetry/loading_base_ct.py
new file mode 100644
index 0000000..4f738b7f
--- /dev/null
+++ b/tools/perf/contrib/cluster_telemetry/loading_base_ct.py
@@ -0,0 +1,46 @@
+# Copyright 2018 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+from benchmarks import loading
+
+from contrib.cluster_telemetry import ct_benchmarks_util
+from contrib.cluster_telemetry import page_set
+
+from telemetry.page import cache_temperature as cache_temperature_module
+from telemetry.page import traffic_setting
+
+# pylint: disable=protected-access
+class _LoadingBaseClusterTelemetry(loading._LoadingBase):
+  """ A base class for cluster telemetry loading benchmarks. """
+
+  options = {'upload_results': True}
+
+  _ALL_NET_CONFIGS = traffic_setting.NETWORK_CONFIGS.keys()
+  _ALL_CACHE_TEMPERATURES = cache_temperature_module.ALL_CACHE_TEMPERATURES
+
+  @classmethod
+  def AddBenchmarkCommandLineArgs(cls, parser):
+    super(_LoadingBaseClusterTelemetry, cls).AddBenchmarkCommandLineArgs(parser)
+    ct_benchmarks_util.AddBenchmarkCommandLineArgs(parser)
+    parser.add_option(
+        '--wait-time',  action='store', type='int',
+        default=60, help='Number of seconds to wait for after navigation.')
+    parser.add_option(
+        '--traffic-setting',  choices=cls._ALL_NET_CONFIGS,
+        default=traffic_setting.REGULAR_4G,
+        help='Traffic condition (string). Default to "%%default". Can be: %s' %
+         ', '.join(cls._ALL_NET_CONFIGS))
+    parser.add_option(
+        '--cache-temperature',  choices=cls._ALL_CACHE_TEMPERATURES,
+        default=cache_temperature_module.COLD,
+        help='Cache temperature (string). Default to "%%default". Can be: %s' %
+         ', '.join(cls._ALL_CACHE_TEMPERATURES))
+
+  def CreateStorySet(self, options):
+    def Wait(action_runner):
+      action_runner.Wait(options.wait_time)
+    return page_set.CTPageSet(
+      options.urls_list, options.user_agent, options.archive_data_file,
+      traffic_setting=options.traffic_setting,
+      cache_temperature=options.cache_temperature,
+      run_page_interaction_callback=Wait)
diff --git a/tools/perf/contrib/cluster_telemetry/loading_ct.py b/tools/perf/contrib/cluster_telemetry/loading_ct.py
index eca50430..9393954 100644
--- a/tools/perf/contrib/cluster_telemetry/loading_ct.py
+++ b/tools/perf/contrib/cluster_telemetry/loading_ct.py
@@ -1,49 +1,10 @@
 # Copyright 2016 The Chromium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
-from benchmarks import loading
-
-from contrib.cluster_telemetry import ct_benchmarks_util
-from contrib.cluster_telemetry import page_set
-
-from telemetry.page import cache_temperature as cache_temperature_module
-from telemetry.page import traffic_setting
-
+from contrib.cluster_telemetry import loading_base_ct
 
 # pylint: disable=protected-access
-class LoadingClusterTelemetry(loading._LoadingBase):
-
-  options = {'upload_results': True}
-
-  _ALL_NET_CONFIGS = traffic_setting.NETWORK_CONFIGS.keys()
-  _ALL_CACHE_TEMPERATURES = cache_temperature_module.ALL_CACHE_TEMPERATURES
-
-  @classmethod
-  def AddBenchmarkCommandLineArgs(cls, parser):
-    super(LoadingClusterTelemetry, cls).AddBenchmarkCommandLineArgs(parser)
-    ct_benchmarks_util.AddBenchmarkCommandLineArgs(parser)
-    parser.add_option(
-        '--wait-time',  action='store', type='int',
-        default=60, help='Number of seconds to wait for after navigation.')
-    parser.add_option(
-        '--traffic-setting',  choices=cls._ALL_NET_CONFIGS,
-        default=traffic_setting.REGULAR_4G,
-        help='Traffic condition (string). Default to "%%default". Can be: %s' %
-         ', '.join(cls._ALL_NET_CONFIGS))
-    parser.add_option(
-        '--cache-temperature',  choices=cls._ALL_CACHE_TEMPERATURES,
-        default=cache_temperature_module.COLD,
-        help='Cache temperature (string). Default to "%%default". Can be: %s' %
-         ', '.join(cls._ALL_CACHE_TEMPERATURES))
-
-  def CreateStorySet(self, options):
-    def Wait(action_runner):
-      action_runner.Wait(options.wait_time)
-    return page_set.CTPageSet(
-      options.urls_list, options.user_agent, options.archive_data_file,
-      traffic_setting=options.traffic_setting,
-      cache_temperature=options.cache_temperature,
-      run_page_interaction_callback=Wait)
+class LoadingClusterTelemetry(loading_base_ct._LoadingBaseClusterTelemetry):
 
   @classmethod
   def Name(cls):
diff --git a/tools/perf/contrib/cluster_telemetry/v8_loading_ct.py b/tools/perf/contrib/cluster_telemetry/v8_loading_ct.py
new file mode 100644
index 0000000..18f365b
--- /dev/null
+++ b/tools/perf/contrib/cluster_telemetry/v8_loading_ct.py
@@ -0,0 +1,30 @@
+# Copyright 2018 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+from benchmarks import v8_browsing
+from contrib.cluster_telemetry import loading_base_ct
+from telemetry.web_perf import timeline_based_measurement
+
+# pylint: disable=protected-access
+class V8LoadingClusterTelemetry(loading_base_ct._LoadingBaseClusterTelemetry):
+
+  @classmethod
+  def AddBenchmarkCommandLineArgs(cls, parser):
+    super(V8LoadingClusterTelemetry, cls).AddBenchmarkCommandLineArgs(parser)
+    parser.add_option('--enable-rcs', action='store_true', default=False,
+        help='Enable expensive V8 Runtime Call Stats metrics.')
+
+  @classmethod
+  def ShouldAddValue(cls, name, from_first_story_run):
+    del from_first_story_run  # unused
+    return v8_browsing.V8BrowsingShouldAddValue(name)
+
+  @classmethod
+  def Name(cls):
+    return 'v8.loading.cluster_telemetry'
+
+  def CreateCoreTimelineBasedMeasurementOptions(self):
+    options = timeline_based_measurement.Options()
+    v8_browsing.AugmentOptionsForV8BrowsingMetrics(options,
+        enable_runtime_call_stats=options.enable_rcs)
+    return options
diff --git a/tools/perf/core/default_local_state.json b/tools/perf/core/default_local_state.json
deleted file mode 100644
index eae1f45..0000000
--- a/tools/perf/core/default_local_state.json
+++ /dev/null
@@ -1,8 +0,0 @@
-{
-  "subresource_filter": {
-    "ruleset_version": {
-      "content": "1000",
-      "format": 23
-    }
-  }
-}
diff --git a/tools/perf/core/perf_benchmark.py b/tools/perf/core/perf_benchmark.py
index 4cb9fd6..64fcfaf 100644
--- a/tools/perf/core/perf_benchmark.py
+++ b/tools/perf/core/perf_benchmark.py
@@ -40,14 +40,15 @@
   if chrome_output_directory is None:
     return []
 
-  ruleset_path = os.path.join(chrome_output_directory, 'gen', 'components',
-      'subresource_filter', 'tools', 'GeneratedRulesetData')
+  gen_path = os.path.join(chrome_output_directory, 'gen', 'components',
+                          'subresource_filter', 'tools')
+  ruleset_path = os.path.join(gen_path, 'GeneratedRulesetData')
   if not os.path.exists(ruleset_path):
     return []
 
-  local_state_path = os.path.join(
-      os.path.dirname(__file__), 'default_local_state.json')
-  assert os.path.exists(local_state_path)
+  local_state_path = os.path.join(gen_path, 'default_local_state.json')
+  if not os.path.exists(local_state_path):
+    return []
 
   with open(local_state_path, 'r') as f:
     state_json = json.load(f)
@@ -105,12 +106,18 @@
     options.AppendExtraBrowserArgs(
         '--disable-gpu-process-for-dx12-vulkan-info-collection')
 
-
     # TODO(crbug.com/881469): remove this once Webview support surface
     # synchronization.
     if 'android-webview' in options.browser_type:
       options.AppendExtraBrowserArgs(
           '--disable-features=SurfaceSynchronization')
+
+    # Switch Chrome to use Perfetto instead of TraceLog as the tracing backend,
+    # needed until the feature gets turned on by default everywhere.
+    if options.browser_type != 'reference':
+      options.AppendExtraBrowserArgs(
+          '--enable-features=TracingPerfettoBackend')
+
     self.SetExtraBrowserOptions(options)
 
   @staticmethod
diff --git a/tools/perf/core/perf_benchmark_unittest.py b/tools/perf/core/perf_benchmark_unittest.py
index 8cd131b..d46c77d 100644
--- a/tools/perf/core/perf_benchmark_unittest.py
+++ b/tools/perf/core/perf_benchmark_unittest.py
@@ -17,9 +17,33 @@
 class PerfBenchmarkTest(unittest.TestCase):
   def setUp(self):
     self._output_dir = tempfile.mkdtemp()
+    self._chrome_root = tempfile.mkdtemp()
 
   def tearDown(self):
     shutil.rmtree(self._output_dir, ignore_errors=True)
+    shutil.rmtree(self._chrome_root, ignore_errors=True)
+
+  def _PopulateGenFiles(self, output_dir=None):
+    root = output_dir if output_dir is not None else self._output_dir
+    gen_path = os.path.join(root, 'gen', 'components', 'subresource_filter',
+                            'tools')
+    os.makedirs(gen_path)
+
+    # Just make an empty ruleset file.
+    open(os.path.join(gen_path, 'GeneratedRulesetData'), 'w').close()
+
+    placeholder_json = {
+        'subresource_filter' : {
+            'ruleset_version' : {
+                'content': '1000',
+                'format': 100,
+                'checksum': 0
+            }
+        }
+    }
+    with open(os.path.join(gen_path, 'default_local_state.json'), 'w') as f:
+      json.dump(placeholder_json, f)
+
 
   def _ExpectAdTaggingProfileFiles(self, browser_options, expect_present):
     files_to_copy = browser_options.profile_files_to_copy
@@ -111,9 +135,7 @@
     self._ExpectAdTaggingProfileFiles(options.browser_options, False)
 
   def testAdTaggingRulesetReference(self):
-    os.makedirs(os.path.join(
-        self._output_dir, 'gen', 'components', 'subresource_filter',
-        'tools','GeneratedRulesetData'))
+    self._PopulateGenFiles()
 
     benchmark = perf_benchmark.PerfBenchmark()
     options = options_for_unittests.GetCopy()
@@ -128,9 +150,7 @@
     self._ExpectAdTaggingProfileFiles(options.browser_options, False)
 
   def testAdTaggingRuleset(self):
-    os.makedirs(os.path.join(
-        self._output_dir, 'gen', 'components', 'subresource_filter',
-        'tools','GeneratedRulesetData'))
+    self._PopulateGenFiles()
 
     benchmark = perf_benchmark.PerfBenchmark()
     options = options_for_unittests.GetCopy()
@@ -144,30 +164,22 @@
     self._ExpectAdTaggingProfileFiles(options.browser_options, True)
 
   def testAdTaggingRulesetNoExplicitOutDir(self):
-    # Make sure _output_dir points to Chrome's root and not the traditional
-    # output directory.
-    os.makedirs(os.path.join(
-        self._output_dir, 'out','Release','gen', 'components',
-        'subresource_filter', 'tools','GeneratedRulesetData'))
+    self._PopulateGenFiles(os.path.join(self._chrome_root, 'out', 'Release'))
 
     benchmark = perf_benchmark.PerfBenchmark()
     options = options_for_unittests.GetCopy()
-    options.chrome_root = self._output_dir
+    options.chrome_root = self._chrome_root
     options.browser_options.browser_type = "release"
 
     benchmark.CustomizeBrowserOptions(options.browser_options)
     self._ExpectAdTaggingProfileFiles(options.browser_options, True)
 
   def testAdTaggingRulesetNoExplicitOutDirAndroidChromium(self):
-    # Make sure _output_dir points to Chrome's root and not the traditional
-    # output directory.
-    os.makedirs(os.path.join(
-        self._output_dir, 'out','Default','gen', 'components',
-        'subresource_filter', 'tools','GeneratedRulesetData'))
+    self._PopulateGenFiles(os.path.join(self._chrome_root, 'out', 'Default'))
 
     benchmark = perf_benchmark.PerfBenchmark()
     options = options_for_unittests.GetCopy()
-    options.chrome_root = self._output_dir
+    options.chrome_root = self._chrome_root
 
     # android-chromium is special cased to search for anything.
     options.browser_options.browser_type = "android-chromium"
@@ -179,13 +191,11 @@
     # Same as the above test but use Debug instead of Release. This should
     # cause the benchmark to fail to find the ruleset because we only check
     # directories matching the browser_type.
-    os.makedirs(os.path.join(
-        self._output_dir, 'out','Debug','gen', 'components',
-        'subresource_filter', 'tools','GeneratedRulesetData'))
+    self._PopulateGenFiles(os.path.join(self._chrome_root, 'out', 'Debug'))
 
     benchmark = perf_benchmark.PerfBenchmark()
     options = options_for_unittests.GetCopy()
-    options.chrome_root = self._output_dir
+    options.chrome_root = self._chrome_root
     options.browser_options.browser_type = "release"
 
     benchmark.CustomizeBrowserOptions(options.browser_options)
diff --git a/tools/perf/validate_wpr_archives b/tools/perf/validate_wpr_archives
index 1209581d..548c4d6 100755
--- a/tools/perf/validate_wpr_archives
+++ b/tools/perf/validate_wpr_archives
@@ -27,6 +27,7 @@
       'rasterize_and_record_micro_ct',
       'multipage_skpicture_printer_ct',
       'loading.cluster_telemetry',
+      'v8.loading.cluster_telemetry',
       'memory.cluster_telemetry',
       'skpicture_printer',
       'cros_tab_switching.typical_24',
diff --git a/ui/accessibility/platform/ax_platform_node_auralinux.cc b/ui/accessibility/platform/ax_platform_node_auralinux.cc
index d48f18f..a3e7af03 100644
--- a/ui/accessibility/platform/ax_platform_node_auralinux.cc
+++ b/ui/accessibility/platform/ax_platform_node_auralinux.cc
@@ -755,6 +755,59 @@
     nullptr, nullptr};
 
 //
+// AtkHypertext interface.
+//
+
+static AtkHyperlink* ax_platform_node_auralinux_hypertext_get_link(
+    AtkHypertext* hypertext,
+    int index) {
+  g_return_val_if_fail(ATK_HYPERTEXT(hypertext), 0);
+  auto* obj = AtkObjectToAXPlatformNodeAuraLinux(ATK_OBJECT(hypertext));
+  if (!obj)
+    return nullptr;
+
+  const ui::AXHypertext& ax_hypertext = obj->GetHypertext();
+  if (index > static_cast<int>(ax_hypertext.hyperlinks.size()) || index < 0)
+    return nullptr;
+
+  int32_t id = ax_hypertext.hyperlinks[index];
+  auto* link = ui::AXPlatformNodeAuraLinux::GetFromUniqueId(id);
+  if (!link)
+    return nullptr;
+
+  return link->GetAtkHyperlink();
+}
+
+static int ax_platform_node_auralinux_get_n_links(AtkHypertext* hypertext) {
+  g_return_val_if_fail(ATK_HYPERTEXT(hypertext), 0);
+  ui::AXPlatformNodeAuraLinux* obj =
+      AtkObjectToAXPlatformNodeAuraLinux(ATK_OBJECT(hypertext));
+  return obj ? obj->GetHypertext().hyperlinks.size() : 0;
+}
+
+static int ax_platform_node_auralinux_get_link_index(AtkHypertext* hypertext,
+                                                     int char_index) {
+  g_return_val_if_fail(ATK_HYPERTEXT(hypertext), 0);
+  ui::AXPlatformNodeAuraLinux* obj =
+      AtkObjectToAXPlatformNodeAuraLinux(ATK_OBJECT(hypertext));
+
+  auto it = obj->GetHypertext().hyperlink_offset_to_index.find(char_index);
+  if (it == obj->GetHypertext().hyperlink_offset_to_index.end())
+    return -1;
+  return it->second;
+}
+
+void ax_hypertext_interface_base_init(AtkHypertextIface* iface) {
+  iface->get_link = ax_platform_node_auralinux_hypertext_get_link;
+  iface->get_n_links = ax_platform_node_auralinux_get_n_links;
+  iface->get_link_index = ax_platform_node_auralinux_get_link_index;
+}
+
+static const GInterfaceInfo HypertextInfo = {
+    reinterpret_cast<GInterfaceInitFunc>(ax_hypertext_interface_base_init),
+    nullptr, nullptr};
+
+//
 // AtkText interface.
 //
 
@@ -932,6 +985,9 @@
   // as well.
   interface_mask |= 1 << ATK_TEXT_INTERFACE;
 
+  if (!IsPlainTextField() && !IsChildOfLeaf())
+    interface_mask |= 1 << ATK_HYPERTEXT_INTERFACE;
+
   // Value Interface
   AtkRole role = GetAtkRole();
   if (IsRoleWithValueInterface(role)) {
@@ -987,6 +1043,8 @@
   if (interface_mask_ & (1 << ATK_HYPERLINK_INTERFACE))
     g_type_add_interface_static(type, ATK_TYPE_HYPERLINK_IMPL,
                                 &HyperlinkImplInfo);
+  if (interface_mask_ & (1 << ATK_HYPERTEXT_INTERFACE))
+    g_type_add_interface_static(type, ATK_TYPE_HYPERTEXT, &HypertextInfo);
   if (interface_mask_ & (1 << ATK_TEXT_INTERFACE))
     g_type_add_interface_static(type, ATK_TYPE_TEXT, &TextInfo);
 
@@ -1033,6 +1091,22 @@
   return AtkObjectToAXPlatformNodeAuraLinux(accessible);
 }
 
+using UniqueIdMap = base::hash_map<int32_t, AXPlatformNodeAuraLinux*>;
+// Map from each AXPlatformNode's unique id to its instance.
+base::LazyInstance<UniqueIdMap>::Leaky g_unique_id_map =
+    LAZY_INSTANCE_INITIALIZER;
+
+// static
+AXPlatformNodeAuraLinux* AXPlatformNodeAuraLinux::GetFromUniqueId(
+    int32_t unique_id) {
+  UniqueIdMap* unique_ids = g_unique_id_map.Pointer();
+  auto iter = unique_ids->find(unique_id);
+  if (iter != unique_ids->end())
+    return iter->second;
+
+  return nullptr;
+}
+
 //
 // AXPlatformNodeAuraLinux implementation.
 //
@@ -1533,6 +1607,8 @@
 }
 
 void AXPlatformNodeAuraLinux::Destroy() {
+  g_unique_id_map.Get().erase(GetUniqueId());
+
   DestroyAtkObjects();
   AXPlatformNodeBase::Destroy();
 }
@@ -1540,6 +1616,7 @@
 void AXPlatformNodeAuraLinux::Init(AXPlatformNodeDelegate* delegate) {
   // Initialize ATK.
   AXPlatformNodeBase::Init(delegate);
+  g_unique_id_map.Get()[GetUniqueId()] = this;
   DataChanged();
 }
 
@@ -1724,6 +1801,10 @@
   hypertext_ = ComputeHypertext();
 }
 
+const AXHypertext& AXPlatformNodeAuraLinux::GetHypertext() {
+  return hypertext_;
+}
+
 int AXPlatformNodeAuraLinux::GetIndexInParent() {
   if (!GetParent())
     return -1;
@@ -1876,9 +1957,6 @@
 //
 
 AtkHyperlink* AXPlatformNodeAuraLinux::GetAtkHyperlink() {
-  DCHECK(ATK_HYPERLINK_IMPL(atk_object_));
-  g_return_val_if_fail(ATK_HYPERLINK_IMPL(atk_object_), 0);
-
   if (!atk_hyperlink_) {
     atk_hyperlink_ =
         ATK_HYPERLINK(g_object_new(AX_PLATFORM_ATK_HYPERLINK_TYPE, 0));
diff --git a/ui/accessibility/platform/ax_platform_node_auralinux.h b/ui/accessibility/platform/ax_platform_node_auralinux.h
index 1843f82..48fd256d6 100644
--- a/ui/accessibility/platform/ax_platform_node_auralinux.h
+++ b/ui/accessibility/platform/ax_platform_node_auralinux.h
@@ -65,6 +65,8 @@
       gint* x, gint* y, gint* width, gint* height,
       AtkCoordType coord_type);
 
+  static AXPlatformNodeAuraLinux* GetFromUniqueId(int32_t unique_id);
+
   // AtkDocument helpers
   const gchar* GetDocumentAttributeValue(const gchar* attribute) const;
   AtkAttributeSet* GetDocumentAttributes() const;
@@ -95,6 +97,7 @@
   std::string GetTextForATK();
 
   void UpdateHypertext();
+  const AXHypertext& GetHypertext();
 
  protected:
   AXHypertext hypertext_;
diff --git a/ui/compositor/compositor.cc b/ui/compositor/compositor.cc
index 2813b2a..8ae7efef 100644
--- a/ui/compositor/compositor.cc
+++ b/ui/compositor/compositor.cc
@@ -445,16 +445,6 @@
          input_handler->GetScrollOffsetForLayer(element_id, offset);
 }
 
-void Compositor::SetAuthoritativeVSyncInterval(
-    const base::TimeDelta& interval) {
-  DCHECK_GT(interval.InMillisecondsF(), 0);
-  refresh_rate_ =
-      base::Time::kMillisecondsPerSecond / interval.InMillisecondsF();
-  if (context_factory_private_)
-    context_factory_private_->SetAuthoritativeVSyncInterval(this, interval);
-  vsync_manager_->SetAuthoritativeVSyncInterval(interval);
-}
-
 void Compositor::SetDisplayVSyncParameters(base::TimeTicks timebase,
                                            base::TimeDelta interval) {
   static bool is_frame_rate_limit_disabled =
diff --git a/ui/compositor/compositor.h b/ui/compositor/compositor.h
index 251a1930..bcbdfc13a 100644
--- a/ui/compositor/compositor.h
+++ b/ui/compositor/compositor.h
@@ -151,8 +151,6 @@
       const gfx::ColorSpace& blending_color_space,
       const gfx::ColorSpace& output_color_space) = 0;
 
-  virtual void SetAuthoritativeVSyncInterval(ui::Compositor* compositor,
-                                             base::TimeDelta interval) = 0;
   // Mac path for transporting vsync parameters to the display.  Other platforms
   // update it via the BrowserCompositorLayerTreeFrameSink directly.
   virtual void SetDisplayVSyncParameters(ui::Compositor* compositor,
@@ -309,14 +307,6 @@
                                gfx::ScrollOffset* offset) const;
   bool ScrollLayerTo(cc::ElementId element_id, const gfx::ScrollOffset& offset);
 
-  // The "authoritative" vsync interval, if provided, will override interval
-  // reported from 3D context. This is typically the value reported by a more
-  // reliable source, e.g, the platform display configuration.
-  // In the particular case of ChromeOS -- this is the value queried through
-  // XRandR, which is more reliable than the value queried through the 3D
-  // context.
-  void SetAuthoritativeVSyncInterval(const base::TimeDelta& interval);
-
   // Most platforms set their vsync info via
   // BrowerCompositorLayerTreeFrameSink::OnUpdateVSyncParametersFromGpu(), but
   // Mac routes vsync info via the browser compositor instead through this path.
diff --git a/ui/compositor/compositor_vsync_manager.cc b/ui/compositor/compositor_vsync_manager.cc
index 2bc25fc..6c53788 100644
--- a/ui/compositor/compositor_vsync_manager.cc
+++ b/ui/compositor/compositor_vsync_manager.cc
@@ -6,23 +6,12 @@
 
 namespace ui {
 
-CompositorVSyncManager::CompositorVSyncManager()
-    : authoritative_vsync_interval_(base::TimeDelta::FromSeconds(0)) {
-}
+CompositorVSyncManager::CompositorVSyncManager() = default;
 
 CompositorVSyncManager::~CompositorVSyncManager() {}
 
-void CompositorVSyncManager::SetAuthoritativeVSyncInterval(
-    base::TimeDelta interval) {
-  authoritative_vsync_interval_ = interval;
-  last_interval_ = interval;
-  NotifyObservers(last_timebase_, last_interval_);
-}
-
 void CompositorVSyncManager::UpdateVSyncParameters(base::TimeTicks timebase,
                                                    base::TimeDelta interval) {
-  if (authoritative_vsync_interval_ != base::TimeDelta::FromSeconds(0))
-    interval = authoritative_vsync_interval_;
   last_timebase_ = timebase;
   last_interval_ = interval;
   NotifyObservers(timebase, interval);
diff --git a/ui/compositor/compositor_vsync_manager.h b/ui/compositor/compositor_vsync_manager.h
index 2876907..3c4989b 100644
--- a/ui/compositor/compositor_vsync_manager.h
+++ b/ui/compositor/compositor_vsync_manager.h
@@ -29,17 +29,8 @@
 
   CompositorVSyncManager();
 
-  // The "authoritative" vsync interval, if provided, will override |interval|
-  // as reported by UpdateVSyncParameters() whenever it is called.  This is
-  // typically the value reported by a more reliable source, e.g. the platform
-  // display configuration.  In the particular case of ChromeOS -- this is the
-  // value queried through XRandR, which is more reliable than the value
-  // queried through the 3D context.
-  void SetAuthoritativeVSyncInterval(base::TimeDelta interval);
-
   // The vsync parameters consist of |timebase|, which is the platform timestamp
   // of the last vsync, and |interval|, which is the interval between vsyncs.
-  // |interval| may be overriden by SetAuthoritativeVSyncInterval() above.
   void UpdateVSyncParameters(base::TimeTicks timebase,
                              base::TimeDelta interval);
 
@@ -57,7 +48,6 @@
 
   base::TimeTicks last_timebase_;
   base::TimeDelta last_interval_;
-  base::TimeDelta authoritative_vsync_interval_;
 
   DISALLOW_COPY_AND_ASSIGN(CompositorVSyncManager);
 };
diff --git a/ui/compositor/host/host_context_factory_private.cc b/ui/compositor/host/host_context_factory_private.cc
index b6010d7b..629153a 100644
--- a/ui/compositor/host/host_context_factory_private.cc
+++ b/ui/compositor/host/host_context_factory_private.cc
@@ -4,11 +4,13 @@
 
 #include "ui/compositor/host/host_context_factory_private.h"
 
+#include "base/command_line.h"
 #include "base/trace_event/trace_event.h"
 #include "build/build_config.h"
 #include "cc/mojo_embedder/async_layer_tree_frame_sink.h"
 #include "components/viz/client/hit_test_data_provider_draw_quad.h"
 #include "components/viz/client/local_surface_id_provider.h"
+#include "components/viz/common/switches.h"
 #include "components/viz/host/host_display_client.h"
 #include "components/viz/host/host_frame_sink_manager.h"
 #include "components/viz/host/renderer_settings_creation.h"
@@ -94,6 +96,10 @@
   root_params->gpu_compositing = gpu_compositing;
   root_params->renderer_settings = renderer_settings_;
 
+  base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
+  if (command_line->HasSwitch(switches::kDisableFrameRateLimit))
+    root_params->disable_frame_rate_limit = true;
+
   // Connects the viz process end of CompositorFrameSink message pipes. The
   // browser compositor may request a new CompositorFrameSink on context loss,
   // which will destroy the existing CompositorFrameSink.
@@ -205,15 +211,6 @@
                                                      output_color_space);
 }
 
-void HostContextFactoryPrivate::SetAuthoritativeVSyncInterval(
-    Compositor* compositor,
-    base::TimeDelta interval) {
-  auto iter = compositor_data_map_.find(compositor);
-  if (iter == compositor_data_map_.end() || !iter->second.display_private)
-    return;
-  iter->second.display_private->SetAuthoritativeVSyncInterval(interval);
-}
-
 void HostContextFactoryPrivate::SetDisplayVSyncParameters(
     Compositor* compositor,
     base::TimeTicks timebase,
diff --git a/ui/compositor/host/host_context_factory_private.h b/ui/compositor/host/host_context_factory_private.h
index 76319de..55028794 100644
--- a/ui/compositor/host/host_context_factory_private.h
+++ b/ui/compositor/host/host_context_factory_private.h
@@ -59,8 +59,6 @@
   void SetDisplayColorSpace(Compositor* compositor,
                             const gfx::ColorSpace& blending_color_space,
                             const gfx::ColorSpace& output_color_space) override;
-  void SetAuthoritativeVSyncInterval(Compositor* compositor,
-                                     base::TimeDelta interval) override;
   void SetDisplayVSyncParameters(Compositor* compositor,
                                  base::TimeTicks timebase,
                                  base::TimeDelta interval) override;
diff --git a/ui/compositor/test/in_process_context_factory.h b/ui/compositor/test/in_process_context_factory.h
index 9a01485..a43f7c2 100644
--- a/ui/compositor/test/in_process_context_factory.h
+++ b/ui/compositor/test/in_process_context_factory.h
@@ -80,8 +80,6 @@
       ui::Compositor* compositor,
       const gfx::ColorSpace& blending_color_space,
       const gfx::ColorSpace& output_color_space) override {}
-  void SetAuthoritativeVSyncInterval(ui::Compositor* compositor,
-                                     base::TimeDelta interval) override {}
   void SetDisplayVSyncParameters(ui::Compositor* compositor,
                                  base::TimeTicks timebase,
                                  base::TimeDelta interval) override {}
diff --git a/ui/file_manager/externs/volume_info_list.js b/ui/file_manager/externs/volume_info_list.js
index b33959d..5852a49c 100644
--- a/ui/file_manager/externs/volume_info_list.js
+++ b/ui/file_manager/externs/volume_info_list.js
@@ -39,38 +39,6 @@
 VolumeInfoList.prototype.remove = function(volumeId) {};
 
 /**
- * Obtains an index from the volume ID.
- * @param {string} volumeId Volume ID.
- * @return {number} Index of the volume.
- */
-VolumeInfoList.prototype.findIndex = function(volumeId) {};
-
-/**
- * Searches the information of the volume that exists on the given device path.
- * @param {string} devicePath Path of the device to search.
- * @return {VolumeInfo} The volume's information, or null if not found.
- */
-VolumeInfoList.prototype.findByDevicePath = function(devicePath) {};
-
-/**
- * Returns a VolumInfo for the volume ID, or null if not found.
- *
- * @param {string} volumeId
- * @return {VolumeInfo} The volume's information, or null if not found.
- */
-VolumeInfoList.prototype.findByVolumeId = function(volumeId) {};
-
-/**
- * Returns a promise that will be resolved when volume info, identified
- * by {@code volumeId} is created.
- *
- * @param {string} volumeId
- * @return {!Promise<!VolumeInfo>} The VolumeInfo. Will not resolve
- *     if the volume is never mounted.
- */
-VolumeInfoList.prototype.whenVolumeInfoReady = function(volumeId) {};
-
-/**
  * @param {number} index The index of the volume in the list.
  * @return {!VolumeInfo} The VolumeInfo instance.
  */
diff --git a/ui/file_manager/externs/volume_manager.js b/ui/file_manager/externs/volume_manager.js
index 7570d04..ad77ac24 100644
--- a/ui/file_manager/externs/volume_manager.js
+++ b/ui/file_manager/externs/volume_manager.js
@@ -73,12 +73,6 @@
 VolumeManager.prototype.getLocationInfo = function(entry) {};
 
 /**
- * Returns current state of VolumeManager.
- * @return {string} Current state of VolumeManager.
- */
-VolumeManager.prototype.toString = function() {};
-
-/**
  * Adds an event listener to the target.
  * @param {string} type The name of the event.
  * @param {EventListenerType} handler The handler for the event. This is
@@ -102,6 +96,22 @@
  */
 VolumeManager.prototype.dispatchEvent = function(event) {};
 
+/**
+ * Searches the information of the volume that exists on the given device path.
+ * @param {string} devicePath Path of the device to search.
+ * @return {VolumeInfo} The volume's information, or null if not found.
+ */
+VolumeManager.prototype.findByDevicePath = function(devicePath) {};
+
+/**
+ * Returns a promise that will be resolved when volume info, identified
+ * by {@code volumeId} is created.
+ *
+ * @param {string} volumeId
+ * @return {!Promise<!VolumeInfo>} The VolumeInfo. Will not resolve
+ *     if the volume is never mounted.
+ */
+VolumeManager.prototype.whenVolumeInfoReady = function(volumeId) {};
 
 /**
  * Event object which is dispached with 'externally-unmounted' event.
diff --git a/ui/file_manager/file_manager/background/js/BUILD.gn b/ui/file_manager/file_manager/background/js/BUILD.gn
index c6e3cd7e..d560e75 100644
--- a/ui/file_manager/file_manager/background/js/BUILD.gn
+++ b/ui/file_manager/file_manager/background/js/BUILD.gn
@@ -232,6 +232,7 @@
     ":volume_info_impl",
     ":volume_info_list_impl",
     ":volume_manager_factory",
+    ":volume_manager_impl",
     "../../common/js:mock_entry",
   ]
 }
diff --git a/ui/file_manager/file_manager/background/js/background.js b/ui/file_manager/file_manager/background/js/background.js
index 883ff0a..6b48c28 100644
--- a/ui/file_manager/file_manager/background/js/background.js
+++ b/ui/file_manager/file_manager/background/js/background.js
@@ -167,8 +167,7 @@
            */
           function(volumeManager) {
             if (event.devicePath) {
-              var volume = volumeManager.volumeInfoList.findByDevicePath(
-                  event.devicePath);
+              let volume = volumeManager.findByDevicePath(event.devicePath);
               if (volume) {
                 this.navigateToVolumeRoot_(volume, event.filePath);
               } else {
@@ -198,14 +197,13 @@
       (/**
         * @param {!VolumeManager} volumeManager
         */
-       function(volumeManager) {
-         return volumeManager.volumeInfoList.whenVolumeInfoReady(volumeId)
-             .catch(function(e) {
-               console.error(
-                   'Unable to find volume for id: ' + volumeId +
-                   '. Error: ' + e.message);
-             });
-       }).bind(this));
+       (volumeManager) => {
+         return volumeManager.whenVolumeInfoReady(volumeId).catch((e) => {
+           console.error(
+               'Unable to find volume for id: ' + volumeId +
+               '. Error: ' + e.message);
+         });
+       }));
 };
 
 /**
diff --git a/ui/file_manager/file_manager/background/js/device_handler.js b/ui/file_manager/file_manager/background/js/device_handler.js
index 2f62504..dcbc4bb 100644
--- a/ui/file_manager/file_manager/background/js/device_handler.js
+++ b/ui/file_manager/file_manager/background/js/device_handler.js
@@ -482,12 +482,11 @@
            * @param {!VolumeManager} volumeManager
            * @return {!Promise<!VolumeInfo>}
            */
-          function(volumeManager) {
+          (volumeManager) => {
             if (!metadata.volumeId) {
               return Promise.reject('No volume id associated with event.');
             }
-            return volumeManager.volumeInfoList.whenVolumeInfoReady(
-                metadata.volumeId);
+            return volumeManager.whenVolumeInfoReady(metadata.volumeId);
           })
       .then(
           /**
@@ -514,15 +513,13 @@
           function(root) {
             return importer.getMediaDirectory(root);
           })
-      .then(
-          (/**
-           * @param {!DirectoryEntry} directory
-           * @this {DeviceHandler}
-           */
-          function(directory) {
-            return importer.isPhotosAppImportEnabled()
-                .then(
-                    (/**
+      .then((/**
+              * @param {!DirectoryEntry} directory
+              * @this {DeviceHandler}
+              */
+             function(directory) {
+               return importer.isPhotosAppImportEnabled().then(
+                   (/**
                      * @param {boolean} appEnabled
                      * @this {DeviceHandler}
                      */
@@ -535,20 +532,18 @@
                             metadata.volumeId, null, directory.fullPath);
                       }
                     }).bind(this));
-          }).bind(this))
-      .catch(
-        function(error) {
-          if (metadata.deviceType && metadata.devicePath) {
-            if (metadata.isReadOnly &&
-                !metadata.isReadOnlyRemovableDevice) {
-              DeviceHandler.Notification.DEVICE_NAVIGATION_READONLY_POLICY.show(
-                  /** @type {string} */ (metadata.devicePath));
-            } else {
-              DeviceHandler.Notification.DEVICE_NAVIGATION.show(
-                  /** @type {string} */ (metadata.devicePath));
-            }
+             }).bind(this))
+      .catch(function(error) {
+        if (metadata.deviceType && metadata.devicePath) {
+          if (metadata.isReadOnly && !metadata.isReadOnlyRemovableDevice) {
+            DeviceHandler.Notification.DEVICE_NAVIGATION_READONLY_POLICY.show(
+                /** @type {string} */ (metadata.devicePath));
+          } else {
+            DeviceHandler.Notification.DEVICE_NAVIGATION.show(
+                /** @type {string} */ (metadata.devicePath));
           }
-        });
+        }
+      });
 };
 
 DeviceHandler.prototype.onUnmount_ = function(event) {
diff --git a/ui/file_manager/file_manager/background/js/device_handler_unittest.html b/ui/file_manager/file_manager/background/js/device_handler_unittest.html
index 49a1c13..b3027af1 100644
--- a/ui/file_manager/file_manager/background/js/device_handler_unittest.html
+++ b/ui/file_manager/file_manager/background/js/device_handler_unittest.html
@@ -22,12 +22,12 @@
 <script src="../../common/js/files_app_entry_types.js"></script>
 <script src="../../common/js/util.js"></script>
 <script src="device_handler.js"></script>
-<script src="mock_volume_manager.js"></script>
 <script src="volume_info_impl.js"></script>
 <script src="volume_info_list_impl.js"></script>
 <script src="volume_manager_factory.js"></script>
 <script src="volume_manager_impl.js"></script>
 <script src="volume_manager_util.js"></script>
+<script src="mock_volume_manager.js"></script>
 
 <script src="device_handler_unittest.js"></script>
 
diff --git a/ui/file_manager/file_manager/background/js/duplicate_finder_unittest.html b/ui/file_manager/file_manager/background/js/duplicate_finder_unittest.html
index 04ee1fe..ca77aa9 100644
--- a/ui/file_manager/file_manager/background/js/duplicate_finder_unittest.html
+++ b/ui/file_manager/file_manager/background/js/duplicate_finder_unittest.html
@@ -26,7 +26,6 @@
   <script src="metadata_proxy.js"></script>
   <script src="entry_location_impl.js"></script>
   <script src="test_import_history.js"></script>
-  <script src="mock_volume_manager.js"></script>
   <script src="import_history.js"></script>
   <script src="duplicate_finder.js"></script>
   <script src="volume_info_impl.js"></script>
@@ -34,6 +33,7 @@
   <script src="volume_manager_factory.js"></script>
   <script src="volume_manager_impl.js"></script>
   <script src="volume_manager_util.js"></script>
+  <script src="mock_volume_manager.js"></script>
 
   <script src="duplicate_finder_unittest.js"></script>
 </body>
diff --git a/ui/file_manager/file_manager/background/js/entry_location_impl.js b/ui/file_manager/file_manager/background/js/entry_location_impl.js
index 7bfa55f..5dd53c12 100644
--- a/ui/file_manager/file_manager/background/js/entry_location_impl.js
+++ b/ui/file_manager/file_manager/background/js/entry_location_impl.js
@@ -43,8 +43,9 @@
   this.isReadOnly = isReadOnly;
 
   /** @type{boolean} */
-  this.hasFixedLabel =
-      this.isRootEntry && rootType !== VolumeManagerCommon.RootType.TEAM_DRIVE;
+  this.hasFixedLabel = this.isRootEntry &&
+      (rootType !== VolumeManagerCommon.RootType.TEAM_DRIVE &&
+       rootType !== VolumeManagerCommon.RootType.COMPUTER);
 
   Object.freeze(this);
 }
diff --git a/ui/file_manager/file_manager/background/js/media_import_handler_unittest.html b/ui/file_manager/file_manager/background/js/media_import_handler_unittest.html
index db6286d5..993c317 100644
--- a/ui/file_manager/file_manager/background/js/media_import_handler_unittest.html
+++ b/ui/file_manager/file_manager/background/js/media_import_handler_unittest.html
@@ -32,7 +32,6 @@
   <script src="mock_drive_sync_handler.js"></script>
   <script src="mock_progress_center.js"></script>
   <script src="mock_media_scanner.js"></script>
-  <script src="mock_volume_manager.js"></script>
   <script src="task_queue.js"></script>
   <script src="media_import_handler.js"></script>
   <script src="duplicate_finder.js"></script>
@@ -42,6 +41,7 @@
   <script src="volume_manager_factory.js"></script>
   <script src="volume_manager_impl.js"></script>
   <script src="volume_manager_util.js"></script>
+  <script src="mock_volume_manager.js"></script>
   <script src="file_operation_util.js"></script>
 
   <script src="media_import_handler_unittest.js"></script>
diff --git a/ui/file_manager/file_manager/background/js/mock_volume_manager.js b/ui/file_manager/file_manager/background/js/mock_volume_manager.js
index 1419f0c..2df2b24 100644
--- a/ui/file_manager/file_manager/background/js/mock_volume_manager.js
+++ b/ui/file_manager/file_manager/background/js/mock_volume_manager.js
@@ -56,23 +56,9 @@
   return volumeInfo;
 };
 
-/**
- * Returns the corresponding VolumeInfo.
- *
- * @param {!Entry|!FilesAppEntry} entry FileEntry pointing anywhere
- *     on a volume.
- * @return {VolumeInfo} Corresponding VolumeInfo.
- */
-MockVolumeManager.prototype.getVolumeInfo = function(entry) {
-  for (var i = 0; i < this.volumeInfoList.length; i++) {
-    var volumeInfo = this.volumeInfoList.item(i);
-    if (volumeInfo.fileSystem &&
-        util.isSameFileSystem(volumeInfo.fileSystem, entry.filesystem)) {
-      return volumeInfo;
-    }
-  }
-  return null;
-};
+/** @override */
+MockVolumeManager.prototype.getVolumeInfo =
+    VolumeManagerImpl.prototype.getVolumeInfo;
 
 /**
  * Obtains location information from an entry.
@@ -100,6 +86,14 @@
         rootType = VolumeManagerCommon.RootType.TEAM_DRIVE;
         isRootEntry = util.isTeamDriveRoot(entry);
       }
+    } else if (entry.fullPath.startsWith('/Computers')) {
+      if (entry.fullPath === '/Computers') {
+        rootType = VolumeManagerCommon.RootType.COMPUTERS_GRAND_ROOT;
+        isRootEntry = true;
+      } else {
+        rootType = VolumeManagerCommon.RootType.COMPUTER;
+        isRootEntry = util.isComputersRoot(entry);
+      }
     }
     return new EntryLocationImpl(volumeInfo, rootType, isRootEntry, true);
   }
@@ -111,6 +105,14 @@
   return new EntryLocationImpl(volumeInfo, rootType, isRootEntry, false);
 };
 
+/** @override */
+MockVolumeManager.prototype.findByDevicePath =
+    VolumeManagerImpl.prototype.findByDevicePath;
+
+/** @override */
+MockVolumeManager.prototype.whenVolumeInfoReady =
+    VolumeManagerImpl.prototype.whenVolumeInfoReady;
+
 /**
  * @param {VolumeManagerCommon.VolumeType} volumeType Volume type.
  * @return {VolumeInfo} Volume info.
diff --git a/ui/file_manager/file_manager/background/js/volume_info_list_impl.js b/ui/file_manager/file_manager/background/js/volume_info_list_impl.js
index bd92078..1b59ada 100644
--- a/ui/file_manager/file_manager/background/js/volume_info_list_impl.js
+++ b/ui/file_manager/file_manager/background/js/volume_info_list_impl.js
@@ -48,6 +48,15 @@
 };
 
 /** @override */
+VolumeInfoListImpl.prototype.item = function(index) {
+  return /** @type {!VolumeInfo} */ (this.model_.item(index));
+};
+
+/**
+ * Obtains an index from the volume ID.
+ * @param {string} volumeId Volume ID.
+ * @return {number} Index of the volume.
+ */
 VolumeInfoListImpl.prototype.findIndex = function(volumeId) {
   for (var i = 0; i < this.model_.length; i++) {
     if (this.model_.item(i).volumeId === volumeId)
@@ -55,48 +64,3 @@
   }
   return -1;
 };
-
-/** @override */
-VolumeInfoListImpl.prototype.findByDevicePath = function(devicePath) {
-  for (var i = 0; i < this.length; i++) {
-    var volumeInfo = this.item(i);
-    if (volumeInfo.devicePath &&
-        volumeInfo.devicePath == devicePath) {
-      return volumeInfo;
-    }
-  }
-  return null;
-};
-
-/**
- * Returns a VolumInfo for the volume ID, or null if not found.
- *
- * @param {string} volumeId
- * @return {VolumeInfo} The volume's information, or null if not found.
- */
-VolumeInfoListImpl.prototype.findByVolumeId = function(volumeId) {
-  var index = this.findIndex(volumeId);
-  return (index !== -1) ?
-      /** @type {VolumeInfo} */ (this.model_.item(index)) :
-      null;
-};
-
-/** @override */
-VolumeInfoListImpl.prototype.whenVolumeInfoReady = function(volumeId) {
-  return new Promise(function(fulfill) {
-    var handler = function() {
-      var info = this.findByVolumeId(volumeId);
-      if (info) {
-        fulfill(info);
-        this.model_.removeEventListener('splice', handler);
-      }
-    }.bind(this);
-    this.model_.addEventListener('splice', handler);
-    handler();
-  }.bind(this));
-};
-
-/** @override */
-VolumeInfoListImpl.prototype.item = function(index) {
-  return /** @type {!VolumeInfo} */ (this.model_.item(index));
-};
diff --git a/ui/file_manager/file_manager/background/js/volume_manager_impl.js b/ui/file_manager/file_manager/background/js/volume_manager_impl.js
index 49a073d9..eb79188 100644
--- a/ui/file_manager/file_manager/background/js/volume_manager_impl.js
+++ b/ui/file_manager/file_manager/background/js/volume_manager_impl.js
@@ -270,15 +270,15 @@
 
 /** @override */
 VolumeManagerImpl.prototype.getVolumeInfo = function(entry) {
-  for (var i = 0; i < this.volumeInfoList.length; i++) {
-    var volumeInfo = this.volumeInfoList.item(i);
+  for (let i = 0; i < this.volumeInfoList.length; i++) {
+    const volumeInfo = this.volumeInfoList.item(i);
     if (volumeInfo.fileSystem &&
         util.isSameFileSystem(volumeInfo.fileSystem, entry.filesystem)) {
       return volumeInfo;
     }
     // Additionally, check fake entries.
-    for (var key in volumeInfo.fakeEntries_) {
-      var fakeEntry = volumeInfo.fakeEntries_[key];
+    for (let key in volumeInfo.fakeEntries_) {
+      const fakeEntry = volumeInfo.fakeEntries_[key];
       if (util.isSameEntry(fakeEntry, entry))
         return volumeInfo;
     }
@@ -315,8 +315,8 @@
   var isReadOnly;
   var isRootEntry;
   if (volumeInfo.volumeType === VolumeManagerCommon.VolumeType.DRIVE) {
-    // For Drive, the roots are /root, /team_drives and /other, instead of /.
-    // Root URLs contain trailing slashes.
+    // For Drive, the roots are /root, /team_drives, /Computers and /other,
+    // instead of /. Root URLs contain trailing slashes.
     if (entry.fullPath == '/root' || entry.fullPath.indexOf('/root/') === 0) {
       rootType = VolumeManagerCommon.RootType.DRIVE;
       isReadOnly = volumeInfo.isReadOnly;
@@ -340,6 +340,25 @@
           isReadOnly = volumeInfo.isReadOnly;
         }
       }
+    } else if (
+        entry.fullPath == VolumeManagerCommon.COMPUTERS_DIRECTORY_PATH ||
+        entry.fullPath.indexOf(
+            VolumeManagerCommon.COMPUTERS_DIRECTORY_PATH + '/') === 0) {
+      if (entry.fullPath == VolumeManagerCommon.COMPUTERS_DIRECTORY_PATH) {
+        rootType = VolumeManagerCommon.RootType.COMPUTERS_GRAND_ROOT;
+        isReadOnly = true;
+        isRootEntry = true;
+      } else {
+        rootType = VolumeManagerCommon.RootType.COMPUTER;
+        if (util.isComputersRoot(entry)) {
+          isReadOnly = false;
+          isRootEntry = true;
+        } else {
+          // Regular files/directories under a Computer entry.
+          isRootEntry = false;
+          isReadOnly = volumeInfo.isReadOnly;
+        }
+      }
     } else if (entry.fullPath == '/other' ||
                entry.fullPath.indexOf('/other/') === 0) {
       rootType = VolumeManagerCommon.RootType.DRIVE_OTHER;
@@ -369,6 +388,31 @@
   return new EntryLocationImpl(volumeInfo, rootType, isRootEntry, isReadOnly);
 };
 
+/** @override */
+VolumeManagerImpl.prototype.findByDevicePath = function(devicePath) {
+  for (let i = 0; i < this.volumeInfoList.length; i++) {
+    const volumeInfo = this.volumeInfoList.item(i);
+    if (volumeInfo.devicePath && volumeInfo.devicePath === devicePath)
+      return volumeInfo;
+  }
+  return null;
+};
+
+/** @override */
+VolumeManagerImpl.prototype.whenVolumeInfoReady = function(volumeId) {
+  return new Promise((fulfill) => {
+    const handler = () => {
+      const index = this.volumeInfoList.findIndex(volumeId);
+      if (index !== -1) {
+        fulfill(this.volumeInfoList.item(index));
+        this.volumeInfoList.removeEventListener('splice', handler);
+      }
+    };
+    this.volumeInfoList.addEventListener('splice', handler);
+    handler();
+  });
+};
+
 /**
  * @param {string} key Key produced by |makeRequestKey_|.
  * @param {function(VolumeInfo)} successCallback To be called when the request
diff --git a/ui/file_manager/file_manager/background/js/volume_manager_unittest.js b/ui/file_manager/file_manager/background/js/volume_manager_unittest.js
index bd3eb0a..d9bba9d 100644
--- a/ui/file_manager/file_manager/background/js/volume_manager_unittest.js
+++ b/ui/file_manager/file_manager/background/js/volume_manager_unittest.js
@@ -310,36 +310,61 @@
             volumeManager.getLocationInfo(androidSubFolder);
         assertFalse(androidSubFolderLocationInfo.isReadOnly);
         assertFalse(androidSubFolderLocationInfo.isRootEntry);
+
+        const computersGrandRoot = new MockFileEntry(
+            new MockFileSystem('drive:drive-foobar%40chromium.org-hash'),
+            '/Computers');
+        const computersGrandRootLocationInfo =
+            volumeManager.getLocationInfo(computersGrandRoot);
+        assertEquals(
+            VolumeManagerCommon.RootType.COMPUTERS_GRAND_ROOT,
+            computersGrandRootLocationInfo.rootType);
+        assertTrue(computersGrandRootLocationInfo.hasFixedLabel);
+        assertTrue(computersGrandRootLocationInfo.isReadOnly);
+        assertTrue(computersGrandRootLocationInfo.isRootEntry);
+
+        const computer = new MockFileEntry(
+            new MockFileSystem('drive:drive-foobar%40chromium.org-hash'),
+            '/Computers/MyComputer');
+        const computerLocationInfo = volumeManager.getLocationInfo(computer);
+        assertEquals(
+            VolumeManagerCommon.RootType.COMPUTER,
+            computerLocationInfo.rootType);
+        assertFalse(computerLocationInfo.hasFixedLabel);
+        assertFalse(computerLocationInfo.isReadOnly);
+        assertTrue(computerLocationInfo.isRootEntry);
       }),
       callback);
 }
 
-function testVolumeInfoListWhenReady(callback) {
-  var list = new VolumeInfoListImpl();
-  var promiseBeforeAdd = list.whenVolumeInfoReady('volumeId');
-  var volumeInfo = new VolumeInfoImpl(
-      /* volumeType */ null,
-      'volumeId',
-      /* fileSystem */ null,
-      /* error */ null,
-      /* deviceType */ null,
-      /* devicePath */ null,
-      /* isReadOnly */ false,
-      /* isReadOnlyRemovableDevice */ false,
-      /* profile */ {},
-      /* label */ null,
-      /* extensionid */ null,
-      /* hasMedia */ false,
-      /* configurable */ false,
-      /* watchable */ true,
-      /* source */ VolumeManagerCommon.Source.FILE);
-  list.add(volumeInfo);
-  var promiseAfterAdd = list.whenVolumeInfoReady('volumeId');
-  reportPromise(Promise.all([promiseBeforeAdd, promiseAfterAdd]).then(
-      function(volumes) {
-        assertEquals(volumeInfo, volumes[0]);
-        assertEquals(volumeInfo, volumes[1]);
-      }), callback);
+function testWhenReady(callback) {
+  volumeManagerFactory.getInstance().then((volumeManager) => {
+    const promiseBeforeAdd = volumeManager.whenVolumeInfoReady('volumeId');
+    const volumeInfo = new VolumeInfoImpl(
+        /* volumeType */ null,
+        /* volumeId */ 'volumeId',
+        /* fileSystem */ null,
+        /* error */ null,
+        /* deviceType */ null,
+        /* devicePath */ null,
+        /* isReadOnly */ false,
+        /* isReadOnlyRemovableDevice */ false,
+        /* profile */ {},
+        /* label */ null,
+        /* extensionid */ null,
+        /* hasMedia */ false,
+        /* configurable */ false,
+        /* watchable */ true,
+        /* source */ VolumeManagerCommon.Source.FILE);
+    volumeManager.volumeInfoList.add(volumeInfo);
+    const promiseAfterAdd = volumeManager.whenVolumeInfoReady('volumeId');
+    reportPromise(
+        Promise.all([promiseBeforeAdd, promiseAfterAdd]).then((volumes) => {
+          assertEquals(volumeInfo, volumes[0]);
+          assertEquals(volumeInfo, volumes[1]);
+        }),
+        callback);
+  });
 }
 
 function testDriveMountedDuringInitialization(callback) {
diff --git a/ui/file_manager/file_manager/common/js/importer_common_unittest.html b/ui/file_manager/file_manager/common/js/importer_common_unittest.html
index 601a062..a148632a 100644
--- a/ui/file_manager/file_manager/common/js/importer_common_unittest.html
+++ b/ui/file_manager/file_manager/common/js/importer_common_unittest.html
@@ -16,13 +16,13 @@
   <script src="../../background/js/entry_location_impl.js"></script>
   <script src="../../background/js/metadata_proxy.js"></script>
   <script src="../../background/js/mock_file_operation_manager.js"></script>
-  <script src="../../background/js/mock_volume_manager.js"></script>
   <script src="../../background/js/mock_media_scanner.js"></script>
   <script src="../../background/js/volume_info_impl.js"></script>
   <script src="../../background/js/volume_info_list_impl.js"></script>
   <script src="../../background/js/volume_manager_factory.js"></script>
   <script src="../../background/js/volume_manager_impl.js"></script>
   <script src="../../background/js/volume_manager_util.js"></script>
+  <script src="../../background/js/mock_volume_manager.js"></script>
   <script src="unittest_util.js"></script>
   <script src="test_importer_common.js"></script>
   <script src="mock_entry.js"></script>
diff --git a/ui/file_manager/file_manager/common/js/util.js b/ui/file_manager/file_manager/common/js/util.js
index 2f8fb65..94e9d41 100644
--- a/ui/file_manager/file_manager/common/js/util.js
+++ b/ui/file_manager/file_manager/common/js/util.js
@@ -728,6 +728,33 @@
 };
 
 /**
+ * Obtains whether an entry is the root directory of a Computer.
+ * @param {Entry|FilesAppEntry} entry Entry or a fake entry.
+ * @return {boolean} True if the given entry is root of a Computer.
+ */
+util.isComputersRoot = function(entry) {
+  if (entry === null)
+    return false;
+  if (!entry.fullPath)
+    return false;
+  var tree = entry.fullPath.split('/');
+  return tree.length == 3 && util.isComputersEntry(entry);
+};
+
+/**
+ * Obtains whether an entry is descendant of the My Computers directory.
+ * @param {!Entry|!FilesAppEntry} entry Entry or a fake entry.
+ * @return {boolean} True if the given entry is under My Computers.
+ */
+util.isComputersEntry = function(entry) {
+  if (!entry.fullPath)
+    return false;
+  var tree = entry.fullPath.split('/');
+  return tree[0] == '' &&
+      tree[1] == VolumeManagerCommon.COMPUTERS_DIRECTORY_NAME;
+};
+
+/**
  * Creates an instance of UserDOMError with given error name that looks like a
  * FileError except that it does not have the deprecated FileError.code member.
  *
diff --git a/ui/file_manager/file_manager/common/js/volume_manager_common.js b/ui/file_manager/file_manager/common/js/volume_manager_common.js
index 6e664c9..f7b92732 100644
--- a/ui/file_manager/file_manager/common/js/volume_manager_common.js
+++ b/ui/file_manager/file_manager/common/js/volume_manager_common.js
@@ -110,6 +110,12 @@
 
   // My Files root, which aggregates DOWNLOADS, ANDROID_FILES and CROSTINI.
   MY_FILES: 'my_files',
+
+  // The grand root entry of My Computers in Drive volume.
+  COMPUTERS_GRAND_ROOT: 'computers_grand_root',
+
+  // Root directory of a Computer.
+  COMPUTER: 'computer',
 };
 Object.freeze(VolumeManagerCommon.RootType);
 
@@ -142,6 +148,8 @@
   VolumeManagerCommon.RootType.CROSTINI,
   VolumeManagerCommon.RootType.ANDROID_FILES,
   VolumeManagerCommon.RootType.MY_FILES,
+  VolumeManagerCommon.RootType.COMPUTERS_GRAND_ROOT,
+  VolumeManagerCommon.RootType.COMPUTER,
 ];
 console.assert(
     Object.keys(VolumeManagerCommon.RootType).length ===
@@ -275,6 +283,8 @@
     case VolumeManagerCommon.RootType.DRIVE_OFFLINE:
     case VolumeManagerCommon.RootType.DRIVE_SHARED_WITH_ME:
     case VolumeManagerCommon.RootType.DRIVE_RECENT:
+    case VolumeManagerCommon.RootType.COMPUTERS_GRAND_ROOT:
+    case VolumeManagerCommon.RootType.COMPUTER:
       return VolumeManagerCommon.VolumeType.DRIVE;
     case VolumeManagerCommon.RootType.MTP:
       return VolumeManagerCommon.VolumeType.MTP;
@@ -367,6 +377,15 @@
     '/' + VolumeManagerCommon.TEAM_DRIVES_DIRECTORY_NAME;
 
 /**
+ * This is the top level directory name for Computers in drive that are using
+ * the backup and sync feature.
+ * @const {string}
+ */
+VolumeManagerCommon.COMPUTERS_DIRECTORY_NAME = 'Computers';
+VolumeManagerCommon.COMPUTERS_DIRECTORY_PATH =
+    '/' + VolumeManagerCommon.COMPUTERS_DIRECTORY_NAME;
+
+/**
  * @const
  */
 VolumeManagerCommon.ARCHIVE_OPENED_EVENT_TYPE = 'archive_opened';
diff --git a/ui/file_manager/file_manager/foreground/js/import_controller_unittest.html b/ui/file_manager/file_manager/foreground/js/import_controller_unittest.html
index 27cc1c9..048b501 100644
--- a/ui/file_manager/file_manager/foreground/js/import_controller_unittest.html
+++ b/ui/file_manager/file_manager/foreground/js/import_controller_unittest.html
@@ -29,12 +29,12 @@
   <script src="../../background/js/entry_location_impl.js"></script>
   <script src="../../background/js/mock_file_operation_manager.js"></script>
   <script src="../../background/js/mock_media_scanner.js"></script>
-  <script src="../../background/js/mock_volume_manager.js"></script>
   <script src="../../background/js/volume_info_impl.js"></script>
   <script src="../../background/js/volume_info_list_impl.js"></script>
   <script src="../../background/js/volume_manager_factory.js"></script>
   <script src="../../background/js/volume_manager_impl.js"></script>
   <script src="../../background/js/volume_manager_util.js"></script>
+  <script src="../../background/js/mock_volume_manager.js"></script>
 
   <script src="import_controller.js"></script>
   <script src="import_controller_unittest.js"></script>
diff --git a/ui/file_manager/file_manager/foreground/js/navigation_list_model_unittest.html b/ui/file_manager/file_manager/foreground/js/navigation_list_model_unittest.html
index 9210e75..abcf28d 100644
--- a/ui/file_manager/file_manager/foreground/js/navigation_list_model_unittest.html
+++ b/ui/file_manager/file_manager/foreground/js/navigation_list_model_unittest.html
@@ -15,12 +15,12 @@
 
 <script src="../../common/js/files_app_entry_types.js"></script>
 <script src="../../common/js/volume_manager_common.js"></script>
-<script src="../../background/js/mock_volume_manager.js"></script>
 <script src="../../background/js/volume_info_impl.js"></script>
 <script src="../../background/js/volume_info_list_impl.js"></script>
 <script src="../../background/js/volume_manager_factory.js"></script>
 <script src="../../background/js/volume_manager_impl.js"></script>
 <script src="../../background/js/volume_manager_util.js"></script>
+<script src="../../background/js/mock_volume_manager.js"></script>
 <script src="../../common/js/async_util.js"></script>
 <script src="../../common/js/mock_entry.js"></script>
 <script src="../../common/js/unittest_util.js"></script>
diff --git a/ui/file_manager/file_manager/foreground/js/providers_model_unittest.html b/ui/file_manager/file_manager/foreground/js/providers_model_unittest.html
index b2c1115..31c71e723 100644
--- a/ui/file_manager/file_manager/foreground/js/providers_model_unittest.html
+++ b/ui/file_manager/file_manager/foreground/js/providers_model_unittest.html
@@ -19,12 +19,12 @@
 <script src="../../common/js/util.js"></script>
 <script src="../../common/js/volume_manager_common.js"></script>
 
-<script src="../../background/js/mock_volume_manager.js"></script>
 <script src="../../background/js/volume_info_impl.js"></script>
 <script src="../../background/js/volume_info_list_impl.js"></script>
 <script src="../../background/js/volume_manager_factory.js"></script>
 <script src="../../background/js/volume_manager_impl.js"></script>
 <script src="../../background/js/volume_manager_util.js"></script>
+<script src="../../background/js/mock_volume_manager.js"></script>
 
 <script src="providers_model.js"></script>
 <script src="providers_model_unittest.js"></script>
diff --git a/ui/file_manager/file_manager/foreground/js/scan_controller.js b/ui/file_manager/file_manager/foreground/js/scan_controller.js
index 9289e4d5..63cb5c8 100644
--- a/ui/file_manager/file_manager/foreground/js/scan_controller.js
+++ b/ui/file_manager/file_manager/foreground/js/scan_controller.js
@@ -120,9 +120,6 @@
     return;
   }
 
-  if (this.commandHandler_)
-    this.commandHandler_.updateAvailability();
-
   this.hideSpinner_();
 
   if (this.scanUpdatedTimer_) {
@@ -132,6 +129,9 @@
 
   this.scanInProgress_ = false;
   this.listContainer_.endBatchUpdates();
+
+  if (this.commandHandler_)
+    this.commandHandler_.updateAvailability();
 };
 
 /**
@@ -169,9 +169,6 @@
     return;
   }
 
-  if (this.commandHandler_)
-    this.commandHandler_.updateAvailability();
-
   this.hideSpinner_();
 
   if (this.scanUpdatedTimer_) {
@@ -181,6 +178,9 @@
 
   this.scanInProgress_ = false;
   this.listContainer_.endBatchUpdates();
+
+  if (this.commandHandler_)
+    this.commandHandler_.updateAvailability();
 };
 
 /**
diff --git a/ui/file_manager/file_manager/foreground/js/ui/directory_tree_unittest.html b/ui/file_manager/file_manager/foreground/js/ui/directory_tree_unittest.html
index 79118e09..0cff23b 100644
--- a/ui/file_manager/file_manager/foreground/js/ui/directory_tree_unittest.html
+++ b/ui/file_manager/file_manager/foreground/js/ui/directory_tree_unittest.html
@@ -21,12 +21,12 @@
   <script src="../../../common/js/volume_manager_common.js"></script>
 
   <script src="../../../background/js/entry_location_impl.js"></script>
-  <script src="../../../background/js/mock_volume_manager.js"></script>
   <script src="../../../background/js/volume_info_impl.js"></script>
   <script src="../../../background/js/volume_info_list_impl.js"></script>
   <script src="../../../background/js/volume_manager_factory.js"></script>
   <script src="../../../background/js/volume_manager_impl.js"></script>
   <script src="../../../background/js/volume_manager_util.js"></script>
+  <script src="../../../background/js/mock_volume_manager.js"></script>
   <script src="../mock_directory_model.js"></script>
   <script src="../mock_navigation_list_model.js"></script>
   <script src="../navigation_list_model.js"></script>
diff --git a/ui/file_manager/file_manager/foreground/js/volume_manager_wrapper.js b/ui/file_manager/file_manager/foreground/js/volume_manager_wrapper.js
index b638d010..5d096da 100644
--- a/ui/file_manager/file_manager/foreground/js/volume_manager_wrapper.js
+++ b/ui/file_manager/file_manager/foreground/js/volume_manager_wrapper.js
@@ -33,28 +33,11 @@
   }
   /** @override */
   add(volumeInfo) {
-    throw new Error('VolumeInfoListWrapper.add not implemented');
+    throw new Error('VolumeInfoListWrapper.add not allowed in foreground');
   }
   /** @override */
   remove(volumeInfo) {
-    throw new Error('VolumeInfoListWrapper.remove not implemented');
-  }
-  /** @override */
-  findIndex(volumeId) {
-    throw new Error('VolumeInfoListWrapper.findIndex not implemented');
-  }
-  /** @override */
-  findByDevicePath(devicePath) {
-    throw new Error('VolumeInfoListWrapper.findByDevicePath not implemented');
-  }
-  /** @override */
-  findByVolumeId(volumeId) {
-    throw new Error('VolumeInfoListWrapper.findByVolumeId not implemented');
-  }
-  /** @override */
-  whenVolumeInfoReady(volumeId) {
-    throw new Error(
-        'VolumeInfoListWrapper.whenVolumeInfoReady not implemented');
+    throw new Error('VolumeInfoListWrapper.remove not allowed in foreground');
   }
   /** @override */
   item(index) {
@@ -377,6 +360,34 @@
   return locationInfo;
 };
 
+/** @override */
+VolumeManagerWrapper.prototype.findByDevicePath = function(devicePath) {
+  for (var i = 0; i < this.volumeInfoList.length; i++) {
+    const volumeInfo = this.volumeInfoList.item(i);
+    if (volumeInfo.devicePath && volumeInfo.devicePath === devicePath)
+      return this.filterDisallowedVolume_(volumeInfo);
+  }
+  return null;
+};
+
+/**
+ * Returns a promise that will be resolved when volume info, identified
+ * by {@code volumeId} is created.
+ *
+ * @param {string} volumeId
+ * @return {!Promise<!VolumeInfo>} The VolumeInfo. Will not resolve
+ *     if the volume is never mounted.
+ */
+VolumeManagerWrapper.prototype.whenVolumeInfoReady = function(volumeId) {
+  return new Promise(resolve => {
+    this.volumeManager_.whenVolumeInfoReady(volumeId).then((volumeInfo) => {
+      volumeInfo = this.filterDisallowedVolume_(volumeInfo);
+      if (volumeInfo)
+        resolve(volumeInfo);
+    });
+  });
+};
+
 /**
  * Requests to mount the archive file.
  * @param {string} fileUrl The path to the archive file to be mounted.
diff --git a/ui/file_manager/file_manager/test/js/chrome_file_manager_private_test_impl.js b/ui/file_manager/file_manager/test/js/chrome_file_manager_private_test_impl.js
index 1210af70e..e056ad5 100644
--- a/ui/file_manager/file_manager/test/js/chrome_file_manager_private_test_impl.js
+++ b/ui/file_manager/file_manager/test/js/chrome_file_manager_private_test_impl.js
@@ -239,9 +239,15 @@
  */
 chrome.fileSystem = {
   requestFileSystem: (options, callback) => {
-    var volume =
-        mockVolumeManager.volumeInfoList.findByVolumeId(options.volumeId);
-    setTimeout(callback, 0, volume ? volume.fileSystem : null);
+    let fs = null;
+    for (let i = 0; i < mockVolumeManager.volumeInfoList.length; i++) {
+      const volume = mockVolumeManager.volumeInfoList.item(i);
+      if (volume.volumeId === options.volumeId) {
+        fs = volume.fileSystem;
+        break;
+      }
+    }
+    setTimeout(callback, 0, fs);
   },
 };
 
diff --git a/ui/file_manager/image_loader/BUILD.gn b/ui/file_manager/image_loader/BUILD.gn
index 26e6ed8..9eda8b2 100644
--- a/ui/file_manager/image_loader/BUILD.gn
+++ b/ui/file_manager/image_loader/BUILD.gn
@@ -115,6 +115,7 @@
     "../file_manager/common/js:metrics",
     "../file_manager/common/js:metrics_events",
   ]
+  externs_list = [ "../externs/platform.js" ]
 }
 
 js_library("scheduler") {
diff --git a/ui/file_manager/image_loader/request.js b/ui/file_manager/image_loader/request.js
index d54e2fa56..2d7b3a6 100644
--- a/ui/file_manager/image_loader/request.js
+++ b/ui/file_manager/image_loader/request.js
@@ -251,6 +251,26 @@
     this.contentType_ = dataUrlMatches[1];
     return;
   }
+  var drivefsUrlMatches = this.request_.url.match(/^drivefs:(.*)/);
+  if (drivefsUrlMatches) {
+    window.webkitResolveLocalFileSystemURL(
+        drivefsUrlMatches[1],
+        entry => {
+          chrome.fileManagerPrivate.getThumbnail(
+              entry, !!this.request_.crop, thumbnail => {
+                if (!thumbnail) {
+                  onFailure();
+                  return;
+                }
+                this.image_.src = thumbnail;
+                this.contentType_ = 'image/png';
+              });
+        },
+        error => {
+          onFailure();
+        });
+    return;
+  }
 
   var fileType = FileType.getTypeForName(this.request_.url);
 
@@ -551,10 +571,10 @@
 ImageRequest.prototype.onImageLoad_ = function() {
   // Perform processing if the url is not a data url, or if there are some
   // operations requested.
-  if (!this.request_.url.match(/^data/) ||
-      ImageLoaderUtil.shouldProcess(this.image_.width,
-                                    this.image_.height,
-                                    this.request_)) {
+  if (!(this.request_.url.match(/^data/) ||
+        this.request_.url.match(/^drivefs:/)) ||
+      ImageLoaderUtil.shouldProcess(
+          this.image_.width, this.image_.height, this.request_)) {
     ImageLoaderUtil.resizeAndCrop(this.image_, this.canvas_, this.request_);
     ImageLoaderUtil.convertColorSpace(
         this.canvas_, this.request_.colorSpace || ColorSpace.SRGB);
diff --git a/ui/gfx/color_utils.cc b/ui/gfx/color_utils.cc
index 62a98b0..c4ae1e2 100644
--- a/ui/gfx/color_utils.cc
+++ b/ui/gfx/color_utils.cc
@@ -307,22 +307,6 @@
                     color, alpha);
 }
 
-SkColor GetThemedAssetColor(SkColor theme_color) {
-  // Minimum theme light color contrast.
-  constexpr float kContrastLightItemThreshold = 3;
-
-  // The amount to darken a light theme color by for use as foreground color.
-  constexpr float kThemedForegroundBlackFraction = 0.64;
-
-  // This mimics |shouldUseLightForegroundOnBackground| from ColorUtils.java.
-  bool use_light_color = GetContrastRatio(SK_ColorWHITE, theme_color) >=
-                         kContrastLightItemThreshold;
-  if (use_light_color)
-    return SK_ColorWHITE;
-  return AlphaBlend(SK_ColorBLACK, theme_color,
-                    255 * kThemedForegroundBlackFraction);
-}
-
 SkColor GetReadableColor(SkColor foreground, SkColor background) {
   return PickContrastingColor(foreground, LightnessInvertColor(foreground),
                               background);
diff --git a/ui/gfx/color_utils.h b/ui/gfx/color_utils.h
index 75e2e83d..747ecbb 100644
--- a/ui/gfx/color_utils.h
+++ b/ui/gfx/color_utils.h
@@ -113,9 +113,6 @@
 // white or black that will be alpha-blended into |color|.
 GFX_EXPORT SkColor BlendTowardOppositeLuma(SkColor color, SkAlpha alpha);
 
-// This is a copy of |getThemedAssetColor()| in ColorUtils.java.
-GFX_EXPORT SkColor GetThemedAssetColor(SkColor theme_color);
-
 // Given a foreground and background color, try to return a foreground color
 // that is "readable" over the background color by luma-inverting the foreground
 // color and then using PickContrastingColor() to pick the one with greater
diff --git a/ui/ozone/demo/demo_window.cc b/ui/ozone/demo/demo_window.cc
index a3397d5..38deff0 100644
--- a/ui/ozone/demo/demo_window.cc
+++ b/ui/ozone/demo/demo_window.cc
@@ -66,16 +66,16 @@
 }
 
 void DemoWindow::Start() {
-  base::ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE,
-      base::BindOnce(&DemoWindow::StartOnGpu, weak_ptr_factory_.GetWeakPtr()));
+  StartRendererIfNecessary();
 }
 
 void DemoWindow::Quit() {
   window_manager_->Quit();
 }
 
-void DemoWindow::OnBoundsChanged(const gfx::Rect& new_bounds) {}
+void DemoWindow::OnBoundsChanged(const gfx::Rect& new_bounds) {
+  StartRendererIfNecessary();
+}
 
 void DemoWindow::OnDamageRect(const gfx::Rect& damaged_region) {}
 
@@ -105,7 +105,9 @@
 
 void DemoWindow::OnActivationChanged(bool active) {}
 
-void DemoWindow::StartOnGpu() {
+void DemoWindow::StartRendererIfNecessary() {
+  if (renderer_ || GetSize().IsEmpty())
+    return;
   renderer_ =
       renderer_factory_->CreateRenderer(GetAcceleratedWidget(), GetSize());
   if (!renderer_->Initialize())
diff --git a/ui/ozone/demo/demo_window.h b/ui/ozone/demo/demo_window.h
index 720ac4c..a0be6aea 100644
--- a/ui/ozone/demo/demo_window.h
+++ b/ui/ozone/demo/demo_window.h
@@ -47,7 +47,7 @@
  private:
   // Since we pretend to have a GPU process, we should also pretend to
   // initialize the GPU resources via a posted task.
-  void StartOnGpu();
+  void StartRendererIfNecessary();
 
   WindowManager* window_manager_;      // Not owned.
   RendererFactory* renderer_factory_;  // Not owned.
diff --git a/ui/views/BUILD.gn b/ui/views/BUILD.gn
index 8332e63..9289cf0 100644
--- a/ui/views/BUILD.gn
+++ b/ui/views/BUILD.gn
@@ -438,6 +438,8 @@
   # Internal sources. TODO(https://crbug.com/871123): Move more headers from
   # public into this list, along with the implementation file.
   sources += [
+    "cocoa/bridge_factory_host.cc",
+    "cocoa/bridge_factory_host.h",
     "cocoa/bridge_factory_impl.h",
     "cocoa/bridge_factory_impl.mm",
     "cocoa/bridged_content_view.h",
@@ -513,7 +515,6 @@
     "//ui/gfx/animation",
     "//ui/gfx/geometry",
     "//ui/views/resources",
-    "//ui/views_bridge_mac:mojo",
   ]
 
   if (use_x11) {
@@ -742,6 +743,7 @@
       "//ui/accelerated_widget_mac",
       "//ui/events:dom_keycode_converter",
     ]
+    public_deps += [ "//ui/views_bridge_mac:mojo" ]
     libs = [
       "AppKit.framework",
       "CoreGraphics.framework",
diff --git a/ui/views/bubble/bubble_dialog_delegate_view.cc b/ui/views/bubble/bubble_dialog_delegate_view.cc
index e89cf97..434ae1e 100644
--- a/ui/views/bubble/bubble_dialog_delegate_view.cc
+++ b/ui/views/bubble/bubble_dialog_delegate_view.cc
@@ -17,7 +17,6 @@
 #include "ui/views/layout/layout_manager.h"
 #include "ui/views/layout/layout_provider.h"
 #include "ui/views/view_properties.h"
-#include "ui/views/view_tracker.h"
 #include "ui/views/views_delegate.h"
 #include "ui/views/widget/widget.h"
 #include "ui/views/widget/widget_observer.h"
@@ -220,6 +219,18 @@
   return anchor_view_tracker_->view();
 }
 
+void BubbleDialogDelegateView::SetHighlightedButton(
+    Button* highlighted_button) {
+  bool visible = GetWidget() && GetWidget()->IsVisible();
+  // If the Widget is visible, ensure the old highlight (if any) is removed
+  // when the highlighted view changes.
+  if (visible)
+    UpdateHighlightedButton(false);
+  highlighted_button_tracker_.SetView(highlighted_button);
+  if (visible)
+    UpdateHighlightedButton(true);
+}
+
 void BubbleDialogDelegateView::SetArrow(BubbleBorder::Arrow arrow) {
   if (arrow_ == arrow)
     return;
@@ -339,8 +350,10 @@
   // change as well.
   if (!anchor_view || anchor_widget() != anchor_view->GetWidget()) {
     if (anchor_widget()) {
-      if (GetWidget() && GetWidget()->IsVisible())
+      if (GetWidget() && GetWidget()->IsVisible()) {
         UpdateAnchorWidgetRenderState(false);
+        UpdateHighlightedButton(false);
+      }
       anchor_widget_->RemoveObserver(this);
       anchor_widget_ = NULL;
     }
@@ -348,7 +361,9 @@
       anchor_widget_ = anchor_view->GetWidget();
       if (anchor_widget_) {
         anchor_widget_->AddObserver(this);
-        UpdateAnchorWidgetRenderState(GetWidget() && GetWidget()->IsVisible());
+        const bool visible = GetWidget() && GetWidget()->IsVisible();
+        UpdateAnchorWidgetRenderState(visible);
+        UpdateHighlightedButton(visible);
       }
     }
   }
@@ -409,8 +424,10 @@
 
 void BubbleDialogDelegateView::HandleVisibilityChanged(Widget* widget,
                                                        bool visible) {
-  if (widget == GetWidget())
+  if (widget == GetWidget()) {
     UpdateAnchorWidgetRenderState(visible);
+    UpdateHighlightedButton(visible);
+  }
 
   // Fire ax::mojom::Event::kAlert for bubbles marked as
   // ax::mojom::Role::kAlertDialog; this instructs accessibility tools to read
@@ -437,4 +454,11 @@
   anchor_widget()->GetTopLevelWidget()->SetAlwaysRenderAsActive(visible);
 }
 
+void BubbleDialogDelegateView::UpdateHighlightedButton(bool highlighted) {
+  Button* button = Button::AsButton(highlighted_button_tracker_.view());
+  button = button ? button : Button::AsButton(anchor_view_tracker_->view());
+  if (button)
+    button->SetHighlighted(highlighted);
+}
+
 }  // namespace views
diff --git a/ui/views/bubble/bubble_dialog_delegate_view.h b/ui/views/bubble/bubble_dialog_delegate_view.h
index f19adfa..6ce0158 100644
--- a/ui/views/bubble/bubble_dialog_delegate_view.h
+++ b/ui/views/bubble/bubble_dialog_delegate_view.h
@@ -12,6 +12,7 @@
 #include "build/build_config.h"
 #include "ui/accessibility/ax_enums.mojom.h"
 #include "ui/views/bubble/bubble_border.h"
+#include "ui/views/view_tracker.h"
 #include "ui/views/widget/widget.h"
 #include "ui/views/widget/widget_observer.h"
 #include "ui/views/window/dialog_delegate.h"
@@ -27,7 +28,7 @@
 namespace views {
 
 class BubbleFrameView;
-class ViewTracker;
+class Button;
 
 // BubbleDialogDelegateView is a special DialogDelegateView for bubbles.
 class VIEWS_EXPORT BubbleDialogDelegateView : public DialogDelegateView,
@@ -68,6 +69,8 @@
   View* GetAnchorView() const;
   Widget* anchor_widget() const { return anchor_widget_; }
 
+  void SetHighlightedButton(Button* highlighted_button);
+
   // The anchor rect is used in the absence of an assigned anchor view.
   const gfx::Rect& anchor_rect() const { return anchor_rect_; }
 
@@ -182,6 +185,10 @@
   // When a bubble is visible, the anchor widget should always render as active.
   void UpdateAnchorWidgetRenderState(bool visible);
 
+  // Update the button highlight, which may be the anchor view or an explicit
+  // view set in |highlighted_button_tracker_|.
+  void UpdateHighlightedButton(bool highlighted);
+
   // A flag controlling bubble closure on deactivation.
   bool close_on_deactivate_;
 
@@ -191,6 +198,11 @@
   std::unique_ptr<ViewTracker> anchor_view_tracker_;
   Widget* anchor_widget_;
 
+  // If provided, this button should be highlighted while the bubble is visible.
+  // If not provided, the anchor_view will attempt to be highlighted. A
+  // ViewTracker is used because the view can be deleted.
+  ViewTracker highlighted_button_tracker_;
+
   // The anchor rect used in the absence of an anchor view.
   mutable gfx::Rect anchor_rect_;
 
diff --git a/ui/views/bubble/bubble_dialog_delegate_view_unittest.cc b/ui/views/bubble/bubble_dialog_delegate_view_unittest.cc
index 453b0bd..cf205c2 100644
--- a/ui/views/bubble/bubble_dialog_delegate_view_unittest.cc
+++ b/ui/views/bubble/bubble_dialog_delegate_view_unittest.cc
@@ -11,7 +11,10 @@
 #include "base/strings/utf_string_conversions.h"
 #include "ui/base/hit_test.h"
 #include "ui/events/event_utils.h"
+#include "ui/views/animation/test/ink_drop_host_view_test_api.h"
+#include "ui/views/animation/test/test_ink_drop.h"
 #include "ui/views/bubble/bubble_frame_view.h"
+#include "ui/views/controls/button/button.h"
 #include "ui/views/controls/button/label_button.h"
 #include "ui/views/controls/styled_label.h"
 #include "ui/views/test/test_views.h"
@@ -22,6 +25,8 @@
 
 namespace views {
 
+using test::TestInkDrop;
+
 namespace {
 
 constexpr int kContentHeight = 200;
@@ -478,6 +483,62 @@
             bubble_widget->GetWindowBoundsInScreen().height());
 }
 
+// Ensure associated buttons are highlighted or unhighlighted when the bubble
+// widget is shown or hidden respectively.
+TEST_F(BubbleDialogDelegateViewTest, AttachedWidgetShowsInkDropWhenVisible) {
+  std::unique_ptr<Widget> anchor_widget(CreateTestWidget());
+  LabelButton* button = new LabelButton(nullptr, base::string16());
+  anchor_widget->GetContentsView()->AddChildView(button);
+  TestInkDrop* ink_drop = new TestInkDrop();
+  test::InkDropHostViewTestApi(button).SetInkDrop(base::WrapUnique(ink_drop));
+  TestBubbleDialogDelegateView* bubble_delegate =
+      new TestBubbleDialogDelegateView(nullptr);
+  bubble_delegate->set_parent_window(anchor_widget->GetNativeView());
+
+  Widget* bubble_widget =
+      BubbleDialogDelegateView::CreateBubble(bubble_delegate);
+  bubble_delegate->SetHighlightedButton(button);
+  bubble_widget->Show();
+  // Explicitly calling OnWidgetVisibilityChanging to test functionality for
+  // OS_WIN. Outside of the test environment this happens automatically by way
+  // of HWNDMessageHandler.
+  bubble_delegate->OnWidgetVisibilityChanging(bubble_widget, true);
+  EXPECT_EQ(InkDropState::ACTIVATED, ink_drop->GetTargetInkDropState());
+
+  bubble_widget->Close();
+  bubble_delegate->OnWidgetVisibilityChanging(bubble_widget, false);
+  EXPECT_EQ(InkDropState::DEACTIVATED, ink_drop->GetTargetInkDropState());
+}
+
+// Ensure associated buttons are highlighted or unhighlighted when the bubble
+// widget is shown or hidden respectively when highlighted button is set after
+// widget is shown.
+TEST_F(BubbleDialogDelegateViewTest, VisibleWidgetShowsInkDropOnAttaching) {
+  std::unique_ptr<Widget> anchor_widget(CreateTestWidget());
+  LabelButton* button = new LabelButton(nullptr, base::string16());
+  anchor_widget->GetContentsView()->AddChildView(button);
+  TestInkDrop* ink_drop = new TestInkDrop();
+  test::InkDropHostViewTestApi(button).SetInkDrop(base::WrapUnique(ink_drop));
+  TestBubbleDialogDelegateView* bubble_delegate =
+      new TestBubbleDialogDelegateView(nullptr);
+  bubble_delegate->set_parent_window(anchor_widget->GetNativeView());
+
+  Widget* bubble_widget =
+      BubbleDialogDelegateView::CreateBubble(bubble_delegate);
+  bubble_widget->Show();
+  // Explicitly calling OnWidgetVisibilityChanging to test functionality for
+  // OS_WIN. Outside of the test environment this happens automatically by way
+  // of HWNDMessageHandler.
+  bubble_delegate->OnWidgetVisibilityChanging(bubble_widget, true);
+  EXPECT_EQ(InkDropState::HIDDEN, ink_drop->GetTargetInkDropState());
+  bubble_delegate->SetHighlightedButton(button);
+  EXPECT_EQ(InkDropState::ACTIVATED, ink_drop->GetTargetInkDropState());
+
+  bubble_widget->Close();
+  bubble_delegate->OnWidgetVisibilityChanging(bubble_widget, false);
+  EXPECT_EQ(InkDropState::DEACTIVATED, ink_drop->GetTargetInkDropState());
+}
+
 TEST_F(BubbleDialogDelegateViewTest, VisibleAnchorChanges) {
   std::unique_ptr<Widget> anchor_widget(CreateTestWidget());
   TestBubbleDialogDelegateView* bubble_delegate =
diff --git a/ui/views/cocoa/bridge_factory_host.cc b/ui/views/cocoa/bridge_factory_host.cc
new file mode 100644
index 0000000..4fafac5
--- /dev/null
+++ b/ui/views/cocoa/bridge_factory_host.cc
@@ -0,0 +1,31 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/views/cocoa/bridge_factory_host.h"
+
+namespace views {
+
+BridgeFactoryHost::BridgeFactoryHost(
+    views_bridge_mac::mojom::BridgeFactoryRequest* request) {
+  *request = mojo::MakeRequest(&bridge_factory_ptr_);
+}
+
+BridgeFactoryHost::~BridgeFactoryHost() {
+  for (Observer& obs : observers_)
+    obs.OnBridgeFactoryHostDestroying(this);
+}
+
+views_bridge_mac::mojom::BridgeFactory* BridgeFactoryHost::GetFactory() {
+  return bridge_factory_ptr_.get();
+}
+
+void BridgeFactoryHost::AddObserver(Observer* observer) {
+  observers_.AddObserver(observer);
+}
+
+void BridgeFactoryHost::RemoveObserver(const Observer* observer) {
+  observers_.RemoveObserver(observer);
+}
+
+}  // namespace views
diff --git a/ui/views/cocoa/bridge_factory_host.h b/ui/views/cocoa/bridge_factory_host.h
new file mode 100644
index 0000000..c5c68fe
--- /dev/null
+++ b/ui/views/cocoa/bridge_factory_host.h
@@ -0,0 +1,38 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_VIEWS_COCOA_BRIDGE_FACTORY_HOST_H_
+#define UI_VIEWS_COCOA_BRIDGE_FACTORY_HOST_H_
+
+#include "base/observer_list.h"
+#include "base/observer_list_types.h"
+#include "ui/views/views_export.h"
+#include "ui/views_bridge_mac/mojo/bridge_factory.mojom.h"
+
+namespace views {
+
+class VIEWS_EXPORT BridgeFactoryHost {
+ public:
+  class Observer : public base::CheckedObserver {
+   public:
+    virtual void OnBridgeFactoryHostDestroying(BridgeFactoryHost* host) = 0;
+
+   protected:
+    ~Observer() override {}
+  };
+
+  BridgeFactoryHost(views_bridge_mac::mojom::BridgeFactoryRequest* request);
+  ~BridgeFactoryHost();
+  views_bridge_mac::mojom::BridgeFactory* GetFactory();
+  void AddObserver(Observer* observer);
+  void RemoveObserver(const Observer* observer);
+
+ private:
+  views_bridge_mac::mojom::BridgeFactoryPtr bridge_factory_ptr_;
+  base::ObserverList<Observer> observers_;
+};
+
+}  // namespace views
+
+#endif  // UI_VIEWS_COCOA_BRIDGE_FACTORY_HOST_H_
diff --git a/ui/views/cocoa/bridge_factory_impl.h b/ui/views/cocoa/bridge_factory_impl.h
index 92533578..1723c305 100644
--- a/ui/views/cocoa/bridge_factory_impl.h
+++ b/ui/views/cocoa/bridge_factory_impl.h
@@ -5,6 +5,7 @@
 #ifndef UI_VIEWS_COCOA_BRIDGE_FACTORY_IMPL_H_
 #define UI_VIEWS_COCOA_BRIDGE_FACTORY_IMPL_H_
 
+#include "mojo/public/cpp/bindings/binding.h"
 #include "ui/views/views_export.h"
 #include "ui/views_bridge_mac/mojo/bridge_factory.mojom.h"
 #include "ui/views_bridge_mac/mojo/bridged_native_widget.mojom.h"
@@ -19,10 +20,20 @@
 class VIEWS_EXPORT BridgeFactoryImpl : public mojom::BridgeFactory {
  public:
   static BridgeFactoryImpl* Get();
+  void BindRequest(mojom::BridgeFactoryRequest request);
+
+  // mojom::BridgeFactory:
   void CreateBridge(uint64_t bridge_id,
                     mojom::BridgedNativeWidgetRequest bridge_request,
                     mojom::BridgedNativeWidgetHostPtr host) override;
   void DestroyBridge(uint64_t bridge_id) override;
+
+ private:
+  friend class base::NoDestructor<BridgeFactoryImpl>;
+  BridgeFactoryImpl();
+  ~BridgeFactoryImpl() override;
+
+  mojo::Binding<mojom::BridgeFactory> binding_;
 };
 
 }  // namespace views_bridge_mac
diff --git a/ui/views/cocoa/bridge_factory_impl.mm b/ui/views/cocoa/bridge_factory_impl.mm
index 41611b1f5..47f17f6 100644
--- a/ui/views/cocoa/bridge_factory_impl.mm
+++ b/ui/views/cocoa/bridge_factory_impl.mm
@@ -58,6 +58,10 @@
   return factory.get();
 }
 
+void BridgeFactoryImpl::BindRequest(mojom::BridgeFactoryRequest request) {
+  binding_.Bind(std::move(request));
+}
+
 void BridgeFactoryImpl::CreateBridge(
     uint64_t bridge_id,
     mojom::BridgedNativeWidgetRequest bridge_request,
@@ -70,4 +74,8 @@
   GetBridgeMap().erase(bridge_id);
 }
 
+BridgeFactoryImpl::BridgeFactoryImpl() : binding_(this) {}
+
+BridgeFactoryImpl::~BridgeFactoryImpl() {}
+
 }  // namespace views_bridge_mac
diff --git a/ui/views/cocoa/bridged_native_widget_host_impl.h b/ui/views/cocoa/bridged_native_widget_host_impl.h
index e5f57a4..08910b3 100644
--- a/ui/views/cocoa/bridged_native_widget_host_impl.h
+++ b/ui/views/cocoa/bridged_native_widget_host_impl.h
@@ -14,12 +14,12 @@
 #include "ui/accelerated_widget_mac/display_link_mac.h"
 #include "ui/base/ime/input_method_delegate.h"
 #include "ui/compositor/layer_owner.h"
+#include "ui/views/cocoa/bridge_factory_host.h"
 #include "ui/views/cocoa/bridged_native_widget_host.h"
 #include "ui/views/focus/focus_manager.h"
 #include "ui/views/views_export.h"
 #include "ui/views/widget/widget.h"
 #include "ui/views/window/dialog_observer.h"
-#include "ui/views_bridge_mac/mojo/bridge_factory.mojom.h"
 #include "ui/views_bridge_mac/mojo/bridged_native_widget.mojom.h"
 #include "ui/views_bridge_mac/mojo/bridged_native_widget_host.mojom.h"
 
@@ -40,6 +40,7 @@
 // APIs, and which may live in an app shim process.
 class VIEWS_EXPORT BridgedNativeWidgetHostImpl
     : public BridgedNativeWidgetHostHelper,
+      public BridgeFactoryHost::Observer,
       public views_bridge_mac::mojom::BridgedNativeWidgetHost,
       public DialogObserver,
       public FocusChangeListener,
@@ -71,8 +72,8 @@
 
   // The bridge factory that was used to create the true NSWindow for this
   // widget. This is nullptr for in-process windows.
-  views_bridge_mac::mojom::BridgeFactory* bridge_factory() const {
-    return bridge_factory_;
+  BridgeFactoryHost* bridge_factory_host() const {
+    return bridge_factory_host_;
   }
 
   // A NSWindow that is guaranteed to exist in this process. If the bridge
@@ -97,7 +98,7 @@
 
   // Create and set the bridge object to be potentially in another process.
   void CreateRemoteBridge(
-      views_bridge_mac::mojom::BridgeFactory* bridge_factory,
+      BridgeFactoryHost* bridge_factory_host,
       views_bridge_mac::mojom::CreateWindowParamsPtr window_create_params,
       uint64_t parent_bridge_id);
 
@@ -189,6 +190,9 @@
                  gfx::Point* baseline_point) override;
   double SheetPositionY() override;
 
+  // BridgeFactoryHost::Observer:
+  void OnBridgeFactoryHostDestroying(BridgeFactoryHost* host) override;
+
   // views_bridge_mac::mojom::BridgedNativeWidgetHost:
   void OnVisibilityChanged(bool visible) override;
   void SetViewSize(const gfx::Size& new_size) override;
@@ -283,10 +287,7 @@
   views::NativeWidgetMac* const native_widget_mac_;  // Weak. Owns |this_|.
 
   // The factory that was used to create |bridge_ptr_|.
-  // TODO(ccameron): The lifetime of this pointer is not correctly managed yet,
-  // and has no way to be set to nullptr when the bridge is deleted (this
-  // pointer is never non-nullptr in production).
-  views_bridge_mac::mojom::BridgeFactory* bridge_factory_ = nullptr;
+  BridgeFactoryHost* bridge_factory_host_ = nullptr;
 
   Widget::InitParams::Type widget_type_ = Widget::InitParams::TYPE_WINDOW;
 
diff --git a/ui/views/cocoa/bridged_native_widget_host_impl.mm b/ui/views/cocoa/bridged_native_widget_host_impl.mm
index 773147f..cf05b20 100644
--- a/ui/views/cocoa/bridged_native_widget_host_impl.mm
+++ b/ui/views/cocoa/bridged_native_widget_host_impl.mm
@@ -85,6 +85,12 @@
 }
 
 BridgedNativeWidgetHostImpl::~BridgedNativeWidgetHostImpl() {
+  if (bridge_factory_host_) {
+    bridge_factory_host_->GetFactory()->DestroyBridge(id_);
+    bridge_factory_host_->RemoveObserver(this);
+    bridge_factory_host_ = nullptr;
+  }
+
   // Ensure that |this| cannot be reached by its id while it is being destroyed.
   auto found = GetIdToWidgetHostImplMap().find(id_);
   DCHECK(found != GetIdToWidgetHostImplMap().end());
@@ -124,10 +130,11 @@
 }
 
 void BridgedNativeWidgetHostImpl::CreateRemoteBridge(
-    views_bridge_mac::mojom::BridgeFactory* bridge_factory,
+    BridgeFactoryHost* bridge_factory_host,
     views_bridge_mac::mojom::CreateWindowParamsPtr window_create_params,
     uint64_t parent_bridge_id) {
-  bridge_factory_ = bridge_factory;
+  bridge_factory_host_ = bridge_factory_host;
+  bridge_factory_host_->AddObserver(this);
 
   // Create the local window with the same parameters as will be used in the
   // other process.
@@ -138,8 +145,8 @@
   // Initialize |bridge_ptr_| to point to a bridge created by |factory|.
   views_bridge_mac::mojom::BridgedNativeWidgetHostPtr host_ptr;
   host_mojo_binding_.Bind(mojo::MakeRequest(&host_ptr));
-  bridge_factory_->CreateBridge(id_, mojo::MakeRequest(&bridge_ptr_),
-                                std::move(host_ptr));
+  bridge_factory_host_->GetFactory()->CreateBridge(
+      id_, mojo::MakeRequest(&bridge_ptr_), std::move(host_ptr));
 
   // Create the window in its process, and attach it to its parent window.
   bridge()->CreateWindow(std::move(window_create_params), parent_bridge_id);
@@ -433,6 +440,16 @@
 }
 
 ////////////////////////////////////////////////////////////////////////////////
+// BridgedNativeWidgetHostImpl, BridgeFactoryHost::Observer:
+void BridgedNativeWidgetHostImpl::OnBridgeFactoryHostDestroying(
+    BridgeFactoryHost* host) {
+  DCHECK_EQ(host, bridge_factory_host_);
+  bridge_factory_host_->RemoveObserver(this);
+  bridge_factory_host_ = nullptr;
+  // TODO(ccameron): This should be treated as the window closing.
+}
+
+////////////////////////////////////////////////////////////////////////////////
 // BridgedNativeWidgetHostImpl,
 // views_bridge_mac::mojom::BridgedNativeWidgetHost:
 
diff --git a/ui/views/controls/button/button.cc b/ui/views/controls/button/button.cc
index 9656e5dc..ebf0864 100644
--- a/ui/views/controls/button/button.cc
+++ b/ui/views/controls/button/button.cc
@@ -170,6 +170,12 @@
   focus_painter_ = std::move(focus_painter);
 }
 
+void Button::SetHighlighted(bool bubble_visible) {
+  AnimateInkDrop(bubble_visible ? views::InkDropState::ACTIVATED
+                                : views::InkDropState::DEACTIVATED,
+                 nullptr);
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // Button, View overrides:
 
diff --git a/ui/views/controls/button/button.h b/ui/views/controls/button/button.h
index b5347ff..050512f 100644
--- a/ui/views/controls/button/button.h
+++ b/ui/views/controls/button/button.h
@@ -151,6 +151,9 @@
 
   void SetFocusPainter(std::unique_ptr<Painter> focus_painter);
 
+  // Highlights the ink drop for the button.
+  void SetHighlighted(bool bubble_visible);
+
   // Overridden from View:
   void OnEnabledChanged() override;
   const char* GetClassName() const override;
diff --git a/ui/views/widget/native_widget_mac.mm b/ui/views/widget/native_widget_mac.mm
index c119ed9e..fd59d4a 100644
--- a/ui/views/widget/native_widget_mac.mm
+++ b/ui/views/widget/native_widget_mac.mm
@@ -116,10 +116,10 @@
   // unreachable.
   BridgedNativeWidgetHostImpl* parent_host =
       BridgedNativeWidgetHostImpl::GetFromNativeWindow([params.parent window]);
-  views_bridge_mac::mojom::BridgeFactory* bridge_factory = nullptr;
+  BridgeFactoryHost* bridge_factory_host = nullptr;
   if (parent_host)
-    bridge_factory = parent_host->bridge_factory();
-  if (bridge_factory) {
+    bridge_factory_host = parent_host->bridge_factory_host();
+  if (bridge_factory_host) {
     // Compute the parameters to describe the NSWindow.
     // TODO(ccameron): This is not yet adequate to capture all NSWindow
     // sub-classes that may be used. Make the parameter structure more
@@ -129,7 +129,7 @@
     create_window_params->style_mask = StyleMaskForParams(params);
 
     bridge_host_->CreateRemoteBridge(
-        bridge_factory, std::move(create_window_params),
+        bridge_factory_host, std::move(create_window_params),
         parent_host ? parent_host->bridged_native_widget_id() : 0);
   } else {
     base::scoped_nsobject<NativeWidgetMacNSWindow> window(
diff --git a/ui/views_bridge_mac/BUILD.gn b/ui/views_bridge_mac/BUILD.gn
index 4c88d0a..676cd39 100644
--- a/ui/views_bridge_mac/BUILD.gn
+++ b/ui/views_bridge_mac/BUILD.gn
@@ -6,6 +6,7 @@
 
 mojom("mojo") {
   cpp_only = true
+  assert(is_mac)
 
   sources = [
     "mojo/bridge_factory.mojom",
diff --git a/webrunner/app/sandbox_policy b/webrunner/app/sandbox_policy
index 2da7fcd..a3bfa2b 100644
--- a/webrunner/app/sandbox_policy
+++ b/webrunner/app/sandbox_policy
@@ -8,6 +8,7 @@
       "fuchsia.netstack.Netstack",
       "fuchsia.process.Launcher",
       "fuchsia.ui.scenic.Scenic",
-      "fuchsia.ui.viewsv1.ViewManager"
+      "fuchsia.ui.viewsv1.ViewManager",
+      "fuchsia.vulkan.loader.Loader"
   ]
 }