diff --git a/ash/BUILD.gn b/ash/BUILD.gn
index 2e2904e..a67d721 100644
--- a/ash/BUILD.gn
+++ b/ash/BUILD.gn
@@ -284,7 +284,6 @@
     "shelf/shelf_button.h",
     "shelf/shelf_button_pressed_metric_tracker.cc",
     "shelf/shelf_button_pressed_metric_tracker.h",
-    "shelf/shelf_constants.cc",
     "shelf/shelf_constants.h",
     "shelf/shelf_controller.cc",
     "shelf/shelf_controller.h",
@@ -525,6 +524,8 @@
     "system/tray/tray_bubble_wrapper.h",
     "system/tray/tray_constants.cc",
     "system/tray/tray_constants.h",
+    "system/tray/tray_container.cc",
+    "system/tray/tray_container.h",
     "system/tray/tray_details_view.cc",
     "system/tray/tray_details_view.h",
     "system/tray/tray_event_filter.cc",
diff --git a/ash/root_window_controller_unittest.cc b/ash/root_window_controller_unittest.cc
index 3671f30..3992e8d 100644
--- a/ash/root_window_controller_unittest.cc
+++ b/ash/root_window_controller_unittest.cc
@@ -219,8 +219,8 @@
   EXPECT_EQ("100,20 100x100",
             normal->GetNativeView()->GetBoundsInRootWindow().ToString());
 
-  // Maximized area on primary display has 47px for non-md and 48px for md
-  // (defined in SHELF_SIZE) inset at the bottom.
+  // Maximized area on primary display has 48px for inset at the bottom
+  // (kShelfSize).
 
   // First clear fullscreen status, since both fullscreen and maximized windows
   // share the same desktop workspace, which cancels the shelf status.
diff --git a/ash/shelf/app_list_button.cc b/ash/shelf/app_list_button.cc
index f033b2d..1cd63f9 100644
--- a/ash/shelf/app_list_button.cc
+++ b/ash/shelf/app_list_button.cc
@@ -57,8 +57,7 @@
   set_ink_drop_visible_opacity(kShelfInkDropVisibleOpacity);
   SetAccessibleName(
       l10n_util::GetStringUTF16(IDS_ASH_SHELF_APP_LIST_LAUNCHER_TITLE));
-  SetSize(
-      gfx::Size(GetShelfConstant(SHELF_SIZE), GetShelfConstant(SHELF_SIZE)));
+  SetSize(gfx::Size(kShelfSize, kShelfSize));
   SetFocusPainter(TrayPopupUtils::CreateFocusPainter());
   set_notify_action(CustomButton::NOTIFY_ON_PRESS);
 }
diff --git a/ash/shelf/overflow_button.cc b/ash/shelf/overflow_button.cc
index e770232..9f4a414c 100644
--- a/ash/shelf/overflow_button.cc
+++ b/ash/shelf/overflow_button.cc
@@ -171,7 +171,7 @@
   gfx::Rect content_bounds = GetContentsBounds();
   // Align the button to the top of a bottom-aligned shelf, to the right edge
   // a left-aligned shelf, and to the left edge of a right-aligned shelf.
-  const int inset = (GetShelfConstant(SHELF_SIZE) - kOverflowButtonSize) / 2;
+  const int inset = (kShelfSize - kOverflowButtonSize) / 2;
   const int x = alignment == SHELF_ALIGNMENT_LEFT
                     ? content_bounds.right() - inset - kOverflowButtonSize
                     : content_bounds.x() + inset;
diff --git a/ash/shelf/shelf_constants.cc b/ash/shelf/shelf_constants.cc
deleted file mode 100644
index 1575a68d..0000000
--- a/ash/shelf/shelf_constants.cc
+++ /dev/null
@@ -1,28 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ash/shelf/shelf_constants.h"
-
-#include "base/logging.h"
-
-namespace ash {
-
-int GetShelfConstant(ShelfConstant shelf_constant) {
-  const int kShelfSize[] = {47, 48, 48};
-  const int kShelfInsetsForAutoHide[] = {3, 0, 0};
-
-  // TODO(estade): clean this up --- remove unneeded constants and reduce
-  // remaining arrays to a single constant.
-  const int mode = 1;
-  switch (shelf_constant) {
-    case SHELF_SIZE:
-      return kShelfSize[mode];
-    case SHELF_INSETS_FOR_AUTO_HIDE:
-      return kShelfInsetsForAutoHide[mode];
-  }
-  NOTREACHED();
-  return 0;
-}
-
-}  // namespace ash
diff --git a/ash/shelf/shelf_constants.h b/ash/shelf/shelf_constants.h
index 65662ec..80749375 100644
--- a/ash/shelf/shelf_constants.h
+++ b/ash/shelf/shelf_constants.h
@@ -10,14 +10,9 @@
 
 namespace ash {
 
-enum ShelfConstant {
-  // Size of the shelf when visible (height when the shelf is horizontal and
-  // width when the shelf is vertical).
-  SHELF_SIZE,
-
-  // Insets allocated for shelf when it is auto hidden.
-  SHELF_INSETS_FOR_AUTO_HIDE
-};
+// Size of the shelf when visible (height when the shelf is horizontal and
+// width when the shelf is vertical).
+ASH_EXPORT constexpr int kShelfSize = 48;
 
 // We reserve a small area on the edge of the workspace area to ensure that
 // the resize handle at the edge of the window can be hit.
@@ -83,8 +78,6 @@
 // The direction of the focus cycling.
 enum CycleDirection { CYCLE_FORWARD, CYCLE_BACKWARD };
 
-ASH_EXPORT int GetShelfConstant(ShelfConstant shelf_constant);
-
 }  // namespace ash
 
 #endif  // ASH_SHELF_SHELF_CONSTANTS_H_
diff --git a/ash/shelf/shelf_layout_manager.cc b/ash/shelf/shelf_layout_manager.cc
index 359d0c6..895a315 100644
--- a/ash/shelf/shelf_layout_manager.cc
+++ b/ash/shelf/shelf_layout_manager.cc
@@ -191,13 +191,12 @@
 }
 
 gfx::Rect ShelfLayoutManager::GetIdealBounds() {
-  const int shelf_size = GetShelfConstant(SHELF_SIZE);
   WmWindow* shelf_window = WmWindow::Get(shelf_widget_->GetNativeWindow());
   gfx::Rect rect(wm::GetDisplayBoundsInParent(shelf_window));
   return SelectValueForShelfAlignment(
-      gfx::Rect(rect.x(), rect.bottom() - shelf_size, rect.width(), shelf_size),
-      gfx::Rect(rect.x(), rect.y(), shelf_size, rect.height()),
-      gfx::Rect(rect.right() - shelf_size, rect.y(), shelf_size,
+      gfx::Rect(rect.x(), rect.bottom() - kShelfSize, rect.width(), kShelfSize),
+      gfx::Rect(rect.x(), rect.y(), kShelfSize, rect.height()),
+      gfx::Rect(rect.right() - kShelfSize, rect.y(), kShelfSize,
                 rect.height()));
 }
 
@@ -659,7 +658,7 @@
 
 void ShelfLayoutManager::CalculateTargetBounds(const State& state,
                                                TargetBounds* target_bounds) {
-  int shelf_size = GetShelfConstant(SHELF_SIZE);
+  int shelf_size = kShelfSize;
   if (state.visibility_state == SHELF_AUTO_HIDE &&
       state.auto_hide_state == SHELF_AUTO_HIDE_HIDDEN) {
     // Auto-hidden shelf always starts with the default size. If a gesture-drag
@@ -693,9 +692,9 @@
   gfx::Size status_size(
       shelf_widget_->status_area_widget()->GetWindowBoundsInScreen().size());
   if (wm_shelf_->IsHorizontalAlignment())
-    status_size.set_height(GetShelfConstant(SHELF_SIZE));
+    status_size.set_height(kShelfSize);
   else
-    status_size.set_width(GetShelfConstant(SHELF_SIZE));
+    status_size.set_width(kShelfSize);
 
   gfx::Point status_origin = SelectValueForShelfAlignment(
       gfx::Point(0, 0), gfx::Point(shelf_width - status_size.width(),
@@ -769,7 +768,7 @@
     // changed since then, e.g. because the tray-menu was shown because of the
     // drag), then allow the drag some resistance-free region at first to make
     // sure the shelf sticks with the finger until the shelf is visible.
-    resistance_free_region = GetShelfConstant(SHELF_SIZE) - kShelfAutoHideSize;
+    resistance_free_region = kShelfSize - kShelfAutoHideSize;
   }
 
   bool resist = SelectValueForShelfAlignment(
@@ -788,11 +787,10 @@
   } else {
     translate = gesture_drag_amount_;
   }
-  int shelf_insets = GetShelfConstant(SHELF_INSETS_FOR_AUTO_HIDE);
   if (horizontal) {
     // Move and size the shelf with the gesture.
     int shelf_height = target_bounds->shelf_bounds_in_root.height() - translate;
-    shelf_height = std::max(shelf_height, shelf_insets);
+    shelf_height = std::max(shelf_height, 0);
     target_bounds->shelf_bounds_in_root.set_height(shelf_height);
     if (wm_shelf_->IsHorizontalAlignment()) {
       target_bounds->shelf_bounds_in_root.set_y(available_bounds.bottom() -
@@ -808,7 +806,7 @@
       shelf_width -= translate;
     else
       shelf_width += translate;
-    shelf_width = std::max(shelf_width, shelf_insets);
+    shelf_width = std::max(shelf_width, 0);
     target_bounds->shelf_bounds_in_root.set_width(shelf_width);
     if (right_aligned) {
       target_bounds->shelf_bounds_in_root.set_x(available_bounds.right() -
@@ -819,8 +817,7 @@
       target_bounds->status_bounds_in_shelf.set_x(0);
     } else {
       target_bounds->status_bounds_in_shelf.set_x(
-          target_bounds->shelf_bounds_in_root.width() -
-          GetShelfConstant(SHELF_SIZE));
+          target_bounds->shelf_bounds_in_root.width() - kShelfSize);
     }
   }
 }
@@ -976,8 +973,6 @@
 int ShelfLayoutManager::GetWorkAreaInsets(const State& state, int size) const {
   if (state.visibility_state == SHELF_VISIBLE)
     return size;
-  if (state.visibility_state == SHELF_AUTO_HIDE)
-    return GetShelfConstant(SHELF_INSETS_FOR_AUTO_HIDE);
   return 0;
 }
 
diff --git a/ash/shelf/shelf_layout_manager_unittest.cc b/ash/shelf/shelf_layout_manager_unittest.cc
index cc45d218..550c212c 100644
--- a/ash/shelf/shelf_layout_manager_unittest.cc
+++ b/ash/shelf/shelf_layout_manager_unittest.cc
@@ -162,10 +162,10 @@
       EXPECT_EQ(auto_hidden_shelf_widget_bounds_.bottom(),
                 shelf_bounds.bottom());
       EXPECT_EQ(shelf_widget_bounds_.bottom(), shelf_bounds.bottom());
-    } else if (SHELF_ALIGNMENT_RIGHT == shelf->GetAlignment()) {
+    } else if (SHELF_ALIGNMENT_RIGHT == shelf->alignment()) {
       EXPECT_EQ(auto_hidden_shelf_widget_bounds_.right(), shelf_bounds.right());
       EXPECT_EQ(shelf_widget_bounds_.right(), shelf_bounds.right());
-    } else if (SHELF_ALIGNMENT_LEFT == shelf->GetAlignment()) {
+    } else if (SHELF_ALIGNMENT_LEFT == shelf->alignment()) {
       EXPECT_EQ(auto_hidden_shelf_widget_bounds_.x(), shelf_bounds.x());
       EXPECT_EQ(shelf_widget_bounds_.x(), shelf_bounds.x());
     }
@@ -393,9 +393,9 @@
   // Swipe down very little. It shouldn't change any state.
   if (shelf->IsHorizontalAlignment())
     end.set_y(start.y() + shelf_shown.height() * 3 / 10);
-  else if (SHELF_ALIGNMENT_LEFT == shelf->GetAlignment())
+  else if (SHELF_ALIGNMENT_LEFT == shelf->alignment())
     end.set_x(start.x() - shelf_shown.width() * 3 / 10);
-  else if (SHELF_ALIGNMENT_RIGHT == shelf->GetAlignment())
+  else if (SHELF_ALIGNMENT_RIGHT == shelf->alignment())
     end.set_x(start.x() + shelf_shown.width() * 3 / 10);
   generator.GestureScrollSequence(start, end, kTimeDelta, 5);
   EXPECT_EQ(SHELF_VISIBLE, shelf->GetVisibilityState());
@@ -421,10 +421,10 @@
   gfx::Point extended_start = start;
   if (shelf->IsHorizontalAlignment())
     extended_start.set_y(GetShelfWidget()->GetWindowBoundsInScreen().y() - 1);
-  else if (SHELF_ALIGNMENT_LEFT == shelf->GetAlignment())
+  else if (SHELF_ALIGNMENT_LEFT == shelf->alignment())
     extended_start.set_x(GetShelfWidget()->GetWindowBoundsInScreen().right() +
                          1);
-  else if (SHELF_ALIGNMENT_RIGHT == shelf->GetAlignment())
+  else if (SHELF_ALIGNMENT_RIGHT == shelf->alignment())
     extended_start.set_x(GetShelfWidget()->GetWindowBoundsInScreen().x() - 1);
   end = extended_start - delta;
   generator.GestureScrollSequenceWithCallback(
@@ -470,9 +470,9 @@
   gfx::Point below_start = start;
   if (shelf->IsHorizontalAlignment())
     below_start.set_y(GetShelfWidget()->GetWindowBoundsInScreen().bottom() + 1);
-  else if (SHELF_ALIGNMENT_LEFT == shelf->GetAlignment())
+  else if (SHELF_ALIGNMENT_LEFT == shelf->alignment())
     below_start.set_x(GetShelfWidget()->GetWindowBoundsInScreen().x() - 1);
-  else if (SHELF_ALIGNMENT_RIGHT == shelf->GetAlignment())
+  else if (SHELF_ALIGNMENT_RIGHT == shelf->alignment())
     below_start.set_x(GetShelfWidget()->GetWindowBoundsInScreen().right() + 1);
   end = below_start - delta;
   generator.GestureScrollSequence(below_start, end, kTimeDelta,
@@ -702,13 +702,12 @@
   // LayoutShelf() forces the animation to completion, at which point the
   // shelf should go off the screen.
   layout_manager->LayoutShelf();
-  const int shelf_insets = GetShelfConstant(SHELF_INSETS_FOR_AUTO_HIDE);
 
   display::Display display = display::Screen::GetScreen()->GetPrimaryDisplay();
   const int display_bottom = display.bounds().bottom();
   EXPECT_EQ(display_bottom - kShelfAutoHideSize,
             GetShelfWidget()->GetWindowBoundsInScreen().y());
-  EXPECT_EQ(display_bottom - shelf_insets, display.work_area().bottom());
+  EXPECT_EQ(display_bottom, display.work_area().bottom());
 
   // Move the mouse to the bottom of the screen.
   generator.MoveMouseTo(0, display_bottom - 1);
@@ -719,7 +718,7 @@
   layout_manager->LayoutShelf();
   EXPECT_EQ(display_bottom - layout_manager->GetIdealBounds().height(),
             GetShelfWidget()->GetWindowBoundsInScreen().y());
-  EXPECT_EQ(display_bottom - shelf_insets, display.work_area().bottom());
+  EXPECT_EQ(display_bottom, display.work_area().bottom());
 
   // Move mouse back up.
   generator.MoveMouseTo(0, 0);
@@ -1280,7 +1279,7 @@
             display.GetWorkAreaInsets().left());
   EXPECT_GE(shelf_bounds.width(),
             GetShelfWidget()->GetContentsView()->GetPreferredSize().width());
-  EXPECT_EQ(SHELF_ALIGNMENT_LEFT, GetPrimarySystemTray()->shelf_alignment());
+  EXPECT_EQ(SHELF_ALIGNMENT_LEFT, GetPrimarySystemTray()->shelf()->alignment());
   StatusAreaWidget* status_area_widget = GetShelfWidget()->status_area_widget();
   gfx::Rect status_bounds(status_area_widget->GetWindowBoundsInScreen());
   // TODO(estade): Re-enable this check. See crbug.com/660928.
@@ -1297,10 +1296,8 @@
   EXPECT_EQ(display.bounds().height(), shelf_bounds.height());
   shelf->SetAutoHideBehavior(SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS);
   display = display::Screen::GetScreen()->GetPrimaryDisplay();
-  EXPECT_EQ(GetShelfConstant(SHELF_INSETS_FOR_AUTO_HIDE),
-            display.GetWorkAreaInsets().left());
-  EXPECT_EQ(GetShelfConstant(SHELF_INSETS_FOR_AUTO_HIDE),
-            display.work_area().x());
+  EXPECT_EQ(0, display.GetWorkAreaInsets().left());
+  EXPECT_EQ(0, display.work_area().x());
 
   shelf->SetAutoHideBehavior(SHELF_AUTO_HIDE_BEHAVIOR_NEVER);
   shelf->SetAlignment(SHELF_ALIGNMENT_RIGHT);
@@ -1311,7 +1308,8 @@
             display.GetWorkAreaInsets().right());
   EXPECT_GE(shelf_bounds.width(),
             GetShelfWidget()->GetContentsView()->GetPreferredSize().width());
-  EXPECT_EQ(SHELF_ALIGNMENT_RIGHT, GetPrimarySystemTray()->shelf_alignment());
+  EXPECT_EQ(SHELF_ALIGNMENT_RIGHT,
+            GetPrimarySystemTray()->shelf()->alignment());
   status_bounds = gfx::Rect(status_area_widget->GetWindowBoundsInScreen());
   // TODO(estade): Re-enable this check. See crbug.com/660928.
   //  EXPECT_GE(
@@ -1327,10 +1325,8 @@
   EXPECT_EQ(display.bounds().height(), shelf_bounds.height());
   shelf->SetAutoHideBehavior(SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS);
   display = display::Screen::GetScreen()->GetPrimaryDisplay();
-  EXPECT_EQ(GetShelfConstant(SHELF_INSETS_FOR_AUTO_HIDE),
-            display.GetWorkAreaInsets().right());
-  EXPECT_EQ(GetShelfConstant(SHELF_INSETS_FOR_AUTO_HIDE),
-            display.bounds().right() - display.work_area().right());
+  EXPECT_EQ(0, display.GetWorkAreaInsets().right());
+  EXPECT_EQ(0, display.bounds().right() - display.work_area().right());
 }
 
 TEST_F(ShelfLayoutManagerTest, GestureDrag) {
diff --git a/ash/shelf/shelf_view.cc b/ash/shelf/shelf_view.cc
index 9be5a085..0bf4302 100644
--- a/ash/shelf/shelf_view.cc
+++ b/ash/shelf/shelf_view.cc
@@ -1334,11 +1334,9 @@
     }
 
     if (wm_shelf_->IsHorizontalAlignment()) {
-      preferred_size =
-          gfx::Size(last_button_bounds.right(), GetShelfConstant(SHELF_SIZE));
+      preferred_size = gfx::Size(last_button_bounds.right(), kShelfSize);
     } else {
-      preferred_size =
-          gfx::Size(GetShelfConstant(SHELF_SIZE), last_button_bounds.bottom());
+      preferred_size = gfx::Size(kShelfSize, last_button_bounds.bottom());
     }
   }
   gfx::Point origin(GetMirroredXWithWidthInView(0, preferred_size.width()), 0);
@@ -1387,7 +1385,6 @@
 gfx::Size ShelfView::GetPreferredSize() const {
   gfx::Rect overflow_bounds;
   CalculateIdealBounds(&overflow_bounds);
-  const int shelf_size = GetShelfConstant(SHELF_SIZE);
 
   int last_button_index = last_visible_index_;
   if (!is_overflow_mode()) {
@@ -1410,12 +1407,12 @@
   const gfx::Rect last_button_bounds =
       last_button_index >= first_visible_index_
           ? view_model_->ideal_bounds(last_button_index)
-          : gfx::Rect(gfx::Size(shelf_size, shelf_size));
+          : gfx::Rect(gfx::Size(kShelfSize, kShelfSize));
 
   if (wm_shelf_->IsHorizontalAlignment())
-    return gfx::Size(last_button_bounds.right(), shelf_size);
+    return gfx::Size(last_button_bounds.right(), kShelfSize);
 
-  return gfx::Size(shelf_size, last_button_bounds.bottom());
+  return gfx::Size(kShelfSize, last_button_bounds.bottom());
 }
 
 void ShelfView::OnBoundsChanged(const gfx::Rect& previous_bounds) {
diff --git a/ash/shelf/shelf_view_unittest.cc b/ash/shelf/shelf_view_unittest.cc
index 0baab22..a2e7b1d 100644
--- a/ash/shelf/shelf_view_unittest.cc
+++ b/ash/shelf/shelf_view_unittest.cc
@@ -249,7 +249,7 @@
     WebNotificationTray::DisableAnimationsForTest(true);
 
     // The bounds should be big enough for 4 buttons + overflow chevron.
-    shelf_view_->SetBounds(0, 0, 500, GetShelfConstant(SHELF_SIZE));
+    shelf_view_->SetBounds(0, 0, 500, kShelfSize);
 
     test_api_.reset(new ShelfViewTestAPI(shelf_view_));
     test_api_->SetAnimationDuration(1);  // Speeds up animation for test.
@@ -1417,7 +1417,7 @@
 
   // Resize shelf view with that animation running and stay overflown.
   gfx::Rect bounds = shelf_view_->bounds();
-  bounds.set_width(bounds.width() - GetShelfConstant(SHELF_SIZE));
+  bounds.set_width(bounds.width() - kShelfSize);
   shelf_view_->SetBoundsRect(bounds);
   ASSERT_TRUE(test_api_->IsOverflowButtonVisible());
 
@@ -1544,7 +1544,7 @@
       secondary_shelf->GetShelfViewForTesting();
 
   // The bounds should be big enough for 4 buttons + overflow chevron.
-  shelf_view_for_secondary->SetBounds(0, 0, 500, GetShelfConstant(SHELF_SIZE));
+  shelf_view_for_secondary->SetBounds(0, 0, 500, kShelfSize);
 
   ShelfViewTestAPI test_api_for_secondary(shelf_view_for_secondary);
   // Speeds up animation for test.
diff --git a/ash/shelf/shelf_widget.cc b/ash/shelf/shelf_widget.cc
index 3d07828..6a6b7c1 100644
--- a/ash/shelf/shelf_widget.cc
+++ b/ash/shelf/shelf_widget.cc
@@ -221,8 +221,7 @@
 
 void ShelfWidget::OnShelfAlignmentChanged() {
   shelf_view_->OnShelfAlignmentChanged();
-  // TODO(jamescook): Status area should not cache alignment.
-  status_area_widget_->SetShelfAlignment(wm_shelf_->GetAlignment());
+  status_area_widget_->UpdateAfterShelfAlignmentChange();
   delegate_view_->SchedulePaint();
 }
 
diff --git a/ash/shelf/shelf_widget_unittest.cc b/ash/shelf/shelf_widget_unittest.cc
index b93a4d7..7cdac60 100644
--- a/ash/shelf/shelf_widget_unittest.cc
+++ b/ash/shelf/shelf_widget_unittest.cc
@@ -48,7 +48,6 @@
 }
 
 TEST_F(ShelfWidgetTest, TestAlignment) {
-  const int kShelfSize = GetShelfConstant(SHELF_SIZE);
   UpdateDisplay("400x400");
   {
     SCOPED_TRACE("Single Bottom");
@@ -75,7 +74,6 @@
 }
 
 TEST_F(ShelfWidgetTest, TestAlignmentForMultipleDisplays) {
-  const int kShelfSize = GetShelfConstant(SHELF_SIZE);
   UpdateDisplay("300x300,500x500");
   std::vector<WmWindow*> root_windows = ShellPort::Get()->GetAllRootWindows();
   {
diff --git a/ash/system/date/tray_system_info.cc b/ash/system/date/tray_system_info.cc
index 5014f9f7..face749 100644
--- a/ash/system/date/tray_system_info.cc
+++ b/ash/system/date/tray_system_info.cc
@@ -4,7 +4,7 @@
 
 #include "ash/system/date/tray_system_info.h"
 
-#include "ash/shelf/wm_shelf_util.h"
+#include "ash/shelf/wm_shelf.h"
 #include "ash/shell.h"
 #include "ash/system/date/date_view.h"
 #include "ash/system/date/system_info_default_view.h"
@@ -43,7 +43,7 @@
 views::View* TraySystemInfo::CreateTrayView(LoginStatus status) {
   CHECK(tray_view_ == nullptr);
   tray::TimeView::ClockLayout clock_layout =
-      IsHorizontalAlignment(system_tray()->shelf_alignment())
+      system_tray()->shelf()->IsHorizontalAlignment()
           ? tray::TimeView::ClockLayout::HORIZONTAL_CLOCK
           : tray::TimeView::ClockLayout::VERTICAL_CLOCK;
   tray_view_ = new tray::TimeView(clock_layout);
@@ -70,10 +70,10 @@
   default_view_ = nullptr;
 }
 
-void TraySystemInfo::UpdateAfterShelfAlignmentChange(ShelfAlignment alignment) {
+void TraySystemInfo::UpdateAfterShelfAlignmentChange() {
   if (tray_view_) {
     tray::TimeView::ClockLayout clock_layout =
-        IsHorizontalAlignment(alignment)
+        system_tray()->shelf()->IsHorizontalAlignment()
             ? tray::TimeView::ClockLayout::HORIZONTAL_CLOCK
             : tray::TimeView::ClockLayout::VERTICAL_CLOCK;
     tray_view_->UpdateClockLayout(clock_layout);
diff --git a/ash/system/date/tray_system_info.h b/ash/system/date/tray_system_info.h
index 04a57fdc..45a3194 100644
--- a/ash/system/date/tray_system_info.h
+++ b/ash/system/date/tray_system_info.h
@@ -42,7 +42,7 @@
   views::View* CreateDefaultView(LoginStatus status) override;
   void DestroyTrayView() override;
   void DestroyDefaultView() override;
-  void UpdateAfterShelfAlignmentChange(ShelfAlignment alignment) override;
+  void UpdateAfterShelfAlignmentChange() override;
 
   // ClockObserver:
   void OnDateFormatChanged() override;
diff --git a/ash/system/ime_menu/ime_menu_tray.cc b/ash/system/ime_menu/ime_menu_tray.cc
index 7913d0f..e160dbf 100644
--- a/ash/system/ime_menu/ime_menu_tray.cc
+++ b/ash/system/ime_menu/ime_menu_tray.cc
@@ -21,6 +21,7 @@
 #include "ash/system/tray/system_tray_delegate.h"
 #include "ash/system/tray/system_tray_notifier.h"
 #include "ash/system/tray/tray_constants.h"
+#include "ash/system/tray/tray_container.h"
 #include "ash/system/tray/tray_popup_item_style.h"
 #include "ash/system/tray/tray_popup_utils.h"
 #include "ash/system/tray/tray_utils.h"
@@ -282,7 +283,7 @@
 }  // namespace
 
 ImeMenuTray::ImeMenuTray(WmShelf* wm_shelf)
-    : TrayBackgroundView(wm_shelf, true),
+    : TrayBackgroundView(wm_shelf),
       label_(new ImeMenuLabel()),
       show_keyboard_(false),
       force_show_keyboard_(false),
diff --git a/ash/system/network/tray_vpn.cc b/ash/system/network/tray_vpn.cc
index 1264d2ce..5de3b00c 100644
--- a/ash/system/network/tray_vpn.cc
+++ b/ash/system/network/tray_vpn.cc
@@ -148,18 +148,14 @@
 
 TrayVPN::~TrayVPN() {}
 
-views::View* TrayVPN::CreateTrayView(LoginStatus status) {
-  return NULL;
-}
-
 views::View* TrayVPN::CreateDefaultView(LoginStatus status) {
-  CHECK(default_ == NULL);
+  CHECK(default_ == nullptr);
   if (!chromeos::NetworkHandler::IsInitialized())
-    return NULL;
+    return nullptr;
   if (status == LoginStatus::NOT_LOGGED_IN)
-    return NULL;
+    return nullptr;
   if (!tray::VpnDefaultView::ShouldShow())
-    return NULL;
+    return nullptr;
 
   const bool is_in_secondary_login_screen =
       Shell::Get()->session_controller()->IsInSecondaryLoginScreen();
@@ -172,9 +168,9 @@
 }
 
 views::View* TrayVPN::CreateDetailedView(LoginStatus status) {
-  CHECK(detailed_ == NULL);
+  CHECK(detailed_ == nullptr);
   if (!chromeos::NetworkHandler::IsInitialized())
-    return NULL;
+    return nullptr;
 
   ShellPort::Get()->RecordUserMetricsAction(UMA_STATUS_AREA_DETAILED_VPN_VIEW);
   detailed_ = new tray::NetworkStateListDetailedView(
@@ -183,20 +179,14 @@
   return detailed_;
 }
 
-void TrayVPN::DestroyTrayView() {}
-
 void TrayVPN::DestroyDefaultView() {
-  default_ = NULL;
+  default_ = nullptr;
 }
 
 void TrayVPN::DestroyDetailedView() {
-  detailed_ = NULL;
+  detailed_ = nullptr;
 }
 
-void TrayVPN::UpdateAfterLoginStatusChange(LoginStatus status) {}
-
-void TrayVPN::UpdateAfterShelfAlignmentChange(ShelfAlignment alignment) {}
-
 void TrayVPN::NetworkStateChanged() {
   if (default_)
     default_->Update();
diff --git a/ash/system/network/tray_vpn.h b/ash/system/network/tray_vpn.h
index 93d361d..21a4bd6 100644
--- a/ash/system/network/tray_vpn.h
+++ b/ash/system/network/tray_vpn.h
@@ -26,14 +26,10 @@
   ~TrayVPN() override;
 
   // SystemTrayItem
-  views::View* CreateTrayView(LoginStatus status) override;
   views::View* CreateDefaultView(LoginStatus status) override;
   views::View* CreateDetailedView(LoginStatus status) override;
-  void DestroyTrayView() override;
   void DestroyDefaultView() override;
   void DestroyDetailedView() override;
-  void UpdateAfterLoginStatusChange(LoginStatus status) override;
-  void UpdateAfterShelfAlignmentChange(ShelfAlignment alignment) override;
 
   // TrayNetworkStateObserver::Delegate
   void NetworkStateChanged() override;
diff --git a/ash/system/overview/overview_button_tray.cc b/ash/system/overview/overview_button_tray.cc
index d07eeebeb..331f445 100644
--- a/ash/system/overview/overview_button_tray.cc
+++ b/ash/system/overview/overview_button_tray.cc
@@ -12,6 +12,7 @@
 #include "ash/strings/grit/ash_strings.h"
 #include "ash/system/tray/system_tray_delegate.h"
 #include "ash/system/tray/tray_constants.h"
+#include "ash/system/tray/tray_container.h"
 #include "ash/wm/maximize_mode/maximize_mode_controller.h"
 #include "ash/wm/overview/window_selector_controller.h"
 #include "ui/base/l10n/l10n_util.h"
@@ -22,13 +23,18 @@
 namespace ash {
 
 OverviewButtonTray::OverviewButtonTray(WmShelf* wm_shelf)
-    : TrayBackgroundView(wm_shelf, true),
+    : TrayBackgroundView(wm_shelf),
       icon_(new views::ImageView()),
       scoped_session_observer_(this) {
   SetInkDropMode(InkDropMode::ON);
 
-  icon_->SetImage(CreateVectorIcon(kShelfOverviewIcon, kShelfIconColor));
-  SetIconBorderForShelfAlignment();
+  gfx::ImageSkia image =
+      gfx::CreateVectorIcon(kShelfOverviewIcon, kShelfIconColor);
+  icon_->SetImage(image);
+  const int vertical_padding = (kTrayItemSize - image.height()) / 2;
+  const int horizontal_padding = (kTrayItemSize - image.width()) / 2;
+  icon_->SetBorder(views::CreateEmptyBorder(
+      gfx::Insets(vertical_padding, horizontal_padding)));
   tray_container()->AddChildView(icon_);
 
   // Since OverviewButtonTray is located on the rightmost position of a
@@ -87,23 +93,6 @@
   // This class has no bubbles to hide.
 }
 
-void OverviewButtonTray::SetShelfAlignment(ShelfAlignment alignment) {
-  if (alignment == shelf_alignment())
-    return;
-
-  TrayBackgroundView::SetShelfAlignment(alignment);
-  SetIconBorderForShelfAlignment();
-}
-
-void OverviewButtonTray::SetIconBorderForShelfAlignment() {
-  // Pad button size to align with other controls in the system tray.
-  const gfx::ImageSkia& image = icon_->GetImage();
-  const int vertical_padding = (kTrayItemSize - image.height()) / 2;
-  const int horizontal_padding = (kTrayItemSize - image.width()) / 2;
-  icon_->SetBorder(views::CreateEmptyBorder(
-      gfx::Insets(vertical_padding, horizontal_padding)));
-}
-
 void OverviewButtonTray::UpdateIconVisibility() {
   // The visibility of the OverviewButtonTray has diverged from
   // WindowSelectorController::CanSelect. The visibility of the button should
diff --git a/ash/system/overview/overview_button_tray.h b/ash/system/overview/overview_button_tray.h
index 5b82d4d..74a6794c 100644
--- a/ash/system/overview/overview_button_tray.h
+++ b/ash/system/overview/overview_button_tray.h
@@ -49,15 +49,10 @@
   void ClickedOutsideBubble() override;
   base::string16 GetAccessibleNameForTray() override;
   void HideBubbleWithView(const views::TrayBubbleView* bubble_view) override;
-  void SetShelfAlignment(ShelfAlignment alignment) override;
 
  private:
   friend class OverviewButtonTrayTest;
 
-  // Creates a new border for the icon. The padding is determined based on the
-  // alignment of the shelf.
-  void SetIconBorderForShelfAlignment();
-
   // Sets the icon to visible if maximize mode is enabled and
   // WindowSelectorController::CanSelect.
   void UpdateIconVisibility();
diff --git a/ash/system/palette/palette_tray.cc b/ash/system/palette/palette_tray.cc
index 98323e0c..f6c46f8 100644
--- a/ash/system/palette/palette_tray.cc
+++ b/ash/system/palette/palette_tray.cc
@@ -21,6 +21,7 @@
 #include "ash/system/tray/system_tray_delegate.h"
 #include "ash/system/tray/tray_bubble_wrapper.h"
 #include "ash/system/tray/tray_constants.h"
+#include "ash/system/tray/tray_container.h"
 #include "ash/system/tray/tray_popup_header_button.h"
 #include "ash/system/tray/tray_popup_item_style.h"
 #include "ash/wm_window.h"
@@ -128,7 +129,7 @@
 }  // namespace
 
 PaletteTray::PaletteTray(WmShelf* wm_shelf)
-    : TrayBackgroundView(wm_shelf, true),
+    : TrayBackgroundView(wm_shelf),
       palette_tool_manager_(new PaletteToolManager(this)),
       scoped_session_observer_(this),
       weak_factory_(this) {
@@ -362,13 +363,6 @@
   return shelf()->GetWindow();
 }
 
-void PaletteTray::SetShelfAlignment(ShelfAlignment alignment) {
-  if (alignment == shelf_alignment())
-    return;
-
-  TrayBackgroundView::SetShelfAlignment(alignment);
-}
-
 void PaletteTray::AnchorUpdated() {
   if (bubble_)
     bubble_->bubble_view()->UpdateBubble();
diff --git a/ash/system/palette/palette_tray.h b/ash/system/palette/palette_tray.h
index 9faa389..5bd0deb6f 100644
--- a/ash/system/palette/palette_tray.h
+++ b/ash/system/palette/palette_tray.h
@@ -5,7 +5,6 @@
 #ifndef ASH_SYSTEM_PALETTE_PALETTE_TRAY_H_
 #define ASH_SYSTEM_PALETTE_PALETTE_TRAY_H_
 
-#include <map>
 #include <memory>
 
 #include "ash/ash_export.h"
@@ -58,7 +57,6 @@
   void ClickedOutsideBubble() override;
   base::string16 GetAccessibleNameForTray() override;
   void HideBubbleWithView(const views::TrayBubbleView* bubble_view) override;
-  void SetShelfAlignment(ShelfAlignment alignment) override;
   void AnchorUpdated() override;
   void Initialize() override;
 
diff --git a/ash/system/session/logout_button_tray.cc b/ash/system/session/logout_button_tray.cc
index fb0cf21..a2783a9 100644
--- a/ash/system/session/logout_button_tray.cc
+++ b/ash/system/session/logout_button_tray.cc
@@ -4,83 +4,56 @@
 
 #include "ash/system/session/logout_button_tray.h"
 
-#include <memory>
-#include <utility>
-
-#include "ash/public/cpp/shelf_types.h"
-#include "ash/resources/grit/ash_resources.h"
 #include "ash/resources/vector_icons/vector_icons.h"
-#include "ash/shelf/wm_shelf_util.h"
+#include "ash/shelf/wm_shelf.h"
 #include "ash/shell.h"
 #include "ash/system/session/logout_confirmation_controller.h"
+#include "ash/system/status_area_widget.h"
 #include "ash/system/tray/system_tray_controller.h"
 #include "ash/system/tray/system_tray_notifier.h"
 #include "ash/system/tray/tray_constants.h"
-#include "ash/system/tray/tray_utils.h"
+#include "ash/system/tray/tray_container.h"
 #include "ash/system/user/login_status.h"
-#include "base/logging.h"
-#include "third_party/skia/include/core/SkColor.h"
-#include "ui/events/event.h"
+#include "ui/accessibility/ax_node_data.h"
 #include "ui/gfx/color_palette.h"
-#include "ui/gfx/geometry/insets.h"
 #include "ui/gfx/geometry/size.h"
 #include "ui/gfx/paint_vector_icon.h"
-#include "ui/views/bubble/tray_bubble_view.h"
-#include "ui/views/controls/button/label_button.h"
-#include "ui/views/controls/button/label_button_border.h"
 #include "ui/views/controls/button/md_text_button.h"
-#include "ui/views/painter.h"
+#include "ui/views/layout/fill_layout.h"
 
 namespace ash {
 
 LogoutButtonTray::LogoutButtonTray(WmShelf* wm_shelf)
-    : TrayBackgroundView(wm_shelf, false),
-      button_(nullptr),
-      login_status_(LoginStatus::NOT_LOGGED_IN),
+    : wm_shelf_(wm_shelf),
+      container_(new TrayContainer(wm_shelf)),
+      button_(views::MdTextButton::Create(this, base::string16())),
       show_logout_button_in_tray_(false) {
-  views::MdTextButton* button =
-      views::MdTextButton::Create(this, base::string16());
-  button->SetProminent(true);
-  button->SetBgColorOverride(gfx::kGoogleRed700);
-  // Base font size + 2 = 14.
-  // TODO(estade): should this 2 be shared with other tray views? See
-  // crbug.com/623987
-  button->AdjustFontSize(2);
-  button_ = button;
+  SetLayoutManager(new views::FillLayout);
+  AddChildView(container_);
 
-  // Since LogoutButtonTray has a red background and it is distinguished
-  // by itself, no separator is needed on its right side.
-  set_separator_visibility(false);
-  tray_container()->AddChildView(button_);
+  button_->SetProminent(true);
+  button_->SetBgColorOverride(gfx::kGoogleRed700);
+  button_->AdjustFontSize(kTrayTextFontSizeIncrease);
+
+  container_->AddChildView(button_);
   Shell::Get()->system_tray_notifier()->AddLogoutButtonObserver(this);
+  SetVisible(false);
 }
 
 LogoutButtonTray::~LogoutButtonTray() {
   Shell::Get()->system_tray_notifier()->RemoveLogoutButtonObserver(this);
 }
 
-void LogoutButtonTray::SetShelfAlignment(ShelfAlignment alignment) {
-  // We must first update the button so that
-  // TrayBackgroundView::SetShelfAlignment() can lay it out correctly.
-  UpdateButtonTextAndImage(login_status_, alignment);
-  TrayBackgroundView::SetShelfAlignment(alignment);
+void LogoutButtonTray::UpdateAfterShelfAlignmentChange() {
+  // We must first update the button so that |container_| can lay it out
+  // correctly.
+  UpdateButtonTextAndImage();
+  container_->UpdateAfterShelfAlignmentChange();
 }
 
-base::string16 LogoutButtonTray::GetAccessibleNameForTray() {
-  return button_->GetText();
-}
-
-void LogoutButtonTray::HideBubbleWithView(
-    const views::TrayBubbleView* bubble_view) {}
-
-void LogoutButtonTray::ClickedOutsideBubble() {}
-
 void LogoutButtonTray::ButtonPressed(views::Button* sender,
                                      const ui::Event& event) {
-  if (sender != button_) {
-    TrayBackgroundView::ButtonPressed(sender, event);
-    return;
-  }
+  DCHECK_EQ(button_, sender);
 
   if (dialog_duration_ <= base::TimeDelta()) {
     // Sign out immediately if |dialog_duration_| is non-positive.
@@ -91,6 +64,11 @@
   }
 }
 
+void LogoutButtonTray::GetAccessibleNodeData(ui::AXNodeData* node_data) {
+  View::GetAccessibleNodeData(node_data);
+  node_data->SetName(button_->GetText());
+}
+
 void LogoutButtonTray::OnShowLogoutButtonInTrayChanged(bool show) {
   show_logout_button_in_tray_ = show;
   UpdateVisibility();
@@ -100,29 +78,29 @@
   dialog_duration_ = duration;
 }
 
-void LogoutButtonTray::UpdateAfterLoginStatusChange(LoginStatus login_status) {
-  UpdateButtonTextAndImage(login_status, shelf_alignment());
+void LogoutButtonTray::UpdateAfterLoginStatusChange() {
+  UpdateButtonTextAndImage();
 }
 
 void LogoutButtonTray::UpdateVisibility() {
+  LoginStatus login_status = wm_shelf_->GetStatusAreaWidget()->login_status();
   SetVisible(show_logout_button_in_tray_ &&
-             login_status_ != LoginStatus::NOT_LOGGED_IN &&
-             login_status_ != LoginStatus::LOCKED);
+             login_status != LoginStatus::NOT_LOGGED_IN &&
+             login_status != LoginStatus::LOCKED);
 }
 
-void LogoutButtonTray::UpdateButtonTextAndImage(LoginStatus login_status,
-                                                ShelfAlignment alignment) {
-  login_status_ = login_status;
+void LogoutButtonTray::UpdateButtonTextAndImage() {
+  LoginStatus login_status = wm_shelf_->GetStatusAreaWidget()->login_status();
   const base::string16 title =
       user::GetLocalizedSignOutStringForStatus(login_status, false);
-  if (IsHorizontalAlignment(alignment)) {
+  if (wm_shelf_->IsHorizontalAlignment()) {
     button_->SetText(title);
-    button_->SetImage(views::LabelButton::STATE_NORMAL, gfx::ImageSkia());
+    button_->SetImage(views::Button::STATE_NORMAL, gfx::ImageSkia());
     button_->SetMinSize(gfx::Size(0, kTrayItemSize));
   } else {
     button_->SetText(base::string16());
     button_->SetAccessibleName(title);
-    button_->SetImage(views::LabelButton::STATE_NORMAL,
+    button_->SetImage(views::Button::STATE_NORMAL,
                       gfx::CreateVectorIcon(kShelfLogoutIcon, kTrayIconColor));
     button_->SetMinSize(gfx::Size(kTrayItemSize, kTrayItemSize));
   }
diff --git a/ash/system/session/logout_button_tray.h b/ash/system/session/logout_button_tray.h
index 37d7d28..0e4b26e7 100644
--- a/ash/system/session/logout_button_tray.h
+++ b/ash/system/session/logout_button_tray.h
@@ -6,49 +6,49 @@
 #define ASH_SYSTEM_SESSION_LOGOUT_BUTTON_TRAY_H_
 
 #include "ash/ash_export.h"
-#include "ash/login_status.h"
 #include "ash/system/session/logout_button_observer.h"
-#include "ash/system/tray/tray_background_view.h"
 #include "base/macros.h"
 #include "base/time/time.h"
 #include "ui/views/controls/button/button.h"
+#include "ui/views/view.h"
 
 namespace views {
-class LabelButton;
+class MdTextButton;
 }
 
 namespace ash {
+class TrayContainer;
+class WmShelf;
 
 // Adds a logout button to the launcher's status area if enabled by the
 // kShowLogoutButtonInTray pref.
-// TODO(mohsen): This is not using much of the TrayBackgroundView functionality.
-// Consider making this a regular View. See https://crbug.com/698134.
-class ASH_EXPORT LogoutButtonTray : public TrayBackgroundView,
-                                    public LogoutButtonObserver {
+class ASH_EXPORT LogoutButtonTray : public views::View,
+                                    public LogoutButtonObserver,
+                                    public views::ButtonListener {
  public:
   explicit LogoutButtonTray(WmShelf* wm_shelf);
   ~LogoutButtonTray() override;
 
-  // TrayBackgroundView:
-  void SetShelfAlignment(ShelfAlignment alignment) override;
-  base::string16 GetAccessibleNameForTray() override;
-  void HideBubbleWithView(const views::TrayBubbleView* bubble_view) override;
-  void ClickedOutsideBubble() override;
-  void ButtonPressed(views::Button* sender, const ui::Event& event) override;
+  void UpdateAfterLoginStatusChange();
+  void UpdateAfterShelfAlignmentChange();
+
+  // views::View:
+  void GetAccessibleNodeData(ui::AXNodeData* node_data) override;
 
   // LogoutButtonObserver:
   void OnShowLogoutButtonInTrayChanged(bool show) override;
   void OnLogoutDialogDurationChanged(base::TimeDelta duration) override;
 
-  void UpdateAfterLoginStatusChange(LoginStatus login_status);
+  // views::ButtonListener:
+  void ButtonPressed(views::Button* sender, const ui::Event& event) override;
 
  private:
   void UpdateVisibility();
-  void UpdateButtonTextAndImage(LoginStatus login_status,
-                                ShelfAlignment alignment);
+  void UpdateButtonTextAndImage();
 
-  views::LabelButton* button_;
-  LoginStatus login_status_;
+  WmShelf* const wm_shelf_;
+  TrayContainer* const container_;
+  views::MdTextButton* const button_;
   bool show_logout_button_in_tray_;
   base::TimeDelta dialog_duration_;
 
diff --git a/ash/system/status_area_widget.cc b/ash/system/status_area_widget.cc
index b78677f..232602fc 100644
--- a/ash/system/status_area_widget.cc
+++ b/ash/system/status_area_widget.cc
@@ -27,7 +27,7 @@
 
 StatusAreaWidget::StatusAreaWidget(WmWindow* status_container,
                                    WmShelf* wm_shelf)
-    : status_area_widget_delegate_(new StatusAreaWidgetDelegate),
+    : status_area_widget_delegate_(new StatusAreaWidgetDelegate(wm_shelf)),
       overview_button_tray_(nullptr),
       system_tray_(nullptr),
       web_notification_tray_(nullptr),
@@ -66,13 +66,12 @@
   // Initialize after all trays have been created.
   system_tray_->InitializeTrayItems(delegate, web_notification_tray_);
   web_notification_tray_->Initialize();
-  logout_button_tray_->Initialize();
   if (palette_tray_)
     palette_tray_->Initialize();
   virtual_keyboard_tray_->Initialize();
   ime_menu_tray_->Initialize();
   overview_button_tray_->Initialize();
-  SetShelfAlignment(system_tray_->shelf_alignment());
+  UpdateAfterShelfAlignmentChange();
   UpdateAfterLoginStatusChange(delegate->GetUserLoginStatus());
 }
 
@@ -104,22 +103,21 @@
   DCHECK_EQ(0, GetContentsView()->child_count());
 }
 
-void StatusAreaWidget::SetShelfAlignment(ShelfAlignment alignment) {
-  status_area_widget_delegate_->set_alignment(alignment);
+void StatusAreaWidget::UpdateAfterShelfAlignmentChange() {
   if (system_tray_)
-    system_tray_->SetShelfAlignment(alignment);
+    system_tray_->UpdateAfterShelfAlignmentChange();
   if (web_notification_tray_)
-    web_notification_tray_->SetShelfAlignment(alignment);
+    web_notification_tray_->UpdateAfterShelfAlignmentChange();
   if (logout_button_tray_)
-    logout_button_tray_->SetShelfAlignment(alignment);
+    logout_button_tray_->UpdateAfterShelfAlignmentChange();
   if (virtual_keyboard_tray_)
-    virtual_keyboard_tray_->SetShelfAlignment(alignment);
+    virtual_keyboard_tray_->UpdateAfterShelfAlignmentChange();
   if (ime_menu_tray_)
-    ime_menu_tray_->SetShelfAlignment(alignment);
+    ime_menu_tray_->UpdateAfterShelfAlignmentChange();
   if (palette_tray_)
-    palette_tray_->SetShelfAlignment(alignment);
+    palette_tray_->UpdateAfterShelfAlignmentChange();
   if (overview_button_tray_)
-    overview_button_tray_->SetShelfAlignment(alignment);
+    overview_button_tray_->UpdateAfterShelfAlignmentChange();
   status_area_widget_delegate_->UpdateLayout();
 }
 
@@ -132,7 +130,7 @@
   if (web_notification_tray_)
     web_notification_tray_->UpdateAfterLoginStatusChange(login_status);
   if (logout_button_tray_)
-    logout_button_tray_->UpdateAfterLoginStatusChange(login_status);
+    logout_button_tray_->UpdateAfterLoginStatusChange();
   if (overview_button_tray_)
     overview_button_tray_->UpdateAfterLoginStatusChange(login_status);
 }
@@ -184,7 +182,6 @@
   web_notification_tray_->UpdateShelfItemBackground(color);
   system_tray_->UpdateShelfItemBackground(color);
   virtual_keyboard_tray_->UpdateShelfItemBackground(color);
-  logout_button_tray_->UpdateShelfItemBackground(color);
   ime_menu_tray_->UpdateShelfItemBackground(color);
   if (palette_tray_)
     palette_tray_->UpdateShelfItemBackground(color);
diff --git a/ash/system/status_area_widget.h b/ash/system/status_area_widget.h
index 6372b27..511b9d1 100644
--- a/ash/system/status_area_widget.h
+++ b/ash/system/status_area_widget.h
@@ -38,7 +38,7 @@
   void Shutdown();
 
   // Update the alignment of the widget and tray views.
-  void SetShelfAlignment(ShelfAlignment alignment);
+  void UpdateAfterShelfAlignmentChange();
 
   // Called by the client when the login status changes. Caches login_status
   // and calls UpdateAfterLoginStatusChange for the system tray and the web
diff --git a/ash/system/status_area_widget_delegate.cc b/ash/system/status_area_widget_delegate.cc
index 9a969403..1209ba5 100644
--- a/ash/system/status_area_widget_delegate.cc
+++ b/ash/system/status_area_widget_delegate.cc
@@ -8,7 +8,6 @@
 #include "ash/root_window_controller.h"
 #include "ash/shelf/shelf_constants.h"
 #include "ash/shelf/wm_shelf.h"
-#include "ash/shelf/wm_shelf_util.h"
 #include "ash/shell.h"
 #include "ash/system/tray/tray_constants.h"
 #include "ash/wm_window.h"
@@ -46,8 +45,10 @@
 
 namespace ash {
 
-StatusAreaWidgetDelegate::StatusAreaWidgetDelegate()
-    : focus_cycler_for_testing_(nullptr), alignment_(SHELF_ALIGNMENT_BOTTOM) {
+StatusAreaWidgetDelegate::StatusAreaWidgetDelegate(WmShelf* wm_shelf)
+    : wm_shelf_(wm_shelf), focus_cycler_for_testing_(nullptr) {
+  DCHECK(wm_shelf_);
+
   // Allow the launcher to surrender the focus to another window upon
   // navigation completion by the user.
   set_allow_deactivate_on_esc(true);
@@ -121,7 +122,7 @@
 
   views::ColumnSet* columns = layout->AddColumnSet(0);
 
-  if (IsHorizontalAlignment(alignment_)) {
+  if (wm_shelf_->IsHorizontalAlignment()) {
     for (int c = child_count() - 1; c >= 0; --c) {
       views::View* child = child_at(c);
       if (!child->visible())
@@ -174,8 +175,8 @@
 void StatusAreaWidgetDelegate::SetBorderOnChild(views::View* child,
                                                 bool extend_border_to_edge) {
   // Tray views are laid out right-to-left or bottom-to-top.
-  const bool horizontal_alignment = IsHorizontalAlignment(alignment_);
-  const int padding = (GetShelfConstant(SHELF_SIZE) - kTrayItemSize) / 2;
+  const bool horizontal_alignment = wm_shelf_->IsHorizontalAlignment();
+  const int padding = (kShelfSize - kTrayItemSize) / 2;
 
   const int top_edge = horizontal_alignment ? padding : 0;
   const int left_edge = horizontal_alignment ? 0 : padding;
diff --git a/ash/system/status_area_widget_delegate.h b/ash/system/status_area_widget_delegate.h
index c60df6a..38a248b1 100644
--- a/ash/system/status_area_widget_delegate.h
+++ b/ash/system/status_area_widget_delegate.h
@@ -14,12 +14,13 @@
 
 namespace ash {
 class FocusCycler;
+class WmShelf;
 
 // The View for the status area widget.
 class ASH_EXPORT StatusAreaWidgetDelegate : public views::AccessiblePaneView,
                                             public views::WidgetDelegate {
  public:
-  StatusAreaWidgetDelegate();
+  explicit StatusAreaWidgetDelegate(WmShelf* wm_shelf);
   ~StatusAreaWidgetDelegate() override;
 
   // Add a tray view to the widget (e.g. system tray, web notifications).
@@ -31,8 +32,6 @@
   // Sets the focus cycler.
   void SetFocusCyclerForTesting(const FocusCycler* focus_cycler);
 
-  void set_alignment(ShelfAlignment alignment) { alignment_ = alignment; }
-
   // Overridden from views::AccessiblePaneView.
   View* GetDefaultFocusableChild() override;
 
@@ -60,11 +59,9 @@
   // screen.
   void SetBorderOnChild(views::View* child, bool extend_border_to_edge);
 
+  WmShelf* const wm_shelf_;
   const FocusCycler* focus_cycler_for_testing_;
 
-  // TODO(jamescook): Get this from WmShelf.
-  ShelfAlignment alignment_;
-
   DISALLOW_COPY_AND_ASSIGN(StatusAreaWidgetDelegate);
 };
 
diff --git a/ash/system/tray/system_tray.cc b/ash/system/tray/system_tray.cc
index 5139ec0..f1909a0 100644
--- a/ash/system/tray/system_tray.cc
+++ b/ash/system/tray/system_tray.cc
@@ -40,6 +40,7 @@
 #include "ash/system/tray/system_tray_item.h"
 #include "ash/system/tray/tray_bubble_wrapper.h"
 #include "ash/system/tray/tray_constants.h"
+#include "ash/system/tray/tray_container.h"
 #include "ash/system/tray_accessibility.h"
 #include "ash/system/tray_caps_lock.h"
 #include "ash/system/tray_tracing.h"
@@ -207,7 +208,7 @@
 
 // SystemTray
 
-SystemTray::SystemTray(WmShelf* wm_shelf) : TrayBackgroundView(wm_shelf, true) {
+SystemTray::SystemTray(WmShelf* wm_shelf) : TrayBackgroundView(wm_shelf) {
   SetInkDropMode(InkDropMode::ON);
 
   // Since user avatar is on the right hand side of System tray of a
@@ -298,7 +299,7 @@
   SystemTrayDelegate* delegate = Shell::Get()->system_tray_delegate();
   views::View* tray_item =
       item_ptr->CreateTrayView(delegate->GetUserLoginStatus());
-  item_ptr->UpdateAfterShelfAlignmentChange(shelf_alignment());
+  item_ptr->UpdateAfterShelfAlignmentChange();
 
   if (tray_item) {
     tray_container()->AddChildViewAt(tray_item, 0);
@@ -369,18 +370,13 @@
   for (const auto& item : items_)
     item->UpdateAfterLoginStatusChange(login_status);
 
-  // Items default to SHELF_ALIGNMENT_BOTTOM. Update them if the initial
-  // position of the shelf differs.
-  if (!IsHorizontalAlignment(shelf_alignment()))
-    UpdateAfterShelfAlignmentChange(shelf_alignment());
-
   SetVisible(true);
   PreferredSizeChanged();
 }
 
-void SystemTray::UpdateAfterShelfAlignmentChange(ShelfAlignment alignment) {
+void SystemTray::UpdateItemsAfterShelfAlignmentChange() {
   for (const auto& item : items_)
-    item->UpdateAfterShelfAlignmentChange(alignment);
+    item->UpdateAfterShelfAlignmentChange();
 }
 
 bool SystemTray::ShouldShowShelf() const {
@@ -547,11 +543,9 @@
                                                     base::kKeepAmPm);
 }
 
-void SystemTray::SetShelfAlignment(ShelfAlignment alignment) {
-  if (alignment == shelf_alignment())
-    return;
-  TrayBackgroundView::SetShelfAlignment(alignment);
-  UpdateAfterShelfAlignmentChange(alignment);
+void SystemTray::UpdateAfterShelfAlignmentChange() {
+  TrayBackgroundView::UpdateAfterShelfAlignmentChange();
+  UpdateItemsAfterShelfAlignmentChange();
   // Destroy any existing bubble so that it is rebuilt correctly.
   CloseSystemBubbleAndDeactivateSystemTray();
   // Rebuild any notification bubble.
diff --git a/ash/system/tray/system_tray.h b/ash/system/tray/system_tray.h
index e54d30d..603997a 100644
--- a/ash/system/tray/system_tray.h
+++ b/ash/system/tray/system_tray.h
@@ -86,7 +86,7 @@
   void UpdateAfterLoginStatusChange(LoginStatus login_status);
 
   // Updates the items when the shelf alignment changes.
-  void UpdateAfterShelfAlignmentChange(ShelfAlignment alignment);
+  void UpdateItemsAfterShelfAlignmentChange();
 
   // Returns true if the shelf should be forced visible when auto-hidden.
   bool ShouldShowShelf() const;
@@ -115,7 +115,7 @@
   TrayAudio* GetTrayAudio() const;
 
   // Overridden from TrayBackgroundView.
-  void SetShelfAlignment(ShelfAlignment alignment) override;
+  void UpdateAfterShelfAlignmentChange() override;
   void AnchorUpdated() override;
   base::string16 GetAccessibleNameForTray() override;
   void BubbleResized(const views::TrayBubbleView* bubble_view) override;
diff --git a/ash/system/tray/system_tray_item.cc b/ash/system/tray/system_tray_item.cc
index dcbc597..51fdb0a 100644
--- a/ash/system/tray/system_tray_item.cc
+++ b/ash/system/tray/system_tray_item.cc
@@ -45,8 +45,7 @@
 
 void SystemTrayItem::UpdateAfterLoginStatusChange(LoginStatus status) {}
 
-void SystemTrayItem::UpdateAfterShelfAlignmentChange(ShelfAlignment alignment) {
-}
+void SystemTrayItem::UpdateAfterShelfAlignmentChange() {}
 
 void SystemTrayItem::ShowDetailedView(int for_seconds, bool activate) {
   system_tray()->ShowDetailedView(this, for_seconds, activate,
diff --git a/ash/system/tray/system_tray_item.h b/ash/system/tray/system_tray_item.h
index ddd9d936..9b3f5cc4 100644
--- a/ash/system/tray/system_tray_item.h
+++ b/ash/system/tray/system_tray_item.h
@@ -101,7 +101,7 @@
 
   // Updates the tray view (if applicable) when shelf's alignment changes.
   // The default implementation does nothing.
-  virtual void UpdateAfterShelfAlignmentChange(ShelfAlignment alignment);
+  virtual void UpdateAfterShelfAlignmentChange();
 
   // Shows the detailed view for this item. If the main popup for the tray is
   // currently visible, then making this call would use the existing window to
diff --git a/ash/system/tray/tray_background_view.cc b/ash/system/tray/tray_background_view.cc
index f3f1ab33..b26fd6f 100644
--- a/ash/system/tray/tray_background_view.cc
+++ b/ash/system/tray/tray_background_view.cc
@@ -9,8 +9,8 @@
 #include "ash/ash_constants.h"
 #include "ash/shelf/shelf_constants.h"
 #include "ash/shelf/wm_shelf.h"
-#include "ash/shelf/wm_shelf_util.h"
 #include "ash/system/tray/tray_constants.h"
+#include "ash/system/tray/tray_container.h"
 #include "ash/system/tray/tray_event_filter.h"
 #include "ash/wm_window.h"
 #include "base/memory/ptr_util.h"
@@ -18,18 +18,16 @@
 #include "ui/compositor/layer.h"
 #include "ui/compositor/layer_animation_element.h"
 #include "ui/compositor/scoped_layer_animation_settings.h"
-#include "ui/events/event_constants.h"
 #include "ui/gfx/animation/tween.h"
 #include "ui/gfx/canvas.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/gfx/geometry/size.h"
-#include "ui/gfx/scoped_canvas.h"
 #include "ui/gfx/transform.h"
 #include "ui/views/animation/flood_fill_ink_drop_ripple.h"
 #include "ui/views/animation/ink_drop_highlight.h"
 #include "ui/views/animation/ink_drop_mask.h"
 #include "ui/views/background.h"
-#include "ui/views/layout/box_layout.h"
+#include "ui/views/layout/fill_layout.h"
 
 namespace {
 
@@ -53,9 +51,9 @@
 
 // Returns background insets relative to the contents bounds of the view and
 // mirrored if RTL mode is active.
-gfx::Insets GetMirroredBackgroundInsets(ash::ShelfAlignment shelf_alignment) {
+gfx::Insets GetMirroredBackgroundInsets(bool is_shelf_horizontal) {
   gfx::Insets insets;
-  if (IsHorizontalAlignment(shelf_alignment)) {
+  if (is_shelf_horizontal) {
     insets.Set(0, ash::kHitRegionPadding, 0,
                ash::kHitRegionPadding + ash::kSeparatorWidth);
   } else {
@@ -107,15 +105,13 @@
   void set_color(SkColor color) { color_ = color; }
 
  private:
-  WmShelf* GetShelf() const { return tray_background_view_->shelf(); }
-
   // Overridden from views::Background.
   void Paint(gfx::Canvas* canvas, views::View* view) const override {
     cc::PaintFlags background_flags;
     background_flags.setAntiAlias(true);
     background_flags.setColor(color_);
-    gfx::Insets insets =
-        GetMirroredBackgroundInsets(GetShelf()->GetAlignment());
+    gfx::Insets insets = GetMirroredBackgroundInsets(
+        tray_background_view_->shelf()->IsHorizontalAlignment());
     gfx::Rect bounds = view->GetLocalBounds();
     bounds.Inset(insets);
     canvas->DrawRoundRect(bounds, kTrayRoundedBorderRadius, background_flags);
@@ -129,80 +125,15 @@
   DISALLOW_COPY_AND_ASSIGN(TrayBackground);
 };
 
-TrayBackgroundView::TrayContainer::TrayContainer(ShelfAlignment alignment)
-    : alignment_(alignment) {
-  UpdateLayout();
-}
-
-void TrayBackgroundView::TrayContainer::SetAlignment(ShelfAlignment alignment) {
-  if (alignment_ == alignment)
-    return;
-  alignment_ = alignment;
-  UpdateLayout();
-}
-
-void TrayBackgroundView::TrayContainer::SetMargin(int main_axis_margin,
-                                                  int cross_axis_margin) {
-  main_axis_margin_ = main_axis_margin;
-  cross_axis_margin_ = cross_axis_margin;
-  UpdateLayout();
-}
-
-void TrayBackgroundView::TrayContainer::ChildPreferredSizeChanged(
-    views::View* child) {
-  PreferredSizeChanged();
-}
-
-void TrayBackgroundView::TrayContainer::ChildVisibilityChanged(View* child) {
-  PreferredSizeChanged();
-}
-
-void TrayBackgroundView::TrayContainer::ViewHierarchyChanged(
-    const ViewHierarchyChangedDetails& details) {
-  if (details.parent == this)
-    PreferredSizeChanged();
-}
-
-void TrayBackgroundView::TrayContainer::UpdateLayout() {
-  bool is_horizontal = IsHorizontalAlignment(alignment_);
-
-  // Adjust the size of status tray dark background by adding additional
-  // empty border.
-  views::BoxLayout::Orientation orientation =
-      is_horizontal ? views::BoxLayout::kHorizontal
-                    : views::BoxLayout::kVertical;
-
-  const int hit_region_with_separator = kHitRegionPadding + kSeparatorWidth;
-  gfx::Insets insets(
-      is_horizontal
-          ? gfx::Insets(0, kHitRegionPadding, 0, hit_region_with_separator)
-          : gfx::Insets(kHitRegionPadding, 0, hit_region_with_separator, 0));
-  MirrorInsetsIfNecessary(&insets);
-  SetBorder(views::CreateEmptyBorder(insets));
-
-  int horizontal_margin = main_axis_margin_;
-  int vertical_margin = cross_axis_margin_;
-  if (!is_horizontal)
-    std::swap(horizontal_margin, vertical_margin);
-  views::BoxLayout* layout =
-      new views::BoxLayout(orientation, horizontal_margin, vertical_margin, 0);
-
-  layout->set_minimum_cross_axis_size(kTrayItemSize);
-  views::View::SetLayoutManager(layout);
-
-  PreferredSizeChanged();
-}
-
 ////////////////////////////////////////////////////////////////////////////////
 // TrayBackgroundView
 
-TrayBackgroundView::TrayBackgroundView(WmShelf* wm_shelf, bool draws_background)
+TrayBackgroundView::TrayBackgroundView(WmShelf* wm_shelf)
     // Note the ink drop style is ignored.
     : ActionableView(nullptr, TrayPopupInkDropStyle::FILL_BOUNDS),
       wm_shelf_(wm_shelf),
-      tray_container_(nullptr),
-      shelf_alignment_(SHELF_ALIGNMENT_BOTTOM),
-      background_(nullptr),
+      tray_container_(new TrayContainer(wm_shelf)),
+      background_(new TrayBackground(this)),
       is_active_(false),
       separator_visible_(true),
       widget_observer_(new TrayWidgetObserver(this)) {
@@ -211,13 +142,9 @@
   set_ink_drop_base_color(kShelfInkDropBaseColor);
   set_ink_drop_visible_opacity(kShelfInkDropVisibleOpacity);
 
-  SetLayoutManager(new views::BoxLayout(views::BoxLayout::kVertical, 0, 0, 0));
+  SetLayoutManager(new views::FillLayout);
 
-  tray_container_ = new TrayContainer(shelf_alignment_);
-  if (draws_background) {
-    background_ = new TrayBackground(this);
-    tray_container_->set_background(background_);
-  }
+  tray_container_->set_background(background_);
   AddChildView(tray_container_);
 
   tray_event_filter_.reset(new TrayEventFilter);
@@ -346,9 +273,8 @@
   return highlight;
 }
 
-void TrayBackgroundView::SetShelfAlignment(ShelfAlignment alignment) {
-  shelf_alignment_ = alignment;
-  tray_container_->SetAlignment(alignment);
+void TrayBackgroundView::UpdateAfterShelfAlignmentChange() {
+  tray_container_->UpdateAfterShelfAlignmentChange();
 }
 
 void TrayBackgroundView::OnImplicitAnimationsCompleted() {
@@ -374,7 +300,7 @@
 
 void TrayBackgroundView::HideTransformation() {
   gfx::Transform transform;
-  if (IsHorizontalAlignment(shelf_alignment_))
+  if (wm_shelf_->IsHorizontalAlignment())
     transform.Translate(width(), 0.0f);
   else
     transform.Translate(0.0f, height());
@@ -382,9 +308,9 @@
 }
 
 TrayBubbleView::AnchorAlignment TrayBackgroundView::GetAnchorAlignment() const {
-  if (shelf_alignment_ == SHELF_ALIGNMENT_LEFT)
+  if (wm_shelf_->alignment() == SHELF_ALIGNMENT_LEFT)
     return TrayBubbleView::ANCHOR_ALIGNMENT_LEFT;
-  if (shelf_alignment_ == SHELF_ALIGNMENT_RIGHT)
+  if (wm_shelf_->alignment() == SHELF_ALIGNMENT_RIGHT)
     return TrayBubbleView::ANCHOR_ALIGNMENT_RIGHT;
   return TrayBubbleView::ANCHOR_ALIGNMENT_BOTTOM;
 }
@@ -404,10 +330,8 @@
 }
 
 void TrayBackgroundView::UpdateShelfItemBackground(SkColor color) {
-  if (background_) {
-    background_->set_color(color);
-    SchedulePaint();
-  }
+  background_->set_color(color);
+  SchedulePaint();
 }
 
 views::View* TrayBackgroundView::GetBubbleAnchor() const {
@@ -475,14 +399,14 @@
   const gfx::Rect local_bounds = GetLocalBounds();
   const SkColor color = SkColorSetA(SK_ColorWHITE, 0x4D);
 
-  if (IsHorizontalAlignment(shelf_alignment_)) {
+  if (wm_shelf_->IsHorizontalAlignment()) {
     const gfx::PointF point(
         base::i18n::IsRTL() ? 0 : (local_bounds.width() - kSeparatorWidth),
-        (GetShelfConstant(SHELF_SIZE) - kTrayItemSize) / 2);
+        (kShelfSize - kTrayItemSize) / 2);
     const gfx::Vector2dF vector(0, kTrayItemSize);
     canvas->Draw1pxLine(point, point + vector, color);
   } else {
-    const gfx::PointF point((GetShelfConstant(SHELF_SIZE) - kTrayItemSize) / 2,
+    const gfx::PointF point((kShelfSize - kTrayItemSize) / 2,
                             local_bounds.height() - kSeparatorWidth);
     const gfx::Vector2dF vector(kTrayItemSize, 0);
     canvas->Draw1pxLine(point, point + vector, color);
@@ -490,7 +414,8 @@
 }
 
 gfx::Insets TrayBackgroundView::GetBackgroundInsets() const {
-  gfx::Insets insets = GetMirroredBackgroundInsets(shelf_alignment_);
+  gfx::Insets insets =
+      GetMirroredBackgroundInsets(wm_shelf_->IsHorizontalAlignment());
 
   // |insets| are relative to contents bounds. Change them to be relative to
   // local bounds.
diff --git a/ash/system/tray/tray_background_view.h b/ash/system/tray/tray_background_view.h
index 7adc60b..456d360 100644
--- a/ash/system/tray/tray_background_view.h
+++ b/ash/system/tray/tray_background_view.h
@@ -8,7 +8,6 @@
 #include <memory>
 
 #include "ash/ash_export.h"
-#include "ash/public/cpp/shelf_types.h"
 #include "ash/shelf/shelf_background_animator_observer.h"
 #include "ash/system/tray/actionable_view.h"
 #include "base/macros.h"
@@ -17,53 +16,22 @@
 #include "ui/views/bubble/tray_bubble_view.h"
 
 namespace ash {
-class TrayEventFilter;
 class TrayBackground;
+class TrayContainer;
+class TrayEventFilter;
 class WmShelf;
 
-// Base class for children of StatusAreaWidget: SystemTray, WebNotificationTray,
-// LogoutButtonTray, OverviewButtonTray.
-// This class handles setting and animating the background when the Launcher
-// is shown/hidden. It also inherits from ActionableView so that the tray
-// items can override PerformAction when clicked on.
+// Base class for some children of StatusAreaWidget. This class handles setting
+// and animating the background when the Launcher is shown/hidden. It also
+// inherits from ActionableView so that the tray items can override
+// PerformAction when clicked on.
 class ASH_EXPORT TrayBackgroundView : public ActionableView,
                                       public ui::ImplicitAnimationObserver,
                                       public ShelfBackgroundAnimatorObserver {
  public:
   static const char kViewClassName[];
 
-  // Base class for tray containers. Sets the border and layout. The container
-  // auto-resizes the widget when necessary.
-  class TrayContainer : public views::View {
-   public:
-    explicit TrayContainer(ShelfAlignment alignment);
-    ~TrayContainer() override {}
-
-    void SetAlignment(ShelfAlignment alignment);
-
-    void SetMargin(int main_axis_margin, int cross_axis_margin);
-
-   protected:
-    // views::View:
-    void ChildPreferredSizeChanged(views::View* child) override;
-    void ChildVisibilityChanged(View* child) override;
-    void ViewHierarchyChanged(
-        const ViewHierarchyChangedDetails& details) override;
-
-   private:
-    void UpdateLayout();
-
-    ShelfAlignment alignment_;
-    int main_axis_margin_ = 0;
-    int cross_axis_margin_ = 0;
-
-    DISALLOW_COPY_AND_ASSIGN(TrayContainer);
-  };
-
-  // TODO(mohsen): Remove |draws_background| paramter when LogoutButtonTray, as
-  // the only reason for existence of this parameter, is no longer a
-  // TrayBackgroundView. See https://crbug.com/698134.
-  TrayBackgroundView(WmShelf* wm_shelf, bool draws_background);
+  explicit TrayBackgroundView(WmShelf* wm_shelf);
   ~TrayBackgroundView() override;
 
   // Called after the tray has been added to the widget containing it.
@@ -86,7 +54,7 @@
       const override;
 
   // Called whenever the shelf alignment changes.
-  virtual void SetShelfAlignment(ShelfAlignment alignment);
+  virtual void UpdateAfterShelfAlignmentChange();
 
   // Called when the anchor (tray or bubble) may have moved or changed.
   virtual void AnchorUpdated() {}
@@ -112,7 +80,6 @@
   bool is_active() const { return is_active_; }
 
   TrayContainer* tray_container() const { return tray_container_; }
-  ShelfAlignment shelf_alignment() const { return shelf_alignment_; }
   TrayEventFilter* tray_event_filter() { return tray_event_filter_.get(); }
   WmShelf* shelf() { return wm_shelf_; }
 
@@ -165,10 +132,6 @@
   // Convenience pointer to the contents view.
   TrayContainer* tray_container_;
 
-  // Shelf alignment.
-  // TODO(jamescook): Don't cache this, get it from WmShelf.
-  ShelfAlignment shelf_alignment_;
-
   // Owned by the view passed to SetContents().
   TrayBackground* background_;
 
diff --git a/ash/system/tray/tray_constants.cc b/ash/system/tray/tray_constants.cc
index 19038b46..16a4eaf 100644
--- a/ash/system/tray/tray_constants.cc
+++ b/ash/system/tray/tray_constants.cc
@@ -22,6 +22,8 @@
 const int kBubblePaddingVerticalBottom = 3;
 const int kBubblePaddingVerticalSide = 15;
 
+const int kTrayTextFontSizeIncrease = 2;
+
 // Top inset of system tray bubble for bottom anchor alignment.
 const int kTrayBubbleAnchorTopInsetBottomAnchor = 3;
 
diff --git a/ash/system/tray/tray_constants.h b/ash/system/tray/tray_constants.h
index 16ae1a2a..6443155f 100644
--- a/ash/system/tray/tray_constants.h
+++ b/ash/system/tray/tray_constants.h
@@ -22,6 +22,10 @@
 extern const int kBubblePaddingVerticalBottom;
 extern const int kBubblePaddingVerticalSide;
 
+// The size delta between the default font and the font size found in tray
+// items like labels and buttons.
+extern const int kTrayTextFontSizeIncrease;
+
 extern const int kTrayBubbleAnchorTopInsetBottomAnchor;
 
 extern const int kTrayImageItemHorizontalPaddingVerticalAlignment;
diff --git a/ash/system/tray/tray_container.cc b/ash/system/tray/tray_container.cc
new file mode 100644
index 0000000..0514967
--- /dev/null
+++ b/ash/system/tray/tray_container.cc
@@ -0,0 +1,80 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ash/system/tray/tray_container.h"
+
+#include <utility>
+
+#include "ash/shelf/wm_shelf.h"
+#include "ash/system/tray/tray_constants.h"
+#include "ui/gfx/geometry/insets.h"
+#include "ui/views/border.h"
+#include "ui/views/layout/box_layout.h"
+
+namespace ash {
+
+TrayContainer::TrayContainer(WmShelf* wm_shelf) : wm_shelf_(wm_shelf) {
+  DCHECK(wm_shelf_);
+
+  UpdateLayout();
+}
+
+TrayContainer::~TrayContainer() {}
+
+void TrayContainer::UpdateAfterShelfAlignmentChange() {
+  UpdateLayout();
+}
+
+void TrayContainer::SetMargin(int main_axis_margin, int cross_axis_margin) {
+  main_axis_margin_ = main_axis_margin;
+  cross_axis_margin_ = cross_axis_margin;
+  UpdateLayout();
+}
+
+void TrayContainer::ChildPreferredSizeChanged(views::View* child) {
+  PreferredSizeChanged();
+}
+
+void TrayContainer::ChildVisibilityChanged(View* child) {
+  PreferredSizeChanged();
+}
+
+void TrayContainer::ViewHierarchyChanged(
+    const ViewHierarchyChangedDetails& details) {
+  if (details.parent == this)
+    PreferredSizeChanged();
+}
+
+void TrayContainer::UpdateLayout() {
+  const bool is_horizontal = wm_shelf_->IsHorizontalAlignment();
+
+  // Adjust the size of status tray dark background by adding additional
+  // empty border.
+  views::BoxLayout::Orientation orientation =
+      is_horizontal ? views::BoxLayout::kHorizontal
+                    : views::BoxLayout::kVertical;
+
+  const int hit_region_with_separator = kHitRegionPadding + kSeparatorWidth;
+  gfx::Insets insets(
+      is_horizontal
+          ? gfx::Insets(0, kHitRegionPadding, 0, hit_region_with_separator)
+          : gfx::Insets(kHitRegionPadding, 0, hit_region_with_separator, 0));
+  if (base::i18n::IsRTL())
+    insets.Set(insets.top(), insets.right(), insets.bottom(), insets.left());
+  SetBorder(views::CreateEmptyBorder(insets));
+
+  int horizontal_margin = main_axis_margin_;
+  int vertical_margin = cross_axis_margin_;
+  if (!is_horizontal)
+    std::swap(horizontal_margin, vertical_margin);
+  views::BoxLayout* layout =
+      new views::BoxLayout(orientation, horizontal_margin, vertical_margin, 0);
+
+  layout->set_minimum_cross_axis_size(kTrayItemSize);
+  views::View::SetLayoutManager(layout);
+
+  PreferredSizeChanged();
+}
+
+}  // namespace ash
diff --git a/ash/system/tray/tray_container.h b/ash/system/tray/tray_container.h
new file mode 100644
index 0000000..c4c7ea76
--- /dev/null
+++ b/ash/system/tray/tray_container.h
@@ -0,0 +1,45 @@
+// 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 ASH_SYSTEM_TRAY_TRAY_CONTAINER_H_
+#define ASH_SYSTEM_TRAY_TRAY_CONTAINER_H_
+
+#include "base/macros.h"
+#include "ui/views/view.h"
+
+namespace ash {
+class WmShelf;
+
+// Base class for tray containers. Sets the border and layout. The container
+// auto-resizes the widget when necessary.
+class TrayContainer : public views::View {
+ public:
+  explicit TrayContainer(WmShelf* wm_shelf);
+  ~TrayContainer() override;
+
+  void UpdateAfterShelfAlignmentChange();
+
+  void SetMargin(int main_axis_margin, int cross_axis_margin);
+
+ protected:
+  // views::View:
+  void ChildPreferredSizeChanged(views::View* child) override;
+  void ChildVisibilityChanged(View* child) override;
+  void ViewHierarchyChanged(
+      const ViewHierarchyChangedDetails& details) override;
+
+ private:
+  void UpdateLayout();
+
+  WmShelf* const wm_shelf_;
+
+  int main_axis_margin_ = 0;
+  int cross_axis_margin_ = 0;
+
+  DISALLOW_COPY_AND_ASSIGN(TrayContainer);
+};
+
+}  // namespace ash
+
+#endif  // ASH_SYSTEM_TRAY_TRAY_CONTAINER_H_
diff --git a/ash/system/tray/tray_item_view.cc b/ash/system/tray/tray_item_view.cc
index f04eb59..1a5845e 100644
--- a/ash/system/tray/tray_item_view.cc
+++ b/ash/system/tray/tray_item_view.cc
@@ -5,7 +5,7 @@
 #include "ash/system/tray/tray_item_view.h"
 
 #include "ash/public/cpp/shelf_types.h"
-#include "ash/shelf/wm_shelf_util.h"
+#include "ash/shelf/wm_shelf.h"
 #include "ash/system/tray/system_tray.h"
 #include "ash/system/tray/system_tray_item.h"
 #include "ash/system/tray/tray_constants.h"
@@ -90,7 +90,7 @@
   gfx::Size size = rect.size();
   if (!animation_.get() || !animation_->is_animating())
     return size;
-  if (IsHorizontalAlignment(owner()->system_tray()->shelf_alignment())) {
+  if (owner()->system_tray()->shelf()->IsHorizontalAlignment()) {
     size.set_width(std::max(
         1, static_cast<int>(size.width() * animation_->GetCurrentValue())));
   } else {
@@ -110,7 +110,7 @@
 
 void TrayItemView::AnimationProgressed(const gfx::Animation* animation) {
   gfx::Transform transform;
-  if (IsHorizontalAlignment(owner()->system_tray()->shelf_alignment())) {
+  if (owner()->system_tray()->shelf()->IsHorizontalAlignment()) {
     transform.Translate(0, animation->CurrentValueBetween(
                                static_cast<double>(height()) / 2, 0.));
   } else {
diff --git a/ash/system/tray/tray_utils.cc b/ash/system/tray/tray_utils.cc
index baf8fa96..6b60b125 100644
--- a/ash/system/tray/tray_utils.cc
+++ b/ash/system/tray/tray_utils.cc
@@ -4,6 +4,7 @@
 
 #include "ash/system/tray/tray_utils.h"
 
+#include "ash/system/tray/tray_constants.h"
 #include "ui/gfx/font_list.h"
 #include "ui/views/controls/label.h"
 
@@ -13,8 +14,8 @@
   // The text is drawn on an transparent bg, so we must disable subpixel
   // rendering.
   label->SetSubpixelRenderingEnabled(false);
-  label->SetFontList(
-      gfx::FontList().Derive(2, gfx::Font::NORMAL, gfx::Font::Weight::MEDIUM));
+  label->SetFontList(gfx::FontList().Derive(
+      kTrayTextFontSizeIncrease, gfx::Font::NORMAL, gfx::Font::Weight::MEDIUM));
 }
 
 }  // namespace ash
diff --git a/ash/system/user/tray_user.cc b/ash/system/user/tray_user.cc
index c37e57b5..80ae36d 100644
--- a/ash/system/user/tray_user.cc
+++ b/ash/system/user/tray_user.cc
@@ -5,7 +5,7 @@
 #include "ash/system/user/tray_user.h"
 
 #include "ash/session/session_controller.h"
-#include "ash/shelf/wm_shelf_util.h"
+#include "ash/shelf/wm_shelf.h"
 #include "ash/shell.h"
 #include "ash/shell_port.h"
 #include "ash/strings/grit/ash_strings.h"
@@ -169,11 +169,11 @@
   UpdateLayoutOfItem();
 }
 
-void TrayUser::UpdateAfterShelfAlignmentChange(ShelfAlignment alignment) {
+void TrayUser::UpdateAfterShelfAlignmentChange() {
   // Inactive users won't have a layout.
   if (!layout_view_)
     return;
-  if (IsHorizontalAlignment(alignment)) {
+  if (system_tray()->shelf()->IsHorizontalAlignment()) {
     if (avatar_) {
       avatar_->SetCornerRadii(0, kTrayRoundedBorderRadius,
                               kTrayRoundedBorderRadius, 0);
@@ -249,7 +249,7 @@
 }
 
 void TrayUser::UpdateLayoutOfItem() {
-  UpdateAfterShelfAlignmentChange(system_tray()->shelf_alignment());
+  UpdateAfterShelfAlignmentChange();
 }
 
 }  // namespace ash
diff --git a/ash/system/user/tray_user.h b/ash/system/user/tray_user.h
index 35461e6..017cf74 100644
--- a/ash/system/user/tray_user.h
+++ b/ash/system/user/tray_user.h
@@ -67,7 +67,7 @@
   void DestroyTrayView() override;
   void DestroyDefaultView() override;
   void UpdateAfterLoginStatusChange(LoginStatus status) override;
-  void UpdateAfterShelfAlignmentChange(ShelfAlignment alignment) override;
+  void UpdateAfterShelfAlignmentChange() override;
 
   // Overridden from SessionObserver.
   void OnActiveUserSessionChanged(const AccountId& account_id) override;
diff --git a/ash/system/virtual_keyboard/virtual_keyboard_tray.cc b/ash/system/virtual_keyboard/virtual_keyboard_tray.cc
index f953e2415..7dd3599 100644
--- a/ash/system/virtual_keyboard/virtual_keyboard_tray.cc
+++ b/ash/system/virtual_keyboard/virtual_keyboard_tray.cc
@@ -13,6 +13,7 @@
 #include "ash/shell.h"
 #include "ash/strings/grit/ash_strings.h"
 #include "ash/system/tray/tray_constants.h"
+#include "ash/system/tray/tray_container.h"
 #include "ash/wm_window.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/display/display.h"
@@ -25,13 +26,18 @@
 namespace ash {
 
 VirtualKeyboardTray::VirtualKeyboardTray(WmShelf* wm_shelf)
-    : TrayBackgroundView(wm_shelf, true),
+    : TrayBackgroundView(wm_shelf),
       icon_(new views::ImageView),
       wm_shelf_(wm_shelf) {
   SetInkDropMode(InkDropMode::ON);
 
-  icon_->SetImage(gfx::CreateVectorIcon(kShelfKeyboardIcon, kShelfIconColor));
-  SetIconBorderForShelfAlignment();
+  gfx::ImageSkia image =
+      gfx::CreateVectorIcon(kShelfKeyboardIcon, kShelfIconColor);
+  icon_->SetImage(image);
+  const int vertical_padding = (kTrayItemSize - image.height()) / 2;
+  const int horizontal_padding = (kTrayItemSize - image.width()) / 2;
+  icon_->SetBorder(views::CreateEmptyBorder(
+      gfx::Insets(vertical_padding, horizontal_padding)));
   tray_container()->AddChildView(icon_);
 
   // The Shell may not exist in some unit tests.
@@ -49,14 +55,6 @@
     Shell::Get()->keyboard_ui()->RemoveObserver(this);
 }
 
-void VirtualKeyboardTray::SetShelfAlignment(ShelfAlignment alignment) {
-  if (alignment == shelf_alignment())
-    return;
-
-  TrayBackgroundView::SetShelfAlignment(alignment);
-  SetIconBorderForShelfAlignment();
-}
-
 base::string16 VirtualKeyboardTray::GetAccessibleNameForTray() {
   return l10n_util::GetStringUTF16(
       IDS_ASH_VIRTUAL_KEYBOARD_TRAY_ACCESSIBLE_NAME);
@@ -99,14 +97,6 @@
 
 void VirtualKeyboardTray::OnKeyboardClosed() {}
 
-void VirtualKeyboardTray::SetIconBorderForShelfAlignment() {
-  const gfx::ImageSkia& image = icon_->GetImage();
-  const int vertical_padding = (kTrayItemSize - image.height()) / 2;
-  const int horizontal_padding = (kTrayItemSize - image.width()) / 2;
-  icon_->SetBorder(views::CreateEmptyBorder(
-      gfx::Insets(vertical_padding, horizontal_padding)));
-}
-
 void VirtualKeyboardTray::ObserveKeyboardController() {
   keyboard::KeyboardController* keyboard_controller =
       keyboard::KeyboardController::GetInstance();
diff --git a/ash/system/virtual_keyboard/virtual_keyboard_tray.h b/ash/system/virtual_keyboard/virtual_keyboard_tray.h
index df8bad6d..92c43775 100644
--- a/ash/system/virtual_keyboard/virtual_keyboard_tray.h
+++ b/ash/system/virtual_keyboard/virtual_keyboard_tray.h
@@ -25,7 +25,6 @@
   ~VirtualKeyboardTray() override;
 
   // TrayBackgroundView:
-  void SetShelfAlignment(ShelfAlignment alignment) override;
   base::string16 GetAccessibleNameForTray() override;
   void HideBubbleWithView(const views::TrayBubbleView* bubble_view) override;
   void ClickedOutsideBubble() override;
@@ -39,10 +38,6 @@
   void OnKeyboardClosed() override;
 
  private:
-  // Creates a new border for the icon. The padding is determined based on the
-  // alignment of the shelf.
-  void SetIconBorderForShelfAlignment();
-
   void ObserveKeyboardController();
   void UnobserveKeyboardController();
 
diff --git a/ash/system/web_notification/ash_popup_alignment_delegate.cc b/ash/system/web_notification/ash_popup_alignment_delegate.cc
index 41fa4f7..360062a0 100644
--- a/ash/system/web_notification/ash_popup_alignment_delegate.cc
+++ b/ash/system/web_notification/ash_popup_alignment_delegate.cc
@@ -62,8 +62,7 @@
   // should be reduced by the height of shelf's shown height.
   if (shelf_->GetVisibilityState() == SHELF_AUTO_HIDE &&
       shelf_->GetAutoHideState() == SHELF_AUTO_HIDE_SHOWN) {
-    tray_bubble_height_ -= GetShelfConstant(SHELF_SIZE) -
-                           GetShelfConstant(SHELF_INSETS_FOR_AUTO_HIDE);
+    tray_bubble_height_ -= kShelfSize;
   }
 
   if (tray_bubble_height_ > 0)
diff --git a/ash/system/web_notification/web_notification_tray.cc b/ash/system/web_notification/web_notification_tray.cc
index 20d91494..e3bc136 100644
--- a/ash/system/web_notification/web_notification_tray.cc
+++ b/ash/system/web_notification/web_notification_tray.cc
@@ -18,6 +18,7 @@
 #include "ash/system/tray/system_tray_delegate.h"
 #include "ash/system/tray/tray_bubble_wrapper.h"
 #include "ash/system/tray/tray_constants.h"
+#include "ash/system/tray/tray_container.h"
 #include "ash/system/tray/tray_utils.h"
 #include "ash/system/web_notification/ash_popup_alignment_delegate.h"
 #include "ash/wm_window.h"
@@ -164,7 +165,7 @@
     // the icons on the left are shifted with the animation.
     // Note that TrayItemView does the same thing.
     gfx::Size size = kTrayItemOuterSize;
-    if (IsHorizontalLayout()) {
+    if (tray_->shelf()->IsHorizontalAlignment()) {
       size.set_width(std::max(
           1, gfx::ToRoundedInt(size.width() * animation_->GetCurrentValue())));
     } else {
@@ -178,15 +179,11 @@
     return GetPreferredSize().height();
   }
 
-  bool IsHorizontalLayout() const {
-    return IsHorizontalAlignment(tray_->shelf_alignment());
-  }
-
  private:
   // gfx::AnimationDelegate:
   void AnimationProgressed(const gfx::Animation* animation) override {
     gfx::Transform transform;
-    if (IsHorizontalLayout()) {
+    if (tray_->shelf()->IsHorizontalAlignment()) {
       transform.Translate(0, animation->CurrentValueBetween(
                                  static_cast<double>(height()) / 2., 0.));
     } else {
@@ -273,7 +270,7 @@
 WebNotificationTray::WebNotificationTray(WmShelf* shelf,
                                          WmWindow* status_area_window,
                                          SystemTray* system_tray)
-    : TrayBackgroundView(shelf, true),
+    : TrayBackgroundView(shelf),
       status_area_window_(status_area_window),
       system_tray_(system_tray),
       show_message_center_on_unlock_(false),
@@ -333,7 +330,7 @@
 
   // In the horizontal case, message center starts from the top of the shelf.
   // In the vertical case, it starts from the bottom of WebNotificationTray.
-  const int max_height = IsHorizontalAlignment(shelf_alignment())
+  const int max_height = shelf()->IsHorizontalAlignment()
                              ? shelf()->GetIdealBounds().y()
                              : GetBoundsInScreen().bottom();
   message_center_bubble->SetMaxHeight(max_height);
@@ -344,7 +341,7 @@
   // For vertical shelf alignments, anchor to the WebNotificationTray, but for
   // horizontal (i.e. bottom) shelves, anchor to the system tray.
   TrayBackgroundView* anchor_tray = this;
-  if (IsHorizontalAlignment(shelf_alignment())) {
+  if (shelf()->IsHorizontalAlignment()) {
     anchor_tray = WmShelf::ForWindow(status_area_window_)
                       ->GetStatusAreaWidget()
                       ->system_tray();
@@ -420,10 +417,8 @@
   OnMessageCenterTrayChanged();
 }
 
-void WebNotificationTray::SetShelfAlignment(ShelfAlignment alignment) {
-  if (alignment == shelf_alignment())
-    return;
-  TrayBackgroundView::SetShelfAlignment(alignment);
+void WebNotificationTray::UpdateAfterShelfAlignmentChange() {
+  TrayBackgroundView::UpdateAfterShelfAlignmentChange();
   // Destroy any existing bubble so that it will be rebuilt correctly.
   message_center_tray_->HideMessageCenterBubble();
   message_center_tray_->HidePopupBubble();
diff --git a/ash/system/web_notification/web_notification_tray.h b/ash/system/web_notification/web_notification_tray.h
index 9dd0109..96bb9ed 100644
--- a/ash/system/web_notification/web_notification_tray.h
+++ b/ash/system/web_notification/web_notification_tray.h
@@ -76,7 +76,7 @@
   void UpdateAfterLoginStatusChange(LoginStatus login_status);
 
   // Overridden from TrayBackgroundView.
-  void SetShelfAlignment(ShelfAlignment alignment) override;
+  void UpdateAfterShelfAlignmentChange() override;
   void AnchorUpdated() override;
   base::string16 GetAccessibleNameForTray() override;
   void HideBubbleWithView(const views::TrayBubbleView* bubble_view) override;
diff --git a/ash/wm/workspace/workspace_layout_manager_unittest.cc b/ash/wm/workspace/workspace_layout_manager_unittest.cc
index 70c89b51..462f4328 100644
--- a/ash/wm/workspace/workspace_layout_manager_unittest.cc
+++ b/ash/wm/workspace/workspace_layout_manager_unittest.cc
@@ -220,9 +220,8 @@
   // is inside 2nd display.
   window_state->Maximize();
   EXPECT_EQ(root_windows[1], window->GetRootWindow());
-  EXPECT_EQ(
-      gfx::Rect(300, 0, 400, 500 - GetShelfConstant(SHELF_SIZE)).ToString(),
-      window->GetBoundsInScreen().ToString());
+  EXPECT_EQ(gfx::Rect(300, 0, 400, 500 - kShelfSize).ToString(),
+            window->GetBoundsInScreen().ToString());
 
   window_state->Restore();
   EXPECT_EQ(root_windows[1], window->GetRootWindow());
@@ -233,9 +232,8 @@
   window_state->SetRestoreBoundsInScreen(gfx::Rect(295, 0, 30, 40));
   window_state->Maximize();
   EXPECT_EQ(root_windows[1], window->GetRootWindow());
-  EXPECT_EQ(
-      gfx::Rect(300, 0, 400, 500 - GetShelfConstant(SHELF_SIZE)).ToString(),
-      window->GetBoundsInScreen().ToString());
+  EXPECT_EQ(gfx::Rect(300, 0, 400, 500 - kShelfSize).ToString(),
+            window->GetBoundsInScreen().ToString());
 
   window_state->Restore();
   EXPECT_EQ(root_windows[1], window->GetRootWindow());
@@ -254,9 +252,8 @@
   EXPECT_TRUE(w1->IsMaximized());
   EXPECT_EQ(root_windows[1],
             WmWindow::Get(w1->GetNativeWindow())->GetRootWindow());
-  EXPECT_EQ(
-      gfx::Rect(300, 0, 400, 500 - GetShelfConstant(SHELF_SIZE)).ToString(),
-      w1->GetWindowBoundsInScreen().ToString());
+  EXPECT_EQ(gfx::Rect(300, 0, 400, 500 - kShelfSize).ToString(),
+            w1->GetWindowBoundsInScreen().ToString());
   w1->Restore();
   EXPECT_EQ(root_windows[1],
             WmWindow::Get(w1->GetNativeWindow())->GetRootWindow());
diff --git a/ash/wm/workspace/workspace_window_resizer_unittest.cc b/ash/wm/workspace/workspace_window_resizer_unittest.cc
index 3fd046e..8b85e3db 100644
--- a/ash/wm/workspace/workspace_window_resizer_unittest.cc
+++ b/ash/wm/workspace/workspace_window_resizer_unittest.cc
@@ -1025,19 +1025,11 @@
   resizer->Drag(CalculateDragPoint(*resizer, distance_to_right + 33, 0), 0);
   EXPECT_EQ("513,112 320x160", window_->bounds().ToString());
 
-  int auto_hidden_shelf_height = GetShelfConstant(SHELF_INSETS_FOR_AUTO_HIDE);
-
   // And the bottom should snap too.
-  resizer->Drag(
-      CalculateDragPoint(*resizer, 0,
-                         distance_to_bottom - auto_hidden_shelf_height - 7),
-      0);
+  resizer->Drag(CalculateDragPoint(*resizer, 0, distance_to_bottom - 7), 0);
   EXPECT_EQ(gfx::Rect(96, 440, 320, 160).ToString(),
             window_->bounds().ToString());
-  resizer->Drag(
-      CalculateDragPoint(*resizer, 0,
-                         distance_to_bottom - auto_hidden_shelf_height + 15),
-      0);
+  resizer->Drag(CalculateDragPoint(*resizer, 0, distance_to_bottom + 15), 0);
   EXPECT_EQ(gfx::Rect(96, 440, 320, 160).ToString(),
             window_->bounds().ToString());
   resizer->Drag(CalculateDragPoint(*resizer, 0, distance_to_bottom - 2 + 32),
@@ -1054,16 +1046,14 @@
   EXPECT_EQ("96,0 320x160", window_->bounds().ToString());
 
   // And bottom/left should snap too.
-  resizer->Drag(
-      CalculateDragPoint(*resizer, 7 - distance_to_left,
-                         distance_to_bottom - auto_hidden_shelf_height - 7),
-      0);
+  resizer->Drag(CalculateDragPoint(*resizer, 7 - distance_to_left,
+                                   distance_to_bottom - 7),
+                0);
   EXPECT_EQ(gfx::Rect(0, 440, 320, 160).ToString(),
             window_->bounds().ToString());
-  resizer->Drag(
-      CalculateDragPoint(*resizer, -15 - distance_to_left,
-                         distance_to_bottom - auto_hidden_shelf_height + 15),
-      0);
+  resizer->Drag(CalculateDragPoint(*resizer, -15 - distance_to_left,
+                                   distance_to_bottom + 15),
+                0);
   EXPECT_EQ(gfx::Rect(0, 440, 320, 160).ToString(),
             window_->bounds().ToString());
   // should move past snap points.
diff --git a/base/allocator/partition_allocator/page_allocator.cc b/base/allocator/partition_allocator/page_allocator.cc
index 606155f..bb737b3 100644
--- a/base/allocator/partition_allocator/page_allocator.cc
+++ b/base/allocator/partition_allocator/page_allocator.cc
@@ -216,7 +216,14 @@
 void DecommitSystemPages(void* address, size_t length) {
   DCHECK(!(length & kSystemPageOffsetMask));
 #if defined(OS_POSIX)
+#if defined(OS_MACOSX)
+  // On macOS, MADV_FREE_REUSABLE has comparable behavior to MADV_FREE, but also
+  // marks the pages with the reusable bit, which allows both Activity Monitor
+  // and memory-infra to correctly track the pages.
+  int ret = madvise(address, length, MADV_FREE_REUSABLE);
+#else
   int ret = madvise(address, length, MADV_FREE);
+#endif
   if (ret != 0 && errno == EINVAL) {
     // MADV_FREE only works on Linux 4.5+ . If request failed,
     // retry with older MADV_DONTNEED . Note that MADV_FREE
diff --git a/base/containers/README.md b/base/containers/README.md
new file mode 100644
index 0000000..33c2f32
--- /dev/null
+++ b/base/containers/README.md
@@ -0,0 +1,20 @@
+# base/containers library
+
+This directory contains some STL-like containers.
+
+Things should be moved here that are generally applicable across the code base.
+Don't add things here just because you need them in one place and think others
+may someday want something similar. You can put specialized containers in
+your component's directory and we can promote them here later if we feel there
+is broad applicability.
+
+## Design and naming
+
+Containers should adhere as closely to STL as possible. Functions and behaviors
+not present in STL should only be added when they are related to the specific
+data structure implemented by the container.
+
+For STL-like containers our policy is that they should use STL-like naming even
+when it may conflict with the style guide. So functions and class names should
+be lower case with underscores. Non-STL-like classes and functions should use
+Google naming. Be sure to use the base namespace.
diff --git a/base/containers/small_map.h b/base/containers/small_map.h
index 2945d58..db27b88 100644
--- a/base/containers/small_map.h
+++ b/base/containers/small_map.h
@@ -31,45 +31,41 @@
 //    do heap allocations for each element.
 //
 //  - If you only ever keep a couple of items and have very simple usage,
-//    consider whether a using a vector and brute-force searching it will be
-//    the most efficient. It's not a lot of generated code (less than a
-//    red-black tree if your key is "weird" and not eliminated as duplicate of
-//    something else) and will probably be faster and do fewer heap allocations
-//    than std::map if you have just a couple of items.
+//    use a base::flat_map.
 //
-//  - base::hash_map should be used if you need O(1) lookups. It may waste
+//  - std::unordered_map should be used if you need O(1) lookups. It may waste
 //    space in the hash table, and it can be easy to write correct-looking
 //    code with the default hash function being wrong or poorly-behaving.
 //
-//  - SmallMap combines the performance benefits of the brute-force-searched
-//    vector for small cases (no extra heap allocations), but can efficiently
-//    fall back if you end up adding many items. It will generate more code
-//    than std::map (at least 160 bytes for operator[]) which is bad if you
-//    have a "weird" key where map functions can't be
+//  - base::small_map combines the performance benefits of the
+//    brute-force-searched vector for small cases (no extra heap allocations),
+//    but can efficiently fall back if you end up adding many items. It will
+//    generate more code than std::map (at least 160 bytes for operator[])
+//    which is bad if you have a "weird" key where map functions can't be
 //    duplicate-code-eliminated. If you have a one-off key and aren't in
 //    performance-critical code, this bloat may negate some of the benefits and
 //    you should consider on of the other options.
 //
-// SmallMap will pick up the comparator from the underlying map type. In
+// base::small_map will pick up the comparator from the underlying map type. In
 // std::map (and in MSVC additionally hash_map) only a "less" operator is
 // defined, which requires us to do two comparisons per element when doing the
 // brute-force search in the simple array.
 //
 // We define default overrides for the common map types to avoid this
 // double-compare, but you should be aware of this if you use your own
-// operator< for your map and supply yor own version of == to the SmallMap.
+// operator< for your map and supply yor own version of == to the small_map.
 // You can use regular operator== by just doing:
 //
-//   base::SmallMap<std::map<MyKey, MyValue>, 4, std::equal_to<KyKey> >
+//   base::small_map<std::map<MyKey, MyValue>, 4, std::equal_to<KyKey>>
 //
 //
 // USAGE
 // -----
 //
 // NormalMap:  The map type to fall back to.  This also defines the key
-//             and value types for the SmallMap.
+//             and value types for the small_map.
 // kArraySize:  The size of the initial array of results. This will be
-//              allocated with the SmallMap object rather than separately on
+//              allocated with the small_map object rather than separately on
 //              the heap. Once the map grows beyond this size, the map type
 //              will be used instead.
 // EqualKey:  A functor which tests two keys for equality.  If the wrapped
@@ -79,14 +75,14 @@
 //            implement equality by default.
 // MapInit: A functor that takes a ManualConstructor<NormalMap>* and uses it to
 //          initialize the map. This functor will be called at most once per
-//          SmallMap, when the map exceeds the threshold of kArraySize and we
+//          small_map, when the map exceeds the threshold of kArraySize and we
 //          are about to copy values from the array to the map. The functor
 //          *must* call one of the Init() methods provided by
 //          ManualConstructor, since after it runs we assume that the NormalMap
 //          has been initialized.
 //
 // example:
-//   base::SmallMap< std::map<string, int> > days;
+//   base::small_map<std::map<string, int>> days;
 //   days["sunday"   ] = 0;
 //   days["monday"   ] = 1;
 //   days["tuesday"  ] = 2;
@@ -95,13 +91,13 @@
 //   days["friday"   ] = 5;
 //   days["saturday" ] = 6;
 //
-// You should assume that SmallMap might invalidate all the iterators
+// You should assume that small_map might invalidate all the iterators
 // on any call to erase(), insert() and operator[].
 
 namespace internal {
 
 template <typename NormalMap>
-class SmallMapDefaultInit {
+class small_map_default_init {
  public:
   void operator()(ManualConstructor<NormalMap>* map) const {
     map->Init();
@@ -178,12 +174,11 @@
 
 template <typename NormalMap,
           int kArraySize = 4,
-          typename EqualKey =
-              typename internal::select_equal_key<
-                  NormalMap,
-                  internal::has_key_equal<NormalMap>::value>::equal_key,
-          typename MapInit = internal::SmallMapDefaultInit<NormalMap> >
-class SmallMap {
+          typename EqualKey = typename internal::select_equal_key<
+              NormalMap,
+              internal::has_key_equal<NormalMap>::value>::equal_key,
+          typename MapInit = internal::small_map_default_init<NormalMap>>
+class small_map {
   // We cannot rely on the compiler to reject array of size 0.  In
   // particular, gcc 2.95.3 does it but later versions allow 0-length
   // arrays.  Therefore, we explicitly reject non-positive kArraySize
@@ -197,16 +192,16 @@
   typedef typename NormalMap::value_type value_type;
   typedef EqualKey key_equal;
 
-  SmallMap() : size_(0), functor_(MapInit()) {}
+  small_map() : size_(0), functor_(MapInit()) {}
 
-  explicit SmallMap(const MapInit& functor) : size_(0), functor_(functor) {}
+  explicit small_map(const MapInit& functor) : size_(0), functor_(functor) {}
 
   // Allow copy-constructor and assignment, since STL allows them too.
-  SmallMap(const SmallMap& src) {
+  small_map(const small_map& src) {
     // size_ and functor_ are initted in InitFrom()
     InitFrom(src);
   }
-  void operator=(const SmallMap& src) {
+  void operator=(const small_map& src) {
     if (&src == this) return;
 
     // This is not optimal. If src and dest are both using the small
@@ -216,9 +211,7 @@
     Destroy();
     InitFrom(src);
   }
-  ~SmallMap() {
-    Destroy();
-  }
+  ~small_map() { Destroy(); }
 
   class const_iterator;
 
@@ -290,7 +283,7 @@
     bool operator!=(const const_iterator& other) const;
 
    private:
-    friend class SmallMap;
+    friend class small_map;
     friend class const_iterator;
     inline explicit iterator(ManualConstructor<value_type>* init)
       : array_iter_(init) {}
@@ -372,7 +365,7 @@
     }
 
    private:
-    friend class SmallMap;
+    friend class small_map;
     inline explicit const_iterator(
         const ManualConstructor<value_type>* init)
       : array_iter_(init) {}
@@ -607,7 +600,7 @@
   }
 
   // Helpers for constructors and destructors.
-  void InitFrom(const SmallMap& src) {
+  void InitFrom(const small_map& src) {
     functor_ = src.functor_;
     size_ = src.size_;
     if (src.size_ >= 0) {
@@ -630,18 +623,20 @@
   }
 };
 
-template <typename NormalMap, int kArraySize, typename EqualKey,
+template <typename NormalMap,
+          int kArraySize,
+          typename EqualKey,
           typename Functor>
-inline bool SmallMap<NormalMap, kArraySize, EqualKey,
-                     Functor>::iterator::operator==(
-    const const_iterator& other) const {
+inline bool small_map<NormalMap, kArraySize, EqualKey, Functor>::iterator::
+operator==(const const_iterator& other) const {
   return other == *this;
 }
-template <typename NormalMap, int kArraySize, typename EqualKey,
+template <typename NormalMap,
+          int kArraySize,
+          typename EqualKey,
           typename Functor>
-inline bool SmallMap<NormalMap, kArraySize, EqualKey,
-                     Functor>::iterator::operator!=(
-    const const_iterator& other) const {
+inline bool small_map<NormalMap, kArraySize, EqualKey, Functor>::iterator::
+operator!=(const const_iterator& other) const {
   return other != *this;
 }
 
diff --git a/base/containers/small_map_unittest.cc b/base/containers/small_map_unittest.cc
index 47889e96..851635dc 100644
--- a/base/containers/small_map_unittest.cc
+++ b/base/containers/small_map_unittest.cc
@@ -17,7 +17,7 @@
 namespace base {
 
 TEST(SmallMap, General) {
-  SmallMap<hash_map<int, int> > m;
+  small_map<hash_map<int, int>> m;
 
   EXPECT_TRUE(m.empty());
 
@@ -35,7 +35,7 @@
   EXPECT_EQ(m[0], 5);
   EXPECT_FALSE(m.UsingFullMap());
 
-  SmallMap<hash_map<int, int> >::iterator iter(m.begin());
+  small_map<hash_map<int, int>>::iterator iter(m.begin());
   ASSERT_TRUE(iter != m.end());
   EXPECT_EQ(iter->first, 0);
   EXPECT_EQ(iter->second, 5);
@@ -66,26 +66,26 @@
   }
   EXPECT_TRUE(iter == m.end());
 
-  const SmallMap<hash_map<int, int> >& ref = m;
+  const small_map<hash_map<int, int>>& ref = m;
   EXPECT_TRUE(ref.find(1234) != m.end());
   EXPECT_TRUE(ref.find(5678) == m.end());
 }
 
 TEST(SmallMap, PostFixIteratorIncrement) {
-  SmallMap<hash_map<int, int> > m;
+  small_map<hash_map<int, int>> m;
   m[0] = 5;
   m[2] = 3;
 
   {
-    SmallMap<hash_map<int, int> >::iterator iter(m.begin());
-    SmallMap<hash_map<int, int> >::iterator last(iter++);
+    small_map<hash_map<int, int>>::iterator iter(m.begin());
+    small_map<hash_map<int, int>>::iterator last(iter++);
     ++last;
     EXPECT_TRUE(last == iter);
   }
 
   {
-    SmallMap<hash_map<int, int> >::const_iterator iter(m.begin());
-    SmallMap<hash_map<int, int> >::const_iterator last(iter++);
+    small_map<hash_map<int, int>>::const_iterator iter(m.begin());
+    small_map<hash_map<int, int>>::const_iterator last(iter++);
     ++last;
     EXPECT_TRUE(last == iter);
   }
@@ -93,17 +93,17 @@
 
 // Based on the General testcase.
 TEST(SmallMap, CopyConstructor) {
-  SmallMap<hash_map<int, int> > src;
+  small_map<hash_map<int, int>> src;
 
   {
-    SmallMap<hash_map<int, int> > m(src);
+    small_map<hash_map<int, int>> m(src);
     EXPECT_TRUE(m.empty());
   }
 
   src[0] = 5;
 
   {
-    SmallMap<hash_map<int, int> > m(src);
+    small_map<hash_map<int, int>> m(src);
     EXPECT_FALSE(m.empty());
     EXPECT_EQ(m.size(), 1u);
   }
@@ -111,7 +111,7 @@
   src[9] = 2;
 
   {
-    SmallMap<hash_map<int, int> > m(src);
+    small_map<hash_map<int, int>> m(src);
     EXPECT_FALSE(m.empty());
     EXPECT_EQ(m.size(), 2u);
 
@@ -125,7 +125,7 @@
   src[-5] = 6;
 
   {
-    SmallMap<hash_map<int, int> > m(src);
+    small_map<hash_map<int, int>> m(src);
     EXPECT_EQ(m[   9],  2);
     EXPECT_EQ(m[   0],  5);
     EXPECT_EQ(m[1234], 90);
@@ -137,27 +137,27 @@
   }
 }
 
-template<class inner>
-static bool SmallMapIsSubset(SmallMap<inner> const& a,
-                             SmallMap<inner> const& b) {
-  typename SmallMap<inner>::const_iterator it;
+template <class inner>
+static bool SmallMapIsSubset(small_map<inner> const& a,
+                             small_map<inner> const& b) {
+  typename small_map<inner>::const_iterator it;
   for (it = a.begin(); it != a.end(); ++it) {
-    typename SmallMap<inner>::const_iterator it_in_b = b.find(it->first);
+    typename small_map<inner>::const_iterator it_in_b = b.find(it->first);
     if (it_in_b == b.end() || it_in_b->second != it->second)
       return false;
   }
   return true;
 }
 
-template<class inner>
-static bool SmallMapEqual(SmallMap<inner> const& a,
-                          SmallMap<inner> const& b) {
+template <class inner>
+static bool SmallMapEqual(small_map<inner> const& a,
+                          small_map<inner> const& b) {
   return SmallMapIsSubset(a, b) && SmallMapIsSubset(b, a);
 }
 
 TEST(SmallMap, AssignmentOperator) {
-  SmallMap<hash_map<int, int> > src_small;
-  SmallMap<hash_map<int, int> > src_large;
+  small_map<hash_map<int, int>> src_small;
+  small_map<hash_map<int, int>> src_large;
 
   src_small[1] = 20;
   src_small[2] = 21;
@@ -173,13 +173,13 @@
   EXPECT_TRUE(src_large.UsingFullMap());
 
   // Assignments to empty.
-  SmallMap<hash_map<int, int> > dest_small;
+  small_map<hash_map<int, int>> dest_small;
   dest_small = src_small;
   EXPECT_TRUE(SmallMapEqual(dest_small, src_small));
   EXPECT_EQ(dest_small.UsingFullMap(),
             src_small.UsingFullMap());
 
-  SmallMap<hash_map<int, int> > dest_large;
+  small_map<hash_map<int, int>> dest_large;
   dest_large = src_large;
   EXPECT_TRUE(SmallMapEqual(dest_large, src_large));
   EXPECT_EQ(dest_large.UsingFullMap(),
@@ -202,14 +202,13 @@
 }
 
 TEST(SmallMap, Insert) {
-  SmallMap<hash_map<int, int> > sm;
+  small_map<hash_map<int, int>> sm;
 
   // loop through the transition from small map to map.
   for (int i = 1; i <= 10; ++i) {
     VLOG(1) << "Iteration " << i;
     // insert an element
-    std::pair<SmallMap<hash_map<int, int> >::iterator,
-        bool> ret;
+    std::pair<small_map<hash_map<int, int>>::iterator, bool> ret;
     ret = sm.insert(std::make_pair(i, 100*i));
     EXPECT_TRUE(ret.second);
     EXPECT_TRUE(ret.first == sm.find(i));
@@ -226,7 +225,7 @@
 
     // check the state of the map.
     for (int j = 1; j <= i; ++j) {
-      SmallMap<hash_map<int, int> >::iterator it = sm.find(j);
+      small_map<hash_map<int, int>>::iterator it = sm.find(j);
       EXPECT_TRUE(it != sm.end());
       EXPECT_EQ(it->first, j);
       EXPECT_EQ(it->second, j * 100);
@@ -245,7 +244,7 @@
       normal_map.insert(std::make_pair(i, 100*i));
     }
 
-    SmallMap<hash_map<int, int> > sm;
+    small_map<hash_map<int, int>> sm;
     sm.insert(normal_map.begin(), normal_map.end());
     EXPECT_EQ(normal_map.size(), sm.size());
     for (int i = 1; i <= elements; ++i) {
@@ -258,8 +257,8 @@
 }
 
 TEST(SmallMap, Erase) {
-  SmallMap<hash_map<std::string, int> > m;
-  SmallMap<hash_map<std::string, int> >::iterator iter;
+  small_map<hash_map<std::string, int>> m;
+  small_map<hash_map<std::string, int>>::iterator iter;
 
   m["monday"] = 1;
   m["tuesday"] = 2;
@@ -332,8 +331,8 @@
 }
 
 TEST(SmallMap, EraseReturnsIteratorFollowingRemovedElement) {
-  SmallMap<hash_map<std::string, int> > m;
-  SmallMap<hash_map<std::string, int> >::iterator iter;
+  small_map<hash_map<std::string, int>> m;
+  small_map<hash_map<std::string, int>>::iterator iter;
 
   m["a"] = 0;
   m["b"] = 1;
@@ -362,7 +361,7 @@
 }
 
 TEST(SmallMap, NonHashMap) {
-  SmallMap<std::map<int, int>, 4, std::equal_to<int> > m;
+  small_map<std::map<int, int>, 4, std::equal_to<int>> m;
   EXPECT_TRUE(m.empty());
 
   m[9] = 2;
@@ -374,7 +373,7 @@
   EXPECT_FALSE(m.empty());
   EXPECT_FALSE(m.UsingFullMap());
 
-  SmallMap<std::map<int, int>, 4, std::equal_to<int> >::iterator iter(
+  small_map<std::map<int, int>, 4, std::equal_to<int>>::iterator iter(
       m.begin());
   ASSERT_TRUE(iter != m.end());
   EXPECT_EQ(iter->first, 9);
@@ -434,9 +433,9 @@
 TEST(SmallMap, DefaultEqualKeyWorks) {
   // If these tests compile, they pass. The EXPECT calls are only there to avoid
   // unused variable warnings.
-  SmallMap<hash_map<int, int> > hm;
+  small_map<hash_map<int, int>> hm;
   EXPECT_EQ(0u, hm.size());
-  SmallMap<std::map<int, int> > m;
+  small_map<std::map<int, int>> m;
   EXPECT_EQ(0u, m.size());
 }
 
@@ -470,8 +469,9 @@
 }  // anonymous namespace
 
 TEST(SmallMap, SubclassInitializationWithFunctionPointer) {
-  SmallMap<hash_map_add_item, 4, std::equal_to<int>,
-      void (&)(ManualConstructor<hash_map_add_item>*)> m(InitMap);
+  small_map<hash_map_add_item, 4, std::equal_to<int>,
+            void (&)(ManualConstructor<hash_map_add_item>*)>
+      m(InitMap);
 
   EXPECT_TRUE(m.empty());
 
@@ -490,8 +490,9 @@
 }
 
 TEST(SmallMap, SubclassInitializationWithFunctionObject) {
-  SmallMap<hash_map_add_item, 4, std::equal_to<int>,
-      hash_map_add_item_initializer> m(hash_map_add_item_initializer(-1));
+  small_map<hash_map_add_item, 4, std::equal_to<int>,
+            hash_map_add_item_initializer>
+      m(hash_map_add_item_initializer(-1));
 
   EXPECT_TRUE(m.empty());
 
@@ -536,18 +537,18 @@
 };
 
 TEST(SmallMap, MoveOnlyValueType) {
-  SmallMap<std::map<int, MoveOnlyType>, 2> m;
+  small_map<std::map<int, MoveOnlyType>, 2> m;
 
   m[0] = MoveOnlyType(1);
   m[1] = MoveOnlyType(2);
   m.erase(m.begin());
 
-  // SmallMap will move m[1] to an earlier index in the internal array.
+  // small_map will move m[1] to an earlier index in the internal array.
   EXPECT_EQ(m.size(), 1u);
   EXPECT_EQ(m[1].value(), 2);
 
   m[0] = MoveOnlyType(1);
-  // SmallMap must move the values from the array into the internal std::map.
+  // small_map must move the values from the array into the internal std::map.
   m[2] = MoveOnlyType(3);
 
   EXPECT_EQ(m.size(), 3u);
@@ -557,7 +558,7 @@
 
   m.erase(m.begin());
 
-  // SmallMap should also let internal std::map erase with a move-only type.
+  // small_map should also let internal std::map erase with a move-only type.
   EXPECT_EQ(m.size(), 2u);
   EXPECT_EQ(m[1].value(), 2);
   EXPECT_EQ(m[2].value(), 3);
diff --git a/base/trace_event/trace_event_memory_overhead.cc b/base/trace_event/trace_event_memory_overhead.cc
index 99f0240a..95177b5 100644
--- a/base/trace_event/trace_event_memory_overhead.cc
+++ b/base/trace_event/trace_event_memory_overhead.cc
@@ -115,7 +115,7 @@
 
 void TraceEventMemoryOverhead::AddSelf() {
   size_t estimated_size = sizeof(*this);
-  // If the SmallMap did overflow its static capacity, its elements will be
+  // If the small_map did overflow its static capacity, its elements will be
   // allocated on the heap and have to be accounted separately.
   if (allocated_objects_.UsingFullMap())
     estimated_size += sizeof(map_type::value_type) * allocated_objects_.size();
diff --git a/base/trace_event/trace_event_memory_overhead.h b/base/trace_event/trace_event_memory_overhead.h
index a69c93f..a5d324e 100644
--- a/base/trace_event/trace_event_memory_overhead.h
+++ b/base/trace_event/trace_event_memory_overhead.h
@@ -60,7 +60,7 @@
     size_t allocated_size_in_bytes;
     size_t resident_size_in_bytes;
   };
-  using map_type = SmallMap<hash_map<const char*, ObjectCountAndSize>, 16>;
+  using map_type = small_map<hash_map<const char*, ObjectCountAndSize>, 16>;
   map_type allocated_objects_;
 
   void AddOrCreateInternal(const char* object_type,
diff --git a/cc/animation/keyframed_animation_curve_unittest.cc b/cc/animation/keyframed_animation_curve_unittest.cc
index df3a0b1..c050404 100644
--- a/cc/animation/keyframed_animation_curve_unittest.cc
+++ b/cc/animation/keyframed_animation_curve_unittest.cc
@@ -535,6 +535,30 @@
   }
 }
 
+// Tests a frames timing function.
+TEST(KeyframedAnimationCurveTest, FramesTimingFunction) {
+  std::unique_ptr<KeyframedFloatAnimationCurve> curve(
+      KeyframedFloatAnimationCurve::Create());
+  curve->AddKeyframe(FloatKeyframe::Create(base::TimeDelta(), 0.f,
+                                           FramesTimingFunction::Create(5)));
+  curve->AddKeyframe(
+      FloatKeyframe::Create(base::TimeDelta::FromSecondsD(1.0), 1.f, nullptr));
+
+  struct Expected {
+    double time;
+    float value;
+  } expectations[] = {
+      {0.0, 0.f},     {0.1999, 0.f},  {0.2001, 0.25f}, {0.3999, 0.25f},
+      {0.4001, 0.5f}, {0.5999, 0.5f}, {0.6001, 0.75f}, {0.7999, 0.75f},
+      {0.8001, 1.f},  {1.0, 1.f},
+  };
+  for (const auto& expectation : expectations) {
+    EXPECT_FLOAT_EQ(
+        expectation.value,
+        curve->GetValue(base::TimeDelta::FromSecondsD(expectation.time)));
+  }
+}
+
 // Tests that animated bounds are computed as expected.
 TEST(KeyframedAnimationCurveTest, AnimatedBounds) {
   std::unique_ptr<KeyframedTransformAnimationCurve> curve(
@@ -851,6 +875,23 @@
   EXPECT_FLOAT_EQ(2.f, curve->GetValue(base::TimeDelta::FromSecondsD(0.75f)));
 }
 
+// Tests that a frames timing function works as expected for inputs outside of
+// range [0,1]
+TEST(KeyframedAnimationCurveTest, FramesTimingInputsOutsideZeroOneRange) {
+  std::unique_ptr<KeyframedFloatAnimationCurve> curve(
+      KeyframedFloatAnimationCurve::Create());
+  curve->AddKeyframe(FloatKeyframe::Create(base::TimeDelta(), 0.f,
+                                           FramesTimingFunction::Create(5)));
+  curve->AddKeyframe(
+      FloatKeyframe::Create(base::TimeDelta::FromSecondsD(1.0), 2.f, nullptr));
+  // Curve timing function producing timing outputs outside of range [0,1].
+  curve->SetTimingFunction(
+      CubicBezierTimingFunction::Create(0.5f, -0.5f, 0.5f, 1.5f));
+
+  EXPECT_FLOAT_EQ(-0.5f, curve->GetValue(base::TimeDelta::FromSecondsD(0.25f)));
+  EXPECT_FLOAT_EQ(2.5f, curve->GetValue(base::TimeDelta::FromSecondsD(0.75f)));
+}
+
 // Tests that an animation with a curve timing function and multiple keyframes
 // works as expected.
 TEST(KeyframedAnimationCurveTest, CurveTimingMultipleKeyframes) {
diff --git a/cc/animation/timing_function.cc b/cc/animation/timing_function.cc
index d0d55ad..0271c57 100644
--- a/cc/animation/timing_function.cc
+++ b/cc/animation/timing_function.cc
@@ -131,4 +131,41 @@
   }
 }
 
+std::unique_ptr<FramesTimingFunction> FramesTimingFunction::Create(int frames) {
+  return base::WrapUnique(new FramesTimingFunction(frames));
+}
+
+FramesTimingFunction::FramesTimingFunction(int frames) : frames_(frames) {}
+
+FramesTimingFunction::~FramesTimingFunction() {}
+
+TimingFunction::Type FramesTimingFunction::GetType() const {
+  return Type::FRAMES;
+}
+
+float FramesTimingFunction::GetValue(double t) const {
+  return static_cast<float>(GetPreciseValue(t));
+}
+
+std::unique_ptr<TimingFunction> FramesTimingFunction::Clone() const {
+  return base::WrapUnique(new FramesTimingFunction(*this));
+}
+
+void FramesTimingFunction::Range(float* min, float* max) const {
+  *min = 0.0f;
+  *max = 1.0f;
+}
+
+float FramesTimingFunction::Velocity(double x) const {
+  return 0.0f;
+}
+
+double FramesTimingFunction::GetPreciseValue(double t) const {
+  const double frames = static_cast<double>(frames_);
+  double output_progress = std::floor(frames * t) / (frames - 1);
+  if (t <= 1 && output_progress > 1)
+    output_progress = 1;
+  return output_progress;
+}
+
 }  // namespace cc
diff --git a/cc/animation/timing_function.h b/cc/animation/timing_function.h
index 0f475e76..5ff8d35 100644
--- a/cc/animation/timing_function.h
+++ b/cc/animation/timing_function.h
@@ -19,7 +19,7 @@
   virtual ~TimingFunction();
 
   // Note that LINEAR is a nullptr TimingFunction (for now).
-  enum class Type { LINEAR, CUBIC_BEZIER, STEPS };
+  enum class Type { LINEAR, CUBIC_BEZIER, STEPS, FRAMES };
 
   virtual Type GetType() const = 0;
   virtual float GetValue(double t) const = 0;
@@ -101,6 +101,29 @@
   DISALLOW_ASSIGN(StepsTimingFunction);
 };
 
+class CC_ANIMATION_EXPORT FramesTimingFunction : public TimingFunction {
+ public:
+  static std::unique_ptr<FramesTimingFunction> Create(int frames);
+  ~FramesTimingFunction() override;
+
+  // TimingFunction implementation.
+  Type GetType() const override;
+  float GetValue(double t) const override;
+  std::unique_ptr<TimingFunction> Clone() const override;
+  void Range(float* min, float* max) const override;
+  float Velocity(double time) const override;
+
+  int frames() const { return frames_; }
+  double GetPreciseValue(double t) const;
+
+ private:
+  explicit FramesTimingFunction(int frames);
+
+  int frames_;
+
+  DISALLOW_ASSIGN(FramesTimingFunction);
+};
+
 }  // namespace cc
 
 #endif  // CC_ANIMATION_TIMING_FUNCTION_H_
diff --git a/cc/resources/resource_provider.h b/cc/resources/resource_provider.h
index 2ca4ba19..2b664ff 100644
--- a/cc/resources/resource_provider.h
+++ b/cc/resources/resource_provider.h
@@ -800,7 +800,7 @@
   // immediately.
   bool batch_return_resources_ = false;
   // Maps from a child id to the set of resources to be returned to it.
-  base::SmallMap<std::map<int, ResourceIdArray>> batched_returning_resources_;
+  base::small_map<std::map<int, ResourceIdArray>> batched_returning_resources_;
 
   base::ThreadChecker thread_checker_;
 
diff --git a/cc/scheduler/begin_frame_source.cc b/cc/scheduler/begin_frame_source.cc
index 36e3a62..5af7b4c 100644
--- a/cc/scheduler/begin_frame_source.cc
+++ b/cc/scheduler/begin_frame_source.cc
@@ -345,8 +345,6 @@
 
 void BeginFrameObserverAckTracker::AsValueInto(
     base::trace_event::TracedValue* state) const {
-  base::SmallMap<std::map<BeginFrameObserver*, uint64_t>, 4>
-      latest_confirmed_sequence_numbers_;
   state->SetInteger("current_source_id", current_source_id_);
   state->SetInteger("current_sequence_number", current_sequence_number_);
   state->SetInteger("num_observers", observers_.size());
diff --git a/cc/scheduler/begin_frame_source.h b/cc/scheduler/begin_frame_source.h
index 5831339..f9c47c55 100644
--- a/cc/scheduler/begin_frame_source.h
+++ b/cc/scheduler/begin_frame_source.h
@@ -283,7 +283,7 @@
   base::flat_set<BeginFrameObserver*> observers_;
   base::flat_set<BeginFrameObserver*> finished_observers_;
   bool observers_had_damage_ = false;
-  base::SmallMap<std::map<BeginFrameObserver*, uint64_t>, 4>
+  base::flat_map<BeginFrameObserver*, uint64_t>
       latest_confirmed_sequence_numbers_;
 
   DISALLOW_COPY_AND_ASSIGN(BeginFrameObserverAckTracker);
diff --git a/cc/tiles/picture_layer_tiling.cc b/cc/tiles/picture_layer_tiling.cc
index 344cb32..75fd37d 100644
--- a/cc/tiles/picture_layer_tiling.cc
+++ b/cc/tiles/picture_layer_tiling.cc
@@ -11,7 +11,7 @@
 #include <limits>
 #include <set>
 
-#include "base/containers/small_map.h"
+#include "base/containers/flat_map.h"
 #include "base/logging.h"
 #include "base/memory/ptr_util.h"
 #include "base/numerics/safe_conversions.h"
@@ -257,12 +257,8 @@
   // twin, so it's slated for removal in the future.
   if (live_tiles_rect_.IsEmpty())
     return;
-  // Pick 16 for the size of the SmallMap before it promotes to a unordered_map.
-  // 4x4 tiles should cover most small invalidations, and walking a vector of
-  // 16 is fast enough. If an invalidation is huge we will fall back to a
-  // unordered_map instead of a vector in the SmallMap.
-  base::SmallMap<std::unordered_map<TileMapKey, gfx::Rect, TileMapKeyHash>, 16>
-      remove_tiles;
+
+  base::flat_map<TileMapKey, gfx::Rect> remove_tiles;
   gfx::Rect expanded_live_tiles_rect =
       tiling_data_.ExpandRectToTileBounds(live_tiles_rect_);
   for (Region::Iterator iter(layer_invalidation); iter.has_rect();
diff --git a/cc/tiles/picture_layer_tiling.h b/cc/tiles/picture_layer_tiling.h
index ecf1974e..a74a058 100644
--- a/cc/tiles/picture_layer_tiling.h
+++ b/cc/tiles/picture_layer_tiling.h
@@ -63,6 +63,9 @@
   bool operator==(const TileMapKey& other) const {
     return index_x == other.index_x && index_y == other.index_y;
   }
+  bool operator<(const TileMapKey& other) const {
+    return std::tie(index_x, index_y) < std::tie(other.index_x, other.index_y);
+  }
 
   int index_x;
   int index_y;
diff --git a/cc/trees/layer_tree_host_impl.cc b/cc/trees/layer_tree_host_impl.cc
index 3a117b2..88873601 100644
--- a/cc/trees/layer_tree_host_impl.cc
+++ b/cc/trees/layer_tree_host_impl.cc
@@ -16,7 +16,7 @@
 
 #include "base/auto_reset.h"
 #include "base/bind.h"
-#include "base/containers/small_map.h"
+#include "base/containers/flat_map.h"
 #include "base/json/json_writer.h"
 #include "base/memory/ptr_util.h"
 #include "base/metrics/histogram.h"
@@ -1139,10 +1139,10 @@
   DCHECK_GE(frame->render_passes.size(), 1u);
 
   // A set of RenderPasses that we have seen.
-  std::set<int> pass_exists;
+  base::flat_set<int> pass_exists;
   // A set of RenderPassDrawQuads that we have seen (stored by the RenderPasses
   // they refer to).
-  base::SmallMap<std::unordered_map<int, int>> pass_references;
+  base::flat_map<int, int> pass_references;
 
   // Iterate RenderPasses in draw order, removing empty render passes (except
   // the root RenderPass).
diff --git a/chrome/browser/OWNERS b/chrome/browser/OWNERS
index 4507f1b..467b294 100644
--- a/chrome/browser/OWNERS
+++ b/chrome/browser/OWNERS
@@ -11,7 +11,7 @@
 
 per-file *_android.*=file://chrome/browser/android/OWNERS
 
-per-file browser_resources.grd=file://ui/webui/OWNERS
+per-file browser_resources.grd=file://ui/webui/PLATFORM_OWNERS
 per-file browser_resources.grd=calamity@chromium.org
 per-file browser_resources.grd=tsergeant@chromium.org
 
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 6d1fa04..7e3b7ad 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -97,7 +97,7 @@
 #include "ui/gl/gl_features.h"
 #include "ui/gl/gl_switches.h"
 #include "ui/keyboard/keyboard_switches.h"
-#include "ui/native_theme/native_theme_features.h"
+#include "ui/native_theme/native_theme_switches.h"
 #include "ui/views/views_switches.h"
 
 #if defined(OS_ANDROID)
@@ -1067,7 +1067,9 @@
      flag_descriptions::kOverlayScrollbarsDescription,
      // Uses the system preference on Mac (a different implementation).
      // On Android, this is always enabled.
-     kOsAura, FEATURE_VALUE_TYPE(features::kOverlayScrollbar)},
+     kOsAura,
+     ENABLE_DISABLE_VALUE_TYPE(switches::kEnableOverlayScrollbar,
+                               switches::kDisableOverlayScrollbar)},
 #endif  // USE_AURA
     {   // See http://crbug.com/120416 for how to remove this flag.
      "save-page-as-mhtml", flag_descriptions::kSavePageAsMhtmlName,
diff --git a/chrome/browser/chromeos/accessibility/select_to_speak_event_handler.cc b/chrome/browser/chromeos/accessibility/select_to_speak_event_handler.cc
index b9b9ba15..40d7840 100644
--- a/chrome/browser/chromeos/accessibility/select_to_speak_event_handler.cc
+++ b/chrome/browser/chromeos/accessibility/select_to_speak_event_handler.cc
@@ -8,11 +8,12 @@
 #include "base/logging.h"
 #include "chrome/browser/speech/tts_controller.h"
 #include "chrome/browser/ui/aura/accessibility/automation_manager_aura.h"
+#include "chrome/common/extensions/api/automation_api_constants.h"
 #include "content/public/browser/browser_thread.h"
+#include "ui/accessibility/ax_tree_id_registry.h"
 #include "ui/aura/window.h"
 #include "ui/display/display.h"
 #include "ui/events/event.h"
-#include "ui/views/focus/view_storage.h"
 #include "ui/views/view.h"
 #include "ui/views/widget/widget.h"
 
@@ -121,42 +122,15 @@
 
   CancelEvent(event);
 
-  // Find the View to post the accessibility event on.
-  aura::Window* event_target = static_cast<aura::Window*>(event->target());
-  aura::Window* hit_window = event_target;
-  if (!hit_window)
-    return;
-
-  views::Widget* hit_widget = views::Widget::GetWidgetForNativeView(hit_window);
-  while (!hit_widget) {
-    hit_window = hit_window->parent();
-    if (!hit_window)
-      break;
-
-    hit_widget = views::Widget::GetWidgetForNativeView(hit_window);
-  }
-
-  if (!hit_window || !hit_widget)
-    return;
-
-  gfx::Point window_location = event->location();
-  aura::Window::ConvertPointToTarget(event_target, hit_window,
-                                     &window_location);
-
-  views::View* root_view = hit_widget->GetRootView();
-  views::View* hit_view = root_view->GetEventHandlerForPoint(window_location);
-
-  if (hit_view) {
-    // Send the accessibility event, then save the view so we can post the
-    // cancel event on the same view if possible.
-    hit_view->NotifyAccessibilityEvent(ax_event, true);
-    if (!last_view_storage_id_) {
-      last_view_storage_id_ =
-          views::ViewStorage::GetInstance()->CreateStorageID();
-    }
-    views::ViewStorage::GetInstance()->RemoveView(last_view_storage_id_);
-    views::ViewStorage::GetInstance()->StoreView(last_view_storage_id_,
-                                                 hit_view);
+  ui::AXTreeIDRegistry* registry = ui::AXTreeIDRegistry::GetInstance();
+  ui::AXHostDelegate* delegate =
+      registry->GetHostDelegate(extensions::api::automation::kDesktopTreeID);
+  if (delegate) {
+    ui::AXActionData action;
+    action.action = ui::AX_ACTION_HIT_TEST;
+    action.target_point = event->root_location();
+    action.hit_test_event_to_fire = ax_event;
+    delegate->PerformAction(action);
   }
 }
 
@@ -169,20 +143,8 @@
 }
 
 void SelectToSpeakEventHandler::SendCancelAXEvent() {
-  // If the user releases Search while the button is still down, cancel
-  // the Select-to-speak gesture. Try to post it on the same View that
-  // was last targeted, but if that's impossible, post it on the desktop.
-  views::View* last_view =
-      last_view_storage_id_
-          ? views::ViewStorage::GetInstance()->RetrieveView(
-                last_view_storage_id_)
-          : nullptr;
-  if (last_view) {
-    last_view->NotifyAccessibilityEvent(ui::AX_EVENT_MOUSE_CANCELED, true);
-  } else {
-    AutomationManagerAura::GetInstance()->HandleEvent(
-        nullptr, nullptr, ui::AX_EVENT_MOUSE_CANCELED);
-  }
+  AutomationManagerAura::GetInstance()->HandleEvent(
+      nullptr, nullptr, ui::AX_EVENT_MOUSE_CANCELED);
 }
 
 }  // namespace chromeos
diff --git a/chrome/browser/chromeos/accessibility/select_to_speak_event_handler.h b/chrome/browser/chromeos/accessibility/select_to_speak_event_handler.h
index 012fb9e..048e2cf 100644
--- a/chrome/browser/chromeos/accessibility/select_to_speak_event_handler.h
+++ b/chrome/browser/chromeos/accessibility/select_to_speak_event_handler.h
@@ -62,8 +62,6 @@
   // is pressed, and only cleared when all keys are released.
   std::set<ui::KeyboardCode> keys_pressed_together_;
 
-  int last_view_storage_id_ = 0;
-
   DISALLOW_COPY_AND_ASSIGN(SelectToSpeakEventHandler);
 };
 
diff --git a/chrome/browser/chromeos/accessibility/select_to_speak_event_handler_unittest.cc b/chrome/browser/chromeos/accessibility/select_to_speak_event_handler_unittest.cc
index 20167745..d3a9865 100644
--- a/chrome/browser/chromeos/accessibility/select_to_speak_event_handler_unittest.cc
+++ b/chrome/browser/chromeos/accessibility/select_to_speak_event_handler_unittest.cc
@@ -219,30 +219,6 @@
   EXPECT_FALSE(event_capturer_.last_key_event());
 }
 
-TEST_F(SelectToSpeakEventHandlerTest, SearchPlusMouseThenCancel) {
-  // If the user holds the Search key and then presses the mouse button,
-  // but then releases the Search key first while the mouse is still down,
-  // a cancel AX event is sent and the subsequent mouse up is canceled too.
-
-  generator_->PressKey(ui::VKEY_LWIN, ui::EF_COMMAND_DOWN);
-  ASSERT_TRUE(event_capturer_.last_key_event());
-  EXPECT_FALSE(event_capturer_.last_key_event()->handled());
-
-  generator_->set_current_location(gfx::Point(100, 12));
-  generator_->PressLeftButton();
-  EXPECT_FALSE(event_capturer_.last_mouse_event());
-  EXPECT_TRUE(event_delegate_->CapturedAXEvent(ui::AX_EVENT_MOUSE_PRESSED));
-
-  event_capturer_.Reset();
-  generator_->ReleaseKey(ui::VKEY_LWIN, ui::EF_COMMAND_DOWN);
-  EXPECT_FALSE(event_capturer_.last_key_event());
-
-  EXPECT_TRUE(event_delegate_->CapturedAXEvent(ui::AX_EVENT_MOUSE_CANCELED));
-
-  generator_->ReleaseLeftButton();
-  EXPECT_FALSE(event_capturer_.last_mouse_event());
-}
-
 TEST_F(SelectToSpeakEventHandlerTest, SearchPlusKeyIgnoresClicks) {
   // If the user presses the Search key and then some other key,
   // we should assume the user does not want select-to-speak, and
diff --git a/chrome/browser/extensions/api/automation/automation_apitest.cc b/chrome/browser/extensions/api/automation/automation_apitest.cc
index 8ccffdc..42bbe9f 100644
--- a/chrome/browser/extensions/api/automation/automation_apitest.cc
+++ b/chrome/browser/extensions/api/automation/automation_apitest.cc
@@ -233,6 +233,11 @@
       << message_;
 }
 
+IN_PROC_BROWSER_TEST_F(AutomationApiTest, DesktopHitTest) {
+  ASSERT_TRUE(RunExtensionSubtest("automation/tests/desktop", "hit_test.html"))
+      << message_;
+}
+
 // Flaky, see http://crbug.com/435449
 IN_PROC_BROWSER_TEST_F(AutomationApiTest, DISABLED_DesktopLoadTabs) {
   ASSERT_TRUE(RunExtensionSubtest("automation/tests/desktop", "load_tabs.html"))
diff --git a/chrome/browser/extensions/api/automation_internal/automation_internal_api.cc b/chrome/browser/extensions/api/automation_internal/automation_internal_api.cc
index 916d440..3871193 100644
--- a/chrome/browser/extensions/api/automation_internal/automation_internal_api.cc
+++ b/chrome/browser/extensions/api/automation_internal/automation_internal_api.cc
@@ -44,6 +44,7 @@
 
 #if defined(USE_AURA)
 #include "chrome/browser/ui/aura/accessibility/automation_manager_aura.h"
+#include "ui/aura/env.h"
 #endif
 
 namespace extensions {
@@ -177,6 +178,9 @@
       params.event_type = event.event_type;
       params.update = event.update;
       params.event_from = event.event_from;
+#if defined(USE_AURA)
+      params.mouse_location = aura::Env::GetInstance()->last_mouse_location();
+#endif
 
       AutomationEventRouter* router = AutomationEventRouter::GetInstance();
       router->DispatchAccessibilityEvent(params);
diff --git a/chrome/browser/media/router/presentation_service_delegate_impl.cc b/chrome/browser/media/router/presentation_service_delegate_impl.cc
index 762df74..d9b55d7 100644
--- a/chrome/browser/media/router/presentation_service_delegate_impl.cc
+++ b/chrome/browser/media/router/presentation_service_delegate_impl.cc
@@ -164,8 +164,8 @@
  private:
   MediaSource GetMediaSourceFromListener(
       content::PresentationScreenAvailabilityListener* listener) const;
-  base::SmallMap<std::map<std::string, MediaRoute>> presentation_id_to_route_;
-  base::SmallMap<
+  base::small_map<std::map<std::string, MediaRoute>> presentation_id_to_route_;
+  base::small_map<
       std::map<std::string, std::unique_ptr<PresentationMediaSinksObserver>>>
       url_to_sinks_observer_;
   std::unordered_map<
diff --git a/chrome/browser/media/webrtc/media_stream_devices_controller.cc b/chrome/browser/media/webrtc/media_stream_devices_controller.cc
index a063d9b..c7df704 100644
--- a/chrome/browser/media/webrtc/media_stream_devices_controller.cc
+++ b/chrome/browser/media/webrtc/media_stream_devices_controller.cc
@@ -190,31 +190,134 @@
 
 }  // namespace
 
+MediaStreamDevicesController::Request::Request(
+    Profile* profile,
+    bool is_asking_for_audio,
+    bool is_asking_for_video,
+    const GURL& security_origin,
+    PromptAnsweredCallback prompt_answered_callback)
+    : profile_(profile),
+      is_asking_for_audio_(is_asking_for_audio),
+      is_asking_for_video_(is_asking_for_video),
+      security_origin_(security_origin),
+      prompt_answered_callback_(prompt_answered_callback),
+      responded_(false) {}
+
+MediaStreamDevicesController::Request::~Request() {
+  if (!responded_) {
+    RecordPermissionAction(is_asking_for_audio_, is_asking_for_video_,
+                           security_origin_, profile_,
+                           base::Bind(PermissionUmaUtil::PermissionIgnored));
+  }
+}
+
+bool MediaStreamDevicesController::Request::IsAskingForAudio() const {
+  return is_asking_for_audio_;
+}
+
+bool MediaStreamDevicesController::Request::IsAskingForVideo() const {
+  return is_asking_for_video_;
+}
+
+base::string16 MediaStreamDevicesController::Request::GetMessageText() const {
+  int message_id = IDS_MEDIA_CAPTURE_AUDIO_AND_VIDEO;
+  if (!IsAskingForAudio())
+    message_id = IDS_MEDIA_CAPTURE_VIDEO_ONLY;
+  else if (!IsAskingForVideo())
+    message_id = IDS_MEDIA_CAPTURE_AUDIO_ONLY;
+  return l10n_util::GetStringFUTF16(
+      message_id,
+      url_formatter::FormatUrlForSecurityDisplay(
+          GetOrigin(), url_formatter::SchemeDisplay::OMIT_CRYPTOGRAPHIC));
+}
+
+PermissionRequest::IconId MediaStreamDevicesController::Request::GetIconId()
+    const {
+#if defined(OS_ANDROID)
+  return IsAskingForVideo() ? IDR_INFOBAR_MEDIA_STREAM_CAMERA
+                            : IDR_INFOBAR_MEDIA_STREAM_MIC;
+#else
+  return IsAskingForVideo() ? ui::kVideocamIcon : ui::kMicrophoneIcon;
+#endif
+}
+
+base::string16 MediaStreamDevicesController::Request::GetMessageTextFragment()
+    const {
+  int message_id = IDS_MEDIA_CAPTURE_AUDIO_AND_VIDEO_PERMISSION_FRAGMENT;
+  if (!IsAskingForAudio())
+    message_id = IDS_MEDIA_CAPTURE_VIDEO_ONLY_PERMISSION_FRAGMENT;
+  else if (!IsAskingForVideo())
+    message_id = IDS_MEDIA_CAPTURE_AUDIO_ONLY_PERMISSION_FRAGMENT;
+  return l10n_util::GetStringUTF16(message_id);
+}
+
+GURL MediaStreamDevicesController::Request::GetOrigin() const {
+  return security_origin_;
+}
+
+void MediaStreamDevicesController::Request::PermissionGranted() {
+  RecordPermissionAction(is_asking_for_audio_, is_asking_for_video_,
+                         security_origin_, profile_,
+                         base::Bind(PermissionUmaUtil::PermissionGranted));
+  responded_ = true;
+  prompt_answered_callback_.Run(CONTENT_SETTING_ALLOW, persist());
+}
+
+void MediaStreamDevicesController::Request::PermissionDenied() {
+  RecordPermissionAction(is_asking_for_audio_, is_asking_for_video_,
+                         security_origin_, profile_,
+                         base::Bind(PermissionUmaUtil::PermissionDenied));
+  responded_ = true;
+  prompt_answered_callback_.Run(CONTENT_SETTING_BLOCK, persist());
+}
+
+void MediaStreamDevicesController::Request::Cancelled() {
+  RecordPermissionAction(is_asking_for_audio_, is_asking_for_video_,
+                         security_origin_, profile_,
+                         base::Bind(PermissionUmaUtil::PermissionDismissed));
+  responded_ = true;
+  prompt_answered_callback_.Run(CONTENT_SETTING_ASK, false /* persist */);
+}
+
+void MediaStreamDevicesController::Request::RequestFinished() {
+  delete this;
+}
+
+PermissionRequestType
+MediaStreamDevicesController::Request::GetPermissionRequestType() const {
+  return PermissionRequestType::MEDIA_STREAM;
+}
+
+bool MediaStreamDevicesController::Request::ShouldShowPersistenceToggle()
+    const {
+  return PermissionUtil::ShouldShowPersistenceToggle();
+}
+
 // Implementation of PermissionPromptDelegate which actually shows a permission
 // prompt.
 class MediaStreamDevicesController::PermissionPromptDelegateImpl
-    : public internal::PermissionPromptDelegate {
+    : public MediaStreamDevicesController::PermissionPromptDelegate {
  public:
   void ShowPrompt(
       bool user_gesture,
       content::WebContents* web_contents,
-      std::unique_ptr<MediaStreamDevicesController> controller) override {
+      std::unique_ptr<MediaStreamDevicesController::Request> request) override {
 #if defined(OS_ANDROID)
     PermissionUmaUtil::RecordPermissionPromptShown(
-        controller->GetPermissionRequestType(),
+        request->GetPermissionRequestType(),
         PermissionUtil::GetGestureType(user_gesture));
     if (PermissionDialogDelegate::ShouldShowDialog(user_gesture)) {
       PermissionDialogDelegate::CreateMediaStreamDialog(
-          web_contents, user_gesture, std::move(controller));
+          web_contents, user_gesture, std::move(request));
     } else {
       MediaStreamInfoBarDelegateAndroid::Create(web_contents, user_gesture,
-                                                std::move(controller));
+                                                std::move(request));
     }
 #else
     PermissionRequestManager* permission_request_manager =
         PermissionRequestManager::FromWebContents(web_contents);
     if (permission_request_manager)
-      permission_request_manager->AddRequest(controller.release());
+      permission_request_manager->AddRequest(request.release());
 #endif
   }
 };
@@ -238,9 +341,6 @@
 
 MediaStreamDevicesController::~MediaStreamDevicesController() {
   if (!callback_.is_null()) {
-    RecordPermissionAction(IsAskingForAudio(), IsAskingForVideo(), GetOrigin(),
-                           profile_,
-                           base::Bind(PermissionUmaUtil::PermissionIgnored));
     callback_.Run(content::MediaStreamDevices(),
                   content::MEDIA_DEVICE_FAILED_DUE_TO_SHUTDOWN,
                   std::unique_ptr<content::MediaStreamUI>());
@@ -255,16 +355,23 @@
   return old_video_setting_ == CONTENT_SETTING_ASK;
 }
 
-base::string16 MediaStreamDevicesController::GetMessageText() const {
-  int message_id = IDS_MEDIA_CAPTURE_AUDIO_AND_VIDEO;
-  if (!IsAskingForAudio())
-    message_id = IDS_MEDIA_CAPTURE_VIDEO_ONLY;
-  else if (!IsAskingForVideo())
-    message_id = IDS_MEDIA_CAPTURE_AUDIO_ONLY;
-  return l10n_util::GetStringFUTF16(
-      message_id,
-      url_formatter::FormatUrlForSecurityDisplay(
-          GetOrigin(), url_formatter::SchemeDisplay::OMIT_CRYPTOGRAPHIC));
+void MediaStreamDevicesController::PromptAnswered(ContentSetting setting,
+                                                  bool persist) {
+  ContentSetting audio_setting = GetNewSetting(
+      CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC, old_audio_setting_, setting);
+  ContentSetting video_setting = GetNewSetting(
+      CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA, old_video_setting_, setting);
+
+  if (persist)
+    StorePermission(audio_setting, video_setting);
+
+  content::MediaStreamRequestResult denial_reason = content::MEDIA_DEVICE_OK;
+  if (setting == CONTENT_SETTING_ASK)
+    denial_reason = content::MEDIA_DEVICE_PERMISSION_DISMISSED;
+  else if (setting == CONTENT_SETTING_BLOCK)
+    denial_reason = content::MEDIA_DEVICE_PERMISSION_DENIED;
+
+  RunCallback(audio_setting, video_setting, denial_reason);
 }
 
 #if defined(OS_ANDROID)
@@ -290,76 +397,11 @@
 }
 #endif  // defined(OS_ANDROID)
 
-PermissionRequest::IconId MediaStreamDevicesController::GetIconId() const {
-#if defined(OS_ANDROID)
-  return IsAskingForVideo() ? IDR_INFOBAR_MEDIA_STREAM_CAMERA
-                            : IDR_INFOBAR_MEDIA_STREAM_MIC;
-#else
-  return IsAskingForVideo() ? ui::kVideocamIcon : ui::kMicrophoneIcon;
-#endif
-}
-
-base::string16 MediaStreamDevicesController::GetMessageTextFragment() const {
-  int message_id = IDS_MEDIA_CAPTURE_AUDIO_AND_VIDEO_PERMISSION_FRAGMENT;
-  if (!IsAskingForAudio())
-    message_id = IDS_MEDIA_CAPTURE_VIDEO_ONLY_PERMISSION_FRAGMENT;
-  else if (!IsAskingForVideo())
-    message_id = IDS_MEDIA_CAPTURE_AUDIO_ONLY_PERMISSION_FRAGMENT;
-  return l10n_util::GetStringUTF16(message_id);
-}
-
-GURL MediaStreamDevicesController::GetOrigin() const {
-  return request_.security_origin;
-}
-
-void MediaStreamDevicesController::PermissionGranted() {
-  RecordPermissionAction(IsAskingForAudio(), IsAskingForVideo(), GetOrigin(),
-                         profile_,
-                         base::Bind(PermissionUmaUtil::PermissionGranted));
-  RunCallback(GetNewSetting(CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC,
-                            old_audio_setting_, CONTENT_SETTING_ALLOW),
-              GetNewSetting(CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA,
-                            old_video_setting_, CONTENT_SETTING_ALLOW),
-              content::MEDIA_DEVICE_PERMISSION_DENIED);
-}
-
-void MediaStreamDevicesController::PermissionDenied() {
-  RecordPermissionAction(IsAskingForAudio(), IsAskingForVideo(), GetOrigin(),
-                         profile_,
-                         base::Bind(PermissionUmaUtil::PermissionDenied));
-  RunCallback(GetNewSetting(CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC,
-                            old_audio_setting_, CONTENT_SETTING_BLOCK),
-              GetNewSetting(CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA,
-                            old_video_setting_, CONTENT_SETTING_BLOCK),
-              content::MEDIA_DEVICE_PERMISSION_DENIED);
-}
-
-bool MediaStreamDevicesController::ShouldShowPersistenceToggle() const {
-  return PermissionUtil::ShouldShowPersistenceToggle();
-}
-
-void MediaStreamDevicesController::Cancelled() {
-  RecordPermissionAction(IsAskingForAudio(), IsAskingForVideo(), GetOrigin(),
-                         profile_,
-                         base::Bind(PermissionUmaUtil::PermissionDismissed));
-  RunCallback(old_audio_setting_, old_video_setting_,
-              content::MEDIA_DEVICE_PERMISSION_DISMISSED);
-}
-
-void MediaStreamDevicesController::RequestFinished() {
-  delete this;
-}
-
-PermissionRequestType MediaStreamDevicesController::GetPermissionRequestType()
-    const {
-  return PermissionRequestType::MEDIA_STREAM;
-}
-
 // static
 void MediaStreamDevicesController::RequestPermissionsWithDelegate(
     const content::MediaStreamRequest& request,
     const content::MediaResponseCallback& callback,
-    internal::PermissionPromptDelegate* delegate) {
+    PermissionPromptDelegate* delegate) {
   content::WebContents* web_contents =
       content::WebContents::FromRenderFrameHost(
           content::RenderFrameHost::FromID(request.render_process_id,
@@ -397,8 +439,15 @@
     return;
   }
 
-  delegate->ShowPrompt(request.user_gesture, web_contents,
-                       std::move(controller));
+  Profile* profile =
+      Profile::FromBrowserContext(web_contents->GetBrowserContext());
+  delegate->ShowPrompt(
+      request.user_gesture, web_contents,
+      base::MakeUnique<MediaStreamDevicesController::Request>(
+          profile, controller->IsAskingForAudio(),
+          controller->IsAskingForVideo(), request.security_origin,
+          base::Bind(&MediaStreamDevicesController::PromptAnswered,
+                     base::Passed(&controller))));
 }
 
 MediaStreamDevicesController::MediaStreamDevicesController(
@@ -547,13 +596,9 @@
     content::MediaStreamRequestResult denial_reason) {
   CHECK(!callback_.is_null());
 
-  // If the kill switch is on we don't update the tab context or persist the
-  // setting.
-  if (denial_reason != content::MEDIA_DEVICE_KILL_SWITCH_ON) {
-    if (persist())
-      StorePermission(audio_setting, video_setting);
+  // If the kill switch is on we don't update the tab context.
+  if (denial_reason != content::MEDIA_DEVICE_KILL_SWITCH_ON)
     UpdateTabSpecificContentSettings(audio_setting, video_setting);
-  }
 
   content::MediaStreamDevices devices =
       GetDevices(audio_setting, video_setting);
@@ -694,8 +739,6 @@
     ContentSettingsType content_type,
     ContentSetting old_setting,
     ContentSetting user_decision) const {
-  DCHECK(user_decision == CONTENT_SETTING_ALLOW ||
-         user_decision == CONTENT_SETTING_BLOCK);
   ContentSetting result = old_setting;
   if (old_setting == CONTENT_SETTING_ASK)
     result = user_decision;
diff --git a/chrome/browser/media/webrtc/media_stream_devices_controller.h b/chrome/browser/media/webrtc/media_stream_devices_controller.h
index acd6b4c6..bcf5ce0 100644
--- a/chrome/browser/media/webrtc/media_stream_devices_controller.h
+++ b/chrome/browser/media/webrtc/media_stream_devices_controller.h
@@ -33,19 +33,66 @@
 class MediaStreamDevicesControllerTestApi;
 }
 
-namespace internal {
-// Delegate showing permission prompts.
-class PermissionPromptDelegate {
+class MediaStreamDevicesController {
  public:
-  virtual void ShowPrompt(
-      bool user_gesture,
-      content::WebContents* web_contents,
-      std::unique_ptr<MediaStreamDevicesController> controller) = 0;
-};
-}
+  // This class is only needed until we unify the codepaths for permission
+  // requests. It can be removed once crbug.com/606138 is fixed.
+  class Request : public PermissionRequest {
+   public:
+    using PromptAnsweredCallback =
+        base::Callback<void(ContentSetting, bool /* persist */)>;
 
-class MediaStreamDevicesController : public PermissionRequest {
- public:
+    Request(Profile* profile,
+            bool is_asking_for_audio,
+            bool is_asking_for_video,
+            const GURL& security_origin,
+            PromptAnsweredCallback prompt_answered_callback);
+
+    ~Request() override;
+
+    bool IsAskingForAudio() const;
+    bool IsAskingForVideo() const;
+    base::string16 GetMessageText() const;
+
+    // PermissionRequest:
+    IconId GetIconId() const override;
+    base::string16 GetMessageTextFragment() const override;
+    GURL GetOrigin() const override;
+    void PermissionGranted() override;
+    void PermissionDenied() override;
+    void Cancelled() override;
+    void RequestFinished() override;
+    PermissionRequestType GetPermissionRequestType() const override;
+    bool ShouldShowPersistenceToggle() const override;
+
+   private:
+    Profile* profile_;
+
+    bool is_asking_for_audio_;
+    bool is_asking_for_video_;
+
+    GURL security_origin_;
+
+    PromptAnsweredCallback prompt_answered_callback_;
+
+    // Whether the prompt has been answered.
+    bool responded_;
+
+    DISALLOW_COPY_AND_ASSIGN(Request);
+  };
+
+  // This class is only needed interally and for tests. It can be removed once
+  // crbug.com/606138 is fixed. Delegate for showing permission prompts. It's
+  // only public because subclassing from a friend class doesn't work in gcc
+  // (see https://codereview.chromium.org/2768923003).
+  class PermissionPromptDelegate {
+   public:
+    virtual void ShowPrompt(
+        bool user_gesture,
+        content::WebContents* web_contents,
+        std::unique_ptr<MediaStreamDevicesController::Request> request) = 0;
+  };
+
   static void RequestPermissions(
       const content::MediaStreamRequest& request,
       const content::MediaResponseCallback& callback);
@@ -53,29 +100,20 @@
   // Registers the prefs backing the audio and video policies.
   static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry);
 
-  ~MediaStreamDevicesController() override;
+  ~MediaStreamDevicesController();
 
   bool IsAskingForAudio() const;
   bool IsAskingForVideo() const;
-  base::string16 GetMessageText() const;
+
+  // Called when a permission prompt has been answered, with the |response| and
+  // whether the choice should be persisted.
+  void PromptAnswered(ContentSetting response, bool persist);
 
 #if defined(OS_ANDROID)
   // Called when the Android OS-level prompt is answered.
   void AndroidOSPromptAnswered(bool allowed);
 #endif  // defined(OS_ANDROID)
 
-  bool ShouldShowPersistenceToggle() const override;
-
-  // PermissionRequest:
-  IconId GetIconId() const override;
-  base::string16 GetMessageTextFragment() const override;
-  GURL GetOrigin() const override;
-  void PermissionGranted() override;
-  void PermissionDenied() override;
-  void Cancelled() override;
-  void RequestFinished() override;
-  PermissionRequestType GetPermissionRequestType() const override;
-
  private:
   friend class MediaStreamDevicesControllerTest;
   friend class test::MediaStreamDevicesControllerTestApi;
@@ -86,7 +124,7 @@
   static void RequestPermissionsWithDelegate(
       const content::MediaStreamRequest& request,
       const content::MediaResponseCallback& callback,
-      internal::PermissionPromptDelegate* delegate);
+      PermissionPromptDelegate* delegate);
 
   MediaStreamDevicesController(content::WebContents* web_contents,
                                const content::MediaStreamRequest& request,
@@ -158,7 +196,7 @@
   // audio/video devices was granted or not.
   content::MediaResponseCallback callback_;
 
-  std::unique_ptr<internal::PermissionPromptDelegate> delegate_;
+  std::unique_ptr<PermissionPromptDelegate> delegate_;
 
   DISALLOW_COPY_AND_ASSIGN(MediaStreamDevicesController);
 };
diff --git a/chrome/browser/media/webrtc/media_stream_devices_controller_browsertest.cc b/chrome/browser/media/webrtc/media_stream_devices_controller_browsertest.cc
index 7526eee..19d44472 100644
--- a/chrome/browser/media/webrtc/media_stream_devices_controller_browsertest.cc
+++ b/chrome/browser/media/webrtc/media_stream_devices_controller_browsertest.cc
@@ -36,21 +36,21 @@
   // we should remove PermissionPromptDelegate and just use
   // MockPermissionPromptFactory instead. The APIs are the same.
   class TestPermissionPromptDelegate
-      : public internal::PermissionPromptDelegate {
+      : public MediaStreamDevicesController::PermissionPromptDelegate {
    public:
-    void ShowPrompt(
-        bool user_gesture,
-        content::WebContents* web_contents,
-        std::unique_ptr<MediaStreamDevicesController> controller) override {
-      if (controller->IsAskingForAudio())
+    void ShowPrompt(bool user_gesture,
+                    content::WebContents* web_contents,
+                    std::unique_ptr<MediaStreamDevicesController::Request>
+                        request) override {
+      if (request->IsAskingForAudio())
         last_requests_.push_back(CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC);
-      if (controller->IsAskingForVideo())
+      if (request->IsAskingForVideo())
         last_requests_.push_back(CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA);
 
       if (response_type_ == PermissionRequestManager::ACCEPT_ALL)
-        controller->PermissionGranted();
+        request->PermissionGranted();
       else if (response_type_ == PermissionRequestManager::DENY_ALL)
-        controller->PermissionDenied();
+        request->PermissionDenied();
     }
 
     void set_response_type(
diff --git a/chrome/browser/media/webrtc/media_stream_infobar_delegate_android.cc b/chrome/browser/media/webrtc/media_stream_infobar_delegate_android.cc
index 075e247b..02f9272 100644
--- a/chrome/browser/media/webrtc/media_stream_infobar_delegate_android.cc
+++ b/chrome/browser/media/webrtc/media_stream_infobar_delegate_android.cc
@@ -34,13 +34,13 @@
 bool MediaStreamInfoBarDelegateAndroid::Create(
     content::WebContents* web_contents,
     bool user_gesture,
-    std::unique_ptr<MediaStreamDevicesController> controller) {
+    std::unique_ptr<MediaStreamDevicesController::Request> request) {
   InfoBarService* infobar_service =
       InfoBarService::FromWebContents(web_contents);
   if (!infobar_service) {
     // Deny the request if there is no place to show the infobar, e.g. when
     // the request comes from a background extension page.
-    controller->Cancelled();
+    request->Cancelled();
     return false;
   }
 
@@ -48,8 +48,7 @@
       CreatePermissionInfoBar(std::unique_ptr<PermissionInfoBarDelegate>(
           new MediaStreamInfoBarDelegateAndroid(
               Profile::FromBrowserContext(web_contents->GetBrowserContext()),
-              user_gesture,
-              std::move(controller)))));
+              user_gesture, std::move(request)))));
   for (size_t i = 0; i < infobar_service->infobar_count(); ++i) {
     infobars::InfoBar* old_infobar = infobar_service->infobar_at(i);
     if (old_infobar->delegate()->AsMediaStreamInfoBarDelegateAndroid()) {
@@ -72,16 +71,16 @@
 }
 
 int MediaStreamInfoBarDelegateAndroid::GetIconId() const {
-  return controller_->IsAskingForVideo() ? IDR_INFOBAR_MEDIA_STREAM_CAMERA
-                                         : IDR_INFOBAR_MEDIA_STREAM_MIC;
+  return request_->IsAskingForVideo() ? IDR_INFOBAR_MEDIA_STREAM_CAMERA
+                                      : IDR_INFOBAR_MEDIA_STREAM_MIC;
 }
 
 MediaStreamInfoBarDelegateAndroid::MediaStreamInfoBarDelegateAndroid(
     Profile* profile,
     bool user_gesture,
-    std::unique_ptr<MediaStreamDevicesController> controller)
+    std::unique_ptr<MediaStreamDevicesController::Request> request)
     : PermissionInfoBarDelegate(
-          controller->GetOrigin(),
+          request->GetOrigin(),
           // The content setting type and permission type here are only passed
           // in to fit into PermissionInfoBarDelegate, even though media infobar
           // controls both mic and camera. This is a temporary state for easy
@@ -92,12 +91,12 @@
           user_gesture,
           profile,
           // This is only passed in to fit into PermissionInfoBarDelegate.
-          // Infobar accepted/denied/dismissed is handled by controller, not via
+          // Infobar accepted/denied/dismissed is handled by |request|, not via
           // callbacks.
           base::Bind(&DoNothing)),
-      controller_(std::move(controller)) {
-  DCHECK(controller_.get());
-  DCHECK(controller_->IsAskingForAudio() || controller_->IsAskingForVideo());
+      request_(std::move(request)) {
+  DCHECK(request_.get());
+  DCHECK(request_->IsAskingForAudio() || request_->IsAskingForVideo());
 }
 
 MediaStreamInfoBarDelegateAndroid::~MediaStreamInfoBarDelegateAndroid() {}
@@ -105,7 +104,7 @@
 void MediaStreamInfoBarDelegateAndroid::InfoBarDismissed() {
   // Deny the request if the infobar was closed with the 'x' button, since
   // we don't want WebRTC to be waiting for an answer that will never come.
-  controller_->Cancelled();
+  request_->Cancelled();
 }
 
 MediaStreamInfoBarDelegateAndroid*
@@ -114,7 +113,7 @@
 }
 
 base::string16 MediaStreamInfoBarDelegateAndroid::GetMessageText() const {
-  return controller_->GetMessageText();
+  return request_->GetMessageText();
 }
 
 base::string16 MediaStreamInfoBarDelegateAndroid::GetButtonLabel(
@@ -131,9 +130,9 @@
 }
 
 int MediaStreamInfoBarDelegateAndroid::GetMessageResourceId() const {
-  if (!controller_->IsAskingForAudio())
+  if (!request_->IsAskingForAudio())
     return IDS_MEDIA_CAPTURE_VIDEO_ONLY;
-  else if (!controller_->IsAskingForVideo())
+  else if (!request_->IsAskingForVideo())
     return IDS_MEDIA_CAPTURE_AUDIO_ONLY;
   return IDS_MEDIA_CAPTURE_AUDIO_AND_VIDEO;
 }
@@ -142,25 +141,25 @@
   // TODO(dominickn): fold these metrics calls into
   // PermissionUmaUtil::PermissionGranted. See crbug.com/638076.
   PermissionUmaUtil::RecordPermissionPromptAccepted(
-      controller_->GetPermissionRequestType(),
+      request_->GetPermissionRequestType(),
       PermissionUtil::GetGestureType(user_gesture()));
 
   bool persist_permission = true;
   if (ShouldShowPersistenceToggle()) {
     persist_permission = persist();
 
-    if (controller_->IsAskingForAudio()) {
+    if (request_->IsAskingForAudio()) {
       PermissionUmaUtil::PermissionPromptAcceptedWithPersistenceToggle(
           CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC, persist_permission);
     }
-    if (controller_->IsAskingForVideo()) {
+    if (request_->IsAskingForVideo()) {
       PermissionUmaUtil::PermissionPromptAcceptedWithPersistenceToggle(
           CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA, persist_permission);
     }
   }
 
-  controller_->set_persist(persist_permission);
-  controller_->PermissionGranted();
+  request_->set_persist(persist_permission);
+  request_->PermissionGranted();
   return true;
 }
 
@@ -168,24 +167,24 @@
   // TODO(dominickn): fold these metrics calls into
   // PermissionUmaUtil::PermissionDenied. See crbug.com/638076.
   PermissionUmaUtil::RecordPermissionPromptDenied(
-      controller_->GetPermissionRequestType(),
+      request_->GetPermissionRequestType(),
       PermissionUtil::GetGestureType(user_gesture()));
 
   bool persist_permission = true;
   if (ShouldShowPersistenceToggle()) {
     persist_permission = persist();
 
-    if (controller_->IsAskingForAudio()) {
+    if (request_->IsAskingForAudio()) {
       PermissionUmaUtil::PermissionPromptDeniedWithPersistenceToggle(
           CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC, persist_permission);
     }
-    if (controller_->IsAskingForVideo()) {
+    if (request_->IsAskingForVideo()) {
       PermissionUmaUtil::PermissionPromptDeniedWithPersistenceToggle(
           CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA, persist_permission);
     }
   }
-  controller_->set_persist(persist_permission);
-  controller_->PermissionDenied();
+  request_->set_persist(persist_permission);
+  request_->PermissionDenied();
   return true;
 }
 
@@ -200,9 +199,9 @@
 std::vector<int> MediaStreamInfoBarDelegateAndroid::content_settings_types()
     const {
   std::vector<int> types;
-  if (controller_->IsAskingForAudio())
+  if (request_->IsAskingForAudio())
     types.push_back(CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC);
-  if (controller_->IsAskingForVideo())
+  if (request_->IsAskingForVideo())
     types.push_back(CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA);
   return types;
 }
diff --git a/chrome/browser/media/webrtc/media_stream_infobar_delegate_android.h b/chrome/browser/media/webrtc/media_stream_infobar_delegate_android.h
index 8f35ce6..3caaa7b 100644
--- a/chrome/browser/media/webrtc/media_stream_infobar_delegate_android.h
+++ b/chrome/browser/media/webrtc/media_stream_infobar_delegate_android.h
@@ -23,14 +23,16 @@
   // then checks for an existing infobar for |web_contents| and replaces it if
   // found, or just adds the new infobar otherwise.  Returns whether an infobar
   // was created.
-  static bool Create(content::WebContents* web_contents,
-                     bool user_gesture,
-                     std::unique_ptr<MediaStreamDevicesController> controller);
+  static bool Create(
+      content::WebContents* web_contents,
+      bool user_gesture,
+      std::unique_ptr<MediaStreamDevicesController::Request> request);
 
   MediaStreamInfoBarDelegateAndroid(
       Profile* profile,
       bool user_gesture,
-      std::unique_ptr<MediaStreamDevicesController> controller);
+      std::unique_ptr<MediaStreamDevicesController::Request> request);
+
  private:
   friend class WebRtcTestBase;
 
@@ -52,7 +54,7 @@
   int GetMessageResourceId() const override;
   std::vector<int> content_settings_types() const override;
 
-  std::unique_ptr<MediaStreamDevicesController> controller_;
+  std::unique_ptr<MediaStreamDevicesController::Request> request_;
 
   DISALLOW_COPY_AND_ASSIGN(MediaStreamInfoBarDelegateAndroid);
 };
diff --git a/chrome/browser/metrics/chrome_browser_main_extra_parts_metrics.cc b/chrome/browser/metrics/chrome_browser_main_extra_parts_metrics.cc
index 9055c02..9be497e 100644
--- a/chrome/browser/metrics/chrome_browser_main_extra_parts_metrics.cc
+++ b/chrome/browser/metrics/chrome_browser_main_extra_parts_metrics.cc
@@ -405,7 +405,7 @@
   constexpr base::TimeDelta kStartupMetricsGatheringDelay =
       base::TimeDelta::FromSeconds(45);
   content::BrowserThread::GetBlockingPool()->PostDelayedTask(
-      FROM_HERE, base::Bind(&RecordStartupMetricsOnBlockingPool),
+      FROM_HERE, base::BindOnce(&RecordStartupMetricsOnBlockingPool),
       kStartupMetricsGatheringDelay);
 #if defined(OS_WIN)
   content::BrowserThread::PostDelayedTask(
diff --git a/chrome/browser/metrics/chrome_metrics_service_client.cc b/chrome/browser/metrics/chrome_metrics_service_client.cc
index aae99b7..bd75e7f 100644
--- a/chrome/browser/metrics/chrome_metrics_service_client.cc
+++ b/chrome/browser/metrics/chrome_metrics_service_client.cc
@@ -190,8 +190,8 @@
     // When metrics reporting is not enabled, any existing file should be
     // deleted in order to preserve user privacy.
     task_runner->PostTask(FROM_HERE,
-                          base::Bind(base::IgnoreResult(&base::DeleteFile),
-                                     metrics_file, /*recursive=*/false));
+                          base::BindOnce(base::IgnoreResult(&base::DeleteFile),
+                                         metrics_file, /*recursive=*/false));
   }
 }
 
@@ -831,7 +831,7 @@
   // Merge histograms from metrics providers into StatisticsRecorder.
   content::BrowserThread::PostTaskAndReply(
       content::BrowserThread::UI, FROM_HERE,
-      base::Bind(&base::StatisticsRecorder::ImportProvidedHistograms),
+      base::BindOnce(&base::StatisticsRecorder::ImportProvidedHistograms),
       callback);
 
   // Set up the callback task to call after we receive histograms from all
diff --git a/chrome/browser/metrics/chrome_metrics_services_manager_client.cc b/chrome/browser/metrics/chrome_metrics_services_manager_client.cc
index 4f3314ed..bb5c6fc1 100644
--- a/chrome/browser/metrics/chrome_metrics_services_manager_client.cc
+++ b/chrome/browser/metrics/chrome_metrics_services_manager_client.cc
@@ -67,12 +67,12 @@
   // ensures that the blocking pool is ready to accept tasks at that time.
   content::BrowserThread::PostTask(
       content::BrowserThread::UI, FROM_HERE,
-      base::Bind(
+      base::BindOnce(
           [](const metrics::ClientInfo& client_info) {
             content::BrowserThread::PostBlockingPoolTask(
                 FROM_HERE,
-                base::Bind(&GoogleUpdateSettings::StoreMetricsClientInfo,
-                           client_info));
+                base::BindOnce(&GoogleUpdateSettings::StoreMetricsClientInfo,
+                               client_info));
           },
           client_info));
 }
diff --git a/chrome/browser/metrics/desktop_session_duration/chrome_visibility_observer.cc b/chrome/browser/metrics/desktop_session_duration/chrome_visibility_observer.cc
index 0e0baf2..3ee6203e 100644
--- a/chrome/browser/metrics/desktop_session_duration/chrome_visibility_observer.cc
+++ b/chrome/browser/metrics/desktop_session_duration/chrome_visibility_observer.cc
@@ -49,8 +49,9 @@
 void ChromeVisibilityObserver::OnBrowserNoLongerActive(Browser* browser) {
   base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
       FROM_HERE,
-      base::Bind(&ChromeVisibilityObserver::SendVisibilityChangeEvent,
-                 weak_factory_.GetWeakPtr(), false, visibility_gap_timeout_),
+      base::BindOnce(&ChromeVisibilityObserver::SendVisibilityChangeEvent,
+                     weak_factory_.GetWeakPtr(), false,
+                     visibility_gap_timeout_),
       visibility_gap_timeout_);
 }
 
diff --git a/chrome/browser/metrics/field_trial_synchronizer.cc b/chrome/browser/metrics/field_trial_synchronizer.cc
index 7483bab..3b3ffef 100644
--- a/chrome/browser/metrics/field_trial_synchronizer.cc
+++ b/chrome/browser/metrics/field_trial_synchronizer.cc
@@ -57,10 +57,8 @@
     const std::string& group_name) {
   BrowserThread::PostTask(
       BrowserThread::UI, FROM_HERE,
-      base::Bind(&FieldTrialSynchronizer::NotifyAllRenderers,
-                 this,
-                 field_trial_name,
-                 group_name));
+      base::BindOnce(&FieldTrialSynchronizer::NotifyAllRenderers, this,
+                     field_trial_name, group_name));
   variations::SetVariationListCrashKeys();
 }
 
diff --git a/chrome/browser/metrics/plugin_metrics_provider.cc b/chrome/browser/metrics/plugin_metrics_provider.cc
index aaa2f5f..d9952290 100644
--- a/chrome/browser/metrics/plugin_metrics_provider.cc
+++ b/chrome/browser/metrics/plugin_metrics_provider.cc
@@ -370,9 +370,9 @@
 
   base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
       FROM_HERE,
-      base::Bind(&PluginMetricsProvider::RecordCurrentState,
-                weak_ptr_factory_.GetWeakPtr()),
-                base::TimeDelta::FromMilliseconds(delay_sec));
+      base::BindOnce(&PluginMetricsProvider::RecordCurrentState,
+                     weak_ptr_factory_.GetWeakPtr()),
+      base::TimeDelta::FromMilliseconds(delay_sec));
   return true;
 }
 
diff --git a/chrome/browser/metrics/thread_watcher.cc b/chrome/browser/metrics/thread_watcher.cc
index 75c4463..b64c70d 100644
--- a/chrome/browser/metrics/thread_watcher.cc
+++ b/chrome/browser/metrics/thread_watcher.cc
@@ -110,8 +110,8 @@
   ping_count_ = unresponsive_threshold_;
   ResetHangCounters();
   base::ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE, base::Bind(&ThreadWatcher::PostPingMessage,
-                            weak_ptr_factory_.GetWeakPtr()));
+      FROM_HERE, base::BindOnce(&ThreadWatcher::PostPingMessage,
+                                weak_ptr_factory_.GetWeakPtr()));
 }
 
 void ThreadWatcher::DeActivateThreadWatching() {
@@ -160,15 +160,14 @@
       base::Bind(&ThreadWatcher::OnPongMessage, weak_ptr_factory_.GetWeakPtr(),
                  ping_sequence_number_));
   if (watched_runner_->PostTask(
-          FROM_HERE,
-          base::Bind(&ThreadWatcher::OnPingMessage, thread_id_,
-                     callback))) {
-      // Post a task to check the responsiveness of watched thread.
-      base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
-          FROM_HERE,
-          base::Bind(&ThreadWatcher::OnCheckResponsiveness,
-                     weak_ptr_factory_.GetWeakPtr(), ping_sequence_number_),
-          unresponsive_time_);
+          FROM_HERE, base::BindOnce(&ThreadWatcher::OnPingMessage, thread_id_,
+                                    callback))) {
+    // Post a task to check the responsiveness of watched thread.
+    base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
+        FROM_HERE,
+        base::BindOnce(&ThreadWatcher::OnCheckResponsiveness,
+                       weak_ptr_factory_.GetWeakPtr(), ping_sequence_number_),
+        unresponsive_time_);
   } else {
     // Watched thread might have gone away, stop watching it.
     DeActivateThreadWatching();
@@ -201,8 +200,9 @@
     return;
 
   base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
-      FROM_HERE, base::Bind(&ThreadWatcher::PostPingMessage,
-                            weak_ptr_factory_.GetWeakPtr()),
+      FROM_HERE,
+      base::BindOnce(&ThreadWatcher::PostPingMessage,
+                     weak_ptr_factory_.GetWeakPtr()),
       sleep_time_);
 }
 
@@ -230,8 +230,8 @@
   // Post a task to check the responsiveness of watched thread.
   base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
       FROM_HERE,
-      base::Bind(&ThreadWatcher::OnCheckResponsiveness,
-                 weak_ptr_factory_.GetWeakPtr(), ping_sequence_number_),
+      base::BindOnce(&ThreadWatcher::OnCheckResponsiveness,
+                     weak_ptr_factory_.GetWeakPtr(), ping_sequence_number_),
       unresponsive_time_);
   responsive_ = false;
 }
@@ -537,9 +537,8 @@
 
   // Disarm the startup timebomb, even if stop has been called.
   BrowserThread::PostTask(
-      BrowserThread::UI,
-      FROM_HERE,
-      base::Bind(&StartupTimeBomb::DisarmStartupTimeBomb));
+      BrowserThread::UI, FROM_HERE,
+      base::BindOnce(&StartupTimeBomb::DisarmStartupTimeBomb));
 
   // This method is deferred in relationship to its StopWatchingAll()
   // counterpart. If a previous initialization has already happened, or if
@@ -917,8 +916,9 @@
     return;
   }
   base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
-      FROM_HERE, base::Bind(&StartupTimeBomb::DeleteStartupWatchdog, thread_id,
-                            base::Unretained(startup_watchdog)),
+      FROM_HERE,
+      base::BindOnce(&StartupTimeBomb::DeleteStartupWatchdog, thread_id,
+                     base::Unretained(startup_watchdog)),
       base::TimeDelta::FromSeconds(10));
 }
 
diff --git a/chrome/browser/metrics/thread_watcher_unittest.cc b/chrome/browser/metrics/thread_watcher_unittest.cc
index 7499884..d4dd444 100644
--- a/chrome/browser/metrics/thread_watcher_unittest.cc
+++ b/chrome/browser/metrics/thread_watcher_unittest.cc
@@ -517,8 +517,8 @@
   // to finish.
   BrowserThread::PostTask(
       BrowserThread::IO, FROM_HERE,
-      base::Bind(&CustomThreadWatcher::VeryLongMethod,
-                 base::Unretained(io_watcher_), kUnresponsiveTime * 10));
+      base::BindOnce(&CustomThreadWatcher::VeryLongMethod,
+                     base::Unretained(io_watcher_), kUnresponsiveTime * 10));
 
   // Activate thread watching.
   WatchDogThread::PostTask(
@@ -600,8 +600,8 @@
   // to finish.
   BrowserThread::PostTask(
       BrowserThread::IO, FROM_HERE,
-      base::Bind(&CustomThreadWatcher::VeryLongMethod,
-                 base::Unretained(io_watcher_), kUnresponsiveTime * 10));
+      base::BindOnce(&CustomThreadWatcher::VeryLongMethod,
+                     base::Unretained(io_watcher_), kUnresponsiveTime * 10));
 
   // Activate watching of DB thread.
   WatchDogThread::PostTask(
diff --git a/chrome/browser/permissions/permission_dialog_delegate.cc b/chrome/browser/permissions/permission_dialog_delegate.cc
index f7c006e..1c1e81c 100644
--- a/chrome/browser/permissions/permission_dialog_delegate.cc
+++ b/chrome/browser/permissions/permission_dialog_delegate.cc
@@ -16,7 +16,6 @@
 #include "chrome/browser/geolocation/geolocation_infobar_delegate_android.h"
 #include "chrome/browser/media/midi_permission_infobar_delegate_android.h"
 #include "chrome/browser/media/protected_media_identifier_infobar_delegate_android.h"
-#include "chrome/browser/media/webrtc/media_stream_devices_controller.h"
 #include "chrome/browser/media/webrtc/media_stream_infobar_delegate_android.h"
 #include "chrome/browser/notifications/notification_permission_infobar_delegate.h"
 #include "chrome/browser/profiles/profile.h"
@@ -64,13 +63,13 @@
 void PermissionDialogDelegate::CreateMediaStreamDialog(
     content::WebContents* web_contents,
     bool user_gesture,
-    std::unique_ptr<MediaStreamDevicesController> controller) {
+    std::unique_ptr<MediaStreamDevicesController::Request> request) {
   DCHECK(web_contents);
 
   // If we don't have a tab, just act as though the prompt was dismissed.
   TabAndroid* tab = TabAndroid::FromWebContents(web_contents);
   if (!tab) {
-    controller->Cancelled();
+    request->Cancelled();
     return;
   }
 
@@ -78,8 +77,7 @@
   std::unique_ptr<PermissionInfoBarDelegate> infobar_delegate;
   infobar_delegate.reset(new MediaStreamInfoBarDelegateAndroid(
       Profile::FromBrowserContext(web_contents->GetBrowserContext()),
-      user_gesture,
-      std::move(controller)));
+      user_gesture, std::move(request)));
 
   // Dispatch the dialog to Java, which manages the lifetime of this object.
   new PermissionDialogDelegate(tab, std::move(infobar_delegate));
diff --git a/chrome/browser/permissions/permission_dialog_delegate.h b/chrome/browser/permissions/permission_dialog_delegate.h
index f57ccd1..76c4b6cd0 100644
--- a/chrome/browser/permissions/permission_dialog_delegate.h
+++ b/chrome/browser/permissions/permission_dialog_delegate.h
@@ -10,6 +10,7 @@
 #include "base/android/scoped_java_ref.h"
 #include "base/callback.h"
 #include "base/macros.h"
+#include "chrome/browser/media/webrtc/media_stream_devices_controller.h"
 #include "chrome/browser/permissions/permission_util.h"
 #include "components/content_settings/core/common/content_settings_types.h"
 
@@ -19,8 +20,6 @@
 namespace content {
 class WebContents;
 }
-
-class MediaStreamDevicesController;
 class GURL;
 class PermissionInfoBarDelegate;
 class Profile;
@@ -53,7 +52,7 @@
   static void CreateMediaStreamDialog(
       content::WebContents* web_contents,
       bool user_gesture,
-      std::unique_ptr<MediaStreamDevicesController> controller);
+      std::unique_ptr<MediaStreamDevicesController::Request> request);
 
   // Returns true if we should show the user a modal permission prompt rather
   // than an infobar.
diff --git a/chrome/browser/permissions/permission_request_manager_browsertest.cc b/chrome/browser/permissions/permission_request_manager_browsertest.cc
index 1c41f60..c5700d3 100644
--- a/chrome/browser/permissions/permission_request_manager_browsertest.cc
+++ b/chrome/browser/permissions/permission_request_manager_browsertest.cc
@@ -31,7 +31,7 @@
 
 namespace test {
 class MediaStreamDevicesControllerTestApi
-    : public internal::PermissionPromptDelegate {
+    : public MediaStreamDevicesController::PermissionPromptDelegate {
  public:
   static void AddRequestToManager(
       PermissionRequestManager* manager,
@@ -48,8 +48,8 @@
   void ShowPrompt(
       bool user_gesture,
       content::WebContents* web_contents,
-      std::unique_ptr<MediaStreamDevicesController> controller) override {
-    manager_->AddRequest(controller.release());
+      std::unique_ptr<MediaStreamDevicesController::Request> request) override {
+    manager_->AddRequest(request.release());
   }
 
   explicit MediaStreamDevicesControllerTestApi(
diff --git a/chrome/browser/policy/policy_browsertest.cc b/chrome/browser/policy/policy_browsertest.cc
index 2ffa65ef..77cd827 100644
--- a/chrome/browser/policy/policy_browsertest.cc
+++ b/chrome/browser/policy/policy_browsertest.cc
@@ -3128,16 +3128,16 @@
   // we should remove PermissionPromptDelegate and just use
   // MockPermissionPromptFactory instead. The APIs are the same.
   class TestPermissionPromptDelegate
-      : public ::internal::PermissionPromptDelegate {
+      : public MediaStreamDevicesController::PermissionPromptDelegate {
    public:
-    void ShowPrompt(
-        bool user_gesture,
-        content::WebContents* web_contents,
-        std::unique_ptr<MediaStreamDevicesController> controller) override {
+    void ShowPrompt(bool user_gesture,
+                    content::WebContents* web_contents,
+                    std::unique_ptr<MediaStreamDevicesController::Request>
+                        request) override {
       if (response_type_ == PermissionRequestManager::ACCEPT_ALL)
-        controller->PermissionGranted();
+        request->PermissionGranted();
       else if (response_type_ == PermissionRequestManager::DENY_ALL)
-        controller->PermissionDenied();
+        request->PermissionDenied();
     }
 
     void set_response_type(
diff --git a/chrome/browser/printing/cloud_print/cloud_print_proxy_service.cc b/chrome/browser/printing/cloud_print/cloud_print_proxy_service.cc
index 04990dd2..ac18905 100644
--- a/chrome/browser/printing/cloud_print/cloud_print_proxy_service.cc
+++ b/chrome/browser/printing/cloud_print/cloud_print_proxy_service.cc
@@ -149,7 +149,7 @@
     UMA_HISTOGRAM_COUNTS_10000("CloudPrint.AvailablePrintersList",
                                printers.size());
     base::ThreadTaskRunnerHandle::Get()->PostTask(
-        FROM_HERE, base::Bind(callback, printers));
+        FROM_HERE, base::BindOnce(callback, printers));
   } else {
     InvokeServiceTask(
         base::Bind(&CloudPrintProxyService::GetCloudPrintProxyPrinters,
diff --git a/chrome/browser/printing/cloud_print/privet_http_impl.cc b/chrome/browser/printing/cloud_print/privet_http_impl.cc
index b83a374..a576f6cd 100644
--- a/chrome/browser/printing/cloud_print/privet_http_impl.cc
+++ b/chrome/browser/printing/cloud_print/privet_http_impl.cc
@@ -157,8 +157,8 @@
 
     base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
         FROM_HERE,
-        base::Bind(&PrivetRegisterOperationImpl::Cancelation::Cleanup,
-                   base::Owned(cancelation)),
+        base::BindOnce(&PrivetRegisterOperationImpl::Cancelation::Cleanup,
+                       base::Owned(cancelation)),
         base::TimeDelta::FromSeconds(kPrivetCancelationTimeoutSeconds));
 
     ongoing_ = false;
@@ -585,8 +585,9 @@
       timeout = std::max(timeout, kPrivetMinimumTimeout);
 
       base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
-          FROM_HERE, base::Bind(&PrivetLocalPrintOperationImpl::DoCreatejob,
-                                weak_factory_.GetWeakPtr()),
+          FROM_HERE,
+          base::BindOnce(&PrivetLocalPrintOperationImpl::DoCreatejob,
+                         weak_factory_.GetWeakPtr()),
           base::TimeDelta::FromSeconds(timeout));
     } else if (use_pdf_ && error == kPrivetErrorInvalidDocumentType) {
       use_pdf_ = false;
diff --git a/chrome/browser/printing/cloud_print/privet_notifications.cc b/chrome/browser/printing/cloud_print/privet_notifications.cc
index acd14d0..060461e 100644
--- a/chrome/browser/printing/cloud_print/privet_notifications.cc
+++ b/chrome/browser/printing/cloud_print/privet_notifications.cc
@@ -202,7 +202,7 @@
     content::BrowserContext* profile)
     : profile_(profile) {
   base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
-      FROM_HERE, base::Bind(&PrivetNotificationService::Start, AsWeakPtr()),
+      FROM_HERE, base::BindOnce(&PrivetNotificationService::Start, AsWeakPtr()),
       base::TimeDelta::FromSeconds(kStartDelaySeconds +
                                    base::RandInt(0, kStartDelaySeconds / 4)));
 }
diff --git a/chrome/browser/printing/cloud_print/privet_traffic_detector.cc b/chrome/browser/printing/cloud_print/privet_traffic_detector.cc
index 633a74a..c5682e53 100644
--- a/chrome/browser/printing/cloud_print/privet_traffic_detector.cc
+++ b/chrome/browser/printing/cloud_print/privet_traffic_detector.cc
@@ -54,7 +54,7 @@
                             8,
                             net::IP_ADDRESS_ATTRIBUTE_NONE));
   content::BrowserThread::PostTask(content::BrowserThread::IO, FROM_HERE,
-                                   base::Bind(callback, ip4_networks));
+                                   base::BindOnce(callback, ip4_networks));
 }
 
 }  // namespace
@@ -74,10 +74,9 @@
 
 void PrivetTrafficDetector::Start() {
   content::BrowserThread::PostTask(
-      content::BrowserThread::IO,
-      FROM_HERE,
-      base::Bind(&PrivetTrafficDetector::StartOnIOThread,
-                 weak_ptr_factory_.GetWeakPtr()));
+      content::BrowserThread::IO, FROM_HERE,
+      base::BindOnce(&PrivetTrafficDetector::StartOnIOThread,
+                     weak_ptr_factory_.GetWeakPtr()));
 }
 
 PrivetTrafficDetector::~PrivetTrafficDetector() {
@@ -104,11 +103,10 @@
   socket_.reset();
   weak_ptr_factory_.InvalidateWeakPtrs();
   content::BrowserThread::PostDelayedTask(
-      content::BrowserThread::FILE,
-      FROM_HERE,
-      base::Bind(&GetNetworkListOnFileThread,
-                 base::Bind(&PrivetTrafficDetector::Restart,
-                            weak_ptr_factory_.GetWeakPtr())),
+      content::BrowserThread::FILE, FROM_HERE,
+      base::BindOnce(&GetNetworkListOnFileThread,
+                     base::Bind(&PrivetTrafficDetector::Restart,
+                                weak_ptr_factory_.GetWeakPtr())),
       base::TimeDelta::FromSeconds(3));
 }
 
diff --git a/chrome/browser/printing/cloud_print/privet_url_fetcher.cc b/chrome/browser/printing/cloud_print/privet_url_fetcher.cc
index 6b8509e..91cd622 100644
--- a/chrome/browser/printing/cloud_print/privet_url_fetcher.cc
+++ b/chrome/browser/printing/cloud_print/privet_url_fetcher.cc
@@ -365,7 +365,8 @@
     timeout_seconds_randomized = 0;
 
   base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
-      FROM_HERE, base::Bind(&PrivetURLFetcher::Try, weak_factory_.GetWeakPtr()),
+      FROM_HERE,
+      base::BindOnce(&PrivetURLFetcher::Try, weak_factory_.GetWeakPtr()),
       base::TimeDelta::FromSeconds(timeout_seconds_randomized));
 }
 
diff --git a/chrome/browser/printing/cloud_print/test/cloud_print_proxy_process_browsertest.cc b/chrome/browser/printing/cloud_print/test/cloud_print_proxy_process_browsertest.cc
index 03805ba..8fea42e6 100644
--- a/chrome/browser/printing/cloud_print/test/cloud_print_proxy_process_browsertest.cc
+++ b/chrome/browser/printing/cloud_print/test/cloud_print_proxy_process_browsertest.cc
@@ -486,8 +486,8 @@
       FROM_HERE,
       base::TaskTraits().MayBlock().WithPriority(
           base::TaskPriority::BACKGROUND),
-      base::Bind(&ConnectAsync, base::Passed(&pipe.handle1),
-                 GetServiceProcessChannel()));
+      base::BindOnce(&ConnectAsync, base::Passed(&pipe.handle1),
+                     GetServiceProcessChannel()));
   ServiceProcessControl::GetInstance()->SetChannel(
       IPC::ChannelProxy::Create(IPC::ChannelMojo::CreateClientFactory(
                                     std::move(pipe.handle0), IOTaskRunner()),
diff --git a/chrome/browser/printing/print_dialog_cloud.cc b/chrome/browser/printing/print_dialog_cloud.cc
index b22d999a..caefbf2 100644
--- a/chrome/browser/printing/print_dialog_cloud.cc
+++ b/chrome/browser/printing/print_dialog_cloud.cc
@@ -45,8 +45,8 @@
 
     if (cloud_devices::IsCloudPrintURL(navigation_handle->GetURL())) {
       base::ThreadTaskRunnerHandle::Get()->PostTask(
-          FROM_HERE, base::Bind(&SignInObserver::OnSignIn,
-                                weak_ptr_factory_.GetWeakPtr()));
+          FROM_HERE, base::BindOnce(&SignInObserver::OnSignIn,
+                                    weak_ptr_factory_.GetWeakPtr()));
     }
   }
 
diff --git a/chrome/browser/printing/print_error_dialog.cc b/chrome/browser/printing/print_error_dialog.cc
index 0510dbf..a85945c 100644
--- a/chrome/browser/printing/print_error_dialog.cc
+++ b/chrome/browser/printing/print_error_dialog.cc
@@ -28,7 +28,7 @@
 void ShowPrintErrorDialog() {
   // Nested loop may destroy caller.
   content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
-                                   base::Bind(&ShowPrintErrorDialogTask));
+                                   base::BindOnce(&ShowPrintErrorDialogTask));
 }
 
 }  // namespace chrome
diff --git a/chrome/browser/printing/print_job.cc b/chrome/browser/printing/print_job.cc
index f001c876..7fde330 100644
--- a/chrome/browser/printing/print_job.cc
+++ b/chrome/browser/printing/print_job.cc
@@ -198,7 +198,7 @@
   scoped_refptr<PrintJob> handle(this);
 
   base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
-      FROM_HERE, base::Bind(&PrintJob::Quit, quit_factory_.GetWeakPtr()),
+      FROM_HERE, base::BindOnce(&PrintJob::Quit, quit_factory_.GetWeakPtr()),
       timeout);
 
   base::MessageLoop::ScopedNestableTaskAllower allow(
@@ -383,7 +383,7 @@
     case JobEventDetails::DOC_DONE: {
       // This will call Stop() and broadcast a JOB_DONE message.
       base::ThreadTaskRunnerHandle::Get()->PostTask(
-          FROM_HERE, base::Bind(&PrintJob::OnDocumentDone, this));
+          FROM_HERE, base::BindOnce(&PrintJob::OnDocumentDone, this));
       break;
     }
     case JobEventDetails::PAGE_DONE:
@@ -449,9 +449,8 @@
   // thread because it may block.
   base::WorkerPool::PostTaskAndReply(
       FROM_HERE,
-      base::Bind(&PrintJobWorker::Stop, base::Unretained(worker_.get())),
-      base::Bind(&PrintJob::HoldUntilStopIsCalled, this),
-      false);
+      base::BindOnce(&PrintJobWorker::Stop, base::Unretained(worker_.get())),
+      base::BindOnce(&PrintJob::HoldUntilStopIsCalled, this), false);
 
   is_job_pending_ = false;
   registrar_.RemoveAll();
diff --git a/chrome/browser/printing/print_job_worker.cc b/chrome/browser/printing/print_job_worker.cc
index 714523e..636038b9 100644
--- a/chrome/browser/printing/print_job_worker.cc
+++ b/chrome/browser/printing/print_job_worker.cc
@@ -159,18 +159,16 @@
   if (ask_user_for_settings) {
     BrowserThread::PostTask(
         BrowserThread::UI, FROM_HERE,
-        base::Bind(&HoldRefCallback, make_scoped_refptr(owner_),
-                   base::Bind(&PrintJobWorker::GetSettingsWithUI,
-                              base::Unretained(this),
-                              document_page_count,
-                              has_selection,
-                              is_scripted)));
+        base::BindOnce(&HoldRefCallback, make_scoped_refptr(owner_),
+                       base::Bind(&PrintJobWorker::GetSettingsWithUI,
+                                  base::Unretained(this), document_page_count,
+                                  has_selection, is_scripted)));
   } else {
     BrowserThread::PostTask(
         BrowserThread::UI, FROM_HERE,
-        base::Bind(&HoldRefCallback, make_scoped_refptr(owner_),
-                   base::Bind(&PrintJobWorker::UseDefaultSettings,
-                              base::Unretained(this))));
+        base::BindOnce(&HoldRefCallback, make_scoped_refptr(owner_),
+                       base::Bind(&PrintJobWorker::UseDefaultSettings,
+                                  base::Unretained(this))));
   }
 }
 
@@ -179,13 +177,11 @@
   DCHECK(task_runner_->RunsTasksOnCurrentThread());
 
   BrowserThread::PostTask(
-      BrowserThread::UI,
-      FROM_HERE,
-      base::Bind(&HoldRefCallback,
-                 make_scoped_refptr(owner_),
-                 base::Bind(&PrintJobWorker::UpdatePrintSettings,
-                            base::Unretained(this),
-                            base::Passed(&new_settings))));
+      BrowserThread::UI, FROM_HERE,
+      base::BindOnce(
+          &HoldRefCallback, make_scoped_refptr(owner_),
+          base::Bind(&PrintJobWorker::UpdatePrintSettings,
+                     base::Unretained(this), base::Passed(&new_settings))));
 }
 
 void PrintJobWorker::UpdatePrintSettings(
@@ -320,7 +316,8 @@
       // We need to wait for the page to be available.
       base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
           FROM_HERE,
-          base::Bind(&PrintJobWorker::OnNewPage, weak_factory_.GetWeakPtr()),
+          base::BindOnce(&PrintJobWorker::OnNewPage,
+                         weak_factory_.GetWeakPtr()),
           base::TimeDelta::FromMilliseconds(500));
       break;
     }
diff --git a/chrome/browser/printing/print_preview_message_handler.cc b/chrome/browser/printing/print_preview_message_handler.cc
index 2ec09eb..ba5ef0c5 100644
--- a/chrome/browser/printing/print_preview_message_handler.cc
+++ b/chrome/browser/printing/print_preview_message_handler.cc
@@ -44,9 +44,9 @@
   scoped_refptr<PrinterQuery> printer_query =
       queue->PopPrinterQuery(document_cookie);
   if (printer_query.get()) {
-    BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
-                            base::Bind(&PrinterQuery::StopWorker,
-                                       printer_query));
+    BrowserThread::PostTask(
+        BrowserThread::IO, FROM_HERE,
+        base::BindOnce(&PrinterQuery::StopWorker, printer_query));
   }
 }
 
diff --git a/chrome/browser/printing/print_view_manager_base.cc b/chrome/browser/printing/print_view_manager_base.cc
index df47130..f45350ce 100644
--- a/chrome/browser/printing/print_view_manager_base.cc
+++ b/chrome/browser/printing/print_view_manager_base.cc
@@ -238,9 +238,9 @@
 
 void PrintViewManagerBase::OnShowInvalidPrinterSettingsError() {
   base::ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE, base::Bind(&ShowWarningMessageBox,
-                            l10n_util::GetStringUTF16(
-                                IDS_PRINT_INVALID_PRINTER_SETTINGS)));
+      FROM_HERE, base::BindOnce(&ShowWarningMessageBox,
+                                l10n_util::GetStringUTF16(
+                                    IDS_PRINT_INVALID_PRINTER_SETTINGS)));
 }
 
 void PrintViewManagerBase::DidStartLoading() {
@@ -575,7 +575,7 @@
     return;
   BrowserThread::PostTask(
       BrowserThread::IO, FROM_HERE,
-      base::Bind(&PrinterQuery::StopWorker, printer_query));
+      base::BindOnce(&PrinterQuery::StopWorker, printer_query));
 }
 
 void PrintViewManagerBase::SendPrintingEnabled(bool enabled,
diff --git a/chrome/browser/printing/printer_manager_dialog_linux.cc b/chrome/browser/printing/printer_manager_dialog_linux.cc
index 29ee3f35..156dda9 100644
--- a/chrome/browser/printing/printer_manager_dialog_linux.cc
+++ b/chrome/browser/printing/printer_manager_dialog_linux.cc
@@ -79,7 +79,7 @@
 
 void PrinterManagerDialog::ShowPrinterManagerDialog() {
   BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
-                          base::Bind(&DetectAndOpenPrinterConfigDialog));
+                          base::BindOnce(&DetectAndOpenPrinterConfigDialog));
 }
 
 }  // namespace printing
diff --git a/chrome/browser/printing/pwg_raster_converter.cc b/chrome/browser/printing/pwg_raster_converter.cc
index 29a0fb5..95934fb 100644
--- a/chrome/browser/printing/pwg_raster_converter.cc
+++ b/chrome/browser/printing/pwg_raster_converter.cc
@@ -170,9 +170,10 @@
 
   BrowserThread::PostTaskAndReply(
       BrowserThread::FILE, FROM_HERE,
-      base::Bind(&FileHandlers::Init, base::Unretained(files_.get()),
-                 base::RetainedRef(data)),
-      base::Bind(&PwgUtilityProcessHostClient::OnFilesReadyOnUIThread, this));
+      base::BindOnce(&FileHandlers::Init, base::Unretained(files_.get()),
+                     base::RetainedRef(data)),
+      base::BindOnce(&PwgUtilityProcessHostClient::OnFilesReadyOnUIThread,
+                     this));
 }
 
 void PwgUtilityProcessHostClient::OnProcessCrashed(int exit_code) {
@@ -211,7 +212,8 @@
   }
   BrowserThread::PostTask(
       BrowserThread::IO, FROM_HERE,
-      base::Bind(&PwgUtilityProcessHostClient::StartProcessOnIOThread, this));
+      base::BindOnce(&PwgUtilityProcessHostClient::StartProcessOnIOThread,
+                     this));
 }
 
 void PwgUtilityProcessHostClient::StartProcessOnIOThread() {
@@ -230,8 +232,8 @@
 void PwgUtilityProcessHostClient::RunCallback(bool success) {
   BrowserThread::PostTask(
       BrowserThread::UI, FROM_HERE,
-      base::Bind(&PwgUtilityProcessHostClient::RunCallbackOnUIThread, this,
-                 success));
+      base::BindOnce(&PwgUtilityProcessHostClient::RunCallbackOnUIThread, this,
+                     success));
 }
 
 void PwgUtilityProcessHostClient::RunCallbackOnUIThread(bool success) {
@@ -239,8 +241,9 @@
   if (callback_.is_null())
     return;
 
-  BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
-                          base::Bind(callback_, success, files_->GetPwgPath()));
+  BrowserThread::PostTask(
+      BrowserThread::UI, FROM_HERE,
+      base::BindOnce(callback_, success, files_->GetPwgPath()));
   callback_.Reset();
 }
 
diff --git a/chrome/browser/resources/OWNERS b/chrome/browser/resources/OWNERS
index 407277e..f005d38 100644
--- a/chrome/browser/resources/OWNERS
+++ b/chrome/browser/resources/OWNERS
@@ -1,4 +1,4 @@
-file://ui/webui/OWNERS
+file://ui/webui/PLATFORM_OWNERS
 
 per-file component_extension_resources.grd=dgozman@chromium.org
 per-file options_resources.grd=file://chrome/browser/resources/options/OWNERS
diff --git a/chrome/browser/resources/uber/OWNERS b/chrome/browser/resources/uber/OWNERS
index 8624e36..e8ea397 100644
--- a/chrome/browser/resources/uber/OWNERS
+++ b/chrome/browser/resources/uber/OWNERS
@@ -1,4 +1,3 @@
 dbeam@chromium.org
-estade@chromium.org
 
 # COMPONENT: UI>Browser>WebUI
diff --git a/chrome/browser/ui/apps/chrome_app_delegate.cc b/chrome/browser/ui/apps/chrome_app_delegate.cc
index 97b75f8..9ede868 100644
--- a/chrome/browser/ui/apps/chrome_app_delegate.cc
+++ b/chrome/browser/ui/apps/chrome_app_delegate.cc
@@ -296,7 +296,7 @@
 
 int ChromeAppDelegate::PreferredIconSize() {
 #if defined(USE_ASH)
-  return ash::GetShelfConstant(ash::SHELF_SIZE);
+  return ash::kShelfSize;
 #else
   return extension_misc::EXTENSION_ICON_SMALL;
 #endif
diff --git a/chrome/browser/ui/ash/launcher/launcher_favicon_loader.cc b/chrome/browser/ui/ash/launcher/launcher_favicon_loader.cc
index 974fa1b..4562bf04 100644
--- a/chrome/browser/ui/ash/launcher/launcher_favicon_loader.cc
+++ b/chrome/browser/ui/ash/launcher/launcher_favicon_loader.cc
@@ -21,7 +21,7 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 // FaviconRawBitmapHandler fetchs all bitmaps with the 'icon' (or 'shortcut
-// icon') link tag, storing the one that best matches ash::SHELF_SIZE. These
+// icon') link tag, storing the one that best matches ash::kShelfSize. These
 // icon bitmaps are not resized and are not cached beyond the lifetime of the
 // class. Bitmaps larger than kMaxBitmapSize are ignored.
 
@@ -144,7 +144,7 @@
   if (new_bitmap.height() > kMaxBitmapSize ||
       new_bitmap.width() > kMaxBitmapSize)
     return;
-  if (new_bitmap.height() < ash::GetShelfConstant(ash::SHELF_SIZE))
+  if (new_bitmap.height() < ash::kShelfSize)
     return;
   if (!bitmap_.isNull()) {
     // We want the smallest icon that is large enough.
diff --git a/chrome/browser/ui/ash/launcher/launcher_favicon_loader_browsertest.cc b/chrome/browser/ui/ash/launcher/launcher_favicon_loader_browsertest.cc
index a4f999a7..822900bf 100644
--- a/chrome/browser/ui/ash/launcher/launcher_favicon_loader_browsertest.cc
+++ b/chrome/browser/ui/ash/launcher/launcher_favicon_loader_browsertest.cc
@@ -158,8 +158,8 @@
 
   EXPECT_FALSE(favicon_loader_->GetFavicon().empty());
   // When multiple favicons are present, the correctly sized icon should be
-  // chosen. The icons are sized assuming ash::SHELF_SIZE < 128.
-  EXPECT_GT(128, ash::GetShelfConstant(ash::SHELF_SIZE));
+  // chosen. The icons are sized assuming ash::kShelfSize < 128.
+  EXPECT_GT(128, ash::kShelfSize);
   EXPECT_EQ(48, favicon_loader_->GetFavicon().height());
 }
 
diff --git a/chrome/browser/ui/aura/accessibility/automation_manager_aura.cc b/chrome/browser/ui/aura/accessibility/automation_manager_aura.cc
index 462a087..1818c11 100644
--- a/chrome/browser/ui/aura/accessibility/automation_manager_aura.cc
+++ b/chrome/browser/ui/aura/accessibility/automation_manager_aura.cc
@@ -15,9 +15,11 @@
 #include "chrome/common/extensions/chrome_extension_messages.h"
 #include "content/public/browser/ax_event_notification_details.h"
 #include "content/public/browser/browser_context.h"
+#include "content/public/browser/render_frame_host.h"
 #include "ui/accessibility/ax_action_data.h"
 #include "ui/accessibility/ax_enums.h"
 #include "ui/accessibility/ax_tree_id_registry.h"
+#include "ui/accessibility/platform/aura_window_properties.h"
 #include "ui/aura/env.h"
 #include "ui/aura/window.h"
 #include "ui/views/accessibility/ax_aura_obj_wrapper.h"
@@ -25,6 +27,7 @@
 #include "ui/views/widget/widget.h"
 
 #if defined(OS_CHROMEOS)
+#include "ash/shell.h"           // nogncheck
 #include "ash/wm/window_util.h"  // nogncheck
 #endif
 
@@ -81,10 +84,16 @@
   SendEvent(context, obj, ui::AX_EVENT_ALERT);
 }
 
-void AutomationManagerAura::PerformAction(
-    const ui::AXActionData& data) {
+void AutomationManagerAura::PerformAction(const ui::AXActionData& data) {
   CHECK(enabled_);
 
+  // Unlike all of the other actions, a hit test requires determining the
+  // node to perform the action on first.
+  if (data.action == ui::AX_ACTION_HIT_TEST) {
+    PerformHitTest(data);
+    return;
+  }
+
   current_tree_->HandleAccessibleAction(data);
 }
 
@@ -161,3 +170,60 @@
               pending_events_copy[i].second);
   }
 }
+
+void AutomationManagerAura::PerformHitTest(
+    const ui::AXActionData& original_action) {
+#if defined(OS_CHROMEOS)
+  ui::AXActionData action = original_action;
+  aura::Window* root_window = ash::Shell::Get()->GetPrimaryRootWindow();
+  if (!root_window)
+    return;
+
+  // Determine which aura Window is associated with the target point.
+  aura::Window* window =
+      root_window->GetEventHandlerForPoint(action.target_point);
+  if (!window)
+    return;
+
+  // Convert point to local coordinates of the hit window.
+  aura::Window::ConvertPointToTarget(root_window, window, &action.target_point);
+
+  // If the window has a child AX tree ID, forward the action to the
+  // associated AXHostDelegate or RenderFrameHost.
+  ui::AXTreeIDRegistry::AXTreeID child_ax_tree_id =
+      window->GetProperty(ui::kChildAXTreeID);
+  if (child_ax_tree_id != ui::AXTreeIDRegistry::kNoAXTreeID) {
+    ui::AXTreeIDRegistry* registry = ui::AXTreeIDRegistry::GetInstance();
+    ui::AXHostDelegate* delegate = registry->GetHostDelegate(child_ax_tree_id);
+    if (delegate) {
+      delegate->PerformAction(action);
+      return;
+    }
+
+    content::RenderFrameHost* rfh =
+        content::RenderFrameHost::FromAXTreeID(child_ax_tree_id);
+    if (rfh)
+      rfh->AccessibilityPerformAction(action);
+    return;
+  }
+
+  // If the window doesn't have a child tree ID, try to fire the event
+  // on a View.
+  views::Widget* widget = views::Widget::GetWidgetForNativeView(window);
+  if (widget) {
+    views::View* root_view = widget->GetRootView();
+    views::View* hit_view =
+        root_view->GetEventHandlerForPoint(action.target_point);
+    if (hit_view) {
+      hit_view->NotifyAccessibilityEvent(action.hit_test_event_to_fire, true);
+      return;
+    }
+  }
+
+  // Otherwise, fire the event directly on the Window.
+  views::AXAuraObjWrapper* window_wrapper =
+      views::AXAuraObjCache::GetInstance()->GetOrCreate(window);
+  if (window_wrapper)
+    SendEvent(nullptr, window_wrapper, action.hit_test_event_to_fire);
+#endif
+}
diff --git a/chrome/browser/ui/aura/accessibility/automation_manager_aura.h b/chrome/browser/ui/aura/accessibility/automation_manager_aura.h
index 1d1baef..ecb3c60 100644
--- a/chrome/browser/ui/aura/accessibility/automation_manager_aura.h
+++ b/chrome/browser/ui/aura/accessibility/automation_manager_aura.h
@@ -78,6 +78,8 @@
                  views::AXAuraObjWrapper* aura_obj,
                  ui::AXEvent event_type);
 
+  void PerformHitTest(const ui::AXActionData& data);
+
   // Whether automation support for views is enabled.
   bool enabled_;
 
diff --git a/chrome/browser/ui/webui/OWNERS b/chrome/browser/ui/webui/OWNERS
index e0f6aed..38e940d 100644
--- a/chrome/browser/ui/webui/OWNERS
+++ b/chrome/browser/ui/webui/OWNERS
@@ -1,4 +1,4 @@
-file://ui/webui/OWNERS
+file://ui/webui/PLATFORM_OWNERS
 
 per-file devtools_ui*=dgozman@chromium.org
 per-file devtools_ui*=pfeldman@chromium.org
diff --git a/chrome/browser/ui/webui/devtools_ui.cc b/chrome/browser/ui/webui/devtools_ui.cc
index 72cf5e9..be09da9 100644
--- a/chrome/browser/ui/webui/devtools_ui.cc
+++ b/chrome/browser/ui/webui/devtools_ui.cc
@@ -248,7 +248,7 @@
           chrome_policy {
             DeveloperToolsDisabled {
               policy_options {mode: MANDATORY}
-              DeveloperToolsDisabled: True
+              DeveloperToolsDisabled: true
             }
           }
         })");
@@ -292,7 +292,7 @@
           chrome_policy {
             DeveloperToolsDisabled {
               policy_options {mode: MANDATORY}
-              DeveloperToolsDisabled: True
+              DeveloperToolsDisabled: true
             }
           }
         })");
diff --git a/chrome/renderer/autofill/form_classifier_browsertest.cc b/chrome/renderer/autofill/form_classifier_browsertest.cc
index 2cccb25..38f45631 100644
--- a/chrome/renderer/autofill/form_classifier_browsertest.cc
+++ b/chrome/renderer/autofill/form_classifier_browsertest.cc
@@ -11,7 +11,7 @@
 #include "third_party/WebKit/public/web/WebFormElement.h"
 #include "third_party/WebKit/public/web/WebLocalFrame.h"
 #include "third_party/WebKit/public/web/WebRuntimeFeatures.h"
-#include "ui/native_theme/native_theme_features.h"
+#include "ui/native_theme/native_theme_switches.h"
 
 namespace autofill {
 
diff --git a/chrome/renderer/resources/OWNERS b/chrome/renderer/resources/OWNERS
index 8999077..9acd4f8 100644
--- a/chrome/renderer/resources/OWNERS
+++ b/chrome/renderer/resources/OWNERS
@@ -1,3 +1,3 @@
-file://ui/webui/OWNERS
+file://ui/webui/PLATFORM_OWNERS
 
 per-file offline.js=edwardjung@chromium.org
diff --git a/chrome/renderer/safe_browsing/phishing_dom_feature_extractor_browsertest.cc b/chrome/renderer/safe_browsing/phishing_dom_feature_extractor_browsertest.cc
index 48b39e95..e8e3a1a 100644
--- a/chrome/renderer/safe_browsing/phishing_dom_feature_extractor_browsertest.cc
+++ b/chrome/renderer/safe_browsing/phishing_dom_feature_extractor_browsertest.cc
@@ -29,7 +29,7 @@
 #include "third_party/WebKit/public/web/WebLocalFrame.h"
 #include "third_party/WebKit/public/web/WebRuntimeFeatures.h"
 #include "third_party/WebKit/public/web/WebScriptSource.h"
-#include "ui/native_theme/native_theme_features.h"
+#include "ui/native_theme/native_theme_switches.h"
 
 using ::testing::DoAll;
 using ::testing::Invoke;
diff --git a/chrome/renderer/safe_browsing/threat_dom_details_browsertest.cc b/chrome/renderer/safe_browsing/threat_dom_details_browsertest.cc
index e5dc97b..27792e9 100644
--- a/chrome/renderer/safe_browsing/threat_dom_details_browsertest.cc
+++ b/chrome/renderer/safe_browsing/threat_dom_details_browsertest.cc
@@ -16,7 +16,7 @@
 #include "net/base/escape.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "third_party/WebKit/public/web/WebRuntimeFeatures.h"
-#include "ui/native_theme/native_theme_features.h"
+#include "ui/native_theme/native_theme_switches.h"
 
 namespace {
 
diff --git a/chrome/test/data/extensions/api_test/automation/tests/desktop/hit_test.html b/chrome/test/data/extensions/api_test/automation/tests/desktop/hit_test.html
new file mode 100644
index 0000000..cc4539f
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/automation/tests/desktop/hit_test.html
@@ -0,0 +1,7 @@
+<!--
+ * 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.
+-->
+<script src="common.js"></script>
+<script src="hit_test.js"></script>
diff --git a/chrome/test/data/extensions/api_test/automation/tests/desktop/hit_test.js b/chrome/test/data/extensions/api_test/automation/tests/desktop/hit_test.js
new file mode 100644
index 0000000..924579c
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/automation/tests/desktop/hit_test.js
@@ -0,0 +1,35 @@
+// 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.
+
+var allTests = [
+  function testHitTestInDesktop() {
+    var url = 'data:text/html,<!doctype html>' +
+        encodeURI('<button>Click Me</button>');
+    var didHitTest = false;
+    chrome.automation.getDesktop(function(desktop) {
+      chrome.tabs.create({url: url});
+
+      desktop.addEventListener('loadComplete', function(event) {
+        if (didHitTest)
+          return;
+        if (event.target.url.indexOf('data:') >= 0) {
+          var button = desktop.find({ attributes: { name: 'Click Me' } });
+          if (button) {
+            didHitTest = true;
+            button.addEventListener(EventType.ALERT, function() {
+              chrome.test.succeed();
+            }, true);
+            var cx = Math.floor(
+                button.location.left + button.location.width / 2);
+            var cy = Math.floor(
+                button.location.top + button.location.height / 2);
+            desktop.hitTest(cx, cy, EventType.ALERT);
+          }
+        }
+      }, false);
+    });
+  },
+];
+
+chrome.test.runTests(allTests);
diff --git a/components/about_ui/OWNERS b/components/about_ui/OWNERS
index e87625e1..918baa1 100644
--- a/components/about_ui/OWNERS
+++ b/components/about_ui/OWNERS
@@ -1,3 +1,3 @@
-file://ui/webui/OWNERS
+file://ui/webui/PLATFORM_OWNERS
 
 # COMPONENT: UI>Browser>WebUI
diff --git a/components/flags_ui/OWNERS b/components/flags_ui/OWNERS
index 506ff92..6fc413e 100644
--- a/components/flags_ui/OWNERS
+++ b/components/flags_ui/OWNERS
@@ -1,4 +1,4 @@
-file://ui/webui/OWNERS
+file://ui/webui/PLATFORM_OWNERS
 
 asvitkine@chromium.org
 
diff --git a/components/sync/engine_impl/apply_control_data_updates_unittest.cc b/components/sync/engine_impl/apply_control_data_updates_unittest.cc
index b72507f..653f540 100644
--- a/components/sync/engine_impl/apply_control_data_updates_unittest.cc
+++ b/components/sync/engine_impl/apply_control_data_updates_unittest.cc
@@ -124,8 +124,8 @@
     // With default encrypted_types, this should be true.
     EXPECT_TRUE(VerifyUnsyncedChangesAreEncrypted(&trans, encrypted_types));
 
-    Syncer::UnsyncedMetaHandles handles;
-    GetUnsyncedEntries(&trans, &handles);
+    syncable::Directory::Metahandles handles;
+    syncable::GetUnsyncedEntries(&trans, &handles);
     EXPECT_TRUE(handles.empty());
   }
 
@@ -166,8 +166,8 @@
     syncable::ReadTransaction trans(FROM_HERE, directory());
     EXPECT_FALSE(VerifyUnsyncedChangesAreEncrypted(&trans, encrypted_types));
 
-    Syncer::UnsyncedMetaHandles handles;
-    GetUnsyncedEntries(&trans, &handles);
+    syncable::Directory::Metahandles handles;
+    syncable::GetUnsyncedEntries(&trans, &handles);
     EXPECT_EQ(2 * batch_s + 1, handles.size());
   }
 
@@ -184,8 +184,8 @@
               directory()->GetNigoriHandler()->GetEncryptedTypes(&trans));
     EXPECT_TRUE(VerifyUnsyncedChangesAreEncrypted(&trans, encrypted_types));
 
-    Syncer::UnsyncedMetaHandles handles;
-    GetUnsyncedEntries(&trans, &handles);
+    syncable::Directory::Metahandles handles;
+    syncable::GetUnsyncedEntries(&trans, &handles);
     EXPECT_EQ(2 * batch_s + 1, handles.size());
   }
 
@@ -210,8 +210,8 @@
               directory()->GetNigoriHandler()->GetEncryptedTypes(&trans));
     EXPECT_TRUE(VerifyUnsyncedChangesAreEncrypted(&trans, encrypted_types));
 
-    Syncer::UnsyncedMetaHandles handles;
-    GetUnsyncedEntries(&trans, &handles);
+    syncable::Directory::Metahandles handles;
+    syncable::GetUnsyncedEntries(&trans, &handles);
     EXPECT_EQ(2 * batch_s + 1, handles.size());
   }
 }
@@ -235,8 +235,8 @@
     // With default encrypted_types, this should be true.
     EXPECT_TRUE(VerifyUnsyncedChangesAreEncrypted(&trans, encrypted_types));
 
-    Syncer::UnsyncedMetaHandles handles;
-    GetUnsyncedEntries(&trans, &handles);
+    syncable::Directory::Metahandles handles;
+    syncable::GetUnsyncedEntries(&trans, &handles);
     EXPECT_TRUE(handles.empty());
   }
 
@@ -278,8 +278,8 @@
     // Ensure we have unsynced nodes that aren't properly encrypted.
     syncable::ReadTransaction trans(FROM_HERE, directory());
     EXPECT_FALSE(VerifyUnsyncedChangesAreEncrypted(&trans, encrypted_types));
-    Syncer::UnsyncedMetaHandles handles;
-    GetUnsyncedEntries(&trans, &handles);
+    syncable::Directory::Metahandles handles;
+    syncable::GetUnsyncedEntries(&trans, &handles);
     EXPECT_EQ(2 * batch_s + 1, handles.size());
   }
 
@@ -298,8 +298,8 @@
     EXPECT_FALSE(cryptographer->is_ready());
     EXPECT_TRUE(cryptographer->has_pending_keys());
 
-    Syncer::UnsyncedMetaHandles handles;
-    GetUnsyncedEntries(&trans, &handles);
+    syncable::Directory::Metahandles handles;
+    syncable::GetUnsyncedEntries(&trans, &handles);
     EXPECT_EQ(2 * batch_s + 1, handles.size());
   }
 }
diff --git a/components/sync/engine_impl/syncer.cc b/components/sync/engine_impl/syncer.cc
index 6d8d71e..069f61d 100644
--- a/components/sync/engine_impl/syncer.cc
+++ b/components/sync/engine_impl/syncer.cc
@@ -21,27 +21,30 @@
 #include "components/sync/engine_impl/get_updates_processor.h"
 #include "components/sync/engine_impl/net/server_connection_manager.h"
 #include "components/sync/syncable/directory.h"
-#include "components/sync/syncable/mutable_entry.h"
 
 namespace syncer {
 
-// TODO(akalin): We may want to propagate this switch up
-// eventually.
+namespace {
+
+// TODO(akalin): We may want to propagate this switch up eventually.
 #if defined(OS_ANDROID) || defined(OS_IOS)
 static const bool kCreateMobileBookmarksFolder = true;
 #else
 static const bool kCreateMobileBookmarksFolder = false;
 #endif
 
+void HandleCycleBegin(SyncCycle* cycle) {
+  cycle->mutable_status_controller()->UpdateStartTime();
+  cycle->SendEventNotification(SyncCycleEvent::SYNC_CYCLE_BEGIN);
+}
+
+}  // namespace
+
 Syncer::Syncer(CancelationSignal* cancelation_signal)
     : cancelation_signal_(cancelation_signal), is_syncing_(false) {}
 
 Syncer::~Syncer() {}
 
-bool Syncer::ExitRequested() {
-  return cancelation_signal_->IsSignalled();
-}
-
 bool Syncer::IsSyncing() const {
   return is_syncing_;
 }
@@ -54,11 +57,8 @@
   if (nudge_tracker->IsGetUpdatesRequired() ||
       cycle->context()->ShouldFetchUpdatesBeforeCommit()) {
     VLOG(1) << "Downloading types " << ModelTypeSetToString(request_types);
-    NormalGetUpdatesDelegate normal_delegate(*nudge_tracker);
-    GetUpdatesProcessor get_updates_processor(
-        cycle->context()->model_type_registry()->update_handler_map(),
-        normal_delegate);
-    if (!DownloadAndApplyUpdates(&request_types, cycle, &get_updates_processor,
+    if (!DownloadAndApplyUpdates(&request_types, cycle,
+                                 NormalGetUpdatesDelegate(*nudge_tracker),
                                  kCreateMobileBookmarksFolder)) {
       return HandleCycleEnd(cycle, nudge_tracker->GetLegacySource());
     }
@@ -89,12 +89,8 @@
   request_types.RetainAll(cycle->context()->GetEnabledTypes());
   VLOG(1) << "Configuring types " << ModelTypeSetToString(request_types);
   HandleCycleBegin(cycle);
-  ConfigureGetUpdatesDelegate configure_delegate(source);
-
-  GetUpdatesProcessor get_updates_processor(
-      cycle->context()->model_type_registry()->update_handler_map(),
-      configure_delegate);
-  DownloadAndApplyUpdates(&request_types, cycle, &get_updates_processor,
+  DownloadAndApplyUpdates(&request_types, cycle,
+                          ConfigureGetUpdatesDelegate(source),
                           kCreateMobileBookmarksFolder);
   return HandleCycleEnd(cycle, source);
 }
@@ -103,22 +99,26 @@
   base::AutoReset<bool> is_syncing(&is_syncing_, true);
   VLOG(1) << "Polling types " << ModelTypeSetToString(request_types);
   HandleCycleBegin(cycle);
-  PollGetUpdatesDelegate poll_delegate;
-  GetUpdatesProcessor get_updates_processor(
-      cycle->context()->model_type_registry()->update_handler_map(),
-      poll_delegate);
-  DownloadAndApplyUpdates(&request_types, cycle, &get_updates_processor,
+  DownloadAndApplyUpdates(&request_types, cycle, PollGetUpdatesDelegate(),
                           kCreateMobileBookmarksFolder);
   return HandleCycleEnd(cycle, sync_pb::GetUpdatesCallerInfo::PERIODIC);
 }
 
+bool Syncer::PostClearServerData(SyncCycle* cycle) {
+  DCHECK(cycle);
+  ClearServerData clear_server_data(cycle->context()->account_name());
+  return clear_server_data.SendRequest(cycle) == SYNCER_OK;
+}
+
 bool Syncer::DownloadAndApplyUpdates(ModelTypeSet* request_types,
                                      SyncCycle* cycle,
-                                     GetUpdatesProcessor* get_updates_processor,
+                                     const GetUpdatesDelegate& delegate,
                                      bool create_mobile_bookmarks_folder) {
+  GetUpdatesProcessor get_updates_processor(
+      cycle->context()->model_type_registry()->update_handler_map(), delegate);
   SyncerError download_result = UNSET;
   do {
-    download_result = get_updates_processor->DownloadUpdates(
+    download_result = get_updates_processor.DownloadUpdates(
         request_types, cycle, create_mobile_bookmarks_folder);
   } while (download_result == SERVER_MORE_TO_DOWNLOAD);
 
@@ -132,11 +132,11 @@
     // Control type updates always get applied first.
     ApplyControlDataUpdates(cycle->context()->directory());
 
-    // Apply upates to the other types.  May or may not involve cross-thread
+    // Apply updates to the other types. May or may not involve cross-thread
     // traffic, depending on the underlying update handlers and the GU type's
     // delegate.
-    get_updates_processor->ApplyUpdates(*request_types,
-                                        cycle->mutable_status_controller());
+    get_updates_processor.ApplyUpdates(*request_types,
+                                       cycle->mutable_status_controller());
 
     cycle->context()->set_hierarchy_conflict_detected(
         cycle->status_controller().num_hierarchy_conflicts() > 0);
@@ -178,9 +178,8 @@
   return SYNCER_OK;
 }
 
-void Syncer::HandleCycleBegin(SyncCycle* cycle) {
-  cycle->mutable_status_controller()->UpdateStartTime();
-  cycle->SendEventNotification(SyncCycleEvent::SYNC_CYCLE_BEGIN);
+bool Syncer::ExitRequested() {
+  return cancelation_signal_->IsSignalled();
 }
 
 bool Syncer::HandleCycleEnd(
@@ -199,11 +198,4 @@
   return success;
 }
 
-bool Syncer::PostClearServerData(SyncCycle* cycle) {
-  DCHECK(cycle);
-  ClearServerData clear_server_data(cycle->context()->account_name());
-  const SyncerError post_result = clear_server_data.SendRequest(cycle);
-  return post_result == SYNCER_OK;
-}
-
 }  // namespace syncer
diff --git a/components/sync/engine_impl/syncer.h b/components/sync/engine_impl/syncer.h
index 77e5d53b..2e86d8e0 100644
--- a/components/sync/engine_impl/syncer.h
+++ b/components/sync/engine_impl/syncer.h
@@ -19,7 +19,7 @@
 
 class CancelationSignal;
 class CommitProcessor;
-class GetUpdatesProcessor;
+class GetUpdatesDelegate;
 class NudgeTracker;
 class SyncCycle;
 
@@ -34,14 +34,9 @@
 // lock contention, or on tasks posted to other threads.
 class Syncer {
  public:
-  using UnsyncedMetaHandles = std::vector<int64_t>;
-
   explicit Syncer(CancelationSignal* cancelation_signal);
   virtual ~Syncer();
 
-  // Whether an early exist was requested due to a cancelation signal.
-  bool ExitRequested();
-
   // Whether the syncer is in the middle of a sync cycle.
   bool IsSyncing() const;
 
@@ -81,31 +76,9 @@
   virtual bool PostClearServerData(SyncCycle* cycle);
 
  private:
-  friend class SyncerTest;
-  FRIEND_TEST_ALL_PREFIXES(SyncerTest, NameClashWithResolver);
-  FRIEND_TEST_ALL_PREFIXES(SyncerTest, IllegalAndLegalUpdates);
-  FRIEND_TEST_ALL_PREFIXES(SyncerTest, TestCommitListOrderingAndNewParent);
-  FRIEND_TEST_ALL_PREFIXES(SyncerTest,
-                           TestCommitListOrderingAndNewParentAndChild);
-  FRIEND_TEST_ALL_PREFIXES(SyncerTest, TestCommitListOrderingCounterexample);
-  FRIEND_TEST_ALL_PREFIXES(SyncerTest, TestCommitListOrderingWithNesting);
-  FRIEND_TEST_ALL_PREFIXES(SyncerTest, TestCommitListOrderingWithNewItems);
-  FRIEND_TEST_ALL_PREFIXES(SyncerTest, TestGetUnsyncedAndSimpleCommit);
-  FRIEND_TEST_ALL_PREFIXES(SyncerTest, TestPurgeWhileUnsynced);
-  FRIEND_TEST_ALL_PREFIXES(SyncerTest, TestPurgeWhileUnapplied);
-  FRIEND_TEST_ALL_PREFIXES(SyncerTest, UnappliedUpdateDuringCommit);
-  FRIEND_TEST_ALL_PREFIXES(SyncerTest, DeletingEntryInFolder);
-  FRIEND_TEST_ALL_PREFIXES(SyncerTest,
-                           LongChangelistCreatesFakeOrphanedEntries);
-  FRIEND_TEST_ALL_PREFIXES(SyncerTest, QuicklyMergeDualCreatedHierarchy);
-  FRIEND_TEST_ALL_PREFIXES(SyncerTest, LongChangelistWithApplicationConflict);
-  FRIEND_TEST_ALL_PREFIXES(SyncerTest, DeletingEntryWithLocalEdits);
-  FRIEND_TEST_ALL_PREFIXES(EntryCreatedInNewFolderTest,
-                           EntryCreatedInNewFolderMidSync);
-
   bool DownloadAndApplyUpdates(ModelTypeSet* request_types,
                                SyncCycle* cycle,
-                               GetUpdatesProcessor* get_updates_processor,
+                               const GetUpdatesDelegate& delegate,
                                bool create_mobile_bookmarks_folder);
 
   // This function will commit batches of unsynced items to the server until the
@@ -117,7 +90,9 @@
                                   SyncCycle* cycle,
                                   CommitProcessor* commit_processor);
 
-  void HandleCycleBegin(SyncCycle* cycle);
+  // Whether an early exist was requested due to a cancelation signal.
+  bool ExitRequested();
+
   bool HandleCycleEnd(SyncCycle* cycle,
                       sync_pb::GetUpdatesCallerInfo::GetUpdatesSource source);
 
diff --git a/components/sync/engine_impl/syncer_unittest.cc b/components/sync/engine_impl/syncer_unittest.cc
index d404a389..01713f58 100644
--- a/components/sync/engine_impl/syncer_unittest.cc
+++ b/components/sync/engine_impl/syncer_unittest.cc
@@ -569,10 +569,10 @@
 
 TEST_F(SyncerTest, TestCallGatherUnsyncedEntries) {
   {
-    Syncer::UnsyncedMetaHandles handles;
+    syncable::Directory::Metahandles handles;
     {
       syncable::ReadTransaction trans(FROM_HERE, directory());
-      GetUnsyncedEntries(&trans, &handles);
+      syncable::GetUnsyncedEntries(&trans, &handles);
     }
     ASSERT_EQ(0u, handles.size());
   }
diff --git a/components/sync/syncable/syncable_util.cc b/components/sync/syncable/syncable_util.cc
index 7ed0bc9..4349703 100644
--- a/components/sync/syncable/syncable_util.cc
+++ b/components/sync/syncable/syncable_util.cc
@@ -6,7 +6,6 @@
 
 #include "base/location.h"
 #include "base/logging.h"
-#include "components/sync/syncable/directory.h"
 #include "components/sync/syncable/entry.h"
 #include "components/sync/syncable/mutable_entry.h"
 #include "components/sync/syncable/syncable_id.h"
@@ -16,7 +15,8 @@
 namespace syncable {
 
 // Returns the number of unsynced entries.
-int GetUnsyncedEntries(BaseTransaction* trans, std::vector<int64_t>* handles) {
+int GetUnsyncedEntries(BaseTransaction* trans,
+                       Directory::Metahandles* handles) {
   trans->directory()->GetUnsyncedMetaHandles(trans, handles);
   DVLOG_IF(1, !handles->empty()) << "Have " << handles->size()
                                  << " unsynced items.";
diff --git a/components/sync/syncable/syncable_util.h b/components/sync/syncable/syncable_util.h
index bcf20d89d..6eecef8 100644
--- a/components/sync/syncable/syncable_util.h
+++ b/components/sync/syncable/syncable_util.h
@@ -9,6 +9,8 @@
 
 #include <vector>
 
+#include "components/sync/syncable/directory.h"
+
 namespace tracked_objects {
 class Location;
 }
@@ -32,7 +34,7 @@
                 const char* msg,
                 BaseTransaction* trans);
 
-int GetUnsyncedEntries(BaseTransaction* trans, std::vector<int64_t>* handles);
+int GetUnsyncedEntries(BaseTransaction* trans, Directory::Metahandles* handles);
 
 }  // namespace syncable
 }  // namespace syncer
diff --git a/components/version_ui/OWNERS b/components/version_ui/OWNERS
index e87625e1..918baa1 100644
--- a/components/version_ui/OWNERS
+++ b/components/version_ui/OWNERS
@@ -1,3 +1,3 @@
-file://ui/webui/OWNERS
+file://ui/webui/PLATFORM_OWNERS
 
 # COMPONENT: UI>Browser>WebUI
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index 92bfe066..c208d95 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -1294,6 +1294,8 @@
     "service_worker/service_worker_context_wrapper.h",
     "service_worker/service_worker_controllee_request_handler.cc",
     "service_worker/service_worker_controllee_request_handler.h",
+    "service_worker/service_worker_data_pipe_reader.cc",
+    "service_worker/service_worker_data_pipe_reader.h",
     "service_worker/service_worker_database.cc",
     "service_worker/service_worker_database.h",
     "service_worker/service_worker_database_task_manager.cc",
@@ -1345,8 +1347,6 @@
     "service_worker/service_worker_script_cache_map.h",
     "service_worker/service_worker_storage.cc",
     "service_worker/service_worker_storage.h",
-    "service_worker/service_worker_stream_reader.cc",
-    "service_worker/service_worker_stream_reader.h",
     "service_worker/service_worker_unregister_job.cc",
     "service_worker/service_worker_unregister_job.h",
     "service_worker/service_worker_url_request_job.cc",
diff --git a/content/browser/DEPS b/content/browser/DEPS
index 391e781..939d94d0 100644
--- a/content/browser/DEPS
+++ b/content/browser/DEPS
@@ -106,6 +106,7 @@
   "+third_party/WebKit/public/platform/modules/serviceworker/WebServiceWorkerResponseType.h",
   "+third_party/WebKit/public/platform/modules/serviceworker/WebServiceWorkerState.h",
   "+third_party/WebKit/public/platform/modules/serviceworker/service_worker_event_status.mojom.h",
+  "+third_party/WebKit/public/platform/modules/serviceworker/service_worker_stream_handle.mojom.h",
   "+third_party/WebKit/public/platform/modules/vr/WebVR.h",
   "+third_party/WebKit/public/platform/modules/websockets/websocket.mojom.h",
   "+third_party/WebKit/public/public_features.h",
diff --git a/content/browser/android/content_startup_flags.cc b/content/browser/android/content_startup_flags.cc
index 4d4d4e7..b7bc16df 100644
--- a/content/browser/android/content_startup_flags.cc
+++ b/content/browser/android/content_startup_flags.cc
@@ -14,6 +14,7 @@
 #include "content/public/common/content_switches.h"
 #include "gpu/command_buffer/service/gpu_switches.h"
 #include "ui/base/ui_base_switches.h"
+#include "ui/native_theme/native_theme_switches.h"
 
 namespace content {
 
@@ -37,6 +38,7 @@
 
   parsed_command_line->AppendSwitch(switches::kEnablePinch);
   parsed_command_line->AppendSwitch(switches::kEnableViewport);
+  parsed_command_line->AppendSwitch(switches::kEnableOverlayScrollbar);
   parsed_command_line->AppendSwitch(switches::kValidateInputEventStream);
 
   if (base::android::BuildInfo::GetInstance()->sdk_int() >=
diff --git a/content/browser/cache_storage/cache_storage_cache.cc b/content/browser/cache_storage/cache_storage_cache.cc
index 4bb7598..9a4a02e 100644
--- a/content/browser/cache_storage/cache_storage_cache.cc
+++ b/content/browser/cache_storage/cache_storage_cache.cc
@@ -240,8 +240,7 @@
       std::move(url_list), metadata.response().status_code(),
       metadata.response().status_text(),
       ProtoResponseTypeToWebResponseType(metadata.response().response_type()),
-      std::move(headers), "", 0, GURL(),
-      blink::kWebServiceWorkerResponseErrorUnknown,
+      std::move(headers), "", 0, blink::kWebServiceWorkerResponseErrorUnknown,
       base::Time::FromInternalValue(metadata.response().response_time()),
       true /* is_in_cache_storage */, cache_name,
       base::MakeUnique<ServiceWorkerHeaderList>(
@@ -1017,9 +1016,6 @@
           operation.request.headers, operation.request.referrer,
           operation.request.is_reload));
 
-  // We don't support streaming for cache.
-  DCHECK(operation.response.stream_url.is_empty());
-
   std::unique_ptr<ServiceWorkerResponse> response =
       base::MakeUnique<ServiceWorkerResponse>(operation.response);
   std::unique_ptr<storage::BlobDataHandle> blob_data_handle;
diff --git a/content/browser/cache_storage/cache_storage_cache_unittest.cc b/content/browser/cache_storage/cache_storage_cache_unittest.cc
index 0c5bb819..3f0227b 100644
--- a/content/browser/cache_storage/cache_storage_cache_unittest.cc
+++ b/content/browser/cache_storage/cache_storage_cache_unittest.cc
@@ -445,9 +445,8 @@
     return ServiceWorkerResponse(
         base::MakeUnique<std::vector<GURL>>(1, GURL(url)), 200, "OK",
         blink::kWebServiceWorkerResponseTypeDefault, std::move(headers),
-        blob_uuid, blob_size, GURL() /* stream_url */,
-        blink::kWebServiceWorkerResponseErrorUnknown, base::Time::Now(),
-        false /* is_in_cache_storage */,
+        blob_uuid, blob_size, blink::kWebServiceWorkerResponseErrorUnknown,
+        base::Time::Now(), false /* is_in_cache_storage */,
         std::string() /* cache_storage_cache_name */,
         std::move(cors_exposed_header_names));
   }
@@ -1520,7 +1519,7 @@
   ServiceWorkerResponse response(
       base::MakeUnique<std::vector<GURL>>(), 200, "OK",
       blink::kWebServiceWorkerResponseTypeDefault,
-      base::MakeUnique<ServiceWorkerHeaderMap>(), "", 0, GURL(),
+      base::MakeUnique<ServiceWorkerHeaderMap>(), "", 0,
       blink::kWebServiceWorkerResponseErrorUnknown, base::Time(),
       false /* is_in_cache_storage */,
       std::string() /* cache_storage_cache_name */,
diff --git a/content/browser/cache_storage/cache_storage_manager_unittest.cc b/content/browser/cache_storage/cache_storage_manager_unittest.cc
index 2bcc478..825fada5 100644
--- a/content/browser/cache_storage/cache_storage_manager_unittest.cc
+++ b/content/browser/cache_storage/cache_storage_manager_unittest.cc
@@ -380,7 +380,7 @@
         std::move(url_list), status_code, "OK",
         blink::kWebServiceWorkerResponseTypeDefault,
         base::MakeUnique<ServiceWorkerHeaderMap>(response_headers),
-        blob_handle->uuid(), request.url.spec().size(), GURL(),
+        blob_handle->uuid(), request.url.spec().size(),
         blink::kWebServiceWorkerResponseErrorUnknown, base::Time(),
         false /* is_in_cache_storage */,
         std::string() /* cache_storage_cache_name */,
diff --git a/content/browser/renderer_host/input/input_router_impl.h b/content/browser/renderer_host/input/input_router_impl.h
index 239d0d2..cbe2e2a 100644
--- a/content/browser/renderer_host/input/input_router_impl.h
+++ b/content/browser/renderer_host/input/input_router_impl.h
@@ -7,6 +7,7 @@
 
 #include <stdint.h>
 
+#include <map>
 #include <memory>
 #include <queue>
 
diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc
index 8bb4f2f..e779da3 100644
--- a/content/browser/renderer_host/render_process_host_impl.cc
+++ b/content/browser/renderer_host/render_process_host_impl.cc
@@ -198,7 +198,7 @@
 #include "ui/gfx/switches.h"
 #include "ui/gl/gl_switches.h"
 #include "ui/gl/gpu_switching_manager.h"
-#include "ui/native_theme/native_theme_features.h"
+#include "ui/native_theme/native_theme_switches.h"
 
 #if defined(OS_ANDROID)
 #include "content/public/browser/android/java_interfaces.h"
@@ -1714,6 +1714,7 @@
     switches::kDisableLogging,
     switches::kDisableMediaSuspend,
     switches::kDisableNotifications,
+    switches::kDisableOverlayScrollbar,
     switches::kDisablePepper3DImageChromium,
     switches::kDisablePermissionsAPI,
     switches::kDisablePresentationAPI,
@@ -1752,6 +1753,7 @@
     switches::kEnableLogging,
     switches::kEnableNetworkInformation,
     switches::kEnableNetworkService,
+    switches::kEnableOverlayScrollbar,
     switches::kEnableNewVp9CodecString,
     switches::kEnablePinch,
     switches::kEnablePluginPlaceholderTesting,
diff --git a/content/browser/renderer_host/render_view_host_impl.cc b/content/browser/renderer_host/render_view_host_impl.cc
index ba2cd466..53a28b21 100644
--- a/content/browser/renderer_host/render_view_host_impl.cc
+++ b/content/browser/renderer_host/render_view_host_impl.cc
@@ -91,7 +91,7 @@
 #include "ui/gfx/color_space.h"
 #include "ui/gfx/image/image_skia.h"
 #include "ui/gfx/native_widget_types.h"
-#include "ui/native_theme/native_theme_features.h"
+#include "ui/native_theme/native_theme_switches.h"
 #include "url/url_constants.h"
 
 #if defined(OS_WIN)
diff --git a/content/browser/service_worker/embedded_worker_test_helper.cc b/content/browser/service_worker/embedded_worker_test_helper.cc
index 08c87749..388d075 100644
--- a/content/browser/service_worker/embedded_worker_test_helper.cc
+++ b/content/browser/service_worker/embedded_worker_test_helper.cc
@@ -200,14 +200,17 @@
     helper_->OnBackgroundFetchedEventStub(tag, fetches, callback);
   }
 
-  void DispatchFetchEvent(int fetch_event_id,
-                          const ServiceWorkerFetchRequest& request,
-                          mojom::FetchEventPreloadHandlePtr preload_handle,
-                          const DispatchFetchEventCallback& callback) override {
+  void DispatchFetchEvent(
+      int fetch_event_id,
+      const ServiceWorkerFetchRequest& request,
+      mojom::FetchEventPreloadHandlePtr preload_handle,
+      mojom::ServiceWorkerFetchResponseCallbackPtr response_callback,
+      const DispatchFetchEventCallback& callback) override {
     if (!helper_)
       return;
     helper_->OnFetchEventStub(thread_id_, fetch_event_id, request,
-                              std::move(preload_handle), callback);
+                              std::move(preload_handle),
+                              std::move(response_callback), callback);
   }
 
   void DispatchNotificationClickEvent(
@@ -481,25 +484,24 @@
 }
 
 void EmbeddedWorkerTestHelper::OnFetchEvent(
-    int embedded_worker_id,
-    int fetch_event_id,
-    const ServiceWorkerFetchRequest& request,
-    mojom::FetchEventPreloadHandlePtr preload_handle,
-    const FetchCallback& callback) {
-  SimulateSend(new ServiceWorkerHostMsg_FetchEventResponse(
-      embedded_worker_id, fetch_event_id,
-      SERVICE_WORKER_FETCH_EVENT_RESULT_RESPONSE,
+    int /* embedded_worker_id */,
+    int /* fetch_event_id */,
+    const ServiceWorkerFetchRequest& /* request */,
+    mojom::FetchEventPreloadHandlePtr /* preload_handle */,
+    mojom::ServiceWorkerFetchResponseCallbackPtr response_callback,
+    const FetchCallback& finish_callback) {
+  response_callback->OnResponse(
       ServiceWorkerResponse(
           base::MakeUnique<std::vector<GURL>>(), 200, "OK",
           blink::kWebServiceWorkerResponseTypeDefault,
-          base::MakeUnique<ServiceWorkerHeaderMap>(), std::string(), 0, GURL(),
+          base::MakeUnique<ServiceWorkerHeaderMap>(), std::string(), 0,
           blink::kWebServiceWorkerResponseErrorUnknown, base::Time(),
           false /* is_in_cache_storage */,
           std::string() /* cache_storage_cache_name */,
           base::MakeUnique<
               ServiceWorkerHeaderList>() /* cors_exposed_header_names */),
-      base::Time::Now()));
-  callback.Run(SERVICE_WORKER_OK, base::Time::Now());
+      base::Time::Now());
+  finish_callback.Run(SERVICE_WORKER_OK, base::Time::Now());
 }
 
 void EmbeddedWorkerTestHelper::OnPushEvent(
@@ -745,12 +747,14 @@
     int fetch_event_id,
     const ServiceWorkerFetchRequest& request,
     mojom::FetchEventPreloadHandlePtr preload_handle,
-    const FetchCallback& callback) {
+    mojom::ServiceWorkerFetchResponseCallbackPtr response_callback,
+    const FetchCallback& finish_callback) {
   base::ThreadTaskRunnerHandle::Get()->PostTask(
       FROM_HERE,
       base::Bind(&EmbeddedWorkerTestHelper::OnFetchEvent, AsWeakPtr(),
                  thread_id_embedded_worker_id_map_[thread_id], fetch_event_id,
-                 request, base::Passed(&preload_handle), callback));
+                 request, base::Passed(&preload_handle),
+                 base::Passed(&response_callback), finish_callback));
 }
 
 void EmbeddedWorkerTestHelper::OnNotificationClickEventStub(
diff --git a/content/browser/service_worker/embedded_worker_test_helper.h b/content/browser/service_worker/embedded_worker_test_helper.h
index 79069d2..62c16408 100644
--- a/content/browser/service_worker/embedded_worker_test_helper.h
+++ b/content/browser/service_worker/embedded_worker_test_helper.h
@@ -213,11 +213,13 @@
       const mojom::ServiceWorkerEventDispatcher::
           DispatchExtendableMessageEventCallback& callback);
   virtual void OnInstallEvent(int embedded_worker_id, int request_id);
-  virtual void OnFetchEvent(int embedded_worker_id,
-                            int fetch_event_id,
-                            const ServiceWorkerFetchRequest& request,
-                            mojom::FetchEventPreloadHandlePtr preload_handle,
-                            const FetchCallback& callback);
+  virtual void OnFetchEvent(
+      int embedded_worker_id,
+      int fetch_event_id,
+      const ServiceWorkerFetchRequest& request,
+      mojom::FetchEventPreloadHandlePtr preload_handle,
+      mojom::ServiceWorkerFetchResponseCallbackPtr response_callback,
+      const FetchCallback& finish_callback);
   virtual void OnNotificationClickEvent(
       const std::string& notification_id,
       const PlatformNotificationData& notification_data,
@@ -294,11 +296,13 @@
       const mojom::ServiceWorkerEventDispatcher::
           DispatchExtendableMessageEventCallback& callback);
   void OnInstallEventStub(int request_id);
-  void OnFetchEventStub(int thread_id,
-                        int fetch_event_id,
-                        const ServiceWorkerFetchRequest& request,
-                        mojom::FetchEventPreloadHandlePtr preload_handle,
-                        const FetchCallback& callback);
+  void OnFetchEventStub(
+      int thread_id,
+      int fetch_event_id,
+      const ServiceWorkerFetchRequest& request,
+      mojom::FetchEventPreloadHandlePtr preload_handle,
+      mojom::ServiceWorkerFetchResponseCallbackPtr response_callback,
+      const FetchCallback& finish_callback);
   void OnNotificationClickEventStub(
       const std::string& notification_id,
       const PlatformNotificationData& notification_data,
diff --git a/content/browser/service_worker/service_worker_browsertest.cc b/content/browser/service_worker/service_worker_browsertest.cc
index 39142e4..2bf03fab 100644
--- a/content/browser/service_worker/service_worker_browsertest.cc
+++ b/content/browser/service_worker/service_worker_browsertest.cc
@@ -887,6 +887,7 @@
       ServiceWorkerStatusCode actual_status,
       ServiceWorkerFetchEventResult actual_result,
       const ServiceWorkerResponse& actual_response,
+      blink::mojom::ServiceWorkerStreamHandlePtr /* stream */,
       const scoped_refptr<ServiceWorkerVersion>& worker) {
     ASSERT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::IO));
     ASSERT_TRUE(fetch_dispatcher_);
diff --git a/content/browser/service_worker/service_worker_data_pipe_reader.cc b/content/browser/service_worker/service_worker_data_pipe_reader.cc
new file mode 100644
index 0000000..d315430
--- /dev/null
+++ b/content/browser/service_worker/service_worker_data_pipe_reader.cc
@@ -0,0 +1,198 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/service_worker/service_worker_data_pipe_reader.h"
+
+#include "base/trace_event/trace_event.h"
+#include "content/browser/service_worker/service_worker_url_request_job.h"
+#include "content/browser/service_worker/service_worker_version.h"
+#include "net/base/io_buffer.h"
+
+namespace content {
+
+ServiceWorkerDataPipeReader::ServiceWorkerDataPipeReader(
+    ServiceWorkerURLRequestJob* owner,
+    scoped_refptr<ServiceWorkerVersion> streaming_version,
+    blink::mojom::ServiceWorkerStreamHandlePtr stream_handle)
+    : owner_(owner),
+      streaming_version_(streaming_version),
+      stream_pending_buffer_size_(0),
+      handle_watcher_(FROM_HERE, mojo::SimpleWatcher::ArmingPolicy::AUTOMATIC),
+      stream_(std::move(stream_handle->stream)),
+      binding_(this, std::move(stream_handle->callback_request)),
+      producer_state_(State::kStreaming) {
+  TRACE_EVENT_ASYNC_BEGIN1("ServiceWorker", "ServiceWorkerDataPipeReader", this,
+                           "Url", owner->request()->url().spec());
+  streaming_version_->AddStreamingURLRequestJob(owner_);
+  binding_.set_connection_error_handler(base::Bind(
+      &ServiceWorkerDataPipeReader::OnAborted, base::Unretained(this)));
+}
+
+ServiceWorkerDataPipeReader::~ServiceWorkerDataPipeReader() {
+  DCHECK(streaming_version_);
+  streaming_version_->RemoveStreamingURLRequestJob(owner_);
+  streaming_version_ = nullptr;
+
+  TRACE_EVENT_ASYNC_END0("ServiceWorker", "ServiceWorkerDataPipeReader", this);
+}
+
+void ServiceWorkerDataPipeReader::Start() {
+  TRACE_EVENT_ASYNC_STEP_INTO0("ServiceWorker", "ServiceWorkerDataPipeReader",
+                               this, "Start");
+  handle_watcher_.Watch(
+      stream_.get(),
+      MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
+      base::Bind(&ServiceWorkerDataPipeReader::OnHandleGotSignal,
+                 base::Unretained(this)));
+  owner_->OnResponseStarted();
+}
+
+void ServiceWorkerDataPipeReader::OnHandleGotSignal(MojoResult) {
+  TRACE_EVENT_ASYNC_STEP_INTO0("ServiceWorker", "ServiceWorkerDataPipeReader",
+                               this, "OnHandleGotSignal");
+  // Do nothing if stream_pending_buffer_ is empty, i.e. there's no ReadRawData
+  // operation waiting for IO completion.
+  if (!stream_pending_buffer_)
+    return;
+
+  // If state() is not STREAMING, it means the data pipe was disconnected and
+  // OnCompleted/OnAborted has already been called.
+  if (state() != State::kStreaming) {
+    handle_watcher_.Cancel();
+    AsyncComplete();
+  }
+
+  // |stream_pending_buffer_| is set to the IOBuffer instance provided to
+  // ReadRawData() by URLRequestJob.
+  uint32_t size_to_pass = stream_pending_buffer_size_;
+  MojoResult mojo_result =
+      mojo::ReadDataRaw(stream_.get(), stream_pending_buffer_->data(),
+                        &size_to_pass, MOJO_READ_DATA_FLAG_NONE);
+
+  switch (mojo_result) {
+    case MOJO_RESULT_OK:
+      stream_pending_buffer_ = nullptr;
+      stream_pending_buffer_size_ = 0;
+      owner_->OnReadRawDataComplete(size_to_pass);
+      return;
+    case MOJO_RESULT_FAILED_PRECONDITION:
+      stream_.reset();
+      handle_watcher_.Cancel();
+      // If OnCompleted/OnAborted has already been called, let this request
+      // complete.
+      if (state() != State::kStreaming)
+        AsyncComplete();
+      return;
+    case MOJO_RESULT_SHOULD_WAIT:
+      return;
+    case MOJO_RESULT_INVALID_ARGUMENT:
+    case MOJO_RESULT_OUT_OF_RANGE:
+    case MOJO_RESULT_BUSY:
+      break;
+  }
+  NOTREACHED();
+}
+
+int ServiceWorkerDataPipeReader::ReadRawData(net::IOBuffer* buf, int buf_size) {
+  TRACE_EVENT_ASYNC_STEP_INTO0("ServiceWorker", "ServiceWorkerDataPipeReader",
+                               this, "ReadRawData");
+  DCHECK(!stream_pending_buffer_);
+  // If state() is not STREAMING, it means the data pipe was disconnected and
+  // OnCompleted/OnAborted has already been called.
+  if (state() != State::kStreaming)
+    return SyncComplete();
+
+  uint32_t size_to_pass = buf_size;
+  MojoResult mojo_result = mojo::ReadDataRaw(
+      stream_.get(), buf->data(), &size_to_pass, MOJO_READ_DATA_FLAG_NONE);
+  switch (mojo_result) {
+    case MOJO_RESULT_OK:
+      return size_to_pass;
+    case MOJO_RESULT_FAILED_PRECONDITION:
+      stream_.reset();
+      handle_watcher_.Cancel();
+      // Complete/Abort asynchronously if OnCompleted/OnAborted has not been
+      // called yet.
+      if (state() == State::kStreaming) {
+        stream_pending_buffer_ = buf;
+        stream_pending_buffer_size_ = buf_size;
+        return net::ERR_IO_PENDING;
+      }
+      return SyncComplete();
+    case MOJO_RESULT_SHOULD_WAIT:
+      stream_pending_buffer_ = buf;
+      stream_pending_buffer_size_ = buf_size;
+      return net::ERR_IO_PENDING;
+    case MOJO_RESULT_INVALID_ARGUMENT:
+    case MOJO_RESULT_OUT_OF_RANGE:
+    case MOJO_RESULT_BUSY:
+      break;
+  }
+  NOTREACHED();
+  return net::ERR_FAILED;
+}
+
+void ServiceWorkerDataPipeReader::OnCompleted() {
+  producer_state_ = State::kCompleted;
+  if (stream_pending_buffer_ && state() != State::kStreaming)
+    AsyncComplete();
+}
+
+void ServiceWorkerDataPipeReader::OnAborted() {
+  producer_state_ = State::kAborted;
+  if (stream_pending_buffer_ && state() != State::kStreaming)
+    AsyncComplete();
+}
+
+void ServiceWorkerDataPipeReader::AsyncComplete() {
+  // This works only after ReadRawData returns net::ERR_IO_PENDING.
+  DCHECK(stream_pending_buffer_);
+
+  switch (state()) {
+    case State::kStreaming:
+      NOTREACHED();
+    case State::kCompleted:
+      stream_pending_buffer_ = nullptr;
+      stream_pending_buffer_size_ = 0;
+      handle_watcher_.Cancel();
+      owner_->RecordResult(ServiceWorkerMetrics::REQUEST_JOB_STREAM_RESPONSE);
+      owner_->OnReadRawDataComplete(net::OK);
+      return;
+    case State::kAborted:
+      stream_pending_buffer_ = nullptr;
+      stream_pending_buffer_size_ = 0;
+      handle_watcher_.Cancel();
+      owner_->RecordResult(
+          ServiceWorkerMetrics::REQUEST_JOB_ERROR_STREAM_ABORTED);
+      owner_->OnReadRawDataComplete(net::ERR_CONNECTION_RESET);
+      return;
+  }
+}
+
+int ServiceWorkerDataPipeReader::SyncComplete() {
+  // This works only in ReadRawData.
+  DCHECK(!stream_pending_buffer_);
+
+  switch (state()) {
+    case State::kStreaming:
+      break;
+    case State::kCompleted:
+      owner_->RecordResult(ServiceWorkerMetrics::REQUEST_JOB_STREAM_RESPONSE);
+      return net::OK;
+    case State::kAborted:
+      owner_->RecordResult(
+          ServiceWorkerMetrics::REQUEST_JOB_ERROR_STREAM_ABORTED);
+      return net::ERR_CONNECTION_RESET;
+  }
+  NOTREACHED();
+  return net::ERR_FAILED;
+}
+
+ServiceWorkerDataPipeReader::State ServiceWorkerDataPipeReader::state() {
+  if (!stream_.is_valid())
+    return producer_state_;
+  return State::kStreaming;
+}
+
+}  // namespace content
diff --git a/content/browser/service_worker/service_worker_data_pipe_reader.h b/content/browser/service_worker/service_worker_data_pipe_reader.h
new file mode 100644
index 0000000..2d7ad29
--- /dev/null
+++ b/content/browser/service_worker/service_worker_data_pipe_reader.h
@@ -0,0 +1,77 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_DATA_PIPE_READER_H_
+#define CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_DATA_PIPE_READER_H_
+
+#include "base/memory/ref_counted.h"
+#include "content/common/content_export.h"
+#include "mojo/public/cpp/bindings/binding.h"
+#include "mojo/public/cpp/system/data_pipe.h"
+#include "mojo/public/cpp/system/simple_watcher.h"
+#include "third_party/WebKit/public/platform/modules/serviceworker/service_worker_stream_handle.mojom.h"
+
+namespace net {
+class IOBuffer;
+}  // namespace net
+
+namespace content {
+
+class ServiceWorkerURLRequestJob;
+class ServiceWorkerVersion;
+
+// Reads a stream response for ServiceWorkerURLRequestJob passed through
+// Mojo's data pipe. Owned by ServiceWorkerURLRequestJob.
+class CONTENT_EXPORT ServiceWorkerDataPipeReader
+    : public blink::mojom::ServiceWorkerStreamCallback {
+ public:
+  ServiceWorkerDataPipeReader(
+      ServiceWorkerURLRequestJob* owner,
+      scoped_refptr<ServiceWorkerVersion> streaming_version,
+      blink::mojom::ServiceWorkerStreamHandlePtr stream_handle);
+  ~ServiceWorkerDataPipeReader() override;
+
+  // Starts reading the stream. Calls owner_->OnResponseStarted.
+  void Start();
+
+  // Same as URLRequestJob::ReadRawData. If ERR_IO_PENDING is returned,
+  // owner_->OnReadRawDataComplete will be called when the read completes.
+  int ReadRawData(net::IOBuffer* buf, int buf_size);
+
+  // Implements mojom::ServiceWorkerStreamCallback.
+  void OnCompleted() override;
+  void OnAborted() override;
+
+ private:
+  enum class State { kStreaming, kCompleted, kAborted };
+
+  // Callback method for |handle_watcher_|.
+  void OnHandleGotSignal(MojoResult);
+
+  // Finalizes the job. These must be called when state() is not
+  // State::STREAMING.
+  int SyncComplete();
+  void AsyncComplete();
+
+  State state();
+
+  ServiceWorkerURLRequestJob* owner_;
+  scoped_refptr<ServiceWorkerVersion> streaming_version_;
+  scoped_refptr<net::IOBuffer> stream_pending_buffer_;
+  int stream_pending_buffer_size_;
+  mojo::SimpleWatcher handle_watcher_;
+  mojo::ScopedDataPipeConsumerHandle stream_;
+  mojo::Binding<blink::mojom::ServiceWorkerStreamCallback> binding_;
+  // State notified via ServiceWorkerStreamCallback. |producer_state_| is
+  // STREAMING until OnCompleted or OnAborted is called. Note that |stream_|
+  // might be closed even if |producer_state_| is STREAMING. In order to see the
+  // state of ServiceWorkerDataPipeReader, use state() instead.
+  State producer_state_;
+
+  DISALLOW_COPY_AND_ASSIGN(ServiceWorkerDataPipeReader);
+};
+
+}  // namespace content
+
+#endif  // CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_DATA_PIPE_READER_H_
diff --git a/content/browser/service_worker/service_worker_data_pipe_reader_unittest.cc b/content/browser/service_worker/service_worker_data_pipe_reader_unittest.cc
new file mode 100644
index 0000000..cdfe5a9
--- /dev/null
+++ b/content/browser/service_worker/service_worker_data_pipe_reader_unittest.cc
@@ -0,0 +1,414 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/service_worker/service_worker_data_pipe_reader.h"
+
+#include "base/run_loop.h"
+#include "content/browser/service_worker/embedded_worker_test_helper.h"
+#include "content/browser/service_worker/service_worker_context_core.h"
+#include "content/browser/service_worker/service_worker_context_wrapper.h"
+#include "content/browser/service_worker/service_worker_registration.h"
+#include "content/browser/service_worker/service_worker_url_request_job.h"
+#include "content/browser/service_worker/service_worker_version.h"
+#include "content/public/test/test_browser_thread_bundle.h"
+#include "net/base/io_buffer.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace content {
+
+namespace {
+
+const char kTestData[] = "Here is sample text for the blob.";
+
+}  // namespace
+
+class MockServiceWorkerURLRequestJob : public ServiceWorkerURLRequestJob {
+ public:
+  explicit MockServiceWorkerURLRequestJob(
+      ServiceWorkerURLRequestJob::Delegate* delegate)
+      : ServiceWorkerURLRequestJob(nullptr,
+                                   nullptr,
+                                   "",
+                                   nullptr,
+                                   nullptr,
+                                   FETCH_REQUEST_MODE_NO_CORS,
+                                   FETCH_CREDENTIALS_MODE_OMIT,
+                                   FetchRedirectMode::FOLLOW_MODE,
+                                   RESOURCE_TYPE_MAIN_FRAME,
+                                   REQUEST_CONTEXT_TYPE_HYPERLINK,
+                                   REQUEST_CONTEXT_FRAME_TYPE_TOP_LEVEL,
+                                   scoped_refptr<ResourceRequestBodyImpl>(),
+                                   ServiceWorkerFetchType::FETCH,
+                                   base::Optional<base::TimeDelta>(),
+                                   delegate),
+        is_response_started_(false) {}
+
+  void OnResponseStarted() override { is_response_started_ = true; }
+
+  void OnReadRawDataComplete(int bytes_read) override {
+    async_read_bytes_.push_back(bytes_read);
+  }
+
+  void RecordResult(ServiceWorkerMetrics::URLRequestJobResult result) override {
+    results_.push_back(result);
+  }
+
+  bool is_response_started() { return is_response_started_; }
+  const std::vector<int>& async_read_bytes() { return async_read_bytes_; }
+  const std::vector<ServiceWorkerMetrics::URLRequestJobResult>& results() {
+    return results_;
+  }
+
+ private:
+  bool is_response_started_;
+  std::vector<int> async_read_bytes_;
+  std::vector<ServiceWorkerMetrics::URLRequestJobResult> results_;
+};
+
+class ServiceWorkerDataPipeReaderTest
+    : public testing::Test,
+      public ServiceWorkerURLRequestJob::Delegate {
+ public:
+  ServiceWorkerDataPipeReaderTest()
+      : thread_bundle_(TestBrowserThreadBundle::IO_MAINLOOP) {}
+
+  void SetUp() override {
+    helper_ = base::MakeUnique<EmbeddedWorkerTestHelper>(base::FilePath());
+    mock_url_request_job_ =
+        base::MakeUnique<MockServiceWorkerURLRequestJob>(this);
+    registration_ = new ServiceWorkerRegistration(
+        GURL("https://example.com/"), 1L, helper_->context()->AsWeakPtr());
+    version_ = new ServiceWorkerVersion(
+        registration_.get(), GURL("https://example.com/service_worker.js"), 1L,
+        helper_->context()->AsWeakPtr());
+    std::vector<ServiceWorkerDatabase::ResourceRecord> records;
+    records.push_back(
+        ServiceWorkerDatabase::ResourceRecord(10, version_->script_url(), 100));
+    version_->script_cache_map()->SetResources(records);
+    version_->set_fetch_handler_existence(
+        ServiceWorkerVersion::FetchHandlerExistence::EXISTS);
+  }
+
+  std::unique_ptr<ServiceWorkerDataPipeReader> CreateTargetDataPipeReader(
+      blink::mojom::ServiceWorkerStreamCallbackPtr* stream_callback,
+      mojo::DataPipe* data_pipe) {
+    blink::mojom::ServiceWorkerStreamHandlePtr stream_handle =
+        blink::mojom::ServiceWorkerStreamHandle::New();
+    stream_handle->stream = std::move(data_pipe->consumer_handle);
+    stream_handle->callback_request = mojo::MakeRequest(stream_callback);
+    return base::MakeUnique<ServiceWorkerDataPipeReader>(
+        mock_url_request_job_.get(), version_, std::move(stream_handle));
+  }
+
+  // Implements ServiceWorkerURLRequestJob::Delegate.
+  void OnPrepareToRestart() override { NOTREACHED(); }
+
+  ServiceWorkerVersion* GetServiceWorkerVersion(
+      ServiceWorkerMetrics::URLRequestJobResult*) override {
+    NOTREACHED();
+    return nullptr;
+  }
+
+  bool RequestStillValid(ServiceWorkerMetrics::URLRequestJobResult*) override {
+    NOTREACHED();
+    return false;
+  }
+
+  void TearDown() override { helper_.reset(); }
+
+  MockServiceWorkerURLRequestJob* mock_url_request_job() {
+    return mock_url_request_job_.get();
+  }
+
+ protected:
+  TestBrowserThreadBundle thread_bundle_;
+
+  std::unique_ptr<EmbeddedWorkerTestHelper> helper_;
+  std::unique_ptr<MockServiceWorkerURLRequestJob> mock_url_request_job_;
+  scoped_refptr<ServiceWorkerRegistration> registration_;
+  scoped_refptr<ServiceWorkerVersion> version_;
+};
+
+class ServiceWorkerDataPipeReaderTestP
+    : public ServiceWorkerDataPipeReaderTest,
+      public testing::WithParamInterface<
+          std::tuple<bool /* should_close_connection_first */,
+                     bool /* has_body */>> {
+ public:
+  ServiceWorkerDataPipeReaderTestP() {}
+  virtual ~ServiceWorkerDataPipeReaderTestP() {}
+
+ protected:
+  bool should_close_connection_first() const { return std::get<0>(GetParam()); }
+  bool has_body() const { return std::get<1>(GetParam()); }
+};
+
+TEST_P(ServiceWorkerDataPipeReaderTestP, SyncRead) {
+  blink::mojom::ServiceWorkerStreamCallbackPtr stream_callback;
+  mojo::DataPipe data_pipe;
+  std::unique_ptr<ServiceWorkerDataPipeReader> data_pipe_reader =
+      CreateTargetDataPipeReader(&stream_callback, &data_pipe);
+
+  // Push enough data.
+  if (has_body()) {
+    std::string expected_response;
+    expected_response.reserve((sizeof(kTestData) - 1) * 1024);
+    for (int i = 0; i < 1024; ++i) {
+      expected_response += kTestData;
+      uint32_t written_bytes = sizeof(kTestData) - 1;
+      MojoResult result =
+          mojo::WriteDataRaw(data_pipe.producer_handle.get(), kTestData,
+                             &written_bytes, MOJO_WRITE_DATA_FLAG_NONE);
+      ASSERT_EQ(MOJO_RESULT_OK, result);
+      EXPECT_EQ(sizeof(kTestData) - 1, written_bytes);
+    }
+  }
+  data_pipe.producer_handle.reset();
+  stream_callback->OnCompleted();
+  base::RunLoop().RunUntilIdle();
+
+  // Nothing has started.
+  EXPECT_FALSE(mock_url_request_job()->is_response_started());
+  EXPECT_EQ(0UL, mock_url_request_job()->async_read_bytes().size());
+  EXPECT_EQ(0UL, mock_url_request_job()->results().size());
+
+  // Start to read.
+  data_pipe_reader->Start();
+  EXPECT_TRUE(mock_url_request_job()->is_response_started());
+  const int buffer_size = sizeof(kTestData);
+  scoped_refptr<net::IOBuffer> buffer = new net::IOBuffer(buffer_size);
+  buffer->data()[buffer_size - 1] = '\0';
+
+  // Read successfully.
+  if (has_body()) {
+    std::string retrieved_response;
+    retrieved_response.reserve(buffer_size * 1024);
+    for (int i = 0; i < 1024; ++i) {
+      EXPECT_EQ(buffer_size - 1,
+                data_pipe_reader->ReadRawData(buffer.get(), buffer_size - 1));
+      EXPECT_STREQ(kTestData, buffer->data());
+      retrieved_response += buffer->data();
+    }
+  }
+
+  // Finish successfully.
+  EXPECT_EQ(net::OK,
+            data_pipe_reader->ReadRawData(buffer.get(), buffer_size - 1));
+  EXPECT_EQ(0UL, mock_url_request_job()->async_read_bytes().size());
+  ASSERT_EQ(1UL, mock_url_request_job()->results().size());
+  EXPECT_EQ(ServiceWorkerMetrics::REQUEST_JOB_STREAM_RESPONSE,
+            mock_url_request_job()->results()[0]);
+}
+
+TEST_P(ServiceWorkerDataPipeReaderTestP, SyncAbort) {
+  blink::mojom::ServiceWorkerStreamCallbackPtr stream_callback;
+  mojo::DataPipe data_pipe;
+  std::unique_ptr<ServiceWorkerDataPipeReader> data_pipe_reader =
+      CreateTargetDataPipeReader(&stream_callback, &data_pipe);
+
+  // Push enough data.
+  if (has_body()) {
+    std::string expected_response;
+    expected_response.reserve((sizeof(kTestData) - 1) * 1024);
+    for (int i = 0; i < 1024; ++i) {
+      expected_response += kTestData;
+      uint32_t written_bytes = sizeof(kTestData) - 1;
+      MojoResult result =
+          mojo::WriteDataRaw(data_pipe.producer_handle.get(), kTestData,
+                             &written_bytes, MOJO_WRITE_DATA_FLAG_NONE);
+      ASSERT_EQ(MOJO_RESULT_OK, result);
+      EXPECT_EQ(sizeof(kTestData) - 1, written_bytes);
+    }
+  }
+  data_pipe.producer_handle.reset();
+  stream_callback->OnAborted();
+  base::RunLoop().RunUntilIdle();
+
+  // Nothing has started.
+  EXPECT_FALSE(mock_url_request_job()->is_response_started());
+  EXPECT_EQ(0UL, mock_url_request_job()->async_read_bytes().size());
+  EXPECT_EQ(0UL, mock_url_request_job()->results().size());
+
+  // Start to read.
+  data_pipe_reader->Start();
+  EXPECT_TRUE(mock_url_request_job()->is_response_started());
+  const int buffer_size = sizeof(kTestData);
+  scoped_refptr<net::IOBuffer> buffer = new net::IOBuffer(buffer_size);
+  buffer->data()[buffer_size - 1] = '\0';
+
+  // Read successfully.
+  if (has_body()) {
+    std::string retrieved_response;
+    retrieved_response.reserve(buffer_size * 1024);
+    for (int i = 0; i < 1024; ++i) {
+      EXPECT_EQ(buffer_size - 1,
+                data_pipe_reader->ReadRawData(buffer.get(), buffer_size - 1));
+      EXPECT_STREQ(kTestData, buffer->data());
+      retrieved_response += buffer->data();
+    }
+  }
+
+  // Abort after all data has been read.
+  EXPECT_EQ(net::ERR_CONNECTION_RESET,
+            data_pipe_reader->ReadRawData(buffer.get(), buffer_size - 1));
+  EXPECT_EQ(0UL, mock_url_request_job()->async_read_bytes().size());
+  ASSERT_EQ(1UL, mock_url_request_job()->results().size());
+  EXPECT_EQ(ServiceWorkerMetrics::REQUEST_JOB_ERROR_STREAM_ABORTED,
+            mock_url_request_job()->results()[0]);
+}
+
+TEST_P(ServiceWorkerDataPipeReaderTestP, AsyncRead) {
+  blink::mojom::ServiceWorkerStreamCallbackPtr stream_callback;
+  mojo::DataPipe data_pipe;
+  std::unique_ptr<ServiceWorkerDataPipeReader> data_pipe_reader =
+      CreateTargetDataPipeReader(&stream_callback, &data_pipe);
+
+  // Nothing has started.
+  EXPECT_FALSE(mock_url_request_job()->is_response_started());
+  EXPECT_EQ(0UL, mock_url_request_job()->async_read_bytes().size());
+  EXPECT_EQ(0UL, mock_url_request_job()->results().size());
+
+  // Start to read.
+  data_pipe_reader->Start();
+  EXPECT_TRUE(mock_url_request_job()->is_response_started());
+  scoped_refptr<net::IOBuffer> buffer = new net::IOBuffer(sizeof(kTestData));
+  buffer->data()[sizeof(kTestData) - 1] = '\0';
+  std::string expected_response;
+  std::string retrieved_response;
+  expected_response.reserve((sizeof(kTestData) - 1) * 1024);
+  retrieved_response.reserve((sizeof(kTestData) - 1) * 1024);
+
+  if (has_body()) {
+    for (int i = 0; i < 1024; ++i) {
+      // Data is not coming. It should be pending state.
+      EXPECT_EQ(net::ERR_IO_PENDING, data_pipe_reader->ReadRawData(
+                                         buffer.get(), sizeof(kTestData) - 1));
+
+      // Push a portion of data.
+      uint32_t written_bytes = sizeof(kTestData) - 1;
+      MojoResult result =
+          mojo::WriteDataRaw(data_pipe.producer_handle.get(), kTestData,
+                             &written_bytes, MOJO_WRITE_DATA_FLAG_NONE);
+      ASSERT_EQ(MOJO_RESULT_OK, result);
+      EXPECT_EQ(sizeof(kTestData) - 1, written_bytes);
+      expected_response += kTestData;
+      base::RunLoop().RunUntilIdle();
+
+      // Read the pushed data correctly.
+      ASSERT_EQ(static_cast<size_t>(i + 1),
+                mock_url_request_job()->async_read_bytes().size());
+      EXPECT_EQ(static_cast<int>(sizeof(kTestData) - 1),
+                mock_url_request_job()->async_read_bytes()[i]);
+      EXPECT_STREQ(kTestData, buffer->data());
+    }
+  }
+
+  // Data is not coming. It should be pending state.
+  EXPECT_EQ(net::ERR_IO_PENDING,
+            data_pipe_reader->ReadRawData(buffer.get(), sizeof(kTestData) - 1));
+
+  // Finish successfully when connection is closed AND OnCompleted is delivered.
+  size_t num_read = mock_url_request_job()->async_read_bytes().size();
+  if (should_close_connection_first()) {
+    data_pipe.producer_handle.reset();
+  } else {
+    stream_callback->OnCompleted();
+  }
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(num_read, mock_url_request_job()->async_read_bytes().size());
+  EXPECT_EQ(0UL, mock_url_request_job()->results().size());
+
+  if (should_close_connection_first()) {
+    stream_callback->OnCompleted();
+  } else {
+    data_pipe.producer_handle.reset();
+  }
+  base::RunLoop().RunUntilIdle();
+  ASSERT_EQ(num_read + 1, mock_url_request_job()->async_read_bytes().size());
+  EXPECT_EQ(net::OK, mock_url_request_job()->async_read_bytes().back());
+  ASSERT_EQ(1UL, mock_url_request_job()->results().size());
+  EXPECT_EQ(ServiceWorkerMetrics::REQUEST_JOB_STREAM_RESPONSE,
+            mock_url_request_job()->results()[0]);
+}
+
+TEST_P(ServiceWorkerDataPipeReaderTestP, AsyncAbort) {
+  blink::mojom::ServiceWorkerStreamCallbackPtr stream_callback;
+  mojo::DataPipe data_pipe;
+  std::unique_ptr<ServiceWorkerDataPipeReader> data_pipe_reader =
+      CreateTargetDataPipeReader(&stream_callback, &data_pipe);
+
+  // Nothing has started.
+  EXPECT_FALSE(mock_url_request_job()->is_response_started());
+  EXPECT_EQ(0UL, mock_url_request_job()->async_read_bytes().size());
+  EXPECT_EQ(0UL, mock_url_request_job()->results().size());
+
+  // Start to read.
+  data_pipe_reader->Start();
+  EXPECT_TRUE(mock_url_request_job()->is_response_started());
+  scoped_refptr<net::IOBuffer> buffer = new net::IOBuffer(sizeof(kTestData));
+  buffer->data()[sizeof(kTestData) - 1] = '\0';
+  std::string expected_response;
+  std::string retrieved_response;
+  expected_response.reserve((sizeof(kTestData) - 1) * 1024);
+  retrieved_response.reserve((sizeof(kTestData) - 1) * 1024);
+
+  if (has_body()) {
+    for (int i = 0; i < 1024; ++i) {
+      // Data is not coming. It should be pending state.
+      EXPECT_EQ(net::ERR_IO_PENDING, data_pipe_reader->ReadRawData(
+                                         buffer.get(), sizeof(kTestData) - 1));
+
+      // Push a portion of data.
+      uint32_t written_bytes = sizeof(kTestData) - 1;
+      MojoResult result =
+          mojo::WriteDataRaw(data_pipe.producer_handle.get(), kTestData,
+                             &written_bytes, MOJO_WRITE_DATA_FLAG_NONE);
+      ASSERT_EQ(MOJO_RESULT_OK, result);
+      EXPECT_EQ(sizeof(kTestData) - 1, written_bytes);
+      expected_response += kTestData;
+      base::RunLoop().RunUntilIdle();
+
+      // Read the pushed data correctly.
+      ASSERT_EQ(static_cast<size_t>(i + 1),
+                mock_url_request_job()->async_read_bytes().size());
+      EXPECT_EQ(static_cast<int>(sizeof(kTestData) - 1),
+                mock_url_request_job()->async_read_bytes()[i]);
+      EXPECT_STREQ(kTestData, buffer->data());
+    }
+  }
+
+  // Data is not coming. It should be pending state.
+  EXPECT_EQ(net::ERR_IO_PENDING,
+            data_pipe_reader->ReadRawData(buffer.get(), sizeof(kTestData) - 1));
+
+  // Abort when connection is closed AND OnAborted is delivered.
+  size_t num_read = mock_url_request_job()->async_read_bytes().size();
+  if (should_close_connection_first()) {
+    data_pipe.producer_handle.reset();
+  } else {
+    stream_callback->OnAborted();
+  }
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(num_read, mock_url_request_job()->async_read_bytes().size());
+  EXPECT_EQ(0UL, mock_url_request_job()->results().size());
+
+  if (should_close_connection_first()) {
+    stream_callback->OnAborted();
+  } else {
+    data_pipe.producer_handle.reset();
+  }
+  base::RunLoop().RunUntilIdle();
+  ASSERT_EQ(num_read + 1, mock_url_request_job()->async_read_bytes().size());
+  EXPECT_EQ(net::ERR_CONNECTION_RESET,
+            mock_url_request_job()->async_read_bytes().back());
+  ASSERT_EQ(1UL, mock_url_request_job()->results().size());
+  EXPECT_EQ(ServiceWorkerMetrics::REQUEST_JOB_ERROR_STREAM_ABORTED,
+            mock_url_request_job()->results()[0]);
+}
+
+INSTANTIATE_TEST_CASE_P(ServiceWorkerDataPipeReaderTest,
+                        ServiceWorkerDataPipeReaderTestP,
+                        testing::Combine(testing::Bool(), testing::Bool()));
+
+}  // namespace content
diff --git a/content/browser/service_worker/service_worker_dispatcher_host_unittest.cc b/content/browser/service_worker/service_worker_dispatcher_host_unittest.cc
index a83b550..46d5a7a4 100644
--- a/content/browser/service_worker/service_worker_dispatcher_host_unittest.cc
+++ b/content/browser/service_worker/service_worker_dispatcher_host_unittest.cc
@@ -833,8 +833,7 @@
   const int kFetchEventId = 91;  // Dummy value
   dispatcher_host_->OnMessageReceived(ServiceWorkerHostMsg_FetchEventResponse(
       version_->embedded_worker()->embedded_worker_id(), kFetchEventId,
-      SERVICE_WORKER_FETCH_EVENT_RESULT_FALLBACK, ServiceWorkerResponse(),
-      base::Time::Now()));
+      ServiceWorkerResponse(), base::Time::Now()));
 
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(0, dispatcher_host_->bad_messages_received_count_);
diff --git a/content/browser/service_worker/service_worker_fetch_dispatcher.cc b/content/browser/service_worker/service_worker_fetch_dispatcher.cc
index 9a4e31e..e004748 100644
--- a/content/browser/service_worker/service_worker_fetch_dispatcher.cc
+++ b/content/browser/service_worker/service_worker_fetch_dispatcher.cc
@@ -283,30 +283,81 @@
 
 // Helper to receive the fetch event response even if
 // ServiceWorkerFetchDispatcher has been destroyed.
-class ServiceWorkerFetchDispatcher::ResponseCallback {
+class ServiceWorkerFetchDispatcher::ResponseCallback
+    : public mojom::ServiceWorkerFetchResponseCallback {
  public:
   ResponseCallback(base::WeakPtr<ServiceWorkerFetchDispatcher> fetch_dispatcher,
-                   ServiceWorkerVersion* version)
-      : fetch_dispatcher_(fetch_dispatcher), version_(version) {}
+                   ServiceWorkerVersion* version,
+                   int fetch_event_id)
+      : fetch_dispatcher_(fetch_dispatcher),
+        version_(version),
+        fetch_event_id_(fetch_event_id),
+        binding_(this) {}
+  ~ResponseCallback() override {}
 
   void Run(int request_id,
-           ServiceWorkerFetchEventResult fetch_result,
            const ServiceWorkerResponse& response,
            base::Time dispatch_event_time) {
-    const bool handled =
-        (fetch_result == SERVICE_WORKER_FETCH_EVENT_RESULT_RESPONSE);
-    if (!version_->FinishRequest(request_id, handled, dispatch_event_time))
-      NOTREACHED() << "Should only receive one reply per event";
+    // Legacy IPC callback is only for blob handling.
+    DCHECK_EQ(fetch_event_id_, request_id);
+    DCHECK(response.blob_uuid.size());
+    HandleResponse(response, blink::mojom::ServiceWorkerStreamHandlePtr(),
+                   SERVICE_WORKER_FETCH_EVENT_RESULT_RESPONSE,
+                   dispatch_event_time);
+  }
 
-    // |fetch_dispatcher| is null if the URLRequest was killed.
-    if (fetch_dispatcher_)
-      fetch_dispatcher_->DidFinish(request_id, fetch_result, response);
+  // Implements mojom::ServiceWorkerFetchResponseCallback.
+  void OnResponse(const ServiceWorkerResponse& response,
+                  base::Time dispatch_event_time) override {
+    HandleResponse(response, blink::mojom::ServiceWorkerStreamHandlePtr(),
+                   SERVICE_WORKER_FETCH_EVENT_RESULT_RESPONSE,
+                   dispatch_event_time);
+  }
+  void OnResponseStream(
+      const ServiceWorkerResponse& response,
+      blink::mojom::ServiceWorkerStreamHandlePtr body_as_stream,
+      base::Time dispatch_event_time) override {
+    HandleResponse(response, std::move(body_as_stream),
+                   SERVICE_WORKER_FETCH_EVENT_RESULT_RESPONSE,
+                   dispatch_event_time);
+  }
+  void OnFallback(base::Time dispatch_event_time) override {
+    HandleResponse(
+        ServiceWorkerResponse(), blink::mojom::ServiceWorkerStreamHandlePtr(),
+        SERVICE_WORKER_FETCH_EVENT_RESULT_FALLBACK, dispatch_event_time);
+  }
+
+  mojom::ServiceWorkerFetchResponseCallbackPtr CreateInterfacePtrAndBind() {
+    return binding_.CreateInterfacePtrAndBind();
   }
 
  private:
+  void HandleResponse(const ServiceWorkerResponse& response,
+                      blink::mojom::ServiceWorkerStreamHandlePtr body_as_stream,
+                      ServiceWorkerFetchEventResult fetch_result,
+                      base::Time dispatch_event_time) {
+    // Copy |fetch_dispatcher_| and |fetch_event_id_| for use after |this| is
+    // destroyed.
+    base::WeakPtr<ServiceWorkerFetchDispatcher> dispatcher(fetch_dispatcher_);
+    const int event_id(fetch_event_id_);
+    // FinishRequest() will delete |this|.
+    if (!version_->FinishRequest(
+            fetch_event_id_,
+            fetch_result == SERVICE_WORKER_FETCH_EVENT_RESULT_RESPONSE,
+            dispatch_event_time))
+      NOTREACHED() << "Should only receive one reply per event";
+    // |dispatcher| is null if the URLRequest was killed.
+    if (!dispatcher)
+      return;
+    dispatcher->DidFinish(event_id, fetch_result, response,
+                          std::move(body_as_stream));
+  }
+
   base::WeakPtr<ServiceWorkerFetchDispatcher> fetch_dispatcher_;
   // Owns |this|.
   ServiceWorkerVersion* version_;
+  const int fetch_event_id_;
+  mojo::Binding<mojom::ServiceWorkerFetchResponseCallback> binding_;
 
   DISALLOW_COPY_AND_ASSIGN(ResponseCallback);
 };
@@ -454,12 +505,15 @@
         base::Bind(&ServiceWorkerUtils::NoOpStatusCallback));
   }
 
-  ResponseCallback* response_callback =
-      new ResponseCallback(weak_factory_.GetWeakPtr(), version_.get());
+  std::unique_ptr<ResponseCallback> response_callback =
+      base::MakeUnique<ResponseCallback>(weak_factory_.GetWeakPtr(),
+                                         version_.get(), fetch_event_id);
+  mojom::ServiceWorkerFetchResponseCallbackPtr response_callback_ptr =
+      response_callback->CreateInterfacePtrAndBind();
   version_->RegisterRequestCallback<ServiceWorkerHostMsg_FetchEventResponse>(
       fetch_event_id,
       base::Bind(&ServiceWorkerFetchDispatcher::ResponseCallback::Run,
-                 base::Owned(response_callback)));
+                 base::Passed(&response_callback)));
 
   if (url_loader_assets_) {
     url_loader_assets_->MayBeReportToDevTools(
@@ -475,6 +529,7 @@
   // assets alive while the FetchEvent is ongoing in the service worker.
   version_->event_dispatcher()->DispatchFetchEvent(
       fetch_event_id, *request_, std::move(preload_handle_),
+      std::move(response_callback_ptr),
       base::Bind(&ServiceWorkerFetchDispatcher::OnFetchEventFinished,
                  base::Unretained(version_.get()), event_finish_id,
                  url_loader_assets_));
@@ -490,21 +545,25 @@
 void ServiceWorkerFetchDispatcher::DidFail(ServiceWorkerStatusCode status) {
   DCHECK_NE(SERVICE_WORKER_OK, status);
   Complete(status, SERVICE_WORKER_FETCH_EVENT_RESULT_FALLBACK,
-           ServiceWorkerResponse());
+           ServiceWorkerResponse(),
+           blink::mojom::ServiceWorkerStreamHandlePtr());
 }
 
 void ServiceWorkerFetchDispatcher::DidFinish(
     int request_id,
     ServiceWorkerFetchEventResult fetch_result,
-    const ServiceWorkerResponse& response) {
+    const ServiceWorkerResponse& response,
+    blink::mojom::ServiceWorkerStreamHandlePtr body_as_stream) {
   net_log_.EndEvent(net::NetLogEventType::SERVICE_WORKER_FETCH_EVENT);
-  Complete(SERVICE_WORKER_OK, fetch_result, response);
+  Complete(SERVICE_WORKER_OK, fetch_result, response,
+           std::move(body_as_stream));
 }
 
 void ServiceWorkerFetchDispatcher::Complete(
     ServiceWorkerStatusCode status,
     ServiceWorkerFetchEventResult fetch_result,
-    const ServiceWorkerResponse& response) {
+    const ServiceWorkerResponse& response,
+    blink::mojom::ServiceWorkerStreamHandlePtr body_as_stream) {
   DCHECK(!fetch_callback_.is_null());
 
   did_complete_ = true;
@@ -514,7 +573,8 @@
 
   FetchCallback fetch_callback = fetch_callback_;
   scoped_refptr<ServiceWorkerVersion> version = version_;
-  fetch_callback.Run(status, fetch_result, response, version);
+  fetch_callback.Run(status, fetch_result, response, std::move(body_as_stream),
+                     version);
 }
 
 bool ServiceWorkerFetchDispatcher::MaybeStartNavigationPreload(
diff --git a/content/browser/service_worker/service_worker_fetch_dispatcher.h b/content/browser/service_worker/service_worker_fetch_dispatcher.h
index 192e17b..47bc334 100644
--- a/content/browser/service_worker/service_worker_fetch_dispatcher.h
+++ b/content/browser/service_worker/service_worker_fetch_dispatcher.h
@@ -21,6 +21,7 @@
 #include "content/common/url_loader.mojom.h"
 #include "content/common/url_loader_factory.mojom.h"
 #include "content/public/common/resource_type.h"
+#include "mojo/public/cpp/system/data_pipe.h"
 #include "net/log/net_log_with_source.h"
 
 namespace net {
@@ -38,6 +39,7 @@
       base::Callback<void(ServiceWorkerStatusCode,
                           ServiceWorkerFetchEventResult,
                           const ServiceWorkerResponse&,
+                          blink::mojom::ServiceWorkerStreamHandlePtr,
                           const scoped_refptr<ServiceWorkerVersion>&)>;
 
   ServiceWorkerFetchDispatcher(
@@ -75,10 +77,12 @@
   void DidFail(ServiceWorkerStatusCode status);
   void DidFinish(int request_id,
                  ServiceWorkerFetchEventResult fetch_result,
-                 const ServiceWorkerResponse& response);
+                 const ServiceWorkerResponse& response,
+                 blink::mojom::ServiceWorkerStreamHandlePtr body_as_stream);
   void Complete(ServiceWorkerStatusCode status,
                 ServiceWorkerFetchEventResult fetch_result,
-                const ServiceWorkerResponse& response);
+                const ServiceWorkerResponse& response,
+                blink::mojom::ServiceWorkerStreamHandlePtr body_as_stream);
 
   static void OnFetchEventFinished(
       ServiceWorkerVersion* version,
diff --git a/content/browser/service_worker/service_worker_stream_reader.cc b/content/browser/service_worker/service_worker_stream_reader.cc
deleted file mode 100644
index 062fe77..0000000
--- a/content/browser/service_worker/service_worker_stream_reader.cc
+++ /dev/null
@@ -1,136 +0,0 @@
-// 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.
-
-#include "content/browser/service_worker/service_worker_stream_reader.h"
-
-#include "content/browser/resource_context_impl.h"
-#include "content/browser/service_worker/service_worker_version.h"
-#include "content/browser/streams/stream.h"
-#include "content/browser/streams/stream_context.h"
-#include "content/browser/streams/stream_registry.h"
-#include "net/base/io_buffer.h"
-
-namespace content {
-
-ServiceWorkerStreamReader::ServiceWorkerStreamReader(
-    ServiceWorkerURLRequestJob* owner,
-    scoped_refptr<ServiceWorkerVersion> streaming_version)
-    : owner_(owner),
-      stream_pending_buffer_size_(0),
-      streaming_version_(streaming_version) {
-  streaming_version_->AddStreamingURLRequestJob(owner_);
-}
-
-ServiceWorkerStreamReader::~ServiceWorkerStreamReader() {
-  if (streaming_version_) {
-    streaming_version_->RemoveStreamingURLRequestJob(owner_);
-    streaming_version_ = nullptr;
-  }
-  if (stream_) {
-    stream_->RemoveReadObserver(this);
-    stream_->Abort();
-    stream_ = nullptr;
-  }
-  if (!waiting_stream_url_.is_empty()) {
-    StreamRegistry* stream_registry =
-        GetStreamContextForResourceContext(owner_->resource_context())
-            ->registry();
-    stream_registry->RemoveRegisterObserver(waiting_stream_url_);
-    stream_registry->AbortPendingStream(waiting_stream_url_);
-  }
-}
-
-void ServiceWorkerStreamReader::Start(const GURL& stream_url) {
-  DCHECK(!stream_);
-  DCHECK(waiting_stream_url_.is_empty());
-
-  StreamContext* stream_context =
-      GetStreamContextForResourceContext(owner_->resource_context());
-  stream_ = stream_context->registry()->GetStream(stream_url);
-  if (!stream_) {
-    waiting_stream_url_ = stream_url;
-    // Wait for StreamHostMsg_StartBuilding message from the ServiceWorker.
-    stream_context->registry()->SetRegisterObserver(waiting_stream_url_, this);
-    return;
-  }
-  stream_->SetReadObserver(this);
-  owner_->OnResponseStarted();
-}
-
-int ServiceWorkerStreamReader::ReadRawData(net::IOBuffer* buf, int buf_size) {
-  DCHECK(stream_);
-  DCHECK(waiting_stream_url_.is_empty());
-
-  int bytes_read = 0;
-  switch (stream_->ReadRawData(buf, buf_size, &bytes_read)) {
-    case Stream::STREAM_HAS_DATA:
-      DCHECK_GT(bytes_read, 0);
-      return bytes_read;
-    case Stream::STREAM_COMPLETE:
-      DCHECK_EQ(0, bytes_read);
-      owner_->RecordResult(ServiceWorkerMetrics::REQUEST_JOB_STREAM_RESPONSE);
-      return 0;
-    case Stream::STREAM_EMPTY:
-      stream_pending_buffer_ = buf;
-      stream_pending_buffer_size_ = buf_size;
-      return net::ERR_IO_PENDING;
-    case Stream::STREAM_ABORTED:
-      // Handle this as connection reset.
-      owner_->RecordResult(
-          ServiceWorkerMetrics::REQUEST_JOB_ERROR_STREAM_ABORTED);
-      return net::ERR_CONNECTION_RESET;
-  }
-  NOTREACHED();
-  return net::ERR_FAILED;
-}
-
-void ServiceWorkerStreamReader::OnDataAvailable(Stream* stream) {
-  // Do nothing if stream_pending_buffer_ is empty, i.e. there's no ReadRawData
-  // operation waiting for IO completion.
-  if (!stream_pending_buffer_)
-    return;
-
-  // stream_pending_buffer_ is set to the IOBuffer instance provided to
-  // ReadRawData() by URLRequestJob.
-
-  int result = 0;
-  switch (stream_->ReadRawData(stream_pending_buffer_.get(),
-                               stream_pending_buffer_size_, &result)) {
-    case Stream::STREAM_HAS_DATA:
-      DCHECK_GT(result, 0);
-      break;
-    case Stream::STREAM_COMPLETE:
-      // Calling NotifyReadComplete with 0 signals completion.
-      DCHECK(!result);
-      owner_->RecordResult(ServiceWorkerMetrics::REQUEST_JOB_STREAM_RESPONSE);
-      break;
-    case Stream::STREAM_EMPTY:
-      NOTREACHED();
-      break;
-    case Stream::STREAM_ABORTED:
-      // Handle this as connection reset.
-      result = net::ERR_CONNECTION_RESET;
-      owner_->RecordResult(
-          ServiceWorkerMetrics::REQUEST_JOB_ERROR_STREAM_ABORTED);
-      break;
-  }
-
-  // Clear the buffers before notifying the read is complete, so that it is
-  // safe for the observer to read.
-  stream_pending_buffer_ = nullptr;
-  stream_pending_buffer_size_ = 0;
-  owner_->OnReadRawDataComplete(result);
-}
-
-void ServiceWorkerStreamReader::OnStreamRegistered(Stream* stream) {
-  StreamContext* stream_context =
-      GetStreamContextForResourceContext(owner_->resource_context());
-  stream_context->registry()->RemoveRegisterObserver(waiting_stream_url_);
-  waiting_stream_url_ = GURL();
-  stream_ = stream;
-  stream_->SetReadObserver(this);
-  owner_->OnResponseStarted();
-}
-
-}  // namespace content
diff --git a/content/browser/service_worker/service_worker_stream_reader.h b/content/browser/service_worker/service_worker_stream_reader.h
deleted file mode 100644
index 7d75294..0000000
--- a/content/browser/service_worker/service_worker_stream_reader.h
+++ /dev/null
@@ -1,60 +0,0 @@
-// 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 CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_STREAM_READER_H_
-#define CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_STREAM_READER_H_
-
-#include "content/browser/service_worker/service_worker_url_request_job.h"
-#include "content/browser/streams/stream_read_observer.h"
-#include "content/browser/streams/stream_register_observer.h"
-#include "net/url_request/url_request.h"
-
-namespace net {
-class IOBuffer;
-}
-
-namespace content {
-
-class ServiceWorkerVersion;
-
-// Reads a stream response for ServiceWorkerURLRequestJob.
-// Owned by ServiceWorkerURLRequestJob.
-class ServiceWorkerStreamReader : public StreamReadObserver,
-                                  public StreamRegisterObserver {
- public:
-  // |streaming_version| is the ServiceWorkerVersion that must be kept alive
-  // while the response is being read.
-  ServiceWorkerStreamReader(
-      ServiceWorkerURLRequestJob* owner,
-      scoped_refptr<ServiceWorkerVersion> streaming_version);
-  ~ServiceWorkerStreamReader() override;
-
-  // Starts reading the stream. owner_->OnResponseStarted will be called when
-  // the response starts.
-  void Start(const GURL& stream_url);
-
-  // Same as URLRequestJob::ReadRawData. If ERR_IO_PENDING is returned,
-  // owner_->OnReadRawDataComplete will be called when the read completes.
-  int ReadRawData(net::IOBuffer* buf, int buf_size);
-
-  // StreamObserver override:
-  void OnDataAvailable(Stream* stream) override;
-
-  // StreamRegisterObserver override:
-  void OnStreamRegistered(Stream* stream) override;
-
- private:
-  ServiceWorkerURLRequestJob* owner_;
-
-  scoped_refptr<Stream> stream_;
-  GURL waiting_stream_url_;
-  scoped_refptr<net::IOBuffer> stream_pending_buffer_;
-  int stream_pending_buffer_size_;
-
-  scoped_refptr<ServiceWorkerVersion> streaming_version_;
-};
-
-}  // namespace content
-
-#endif  // CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_STREAM_READER_H_
diff --git a/content/browser/service_worker/service_worker_url_request_job.cc b/content/browser/service_worker/service_worker_url_request_job.cc
index 4c7dbf3..8b2d029 100644
--- a/content/browser/service_worker/service_worker_url_request_job.cc
+++ b/content/browser/service_worker/service_worker_url_request_job.cc
@@ -29,10 +29,10 @@
 #include "content/browser/resource_context_impl.h"
 #include "content/browser/service_worker/embedded_worker_instance.h"
 #include "content/browser/service_worker/service_worker_blob_reader.h"
+#include "content/browser/service_worker/service_worker_data_pipe_reader.h"
 #include "content/browser/service_worker/service_worker_fetch_dispatcher.h"
 #include "content/browser/service_worker/service_worker_provider_host.h"
 #include "content/browser/service_worker/service_worker_response_info.h"
-#include "content/browser/service_worker/service_worker_stream_reader.h"
 #include "content/common/resource_request_body_impl.h"
 #include "content/common/service_worker/service_worker_types.h"
 #include "content/common/service_worker/service_worker_utils.h"
@@ -261,7 +261,7 @@
 }
 
 ServiceWorkerURLRequestJob::~ServiceWorkerURLRequestJob() {
-  stream_reader_.reset();
+  data_pipe_reader_.reset();
   file_size_resolver_.reset();
 
   if (!ShouldRecordResult())
@@ -312,7 +312,7 @@
 
 void ServiceWorkerURLRequestJob::Kill() {
   net::URLRequestJob::Kill();
-  stream_reader_.reset();
+  data_pipe_reader_.reset();
   fetch_dispatcher_.reset();
   blob_reader_.reset();
   weak_factory_.InvalidateWeakPtrs();
@@ -367,8 +367,8 @@
   DCHECK(buf);
   DCHECK_GE(buf_size, 0);
 
-  if (stream_reader_)
-    return stream_reader_->ReadRawData(buf, buf_size);
+  if (data_pipe_reader_)
+    return data_pipe_reader_->ReadRawData(buf, buf_size);
   if (blob_reader_)
     return blob_reader_->ReadRawData(buf, buf_size);
 
@@ -564,6 +564,7 @@
     ServiceWorkerStatusCode status,
     ServiceWorkerFetchEventResult fetch_result,
     const ServiceWorkerResponse& response,
+    blink::mojom::ServiceWorkerStreamHandlePtr body_as_stream,
     const scoped_refptr<ServiceWorkerVersion>& version) {
   // Do not clear |fetch_dispatcher_| if it has dispatched a navigation preload
   // request to keep the mojom::URLLoader related objects in it, because the
@@ -629,13 +630,13 @@
   DCHECK(main_script_http_info);
   http_response_info_.reset(new net::HttpResponseInfo(*main_script_http_info));
 
-  // Set up a request for reading the stream.
-  if (response.stream_url.is_valid()) {
-    DCHECK(response.blob_uuid.empty());
+  // Process stream using Mojo's data pipe.
+  if (!body_as_stream.is_null()) {
     SetResponseBodyType(STREAM);
     SetResponse(response);
-    stream_reader_.reset(new ServiceWorkerStreamReader(this, version));
-    stream_reader_->Start(response.stream_url);
+    data_pipe_reader_.reset(new ServiceWorkerDataPipeReader(
+        this, version, std::move(body_as_stream)));
+    data_pipe_reader_->Start();
     return;
   }
 
diff --git a/content/browser/service_worker/service_worker_url_request_job.h b/content/browser/service_worker/service_worker_url_request_job.h
index 3b54696..bd4a021 100644
--- a/content/browser/service_worker/service_worker_url_request_job.h
+++ b/content/browser/service_worker/service_worker_url_request_job.h
@@ -20,11 +20,13 @@
 #include "content/browser/service_worker/embedded_worker_status.h"
 #include "content/browser/service_worker/service_worker_metrics.h"
 #include "content/common/content_export.h"
+#include "content/common/service_worker/service_worker_event_dispatcher.mojom.h"
 #include "content/common/service_worker/service_worker_status_code.h"
 #include "content/common/service_worker/service_worker_types.h"
 #include "content/public/common/request_context_frame_type.h"
 #include "content/public/common/request_context_type.h"
 #include "content/public/common/resource_type.h"
+#include "mojo/public/cpp/system/data_pipe.h"
 #include "net/http/http_byte_range.h"
 #include "net/url_request/url_request.h"
 #include "net/url_request/url_request_job.h"
@@ -47,10 +49,9 @@
 class ResourceContext;
 class ResourceRequestBodyImpl;
 class ServiceWorkerBlobReader;
-class ServiceWorkerStreamReader;
+class ServiceWorkerDataPipeReader;
 class ServiceWorkerFetchDispatcher;
 class ServiceWorkerVersion;
-class Stream;
 
 class CONTENT_EXPORT ServiceWorkerURLRequestJob : public net::URLRequestJob {
  public:
@@ -139,10 +140,10 @@
   int ReadRawData(net::IOBuffer* buf, int buf_size) override;
 
   //----------------------------------------------------------------------------
-  // The following are intended for use by ServiceWorker(Blob|Stream)Reader.
-  void OnResponseStarted();
-  void OnReadRawDataComplete(int bytes_read);
-  void RecordResult(ServiceWorkerMetrics::URLRequestJobResult result);
+  // The following are intended for use by ServiceWorker(Blob|DataPipe)Reader.
+  virtual void OnResponseStarted();
+  virtual void OnReadRawDataComplete(int bytes_read);
+  virtual void RecordResult(ServiceWorkerMetrics::URLRequestJobResult result);
   //----------------------------------------------------------------------------
 
   base::WeakPtr<ServiceWorkerURLRequestJob> GetWeakPtr();
@@ -187,6 +188,7 @@
       ServiceWorkerStatusCode status,
       ServiceWorkerFetchEventResult fetch_result,
       const ServiceWorkerResponse& response,
+      blink::mojom::ServiceWorkerStreamHandlePtr body_as_stream,
       const scoped_refptr<ServiceWorkerVersion>& version);
   void SetResponse(const ServiceWorkerResponse& response);
 
@@ -302,9 +304,9 @@
   std::string client_id_;
   base::WeakPtr<storage::BlobStorageContext> blob_storage_context_;
   const ResourceContext* resource_context_;
-  // Only one of |blob_reader_| and |stream_reader_| can be non-null.
+  // Only one of |blob_reader_| and |data_pipe_reader_| can be non-null.
   std::unique_ptr<ServiceWorkerBlobReader> blob_reader_;
-  std::unique_ptr<ServiceWorkerStreamReader> stream_reader_;
+  std::unique_ptr<ServiceWorkerDataPipeReader> data_pipe_reader_;
 
   FetchRequestMode request_mode_;
   FetchCredentialsMode credentials_mode_;
diff --git a/content/browser/service_worker/service_worker_url_request_job_unittest.cc b/content/browser/service_worker/service_worker_url_request_job_unittest.cc
index 58e1058..9896129 100644
--- a/content/browser/service_worker/service_worker_url_request_job_unittest.cc
+++ b/content/browser/service_worker/service_worker_url_request_job_unittest.cc
@@ -28,9 +28,6 @@
 #include "content/browser/service_worker/service_worker_response_info.h"
 #include "content/browser/service_worker/service_worker_test_utils.h"
 #include "content/browser/service_worker/service_worker_version.h"
-#include "content/browser/streams/stream.h"
-#include "content/browser/streams/stream_context.h"
-#include "content/browser/streams/stream_registry.h"
 #include "content/common/resource_request_body_impl.h"
 #include "content/common/service_worker/service_worker_messages.h"
 #include "content/common/service_worker/service_worker_status_code.h"
@@ -402,26 +399,26 @@
   ~ProviderDeleteHelper() override {}
 
  protected:
-  void OnFetchEvent(int embedded_worker_id,
-                    int fetch_event_id,
-                    const ServiceWorkerFetchRequest& request,
-                    mojom::FetchEventPreloadHandlePtr preload_handle,
-                    const FetchCallback& callback) override {
+  void OnFetchEvent(
+      int /* embedded_worker_id */,
+      int /* fetch_event_id */,
+      const ServiceWorkerFetchRequest& /* request */,
+      mojom::FetchEventPreloadHandlePtr /* preload_handle */,
+      mojom::ServiceWorkerFetchResponseCallbackPtr response_callback,
+      const FetchCallback& finish_callback) override {
     context()->RemoveProviderHost(mock_render_process_id(), kProviderID);
-    SimulateSend(new ServiceWorkerHostMsg_FetchEventResponse(
-        embedded_worker_id, fetch_event_id,
-        SERVICE_WORKER_FETCH_EVENT_RESULT_RESPONSE,
+    response_callback->OnResponse(
         ServiceWorkerResponse(
             base::MakeUnique<std::vector<GURL>>(), 200, "OK",
             blink::kWebServiceWorkerResponseTypeDefault,
             base::MakeUnique<ServiceWorkerHeaderMap>(), std::string(), 0,
-            GURL(), blink::kWebServiceWorkerResponseErrorUnknown, base::Time(),
+            blink::kWebServiceWorkerResponseErrorUnknown, base::Time(),
             false /* response_is_in_cache_storage */,
             std::string() /* response_cache_storage_cache_name */,
             base::MakeUnique<
                 ServiceWorkerHeaderList>() /* cors_exposed_header_names */),
-        base::Time::Now()));
-    callback.Run(SERVICE_WORKER_OK, base::Time::Now());
+        base::Time::Now());
+    finish_callback.Run(SERVICE_WORKER_OK, base::Time::Now());
   }
 
  private:
@@ -488,25 +485,25 @@
   ~BlobResponder() override {}
 
  protected:
-  void OnFetchEvent(int embedded_worker_id,
-                    int fetch_event_id,
-                    const ServiceWorkerFetchRequest& request,
-                    mojom::FetchEventPreloadHandlePtr preload_handle,
-                    const FetchCallback& callback) override {
-    SimulateSend(new ServiceWorkerHostMsg_FetchEventResponse(
-        embedded_worker_id, fetch_event_id,
-        SERVICE_WORKER_FETCH_EVENT_RESULT_RESPONSE,
+  void OnFetchEvent(
+      int /* embedded_worker_id */,
+      int /* fetch_event_id */,
+      const ServiceWorkerFetchRequest& /* request */,
+      mojom::FetchEventPreloadHandlePtr /* preload_handle */,
+      mojom::ServiceWorkerFetchResponseCallbackPtr response_callback,
+      const FetchCallback& finish_callback) override {
+    response_callback->OnResponse(
         ServiceWorkerResponse(
             base::MakeUnique<std::vector<GURL>>(), 200, "OK",
             blink::kWebServiceWorkerResponseTypeDefault, MakeHeaders(),
-            blob_uuid_, blob_size_, GURL(),
+            blob_uuid_, blob_size_,
             blink::kWebServiceWorkerResponseErrorUnknown, base::Time(),
             false /* response_is_in_cache_storage */,
             std::string() /* response_cache_storage_cache_name */,
             base::MakeUnique<
                 ServiceWorkerHeaderList>() /* cors_exposed_header_names */),
-        base::Time::Now()));
-    callback.Run(SERVICE_WORKER_OK, base::Time::Now());
+        base::Time::Now());
+    finish_callback.Run(SERVICE_WORKER_OK, base::Time::Now());
   }
 
   std::string blob_uuid_;
@@ -569,45 +566,51 @@
 // Responds to fetch events with a stream.
 class StreamResponder : public EmbeddedWorkerTestHelper {
  public:
-  explicit StreamResponder(const GURL& stream_url)
-      : EmbeddedWorkerTestHelper(base::FilePath()), stream_url_(stream_url) {}
+  explicit StreamResponder(
+      blink::mojom::ServiceWorkerStreamCallbackRequest callback_request,
+      mojo::ScopedDataPipeConsumerHandle consumer_handle)
+      : EmbeddedWorkerTestHelper(base::FilePath()) {
+    EXPECT_TRUE(stream_handle_.is_null());
+    stream_handle_ = blink::mojom::ServiceWorkerStreamHandle::New();
+    stream_handle_->callback_request = std::move(callback_request);
+    stream_handle_->stream = std::move(consumer_handle);
+  }
   ~StreamResponder() override {}
 
  protected:
-  void OnFetchEvent(int embedded_worker_id,
-                    int fetch_event_id,
-                    const ServiceWorkerFetchRequest& request,
-                    mojom::FetchEventPreloadHandlePtr preload_handle,
-                    const FetchCallback& callback) override {
-    SimulateSend(new ServiceWorkerHostMsg_FetchEventResponse(
-        embedded_worker_id, fetch_event_id,
-        SERVICE_WORKER_FETCH_EVENT_RESULT_RESPONSE,
+  void OnFetchEvent(
+      int /* embedded_worker_id */,
+      int /* fetch_event_id */,
+      const ServiceWorkerFetchRequest& /* request */,
+      mojom::FetchEventPreloadHandlePtr /* preload_handle */,
+      mojom::ServiceWorkerFetchResponseCallbackPtr response_callback,
+      const FetchCallback& finish_callback) override {
+    ASSERT_FALSE(stream_handle_.is_null());
+    response_callback->OnResponseStream(
         ServiceWorkerResponse(
             base::MakeUnique<std::vector<GURL>>(), 200, "OK",
             blink::kWebServiceWorkerResponseTypeDefault, MakeHeaders(), "", 0,
-            stream_url_, blink::kWebServiceWorkerResponseErrorUnknown,
-            base::Time(), false /* response_is_in_cache_storage */,
+            blink::kWebServiceWorkerResponseErrorUnknown, base::Time(),
+            false /* response_is_in_cache_storage */,
             std::string() /* response_cache_storage_cache_name */,
             base::MakeUnique<
                 ServiceWorkerHeaderList>() /* cors_exposed_header_names */),
-        base::Time::Now()));
-    callback.Run(SERVICE_WORKER_OK, base::Time::Now());
+        std::move(stream_handle_), base::Time::Now());
+    finish_callback.Run(SERVICE_WORKER_OK, base::Time::Now());
   }
 
-  const GURL stream_url_;
+  blink::mojom::ServiceWorkerStreamHandlePtr stream_handle_;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(StreamResponder);
 };
 
 TEST_F(ServiceWorkerURLRequestJobTest, StreamResponse) {
-  const GURL stream_url("blob://stream");
-  StreamContext* stream_context =
-      GetStreamContextForResourceContext(
-          browser_context_->GetResourceContext());
-  scoped_refptr<Stream> stream =
-      new Stream(stream_context->registry(), nullptr, stream_url);
-  SetUpWithHelper(new StreamResponder(stream_url));
+  blink::mojom::ServiceWorkerStreamCallbackPtr stream_callback;
+  mojo::DataPipe data_pipe;
+  SetUpWithHelper(new StreamResponder(mojo::MakeRequest(&stream_callback),
+                                      std::move(data_pipe.consumer_handle)));
+
   version_->SetStatus(ServiceWorkerVersion::ACTIVATED);
   request_ = url_request_context_.CreateRequest(
       GURL("https://example.com/foo.html"), net::DEFAULT_PRIORITY,
@@ -619,9 +622,15 @@
   expected_response.reserve((sizeof(kTestData) - 1) * 1024);
   for (int i = 0; i < 1024; ++i) {
     expected_response += kTestData;
-    stream->AddData(kTestData, sizeof(kTestData) - 1);
+    uint32_t written_bytes = sizeof(kTestData) - 1;
+    MojoResult result =
+        mojo::WriteDataRaw(data_pipe.producer_handle.get(), kTestData,
+                           &written_bytes, MOJO_WRITE_DATA_FLAG_NONE);
+    ASSERT_EQ(MOJO_RESULT_OK, result);
+    EXPECT_EQ(sizeof(kTestData) - 1, written_bytes);
   }
-  stream->Finalize(net::OK);
+  stream_callback->OnCompleted();
+  data_pipe.producer_handle.reset();
 
   EXPECT_FALSE(HasWork());
   base::RunLoop().RunUntilIdle();
@@ -649,112 +658,11 @@
   EXPECT_FALSE(HasWork());
 }
 
-TEST_F(ServiceWorkerURLRequestJobTest, StreamResponse_DelayedRegistration) {
-  const GURL stream_url("blob://stream");
-  StreamContext* stream_context =
-      GetStreamContextForResourceContext(
-          browser_context_->GetResourceContext());
-  SetUpWithHelper(new StreamResponder(stream_url));
-
-  version_->SetStatus(ServiceWorkerVersion::ACTIVATED);
-  request_ = url_request_context_.CreateRequest(
-      GURL("https://example.com/foo.html"), net::DEFAULT_PRIORITY,
-      &url_request_delegate_);
-  request_->set_method("GET");
-  request_->Start();
-
-  scoped_refptr<Stream> stream =
-      new Stream(stream_context->registry(), nullptr, stream_url);
-  std::string expected_response;
-  expected_response.reserve((sizeof(kTestData) - 1) * 1024);
-  for (int i = 0; i < 1024; ++i) {
-    expected_response += kTestData;
-    stream->AddData(kTestData, sizeof(kTestData) - 1);
-  }
-  stream->Finalize(net::OK);
-
-  EXPECT_FALSE(HasWork());
-  base::RunLoop().RunUntilIdle();
-  EXPECT_TRUE(HasWork());
-  EXPECT_TRUE(request_->status().is_success());
-  EXPECT_EQ(200,
-            request_->response_headers()->response_code());
-  EXPECT_EQ("OK",
-            request_->response_headers()->GetStatusText());
-  EXPECT_EQ(expected_response, url_request_delegate_.data_received());
-
-  EXPECT_EQ(0, times_prepare_to_restart_invoked_);
-  ServiceWorkerResponseInfo* info =
-      ServiceWorkerResponseInfo::ForRequest(request_.get());
-  ASSERT_TRUE(info);
-  EXPECT_TRUE(info->was_fetched_via_service_worker());
-  EXPECT_FALSE(info->was_fallback_required());
-  EXPECT_EQ(0u, info->url_list_via_service_worker().size());
-  EXPECT_EQ(blink::kWebServiceWorkerResponseTypeDefault,
-            info->response_type_via_service_worker());
-  EXPECT_FALSE(info->service_worker_start_time().is_null());
-  EXPECT_FALSE(info->service_worker_ready_time().is_null());
-
-  request_.reset();
-  EXPECT_FALSE(HasWork());
-}
-
-TEST_F(ServiceWorkerURLRequestJobTest, StreamResponse_QuickFinalize) {
-  const GURL stream_url("blob://stream");
-  StreamContext* stream_context =
-      GetStreamContextForResourceContext(
-          browser_context_->GetResourceContext());
-  scoped_refptr<Stream> stream =
-      new Stream(stream_context->registry(), nullptr, stream_url);
-  std::string expected_response;
-  expected_response.reserve((sizeof(kTestData) - 1) * 1024);
-  for (int i = 0; i < 1024; ++i) {
-    expected_response += kTestData;
-    stream->AddData(kTestData, sizeof(kTestData) - 1);
-  }
-  stream->Finalize(net::OK);
-  SetUpWithHelper(new StreamResponder(stream_url));
-
-  version_->SetStatus(ServiceWorkerVersion::ACTIVATED);
-  request_ = url_request_context_.CreateRequest(
-      GURL("https://example.com/foo.html"), net::DEFAULT_PRIORITY,
-      &url_request_delegate_);
-  request_->set_method("GET");
-  request_->Start();
-  EXPECT_FALSE(HasWork());
-  base::RunLoop().RunUntilIdle();
-  EXPECT_TRUE(HasWork());
-  EXPECT_TRUE(request_->status().is_success());
-  EXPECT_EQ(200,
-            request_->response_headers()->response_code());
-  EXPECT_EQ("OK",
-            request_->response_headers()->GetStatusText());
-  EXPECT_EQ(expected_response, url_request_delegate_.data_received());
-
-  EXPECT_EQ(0, times_prepare_to_restart_invoked_);
-  ServiceWorkerResponseInfo* info =
-      ServiceWorkerResponseInfo::ForRequest(request_.get());
-  ASSERT_TRUE(info);
-  EXPECT_TRUE(info->was_fetched_via_service_worker());
-  EXPECT_FALSE(info->was_fallback_required());
-  EXPECT_EQ(0u, info->url_list_via_service_worker().size());
-  EXPECT_EQ(blink::kWebServiceWorkerResponseTypeDefault,
-            info->response_type_via_service_worker());
-  EXPECT_FALSE(info->service_worker_start_time().is_null());
-  EXPECT_FALSE(info->service_worker_ready_time().is_null());
-
-  request_.reset();
-  EXPECT_FALSE(HasWork());
-}
-
-TEST_F(ServiceWorkerURLRequestJobTest, StreamResponse_Flush) {
-  const GURL stream_url("blob://stream");
-  StreamContext* stream_context =
-      GetStreamContextForResourceContext(
-          browser_context_->GetResourceContext());
-  scoped_refptr<Stream> stream =
-      new Stream(stream_context->registry(), nullptr, stream_url);
-  SetUpWithHelper(new StreamResponder(stream_url));
+TEST_F(ServiceWorkerURLRequestJobTest, StreamResponse_ConsecutiveRead) {
+  blink::mojom::ServiceWorkerStreamCallbackPtr stream_callback;
+  mojo::DataPipe data_pipe;
+  SetUpWithHelper(new StreamResponder(mojo::MakeRequest(&stream_callback),
+                                      std::move(data_pipe.consumer_handle)));
 
   version_->SetStatus(ServiceWorkerVersion::ACTIVATED);
   request_ = url_request_context_.CreateRequest(
@@ -766,12 +674,16 @@
   expected_response.reserve((sizeof(kTestData) - 1) * 1024);
   for (int i = 0; i < 1024; ++i) {
     expected_response += kTestData;
-    stream->AddData(kTestData, sizeof(kTestData) - 1);
-    stream->Flush();
+    uint32_t written_bytes = sizeof(kTestData) - 1;
+    MojoResult result =
+        mojo::WriteDataRaw(data_pipe.producer_handle.get(), kTestData,
+                           &written_bytes, MOJO_WRITE_DATA_FLAG_NONE);
+    ASSERT_EQ(MOJO_RESULT_OK, result);
+    EXPECT_EQ(sizeof(kTestData) - 1, written_bytes);
     base::RunLoop().RunUntilIdle();
     EXPECT_EQ(expected_response, url_request_delegate_.data_received());
   }
-  stream->Finalize(net::OK);
+  stream_callback->OnCompleted();
   base::RunLoop().RunUntilIdle();
   EXPECT_TRUE(request_->status().is_success());
   EXPECT_EQ(200,
@@ -794,15 +706,10 @@
 }
 
 TEST_F(ServiceWorkerURLRequestJobTest, StreamResponseAndCancel) {
-  const GURL stream_url("blob://stream");
-  StreamContext* stream_context =
-      GetStreamContextForResourceContext(
-          browser_context_->GetResourceContext());
-  scoped_refptr<Stream> stream =
-      new Stream(stream_context->registry(), nullptr, stream_url);
-  ASSERT_EQ(stream.get(),
-            stream_context->registry()->GetStream(stream_url).get());
-  SetUpWithHelper(new StreamResponder(stream_url));
+  blink::mojom::ServiceWorkerStreamCallbackPtr stream_callback;
+  mojo::DataPipe data_pipe;
+  SetUpWithHelper(new StreamResponder(mojo::MakeRequest(&stream_callback),
+                                      std::move(data_pipe.consumer_handle)));
 
   version_->SetStatus(ServiceWorkerVersion::ACTIVATED);
   request_ = url_request_context_.CreateRequest(
@@ -814,23 +721,29 @@
   base::RunLoop().RunUntilIdle();
   EXPECT_TRUE(HasWork());
 
-  std::string expected_response;
-  expected_response.reserve((sizeof(kTestData) - 1) * 1024);
   for (int i = 0; i < 512; ++i) {
-    expected_response += kTestData;
-    stream->AddData(kTestData, sizeof(kTestData) - 1);
+    uint32_t written_bytes = sizeof(kTestData) - 1;
+    MojoResult result =
+        mojo::WriteDataRaw(data_pipe.producer_handle.get(), kTestData,
+                           &written_bytes, MOJO_WRITE_DATA_FLAG_NONE);
+    ASSERT_EQ(MOJO_RESULT_OK, result);
+    EXPECT_EQ(sizeof(kTestData) - 1, written_bytes);
   }
-  ASSERT_TRUE(stream_context->registry()->GetStream(stream_url).get());
+  EXPECT_TRUE(data_pipe.producer_handle.is_valid());
   request_->Cancel();
   EXPECT_FALSE(HasWork());
-  ASSERT_FALSE(stream_context->registry()->GetStream(stream_url).get());
-  for (int i = 0; i < 512; ++i) {
-    expected_response += kTestData;
-    stream->AddData(kTestData, sizeof(kTestData) - 1);
-  }
-  stream->Finalize(net::OK);
+
+  // Fail to write the data pipe because it's already canceled.
+  uint32_t written_bytes = sizeof(kTestData) - 1;
+  MojoResult result =
+      mojo::WriteDataRaw(data_pipe.producer_handle.get(), kTestData,
+                         &written_bytes, MOJO_WRITE_DATA_FLAG_NONE);
+  ASSERT_EQ(MOJO_RESULT_FAILED_PRECONDITION, result);
+
+  stream_callback->OnAborted();
 
   base::RunLoop().RunUntilIdle();
+  EXPECT_FALSE(data_pipe.consumer_handle.is_valid());
   EXPECT_FALSE(request_->status().is_success());
 
   EXPECT_EQ(0, times_prepare_to_restart_invoked_);
@@ -846,13 +759,11 @@
   EXPECT_FALSE(info->service_worker_ready_time().is_null());
 }
 
-TEST_F(ServiceWorkerURLRequestJobTest,
-       StreamResponse_DelayedRegistrationAndCancel) {
-  const GURL stream_url("blob://stream");
-  StreamContext* stream_context =
-      GetStreamContextForResourceContext(
-          browser_context_->GetResourceContext());
-  SetUpWithHelper(new StreamResponder(stream_url));
+TEST_F(ServiceWorkerURLRequestJobTest, StreamResponse_Abort) {
+  blink::mojom::ServiceWorkerStreamCallbackPtr stream_callback;
+  mojo::DataPipe data_pipe;
+  SetUpWithHelper(new StreamResponder(mojo::MakeRequest(&stream_callback),
+                                      std::move(data_pipe.consumer_handle)));
 
   version_->SetStatus(ServiceWorkerVersion::ACTIVATED);
   request_ = url_request_context_.CreateRequest(
@@ -860,25 +771,196 @@
       &url_request_delegate_);
   request_->set_method("GET");
   request_->Start();
+
+  std::string expected_response;
+  expected_response.reserve((sizeof(kTestData) - 1) * 1024);
+  for (int i = 0; i < 1024; ++i) {
+    expected_response += kTestData;
+    uint32_t written_bytes = sizeof(kTestData) - 1;
+    MojoResult result =
+        mojo::WriteDataRaw(data_pipe.producer_handle.get(), kTestData,
+                           &written_bytes, MOJO_WRITE_DATA_FLAG_NONE);
+    ASSERT_EQ(MOJO_RESULT_OK, result);
+    EXPECT_EQ(sizeof(kTestData) - 1, written_bytes);
+  }
+  stream_callback->OnAborted();
+  data_pipe.producer_handle.reset();
+
   EXPECT_FALSE(HasWork());
   base::RunLoop().RunUntilIdle();
   EXPECT_TRUE(HasWork());
-  request_->Cancel();
-  EXPECT_FALSE(HasWork());
-
-  scoped_refptr<Stream> stream =
-      new Stream(stream_context->registry(), nullptr, stream_url);
-  // The stream should not be registered to the stream registry.
-  ASSERT_FALSE(stream_context->registry()->GetStream(stream_url).get());
-  for (int i = 0; i < 1024; ++i)
-    stream->AddData(kTestData, sizeof(kTestData) - 1);
-  stream->Finalize(net::OK);
-
-  base::RunLoop().RunUntilIdle();
   EXPECT_FALSE(request_->status().is_success());
+  net::HttpResponseHeaders* headers = request_->response_headers();
+  EXPECT_EQ(200, headers->response_code());
+  EXPECT_EQ("OK", headers->GetStatusText());
+  CheckHeaders(headers);
+  EXPECT_EQ(expected_response, url_request_delegate_.data_received());
 
   EXPECT_EQ(0, times_prepare_to_restart_invoked_);
-  EXPECT_FALSE(ServiceWorkerResponseInfo::ForRequest(request_.get()));
+  ServiceWorkerResponseInfo* info =
+      ServiceWorkerResponseInfo::ForRequest(request_.get());
+  ASSERT_TRUE(info);
+  EXPECT_TRUE(info->was_fetched_via_service_worker());
+  EXPECT_FALSE(info->was_fallback_required());
+  EXPECT_EQ(0u, info->url_list_via_service_worker().size());
+  EXPECT_EQ(blink::kWebServiceWorkerResponseTypeDefault,
+            info->response_type_via_service_worker());
+  EXPECT_FALSE(info->service_worker_start_time().is_null());
+  EXPECT_FALSE(info->service_worker_ready_time().is_null());
+
+  request_.reset();
+  EXPECT_FALSE(HasWork());
+}
+
+TEST_F(ServiceWorkerURLRequestJobTest, StreamResponse_AbortBeforeData) {
+  blink::mojom::ServiceWorkerStreamCallbackPtr stream_callback;
+  mojo::DataPipe data_pipe;
+  SetUpWithHelper(new StreamResponder(mojo::MakeRequest(&stream_callback),
+                                      std::move(data_pipe.consumer_handle)));
+
+  version_->SetStatus(ServiceWorkerVersion::ACTIVATED);
+
+  request_ = url_request_context_.CreateRequest(
+      GURL("https://example.com/foo.html"), net::DEFAULT_PRIORITY,
+      &url_request_delegate_);
+  request_->set_method("GET");
+  request_->Start();
+  base::RunLoop().RunUntilIdle();
+  EXPECT_TRUE(request_->status().is_io_pending());
+
+  stream_callback->OnAborted();
+  base::RunLoop().RunUntilIdle();
+  EXPECT_TRUE(request_->status().is_io_pending());
+
+  std::string expected_response;
+  expected_response.reserve((sizeof(kTestData) - 1) * 1024);
+  for (int i = 0; i < 1024; ++i) {
+    expected_response += kTestData;
+    uint32_t written_bytes = sizeof(kTestData) - 1;
+    MojoResult result =
+        mojo::WriteDataRaw(data_pipe.producer_handle.get(), kTestData,
+                           &written_bytes, MOJO_WRITE_DATA_FLAG_NONE);
+    ASSERT_EQ(MOJO_RESULT_OK, result);
+    EXPECT_EQ(sizeof(kTestData) - 1, written_bytes);
+    base::RunLoop().RunUntilIdle();
+    EXPECT_EQ(expected_response, url_request_delegate_.data_received());
+  }
+
+  data_pipe.producer_handle.reset();
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_EQ(net::ERR_CONNECTION_RESET, request_->status().ToNetError());
+  EXPECT_EQ(net::ERR_CONNECTION_RESET, url_request_delegate_.request_status());
+  EXPECT_EQ(200,
+            request_->response_headers()->response_code());
+  EXPECT_EQ("OK",
+            request_->response_headers()->GetStatusText());
+  EXPECT_EQ(expected_response, url_request_delegate_.data_received());
+
+  EXPECT_EQ(0, times_prepare_to_restart_invoked_);
+  ServiceWorkerResponseInfo* info =
+      ServiceWorkerResponseInfo::ForRequest(request_.get());
+  ASSERT_TRUE(info);
+  EXPECT_TRUE(info->was_fetched_via_service_worker());
+  EXPECT_FALSE(info->was_fallback_required());
+  EXPECT_EQ(0u, info->url_list_via_service_worker().size());
+  EXPECT_EQ(blink::kWebServiceWorkerResponseTypeDefault,
+            info->response_type_via_service_worker());
+  EXPECT_FALSE(info->service_worker_start_time().is_null());
+  EXPECT_FALSE(info->service_worker_ready_time().is_null());
+}
+
+TEST_F(ServiceWorkerURLRequestJobTest, StreamResponse_AbortAfterData) {
+  blink::mojom::ServiceWorkerStreamCallbackPtr stream_callback;
+  mojo::DataPipe data_pipe;
+  SetUpWithHelper(new StreamResponder(mojo::MakeRequest(&stream_callback),
+                                      std::move(data_pipe.consumer_handle)));
+
+  version_->SetStatus(ServiceWorkerVersion::ACTIVATED);
+
+  request_ = url_request_context_.CreateRequest(
+      GURL("https://example.com/foo.html"), net::DEFAULT_PRIORITY,
+      &url_request_delegate_);
+  request_->set_method("GET");
+  request_->Start();
+  base::RunLoop().RunUntilIdle();
+  EXPECT_TRUE(request_->status().is_io_pending());
+
+  data_pipe.producer_handle.reset();
+  base::RunLoop().RunUntilIdle();
+  EXPECT_TRUE(request_->status().is_io_pending());
+
+  stream_callback->OnAborted();
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_FALSE(request_->status().is_io_pending());
+  EXPECT_FALSE(request_->status().is_success());
+  EXPECT_EQ(net::ERR_CONNECTION_RESET, request_->status().ToNetError());
+  EXPECT_EQ(net::ERR_CONNECTION_RESET, url_request_delegate_.request_status());
+  EXPECT_EQ(200, request_->response_headers()->response_code());
+  EXPECT_EQ("OK", request_->response_headers()->GetStatusText());
+  EXPECT_EQ("", url_request_delegate_.data_received());
+
+  EXPECT_EQ(0, times_prepare_to_restart_invoked_);
+  ServiceWorkerResponseInfo* info =
+      ServiceWorkerResponseInfo::ForRequest(request_.get());
+  ASSERT_TRUE(info);
+  EXPECT_TRUE(info->was_fetched_via_service_worker());
+  EXPECT_FALSE(info->was_fallback_required());
+  EXPECT_EQ(0u, info->url_list_via_service_worker().size());
+  EXPECT_EQ(blink::kWebServiceWorkerResponseTypeDefault,
+            info->response_type_via_service_worker());
+  EXPECT_FALSE(info->service_worker_start_time().is_null());
+  EXPECT_FALSE(info->service_worker_ready_time().is_null());
+}
+
+TEST_F(ServiceWorkerURLRequestJobTest, StreamResponse_ConsecutiveReadAndAbort) {
+  blink::mojom::ServiceWorkerStreamCallbackPtr stream_callback;
+  mojo::DataPipe data_pipe;
+  SetUpWithHelper(new StreamResponder(mojo::MakeRequest(&stream_callback),
+                                      std::move(data_pipe.consumer_handle)));
+
+  version_->SetStatus(ServiceWorkerVersion::ACTIVATED);
+  request_ = url_request_context_.CreateRequest(
+      GURL("https://example.com/foo.html"), net::DEFAULT_PRIORITY,
+      &url_request_delegate_);
+  request_->set_method("GET");
+  request_->Start();
+  std::string expected_response;
+  expected_response.reserve((sizeof(kTestData) - 1) * 1024);
+  for (int i = 0; i < 512; ++i) {
+    expected_response += kTestData;
+    uint32_t written_bytes = sizeof(kTestData) - 1;
+    MojoResult result =
+        mojo::WriteDataRaw(data_pipe.producer_handle.get(), kTestData,
+                           &written_bytes, MOJO_WRITE_DATA_FLAG_NONE);
+    ASSERT_EQ(MOJO_RESULT_OK, result);
+    EXPECT_EQ(sizeof(kTestData) - 1, written_bytes);
+
+    base::RunLoop().RunUntilIdle();
+
+    EXPECT_EQ(expected_response, url_request_delegate_.data_received());
+  }
+  stream_callback->OnAborted();
+
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_TRUE(request_->status().is_success());
+  EXPECT_EQ(200, request_->response_headers()->response_code());
+  EXPECT_EQ("OK", request_->response_headers()->GetStatusText());
+  EXPECT_EQ(expected_response, url_request_delegate_.data_received());
+
+  EXPECT_EQ(0, times_prepare_to_restart_invoked_);
+  ServiceWorkerResponseInfo* info =
+      ServiceWorkerResponseInfo::ForRequest(request_.get());
+  ASSERT_TRUE(info);
+  EXPECT_TRUE(info->was_fetched_via_service_worker());
+  EXPECT_FALSE(info->was_fallback_required());
+  EXPECT_EQ(0u, info->url_list_via_service_worker().size());
+  EXPECT_EQ(blink::kWebServiceWorkerResponseTypeDefault,
+            info->response_type_via_service_worker());
+  EXPECT_FALSE(info->service_worker_start_time().is_null());
+  EXPECT_FALSE(info->service_worker_ready_time().is_null());
 }
 
 // Helper to simulate failing to dispatch a fetch event to a worker.
@@ -888,13 +970,15 @@
   ~FailFetchHelper() override {}
 
  protected:
-  void OnFetchEvent(int embedded_worker_id,
-                    int fetch_event_id,
-                    const ServiceWorkerFetchRequest& request,
-                    mojom::FetchEventPreloadHandlePtr preload_handle,
-                    const FetchCallback& callback) override {
+  void OnFetchEvent(
+      int embedded_worker_id,
+      int /* fetch_event_id */,
+      const ServiceWorkerFetchRequest& /* request */,
+      mojom::FetchEventPreloadHandlePtr /* preload_handle */,
+      mojom::ServiceWorkerFetchResponseCallbackPtr /* response_callback */,
+      const FetchCallback& finish_callback) override {
     SimulateWorkerStopped(embedded_worker_id);
-    callback.Run(SERVICE_WORKER_ERROR_ABORT, base::Time::Now());
+    finish_callback.Run(SERVICE_WORKER_ERROR_ABORT, base::Time::Now());
   }
 
  private:
@@ -969,33 +1053,33 @@
   ~EarlyResponseHelper() override {}
 
   void FinishWaitUntil() {
-    callback_.Run(SERVICE_WORKER_OK, base::Time::Now());
+    finish_callback_.Run(SERVICE_WORKER_OK, base::Time::Now());
   }
 
  protected:
-  void OnFetchEvent(int embedded_worker_id,
-                    int fetch_event_id,
-                    const ServiceWorkerFetchRequest& request,
-                    mojom::FetchEventPreloadHandlePtr preload_handle,
-                    const FetchCallback& callback) override {
-    callback_ = callback;
-    SimulateSend(new ServiceWorkerHostMsg_FetchEventResponse(
-        embedded_worker_id, fetch_event_id,
-        SERVICE_WORKER_FETCH_EVENT_RESULT_RESPONSE,
+  void OnFetchEvent(
+      int /* embedded_worker_id */,
+      int /* fetch_event_id */,
+      const ServiceWorkerFetchRequest& /* request */,
+      mojom::FetchEventPreloadHandlePtr /* preload_handle */,
+      mojom::ServiceWorkerFetchResponseCallbackPtr response_callback,
+      const FetchCallback& finish_callback) override {
+    finish_callback_ = finish_callback;
+    response_callback->OnResponse(
         ServiceWorkerResponse(
             base::MakeUnique<std::vector<GURL>>(), 200, "OK",
             blink::kWebServiceWorkerResponseTypeDefault,
             base::MakeUnique<ServiceWorkerHeaderMap>(), std::string(), 0,
-            GURL(), blink::kWebServiceWorkerResponseErrorUnknown, base::Time(),
+            blink::kWebServiceWorkerResponseErrorUnknown, base::Time(),
             false /* response_is_in_cache_storage */,
             std::string() /* response_cache_storage_cache_name */,
             base::MakeUnique<
                 ServiceWorkerHeaderList>() /* cors_exposed_header_names */),
-        base::Time::Now()));
+        base::Time::Now());
   }
 
  private:
-  FetchCallback callback_;
+  FetchCallback finish_callback_;
   DISALLOW_COPY_AND_ASSIGN(EarlyResponseHelper);
 };
 
@@ -1035,37 +1119,39 @@
   ~DelayedResponseHelper() override {}
 
   void Respond() {
-    SimulateSend(new ServiceWorkerHostMsg_FetchEventResponse(
-        embedded_worker_id_, fetch_event_id_,
-        SERVICE_WORKER_FETCH_EVENT_RESULT_RESPONSE,
+    response_callback_->OnResponse(
         ServiceWorkerResponse(
             base::MakeUnique<std::vector<GURL>>(), 200, "OK",
             blink::kWebServiceWorkerResponseTypeDefault,
             base::MakeUnique<ServiceWorkerHeaderMap>(), std::string(), 0,
-            GURL(), blink::kWebServiceWorkerResponseErrorUnknown, base::Time(),
+            blink::kWebServiceWorkerResponseErrorUnknown, base::Time(),
             false /* response_is_in_cache_storage */,
             std::string() /* response_cache_storage_cache_name */,
             base::MakeUnique<
                 ServiceWorkerHeaderList>() /* cors_exposed_header_names */),
-        base::Time::Now()));
-    callback_.Run(SERVICE_WORKER_OK, base::Time::Now());
+        base::Time::Now());
+    finish_callback_.Run(SERVICE_WORKER_OK, base::Time::Now());
   }
 
  protected:
-  void OnFetchEvent(int embedded_worker_id,
-                    int fetch_event_id,
-                    const ServiceWorkerFetchRequest& request,
-                    mojom::FetchEventPreloadHandlePtr preload_handle,
-                    const FetchCallback& callback) override {
+  void OnFetchEvent(
+      int embedded_worker_id,
+      int fetch_event_id,
+      const ServiceWorkerFetchRequest& /* request */,
+      mojom::FetchEventPreloadHandlePtr /* preload_handle */,
+      mojom::ServiceWorkerFetchResponseCallbackPtr response_callback,
+      const FetchCallback& finish_callback) override {
     embedded_worker_id_ = embedded_worker_id;
     fetch_event_id_ = fetch_event_id;
-    callback_ = callback;
+    response_callback_ = std::move(response_callback);
+    finish_callback_ = finish_callback;
   }
 
  private:
   int embedded_worker_id_ = 0;
   int fetch_event_id_ = 0;
-  FetchCallback callback_;
+  mojom::ServiceWorkerFetchResponseCallbackPtr response_callback_;
+  FetchCallback finish_callback_;
   DISALLOW_COPY_AND_ASSIGN(DelayedResponseHelper);
 };
 
diff --git a/content/browser/site_per_process_browsertest.cc b/content/browser/site_per_process_browsertest.cc
index 7e7f4d21..372a996 100644
--- a/content/browser/site_per_process_browsertest.cc
+++ b/content/browser/site_per_process_browsertest.cc
@@ -15,7 +15,6 @@
 #include "base/bind.h"
 #include "base/callback.h"
 #include "base/command_line.h"
-#include "base/feature_list.h"
 #include "base/location.h"
 #include "base/memory/ptr_util.h"
 #include "base/path_service.h"
@@ -88,7 +87,7 @@
 #include "ui/events/event_utils.h"
 #include "ui/gfx/geometry/point.h"
 #include "ui/latency/latency_info.h"
-#include "ui/native_theme/native_theme_features.h"
+#include "ui/native_theme/native_theme_switches.h"
 
 #if defined(USE_AURA)
 #include "content/browser/renderer_host/render_widget_host_view_aura.h"
@@ -620,15 +619,14 @@
 void SitePerProcessBrowserTest::SetUpCommandLine(
     base::CommandLine* command_line) {
   IsolateAllSitesForTesting(command_line);
-}
-
-void SitePerProcessBrowserTest::SetUpOnMainThread() {
 #if !defined(OS_ANDROID)
   // TODO(bokan): Needed for scrollability check in
   // FrameOwnerPropertiesPropagationScrolling. crbug.com/662196.
-  feature_list_.InitAndDisableFeature(features::kOverlayScrollbar);
+  command_line->AppendSwitch(switches::kDisableOverlayScrollbar);
 #endif
+};
 
+void SitePerProcessBrowserTest::SetUpOnMainThread() {
   host_resolver()->AddRule("*", "127.0.0.1");
   SetupCrossSiteRedirector(embedded_test_server());
   ASSERT_TRUE(embedded_test_server()->Start());
diff --git a/content/browser/site_per_process_browsertest.h b/content/browser/site_per_process_browsertest.h
index b37d7341..fe8b84b0 100644
--- a/content/browser/site_per_process_browsertest.h
+++ b/content/browser/site_per_process_browsertest.h
@@ -8,7 +8,6 @@
 #include <string>
 
 #include "base/macros.h"
-#include "base/test/scoped_feature_list.h"
 #include "content/browser/web_contents/web_contents_impl.h"
 #include "content/public/test/content_browser_test.h"
 #include "content/shell/browser/shell.h"
@@ -35,7 +34,6 @@
 
  private:
   FrameTreeVisualizer visualizer_;
-  base::test::ScopedFeatureList feature_list_;
 
   DISALLOW_COPY_AND_ASSIGN(SitePerProcessBrowserTest);
 };
diff --git a/content/browser/streams/stream_url_request_job_unittest.cc b/content/browser/streams/stream_url_request_job_unittest.cc
index c0e1bd13..2301b0d 100644
--- a/content/browser/streams/stream_url_request_job_unittest.cc
+++ b/content/browser/streams/stream_url_request_job_unittest.cc
@@ -14,6 +14,7 @@
 #include "net/base/request_priority.h"
 #include "net/http/http_byte_range.h"
 #include "net/http/http_response_headers.h"
+#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
 #include "net/url_request/url_request.h"
 #include "net/url_request/url_request_context.h"
 #include "net/url_request/url_request_job_factory_impl.h"
@@ -80,7 +81,7 @@
                    const std::string& expected_response) {
     net::TestDelegate delegate;
     request_ = url_request_context_.CreateRequest(
-        url, net::DEFAULT_PRIORITY, &delegate);
+        url, net::DEFAULT_PRIORITY, &delegate, TRAFFIC_ANNOTATION_FOR_TESTS);
     request_->set_method(method);
     if (!extra_headers.IsEmpty())
       request_->SetExtraRequestHeaders(extra_headers);
@@ -141,7 +142,8 @@
 TEST_F(StreamURLRequestJobTest, TestGetNonExistentStreamRequest) {
   net::TestDelegate delegate;
   request_ = url_request_context_.CreateRequest(
-      kStreamURL, net::DEFAULT_PRIORITY, &delegate);
+      kStreamURL, net::DEFAULT_PRIORITY, &delegate,
+      TRAFFIC_ANNOTATION_FOR_TESTS);
   request_->set_method("GET");
   request_->Start();
 
diff --git a/content/browser/webui/OWNERS b/content/browser/webui/OWNERS
index e49cb53..7e3290f 100644
--- a/content/browser/webui/OWNERS
+++ b/content/browser/webui/OWNERS
@@ -1,7 +1,5 @@
 dbeam@chromium.org
+groby@chromium.org
 tommycli@chromium.org
 
-# Emeritus
-estade@chromium.org
-
 # COMPONENT: UI>Browser>WebUI
diff --git a/content/browser/webui/url_data_manager_backend_unittest.cc b/content/browser/webui/url_data_manager_backend_unittest.cc
index eb35997..660b33fc 100644
--- a/content/browser/webui/url_data_manager_backend_unittest.cc
+++ b/content/browser/webui/url_data_manager_backend_unittest.cc
@@ -12,6 +12,7 @@
 #include "content/public/test/test_browser_thread_bundle.h"
 #include "net/http/http_response_headers.h"
 #include "net/http/http_response_info.h"
+#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
 #include "net/url_request/url_request_context.h"
 #include "net/url_request/url_request_job.h"
 #include "net/url_request/url_request_job_factory_impl.h"
@@ -60,7 +61,7 @@
         url_request_context_.CreateRequest(
             GURL(
                 "chrome://resources/polymer/v1_0/polymer/polymer-extracted.js"),
-            net::HIGHEST, delegate);
+            net::HIGHEST, delegate, TRAFFIC_ANNOTATION_FOR_TESTS);
     request->SetExtraRequestHeaderByName("Origin", origin, true);
     return request;
   }
@@ -119,7 +120,8 @@
 TEST_F(UrlDataManagerBackendTest, ChromeNetworkErrorPageRequest) {
   std::unique_ptr<net::URLRequest> error_request =
       url_request_context_.CreateRequest(GURL("chrome://network-error/-105"),
-                                         net::HIGHEST, &delegate_);
+                                         net::HIGHEST, &delegate_,
+                                         TRAFFIC_ANNOTATION_FOR_TESTS);
   error_request->Start();
   base::RunLoop().Run();
   EXPECT_EQ(net::URLRequestStatus::FAILED, error_request->status().status());
@@ -130,7 +132,8 @@
 TEST_F(UrlDataManagerBackendTest, ChromeNetworkErrorPageRequestFailed) {
   std::unique_ptr<net::URLRequest> error_request =
       url_request_context_.CreateRequest(
-          GURL("chrome://network-error/-123456789"), net::HIGHEST, &delegate_);
+          GURL("chrome://network-error/-123456789"), net::HIGHEST, &delegate_,
+          TRAFFIC_ANNOTATION_FOR_TESTS);
   error_request->Start();
   base::RunLoop().Run();
   EXPECT_EQ(net::URLRequestStatus::FAILED, error_request->status().status());
diff --git a/content/child/runtime_features.cc b/content/child/runtime_features.cc
index 6bb73b4..d0a3c95 100644
--- a/content/child/runtime_features.cc
+++ b/content/child/runtime_features.cc
@@ -18,7 +18,7 @@
 #include "services/device/public/cpp/device_features.h"
 #include "third_party/WebKit/public/web/WebRuntimeFeatures.h"
 #include "ui/gl/gl_switches.h"
-#include "ui/native_theme/native_theme_features.h"
+#include "ui/native_theme/native_theme_switches.h"
 
 using blink::WebRuntimeFeatures;
 
diff --git a/content/common/service_worker/service_worker_event_dispatcher.mojom b/content/common/service_worker/service_worker_event_dispatcher.mojom
index 42d1218c..446cda0 100644
--- a/content/common/service_worker/service_worker_event_dispatcher.mojom
+++ b/content/common/service_worker/service_worker_event_dispatcher.mojom
@@ -11,6 +11,7 @@
 import "third_party/WebKit/public/platform/modules/background_sync/background_sync.mojom";
 import "third_party/WebKit/public/platform/modules/fetch/fetch_api_request.mojom";
 import "third_party/WebKit/public/platform/modules/serviceworker/service_worker_event_status.mojom";
+import "third_party/WebKit/public/platform/modules/serviceworker/service_worker_stream_handle.mojom";
 import "url/mojo/origin.mojom";
 
 [Native]
@@ -50,6 +51,26 @@
   ExtendableMessageEventSource source;
 };
 
+// Browser-side interface which is passed through DispatchFetchEvent. The
+// renderer uses this interface to respond to a fetch event. This interface is
+// only used for responses where the body is a stream or doesn't exist. When the
+// body is a blob, the legacy IPC (ServiceWorkerHostMsg_FetchEventResponse) is
+// used.
+// TODO(shimazu): Use this interface for all responses after the blob system is
+// mojoified: https://crbug.com/611935.
+interface ServiceWorkerFetchResponseCallback {
+  // Responds to the request with |response|. The body is empty.
+  OnResponse(ServiceWorkerResponse response,
+             mojo.common.mojom.Time dispatch_event_time);
+  // Responds to the request with |response|. The body is returned as a stream.
+  OnResponseStream(ServiceWorkerResponse response,
+                   blink.mojom.ServiceWorkerStreamHandle body_as_stream,
+                   mojo.common.mojom.Time dispatch_event_time);
+  // Provides no response to the request. The browser will fall back to the
+  // network.
+  OnFallback(mojo.common.mojom.Time dispatch_event_time);
+};
+
 // Renderer-side interface bound to ServiceWorkerContextClient for dispatching
 // events.
 // Those events expecting such response
@@ -74,10 +95,16 @@
                                  array<BackgroundFetchSettledFetch> fetches)
       => (blink.mojom.ServiceWorkerEventStatus status,
           mojo.common.mojom.Time dispatch_event_time);
-  // |fetch_event_id| is used internally when sending the response back to the
-  // browser process.
+  // The callback is called once the event finishes, which means the event
+  // handler ran and all outstanding respondWith() and waitUntil() promises have
+  // settled. |response_callback| is called once the promise to respondWith()
+  // settles, or when the event handler ran without calling respondWith().
+  // |fetch_event_id| is used internally when sending the response with a blob
+  // body back to the browser process. In that case, |response_callback| won't
+  // be called.
   DispatchFetchEvent(int32 fetch_event_id, blink.mojom.FetchAPIRequest request,
-                     FetchEventPreloadHandle? preload_handle)
+                     FetchEventPreloadHandle? preload_handle,
+                     ServiceWorkerFetchResponseCallback response_callback)
       => (blink.mojom.ServiceWorkerEventStatus status,
           mojo.common.mojom.Time dispatch_event_time);
   DispatchNotificationClickEvent(string notification_id,
diff --git a/content/common/service_worker/service_worker_event_dispatcher.typemap b/content/common/service_worker/service_worker_event_dispatcher.typemap
index 3cc80860..e41dd9df 100644
--- a/content/common/service_worker/service_worker_event_dispatcher.typemap
+++ b/content/common/service_worker/service_worker_event_dispatcher.typemap
@@ -24,4 +24,5 @@
   "content.mojom.PlatformNotificationData=::content::PlatformNotificationData",
   "content.mojom.PushEventPayload=::content::PushEventPayload",
   "content.mojom.ServiceWorkerFetchRequest=::content::ServiceWorkerFetchRequest",
+  "content.mojom.ServiceWorkerResponse=::content::ServiceWorkerResponse",
 ]
diff --git a/content/common/service_worker/service_worker_messages.h b/content/common/service_worker/service_worker_messages.h
index 0f0356a..92a67773 100644
--- a/content/common/service_worker/service_worker_messages.h
+++ b/content/common/service_worker/service_worker_messages.h
@@ -92,7 +92,6 @@
   IPC_STRUCT_TRAITS_MEMBER(headers)
   IPC_STRUCT_TRAITS_MEMBER(blob_uuid)
   IPC_STRUCT_TRAITS_MEMBER(blob_size)
-  IPC_STRUCT_TRAITS_MEMBER(stream_url)
   IPC_STRUCT_TRAITS_MEMBER(error)
   IPC_STRUCT_TRAITS_MEMBER(response_time)
   IPC_STRUCT_TRAITS_MEMBER(is_in_cache_storage)
@@ -245,9 +244,11 @@
                     bool /* has_fetch_event_handler */,
                     base::Time /* dispatch_event_time */)
 
-IPC_MESSAGE_ROUTED4(ServiceWorkerHostMsg_FetchEventResponse,
+// Returns the response as the result of fetch event. This is used only for blob
+// to keep the IPC ordering. Mojo IPC is used when the response body is a stream
+// or is empty, and for the fallback-to-network response.
+IPC_MESSAGE_ROUTED3(ServiceWorkerHostMsg_FetchEventResponse,
                     int /* fetch_event_id */,
-                    content::ServiceWorkerFetchEventResult,
                     content::ServiceWorkerResponse,
                     base::Time /* dispatch_event_time */)
 
diff --git a/content/common/service_worker/service_worker_types.cc b/content/common/service_worker/service_worker_types.cc
index 9e8d63f..4ebe7bd 100644
--- a/content/common/service_worker/service_worker_types.cc
+++ b/content/common/service_worker/service_worker_types.cc
@@ -83,7 +83,6 @@
     std::unique_ptr<ServiceWorkerHeaderMap> headers,
     const std::string& blob_uuid,
     uint64_t blob_size,
-    const GURL& stream_url,
     blink::WebServiceWorkerResponseError error,
     base::Time response_time,
     bool is_in_cache_storage,
@@ -94,7 +93,6 @@
       response_type(response_type),
       blob_uuid(blob_uuid),
       blob_size(blob_size),
-      stream_url(stream_url),
       error(error),
       response_time(response_time),
       is_in_cache_storage(is_in_cache_storage),
@@ -114,7 +112,6 @@
   for (const auto& url : url_list)
     size += url.spec().size();
   size += blob_uuid.size();
-  size += stream_url.spec().size();
   size += cache_storage_cache_name.size();
   for (const auto& key_and_value : headers) {
     size += key_and_value.first.size();
diff --git a/content/common/service_worker/service_worker_types.h b/content/common/service_worker/service_worker_types.h
index a2ace94..ea22f5c3 100644
--- a/content/common/service_worker/service_worker_types.h
+++ b/content/common/service_worker/service_worker_types.h
@@ -179,7 +179,6 @@
       std::unique_ptr<ServiceWorkerHeaderMap> headers,
       const std::string& blob_uuid,
       uint64_t blob_size,
-      const GURL& stream_url,
       blink::WebServiceWorkerResponseError error,
       base::Time response_time,
       bool is_in_cache_storage,
@@ -195,9 +194,11 @@
   std::string status_text;
   blink::WebServiceWorkerResponseType response_type;
   ServiceWorkerHeaderMap headers;
+  // |blob_uuid| and |blob_size| are set when the body is a blob. For other
+  // types of responses, the body is provided separately in Mojo IPC via
+  // ServiceWorkerFetchResponseCallback.
   std::string blob_uuid;
   uint64_t blob_size;
-  GURL stream_url;
   blink::WebServiceWorkerResponseError error;
   base::Time response_time;
   bool is_in_cache_storage = false;
diff --git a/content/ppapi_plugin/ppapi_blink_platform_impl.cc b/content/ppapi_plugin/ppapi_blink_platform_impl.cc
index bf0088e..b13765a4 100644
--- a/content/ppapi_plugin/ppapi_blink_platform_impl.cc
+++ b/content/ppapi_plugin/ppapi_blink_platform_impl.cc
@@ -205,7 +205,7 @@
   return NULL;
 }
 
-blink::WebURLLoader* PpapiBlinkPlatformImpl::CreateURLLoader() {
+std::unique_ptr<blink::WebURLLoader> PpapiBlinkPlatformImpl::CreateURLLoader() {
   NOTREACHED();
   return NULL;
 }
diff --git a/content/ppapi_plugin/ppapi_blink_platform_impl.h b/content/ppapi_plugin/ppapi_blink_platform_impl.h
index afcf809..097b5ebd0 100644
--- a/content/ppapi_plugin/ppapi_blink_platform_impl.h
+++ b/content/ppapi_plugin/ppapi_blink_platform_impl.h
@@ -43,7 +43,7 @@
       const blink::WebURL& first_party_for_cookies);
   blink::WebString DefaultLocale() override;
   blink::WebThemeEngine* ThemeEngine() override;
-  blink::WebURLLoader* CreateURLLoader() override;
+  std::unique_ptr<blink::WebURLLoader> CreateURLLoader() override;
   void GetPluginList(bool refresh,
                      const blink::WebSecurityOrigin& mainFrameOrigin,
                      blink::WebPluginListBuilder*) override;
diff --git a/content/renderer/cache_storage/cache_storage_dispatcher.cc b/content/renderer/cache_storage/cache_storage_dispatcher.cc
index 6225a78..51b09d23 100644
--- a/content/renderer/cache_storage/cache_storage_dispatcher.cc
+++ b/content/renderer/cache_storage/cache_storage_dispatcher.cc
@@ -104,9 +104,6 @@
 
 CacheStorageBatchOperation BatchOperationFromWebBatchOperation(
     const blink::WebServiceWorkerCache::BatchOperation& web_operation) {
-  // We don't support streaming for cache.
-  DCHECK(web_operation.response.StreamURL().IsEmpty());
-
   CacheStorageBatchOperation operation;
   operation.operation_type =
       CacheOperationTypeFromWebCacheOperationType(web_operation.operation_type);
diff --git a/content/renderer/fetchers/resource_fetcher_impl.cc b/content/renderer/fetchers/resource_fetcher_impl.cc
index 9abded9..25c2676 100644
--- a/content/renderer/fetchers/resource_fetcher_impl.cc
+++ b/content/renderer/fetchers/resource_fetcher_impl.cc
@@ -184,7 +184,7 @@
 
   client_.reset(new ClientImpl(this, callback));
 
-  loader_.reset(blink::Platform::Current()->CreateURLLoader());
+  loader_ = blink::Platform::Current()->CreateURLLoader();
   loader_->LoadAsynchronously(request_, client_.get());
 
   // No need to hold on to the request; reset it now.
diff --git a/content/renderer/gpu/render_widget_compositor.cc b/content/renderer/gpu/render_widget_compositor.cc
index 6b637c3..9ada1963 100644
--- a/content/renderer/gpu/render_widget_compositor.cc
+++ b/content/renderer/gpu/render_widget_compositor.cc
@@ -67,7 +67,7 @@
 #include "third_party/WebKit/public/web/WebRuntimeFeatures.h"
 #include "third_party/WebKit/public/web/WebSelection.h"
 #include "ui/gl/gl_switches.h"
-#include "ui/native_theme/native_theme_features.h"
+#include "ui/native_theme/native_theme_switches.h"
 #include "ui/native_theme/overlay_scrollbar_constants_aura.h"
 
 namespace base {
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc
index 3e083e5..3e6848ba 100644
--- a/content/renderer/render_frame_impl.cc
+++ b/content/renderer/render_frame_impl.cc
@@ -3297,11 +3297,8 @@
 }
 
 void RenderFrameImpl::DidMatchCSS(
-    blink::WebLocalFrame* frame,
     const blink::WebVector<blink::WebString>& newly_matching_selectors,
     const blink::WebVector<blink::WebString>& stopped_matching_selectors) {
-  DCHECK_EQ(frame_, frame);
-
   for (auto& observer : observers_)
     observer.DidMatchCSS(newly_matching_selectors, stopped_matching_selectors);
 }
@@ -3579,35 +3576,33 @@
 }
 
 void RenderFrameImpl::DidFailProvisionalLoad(
-    blink::WebLocalFrame* frame,
     const blink::WebURLError& error,
     blink::WebHistoryCommitType commit_type) {
   TRACE_EVENT1("navigation,benchmark,rail",
                "RenderFrameImpl::didFailProvisionalLoad", "id", routing_id_);
-  DCHECK_EQ(frame_, frame);
   // Note: It is important this notification occur before DidStopLoading so the
   //       SSL manager can react to the provisional load failure before being
   //       notified the load stopped.
   //
   for (auto& observer : render_view_->observers())
-    observer.DidFailProvisionalLoad(frame, error);
+    observer.DidFailProvisionalLoad(frame_, error);
   for (auto& observer : observers_)
     observer.DidFailProvisionalLoad(error);
 
-  WebDataSource* ds = frame->ProvisionalDataSource();
+  WebDataSource* ds = frame_->ProvisionalDataSource();
   if (!ds)
     return;
 
   const WebURLRequest& failed_request = ds->GetRequest();
 
   // Notify the browser that we failed a provisional load with an error.
-  SendFailedProvisionalLoad(failed_request, error, frame);
+  SendFailedProvisionalLoad(failed_request, error, frame_);
 
   if (!ShouldDisplayErrorPageForFailedLoad(error.reason, error.unreachable_url))
     return;
 
   // Make sure we never show errors in view source mode.
-  frame->EnableViewSourceMode(false);
+  frame_->EnableViewSourceMode(false);
 
   DocumentState* document_state = DocumentState::FromDataSource(ds);
   NavigationStateImpl* navigation_state =
@@ -5312,9 +5307,8 @@
   // notification.
   bool had_provisional_data_source = frame_->ProvisionalDataSource();
   if (request_params.nav_entry_id == 0) {
-    DidFailProvisionalLoad(
-        frame_, error,
-        replace ? blink::kWebHistoryInertCommit : blink::kWebStandardCommit);
+    DidFailProvisionalLoad(error, replace ? blink::kWebHistoryInertCommit
+                                          : blink::kWebStandardCommit);
   }
 
   // If we didn't call didFailProvisionalLoad or there wasn't a
diff --git a/content/renderer/render_frame_impl.h b/content/renderer/render_frame_impl.h
index f6c9cfe..bcb48d1 100644
--- a/content/renderer/render_frame_impl.h
+++ b/content/renderer/render_frame_impl.h
@@ -537,7 +537,6 @@
       blink::WebFrame* child_frame,
       const blink::WebFrameOwnerProperties& frame_owner_properties) override;
   void DidMatchCSS(
-      blink::WebLocalFrame* frame,
       const blink::WebVector<blink::WebString>& newly_matching_selectors,
       const blink::WebVector<blink::WebString>& stopped_matching_selectors)
       override;
@@ -563,8 +562,7 @@
   void DidStartProvisionalLoad(blink::WebDataSource* data_source,
                                blink::WebURLRequest& request) override;
   void DidReceiveServerRedirectForProvisionalLoad() override;
-  void DidFailProvisionalLoad(blink::WebLocalFrame* frame,
-                              const blink::WebURLError& error,
+  void DidFailProvisionalLoad(const blink::WebURLError& error,
                               blink::WebHistoryCommitType commit_type) override;
   void DidCommitProvisionalLoad(
       blink::WebLocalFrame* frame,
diff --git a/content/renderer/render_view_browsertest.cc b/content/renderer/render_view_browsertest.cc
index 8b2ded4..786f109 100644
--- a/content/renderer/render_view_browsertest.cc
+++ b/content/renderer/render_view_browsertest.cc
@@ -86,7 +86,7 @@
 #include "ui/events/keycodes/keyboard_codes.h"
 #include "ui/gfx/codec/jpeg_codec.h"
 #include "ui/gfx/range/range.h"
-#include "ui/native_theme/native_theme_features.h"
+#include "ui/native_theme/native_theme_switches.h"
 
 #if defined(OS_WIN)
 #include "base/win/windows_version.h"
@@ -1357,7 +1357,7 @@
 
   // An error occurred.
   view()->GetMainRenderFrame()->DidFailProvisionalLoad(
-      web_frame, error, blink::kWebStandardCommit);
+      error, blink::kWebStandardCommit);
   // Frame should exit view-source mode.
   EXPECT_FALSE(web_frame->IsViewSourceModeEnabled());
 }
@@ -1380,7 +1380,7 @@
 
   // A cancellation occurred.
   view()->GetMainRenderFrame()->DidFailProvisionalLoad(
-      web_frame, error, blink::kWebStandardCommit);
+      error, blink::kWebStandardCommit);
   // Frame should stay in view-source mode.
   EXPECT_TRUE(web_frame->IsViewSourceModeEnabled());
 }
@@ -1815,7 +1815,6 @@
   error.domain = WebString::FromUTF8(net::kErrorDomain);
   error.reason = net::ERR_FILE_NOT_FOUND;
   error.unreachable_url = GURL("http://example.com/suppress");
-  WebLocalFrame* web_frame = GetMainFrame();
 
   // Start a load that will reach provisional state synchronously,
   // but won't complete synchronously.
@@ -1827,8 +1826,7 @@
                        RequestNavigationParams());
 
   // An error occurred.
-  main_frame->DidFailProvisionalLoad(web_frame, error,
-                                     blink::kWebStandardCommit);
+  main_frame->DidFailProvisionalLoad(error, blink::kWebStandardCommit);
   const int kMaxOutputCharacters = 22;
   EXPECT_EQ("", WebFrameContentDumper::DumpWebViewAsText(view()->GetWebView(),
                                                          kMaxOutputCharacters)
@@ -1847,7 +1845,6 @@
   error.domain = WebString::FromUTF8(net::kErrorDomain);
   error.reason = net::ERR_FILE_NOT_FOUND;
   error.unreachable_url = GURL("http://example.com/dont-suppress");
-  WebLocalFrame* web_frame = GetMainFrame();
 
   // Start a load that will reach provisional state synchronously,
   // but won't complete synchronously.
@@ -1859,8 +1856,7 @@
                        RequestNavigationParams());
 
   // An error occurred.
-  main_frame->DidFailProvisionalLoad(web_frame, error,
-                                     blink::kWebStandardCommit);
+  main_frame->DidFailProvisionalLoad(error, blink::kWebStandardCommit);
 
   // The error page itself is loaded asynchronously.
   FrameLoadWaiter(main_frame).Wait();
diff --git a/content/renderer/renderer_blink_platform_impl.cc b/content/renderer/renderer_blink_platform_impl.cc
index 91c24cf..4f920ce 100644
--- a/content/renderer/renderer_blink_platform_impl.cc
+++ b/content/renderer/renderer_blink_platform_impl.cc
@@ -302,7 +302,8 @@
 
 //------------------------------------------------------------------------------
 
-blink::WebURLLoader* RendererBlinkPlatformImpl::CreateURLLoader() {
+std::unique_ptr<blink::WebURLLoader>
+RendererBlinkPlatformImpl::CreateURLLoader() {
   ChildThreadImpl* child_thread = ChildThreadImpl::current();
 
   mojom::URLLoaderFactory* factory =
@@ -325,7 +326,7 @@
 
   // There may be no child thread in RenderViewTests.  These tests can still use
   // data URLs to bypass the ResourceDispatcher.
-  return new content::WebURLLoaderImpl(
+  return base::MakeUnique<WebURLLoaderImpl>(
       child_thread ? child_thread->resource_dispatcher() : nullptr, factory);
 }
 
diff --git a/content/renderer/renderer_blink_platform_impl.h b/content/renderer/renderer_blink_platform_impl.h
index 0b3fa167..ff5ee76 100644
--- a/content/renderer/renderer_blink_platform_impl.h
+++ b/content/renderer/renderer_blink_platform_impl.h
@@ -234,7 +234,7 @@
     return web_database_observer_impl_.get();
   }
 
-  blink::WebURLLoader* CreateURLLoader() override;
+  std::unique_ptr<blink::WebURLLoader> CreateURLLoader() override;
 
   void RequestPurgeMemory() override;
 
diff --git a/content/renderer/service_worker/service_worker_context_client.cc b/content/renderer/service_worker/service_worker_context_client.cc
index dd792b3..c621eda 100644
--- a/content/renderer/service_worker/service_worker_context_client.cc
+++ b/content/renderer/service_worker/service_worker_context_client.cc
@@ -4,6 +4,7 @@
 
 #include "content/renderer/service_worker/service_worker_context_client.h"
 
+#include <map>
 #include <memory>
 #include <utility>
 
@@ -101,6 +102,22 @@
   std::unique_ptr<ServiceWorkerNetworkProvider> provider_;
 };
 
+class StreamHandleListener
+    : public blink::WebServiceWorkerStreamHandle::Listener {
+ public:
+  StreamHandleListener(
+      blink::mojom::ServiceWorkerStreamCallbackPtr callback_ptr)
+      : callback_ptr_(std::move(callback_ptr)) {}
+
+  ~StreamHandleListener() override {}
+
+  void OnAborted() override { callback_ptr_->OnAborted(); }
+  void OnCompleted() override { callback_ptr_->OnCompleted(); }
+
+ private:
+  blink::mojom::ServiceWorkerStreamCallbackPtr callback_ptr_;
+};
+
 ServiceWorkerStatusCode EventResultToStatus(
     blink::WebServiceWorkerEventResult result) {
   switch (result) {
@@ -210,7 +227,6 @@
     web_response->SetBlob(blink::WebString::FromASCII(response.blob_uuid),
                           response.blob_size);
   }
-  web_response->SetStreamURL(blink::WebURL(response.stream_url));
   web_response->SetError(response.error);
   web_response->SetResponseTime(response.response_time.ToInternalValue());
   if (response.is_in_cache_storage) {
@@ -235,11 +251,19 @@
   }
 }
 
+template <typename Key, typename Callback>
+void AbortPendingEventCallbacks(std::map<Key, Callback>& callbacks) {
+  for (const auto& it : callbacks)
+    it.second.Run(SERVICE_WORKER_ERROR_ABORT, base::Time::Now());
+}
+
 }  // namespace
 
 // Holding data that needs to be bound to the worker context on the
 // worker thread.
 struct ServiceWorkerContextClient::WorkerContextData {
+  using SimpleEventCallback =
+      base::Callback<void(ServiceWorkerStatusCode, base::Time)>;
   using ClientsCallbacksMap =
       IDMap<std::unique_ptr<blink::WebServiceWorkerClientsCallbacks>>;
   using ClaimClientsCallbacksMap =
@@ -265,7 +289,6 @@
       IDMap<std::unique_ptr<const DispatchNotificationCloseEventCallback>>;
   using PushEventCallbacksMap =
       IDMap<std::unique_ptr<const DispatchPushEventCallback>>;
-  using FetchEventCallbacksMap = IDMap<std::unique_ptr<const FetchCallback>>;
   using ExtendableMessageEventCallbacksMap =
       IDMap<std::unique_ptr<const DispatchExtendableMessageEventCallback>>;
   using NavigationPreloadRequestsMap = IDMap<
@@ -332,7 +355,12 @@
   PushEventCallbacksMap push_event_callbacks;
 
   // Pending callbacks for Fetch Events.
-  FetchEventCallbacksMap fetch_event_callbacks;
+  std::map<int /* fetch_event_id */, SimpleEventCallback> fetch_event_callbacks;
+
+  // Pending callbacks for respondWith on each fetch event.
+  std::map<int /* fetch_event_id */,
+           mojom::ServiceWorkerFetchResponseCallbackPtr>
+      fetch_response_callbacks;
 
   // Pending callbacks for Extendable Message Events.
   ExtendableMessageEventCallbacksMap message_event_callbacks;
@@ -872,36 +900,73 @@
       base::Time::FromDoubleT(event_dispatch_time)));
 }
 
-void ServiceWorkerContextClient::RespondToFetchEvent(
+void ServiceWorkerContextClient::RespondToFetchEventWithNoResponse(
     int fetch_event_id,
     double event_dispatch_time) {
-  Send(new ServiceWorkerHostMsg_FetchEventResponse(
-      GetRoutingID(), fetch_event_id,
-      SERVICE_WORKER_FETCH_EVENT_RESULT_FALLBACK, ServiceWorkerResponse(),
-      base::Time::FromDoubleT(event_dispatch_time)));
+  const mojom::ServiceWorkerFetchResponseCallbackPtr& response_callback =
+      context_->fetch_response_callbacks[fetch_event_id];
+  DCHECK(response_callback.is_bound());
+  response_callback->OnFallback(base::Time::FromDoubleT(event_dispatch_time));
+  context_->fetch_response_callbacks.erase(fetch_event_id);
 }
 
 void ServiceWorkerContextClient::RespondToFetchEvent(
     int fetch_event_id,
     const blink::WebServiceWorkerResponse& web_response,
     double event_dispatch_time) {
-  Send(new ServiceWorkerHostMsg_FetchEventResponse(
-      GetRoutingID(), fetch_event_id,
-      SERVICE_WORKER_FETCH_EVENT_RESULT_RESPONSE,
-      GetServiceWorkerResponseFromWebResponse(web_response),
-      base::Time::FromDoubleT(event_dispatch_time)));
+  ServiceWorkerResponse response(
+      GetServiceWorkerResponseFromWebResponse(web_response));
+  const mojom::ServiceWorkerFetchResponseCallbackPtr& response_callback =
+      context_->fetch_response_callbacks[fetch_event_id];
+  if (response.blob_uuid.size()) {
+    // Send the legacy IPC due to the ordering issue between respondWith and
+    // blob.
+    // TODO(shimazu): mojofy this IPC after blob starts using sync IPC for
+    // creation or is mojofied: https://crbug.com/611935.
+    Send(new ServiceWorkerHostMsg_FetchEventResponse(
+        GetRoutingID(), fetch_event_id, response,
+        base::Time::FromDoubleT(event_dispatch_time)));
+  } else {
+    response_callback->OnResponse(response,
+                                  base::Time::FromDoubleT(event_dispatch_time));
+  }
+  context_->fetch_response_callbacks.erase(fetch_event_id);
+}
+
+void ServiceWorkerContextClient::RespondToFetchEventWithResponseStream(
+    int fetch_event_id,
+    const blink::WebServiceWorkerResponse& web_response,
+    blink::WebServiceWorkerStreamHandle* web_body_as_stream,
+    double event_dispatch_time) {
+  ServiceWorkerResponse response(
+      GetServiceWorkerResponseFromWebResponse(web_response));
+  const mojom::ServiceWorkerFetchResponseCallbackPtr& response_callback =
+      context_->fetch_response_callbacks[fetch_event_id];
+  blink::mojom::ServiceWorkerStreamHandlePtr body_as_stream =
+      blink::mojom::ServiceWorkerStreamHandle::New();
+  blink::mojom::ServiceWorkerStreamCallbackPtr callback_ptr;
+  body_as_stream->callback_request = mojo::MakeRequest(&callback_ptr);
+  body_as_stream->stream = web_body_as_stream->DrainStreamDataPipe();
+
+  web_body_as_stream->SetListener(
+      base::MakeUnique<StreamHandleListener>(std::move(callback_ptr)));
+
+  response_callback->OnResponseStream(
+      response, std::move(body_as_stream),
+      base::Time::FromDoubleT(event_dispatch_time));
+  context_->fetch_response_callbacks.erase(fetch_event_id);
 }
 
 void ServiceWorkerContextClient::DidHandleFetchEvent(
     int fetch_event_id,
     blink::WebServiceWorkerEventResult result,
     double event_dispatch_time) {
-  const FetchCallback* callback =
-      context_->fetch_event_callbacks.Lookup(fetch_event_id);
+  const WorkerContextData::SimpleEventCallback& callback =
+      context_->fetch_event_callbacks[fetch_event_id];
   DCHECK(callback);
-  callback->Run(EventResultToStatus(result),
-                base::Time::FromDoubleT(event_dispatch_time));
-  context_->fetch_event_callbacks.Remove(fetch_event_id);
+  callback.Run(EventResultToStatus(result),
+               base::Time::FromDoubleT(event_dispatch_time));
+  context_->fetch_event_callbacks.erase(fetch_event_id);
 }
 
 void ServiceWorkerContextClient::DidHandleNotificationClickEvent(
@@ -1251,6 +1316,7 @@
     int fetch_event_id,
     const ServiceWorkerFetchRequest& request,
     mojom::FetchEventPreloadHandlePtr preload_handle,
+    mojom::ServiceWorkerFetchResponseCallbackPtr response_callback,
     const DispatchFetchEventCallback& callback) {
   std::unique_ptr<NavigationPreloadRequest> preload_request =
       preload_handle
@@ -1260,8 +1326,10 @@
   const bool navigation_preload_sent = !!preload_request;
   TRACE_EVENT0("ServiceWorker",
                "ServiceWorkerContextClient::DispatchFetchEvent");
-  context_->fetch_event_callbacks.AddWithID(
-      base::MakeUnique<FetchCallback>(callback), fetch_event_id);
+  context_->fetch_response_callbacks.insert(
+      std::make_pair(fetch_event_id, std::move(response_callback)));
+  context_->fetch_event_callbacks.insert(
+      std::make_pair(fetch_event_id, std::move(callback)));
   if (preload_request) {
     context_->preload_requests.AddWithID(std::move(preload_request),
                                          fetch_event_id);
diff --git a/content/renderer/service_worker/service_worker_context_client.h b/content/renderer/service_worker/service_worker_context_client.h
index 3ec1e59..f73acbb 100644
--- a/content/renderer/service_worker/service_worker_context_client.h
+++ b/content/renderer/service_worker/service_worker_context_client.h
@@ -159,11 +159,16 @@
   void DidHandleInstallEvent(int request_id,
                              blink::WebServiceWorkerEventResult result,
                              double event_dispatch_time) override;
-  void RespondToFetchEvent(int fetch_event_id,
-                           double event_dispatch_time) override;
+  void RespondToFetchEventWithNoResponse(int fetch_event_id,
+                                         double event_dispatch_time) override;
   void RespondToFetchEvent(int fetch_event_id,
                            const blink::WebServiceWorkerResponse& response,
                            double event_dispatch_time) override;
+  void RespondToFetchEventWithResponseStream(
+      int fetch_event_id,
+      const blink::WebServiceWorkerResponse& response,
+      blink::WebServiceWorkerStreamHandle* web_body_as_stream,
+      double event_dispatch_time) override;
   void DidHandleFetchEvent(int fetch_event_id,
                            blink::WebServiceWorkerEventResult result,
                            double dispatch_event_time) override;
@@ -246,10 +251,12 @@
   void DispatchExtendableMessageEvent(
       mojom::ExtendableMessageEventPtr event,
       const DispatchExtendableMessageEventCallback& callback) override;
-  void DispatchFetchEvent(int fetch_event_id,
-                          const ServiceWorkerFetchRequest& request,
-                          mojom::FetchEventPreloadHandlePtr preload_handle,
-                          const DispatchFetchEventCallback& callback) override;
+  void DispatchFetchEvent(
+      int fetch_event_id,
+      const ServiceWorkerFetchRequest& request,
+      mojom::FetchEventPreloadHandlePtr preload_handle,
+      mojom::ServiceWorkerFetchResponseCallbackPtr response_callback,
+      const DispatchFetchEventCallback& callback) override;
   void DispatchNotificationClickEvent(
       const std::string& notification_id,
       const PlatformNotificationData& notification_data,
diff --git a/content/renderer/service_worker/service_worker_type_util.cc b/content/renderer/service_worker/service_worker_type_util.cc
index 4d4818c..9baae86 100644
--- a/content/renderer/service_worker/service_worker_type_util.cc
+++ b/content/renderer/service_worker/service_worker_type_util.cc
@@ -86,8 +86,7 @@
       GetURLList(web_response.UrlList()), web_response.Status(),
       web_response.StatusText().Utf8(), web_response.ResponseType(),
       GetHeaderMap(web_response), web_response.BlobUUID().Utf8(),
-      web_response.BlobSize(), web_response.StreamURL(),
-      web_response.GetError(),
+      web_response.BlobSize(), web_response.GetError(),
       base::Time::FromInternalValue(web_response.ResponseTime()),
       !web_response.CacheStorageCacheName().IsNull(),
       web_response.CacheStorageCacheName().Utf8(),
diff --git a/content/shell/test_runner/web_frame_test_client.cc b/content/shell/test_runner/web_frame_test_client.cc
index 5effc91..9ccbecf5 100644
--- a/content/shell/test_runner/web_frame_test_client.cc
+++ b/content/shell/test_runner/web_frame_test_client.cc
@@ -446,11 +446,10 @@
 }
 
 void WebFrameTestClient::DidFailProvisionalLoad(
-    blink::WebLocalFrame* frame,
     const blink::WebURLError& error,
     blink::WebHistoryCommitType commit_type) {
   if (test_runner()->shouldDumpFrameLoadCallbacks()) {
-    PrintFrameDescription(delegate_, frame);
+    PrintFrameDescription(delegate_, web_frame_test_proxy_base_->web_frame());
     delegate_->PrintMessage(" - didFailProvisionalLoadWithError\n");
   }
 }
diff --git a/content/shell/test_runner/web_frame_test_client.h b/content/shell/test_runner/web_frame_test_client.h
index 7aeeebf..0ca3d1b 100644
--- a/content/shell/test_runner/web_frame_test_client.h
+++ b/content/shell/test_runner/web_frame_test_client.h
@@ -64,8 +64,7 @@
   void DidStartProvisionalLoad(blink::WebDataSource* data_source,
                                blink::WebURLRequest& request) override;
   void DidReceiveServerRedirectForProvisionalLoad() override;
-  void DidFailProvisionalLoad(blink::WebLocalFrame* frame,
-                              const blink::WebURLError& error,
+  void DidFailProvisionalLoad(const blink::WebURLError& error,
                               blink::WebHistoryCommitType commit_type) override;
   void DidCommitProvisionalLoad(
       blink::WebLocalFrame* frame,
diff --git a/content/shell/test_runner/web_frame_test_proxy.h b/content/shell/test_runner/web_frame_test_proxy.h
index ff9bef4..d505374 100644
--- a/content/shell/test_runner/web_frame_test_proxy.h
+++ b/content/shell/test_runner/web_frame_test_proxy.h
@@ -108,15 +108,14 @@
   }
 
   void DidFailProvisionalLoad(
-      blink::WebLocalFrame* frame,
       const blink::WebURLError& error,
       blink::WebHistoryCommitType commit_type) override {
-    test_client()->DidFailProvisionalLoad(frame, error, commit_type);
+    test_client()->DidFailProvisionalLoad(error, commit_type);
     // If the test finished, don't notify the embedder of the failed load,
     // as we already destroyed the document loader.
-    if (!frame->ProvisionalDataSource())
+    if (!web_frame()->ProvisionalDataSource())
       return;
-    Base::DidFailProvisionalLoad(frame, error, commit_type);
+    Base::DidFailProvisionalLoad(error, commit_type);
   }
 
   void DidCommitProvisionalLoad(
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index d7a1a5e..5e3cc48 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -1237,6 +1237,7 @@
     "../browser/service_worker/service_worker_context_request_handler_unittest.cc",
     "../browser/service_worker/service_worker_context_unittest.cc",
     "../browser/service_worker/service_worker_controllee_request_handler_unittest.cc",
+    "../browser/service_worker/service_worker_data_pipe_reader_unittest.cc",
     "../browser/service_worker/service_worker_database_unittest.cc",
     "../browser/service_worker/service_worker_dispatcher_host_unittest.cc",
     "../browser/service_worker/service_worker_handle_unittest.cc",
diff --git a/content/test/test_blink_web_unit_test_support.cc b/content/test/test_blink_web_unit_test_support.cc
index 8439a62..5034966423 100644
--- a/content/test/test_blink_web_unit_test_support.cc
+++ b/content/test/test_blink_web_unit_test_support.cc
@@ -188,11 +188,12 @@
   return NULL;
 }
 
-blink::WebURLLoader* TestBlinkWebUnitTestSupport::CreateURLLoader() {
+std::unique_ptr<blink::WebURLLoader>
+TestBlinkWebUnitTestSupport::CreateURLLoader() {
   // This loader should be used only for process-local resources such as
   // data URLs.
-  blink::WebURLLoader* default_loader = new WebURLLoaderImpl(nullptr, nullptr);
-  return url_loader_factory_->CreateURLLoader(default_loader);
+  auto default_loader = base::MakeUnique<WebURLLoaderImpl>(nullptr, nullptr);
+  return url_loader_factory_->CreateURLLoader(std::move(default_loader));
 }
 
 blink::WebString TestBlinkWebUnitTestSupport::UserAgent() {
diff --git a/content/test/test_blink_web_unit_test_support.h b/content/test/test_blink_web_unit_test_support.h
index 3adb4142..ff8c041 100644
--- a/content/test/test_blink_web_unit_test_support.h
+++ b/content/test/test_blink_web_unit_test_support.h
@@ -41,7 +41,7 @@
   blink::WebFileUtilities* GetFileUtilities() override;
   blink::WebIDBFactory* IdbFactory() override;
 
-  blink::WebURLLoader* CreateURLLoader() override;
+  std::unique_ptr<blink::WebURLLoader> CreateURLLoader() override;
   blink::WebString UserAgent() override;
   blink::WebString QueryLocalizedString(
       blink::WebLocalizedString::Name name) override;
diff --git a/content/zygote/zygote_linux.h b/content/zygote/zygote_linux.h
index 79a06eb..3eb7a156 100644
--- a/content/zygote/zygote_linux.h
+++ b/content/zygote/zygote_linux.h
@@ -51,14 +51,13 @@
     // Notes whether the zygote has sent SIGKILL to this process.
     bool sent_sigkill;
   };
-  typedef base::SmallMap< std::map<base::ProcessHandle, ZygoteProcessInfo> >
-      ZygoteProcessMap;
+  using ZygoteProcessMap =
+      base::small_map<std::map<base::ProcessHandle, ZygoteProcessInfo>>;
 
   // Retrieve a ZygoteProcessInfo from the process_info_map_.
   // Returns true and write to process_info if |pid| can be found, return
   // false otherwise.
-  bool GetProcessInfo(base::ProcessHandle pid,
-                      ZygoteProcessInfo* process_info);
+  bool GetProcessInfo(base::ProcessHandle pid, ZygoteProcessInfo* process_info);
 
   // Returns true if the SUID sandbox is active.
   bool UsingSUIDSandbox() const;
diff --git a/extensions/OWNERS b/extensions/OWNERS
index 6abeb181..c736a99 100644
--- a/extensions/OWNERS
+++ b/extensions/OWNERS
@@ -11,7 +11,6 @@
 #   chrome/utility/extensions/OWNERS
 
 rdevlin.cronin@chromium.org
-asargent@chromium.org
 benwells@chromium.org
 reillyg@chromium.org
 finnur@chromium.org
diff --git a/gpu/command_buffer/common/gpu_memory_buffer_support.cc b/gpu/command_buffer/common/gpu_memory_buffer_support.cc
index 0159b48f..481f0b2 100644
--- a/gpu/command_buffer/common/gpu_memory_buffer_support.cc
+++ b/gpu/command_buffer/common/gpu_memory_buffer_support.cc
@@ -50,18 +50,6 @@
 
 }  // namespace
 
-gfx::BufferFormat DefaultBufferFormatForImageFormat(unsigned internalformat) {
-  switch (internalformat) {
-    case GL_RGB:
-      return gfx::BufferFormat::BGRX_8888;
-    case GL_RGBA:
-      return gfx::BufferFormat::RGBA_8888;
-    default:
-      NOTREACHED();
-      return gfx::BufferFormat::RGBA_8888;
-  }
-}
-
 bool IsImageFormatCompatibleWithGpuMemoryBufferFormat(
     unsigned internalformat,
     gfx::BufferFormat format) {
diff --git a/gpu/command_buffer/common/gpu_memory_buffer_support.h b/gpu/command_buffer/common/gpu_memory_buffer_support.h
index aa8c567d..9f1dfbd 100644
--- a/gpu/command_buffer/common/gpu_memory_buffer_support.h
+++ b/gpu/command_buffer/common/gpu_memory_buffer_support.h
@@ -14,11 +14,6 @@
 
 struct Capabilities;
 
-// Returns a valid GpuMemoryBuffer format given a valid internalformat as
-// defined by CHROMIUM_image.
-GPU_EXPORT gfx::BufferFormat DefaultBufferFormatForImageFormat(
-    unsigned internalformat);
-
 // Returns true if |internalformat| is compatible with |format|.
 GPU_EXPORT bool IsImageFormatCompatibleWithGpuMemoryBufferFormat(
     unsigned internalformat,
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder.cc b/gpu/command_buffer/service/gles2_cmd_decoder.cc
index b34e690..ac14422 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder.cc
@@ -14355,6 +14355,32 @@
   const gfx::Rect dst(0, 0, size.width(), size.height());
   src.Intersect(dst);
 
+  GLenum final_internal_format = TextureManager::AdjustTexInternalFormat(
+      feature_info_.get(), internal_format);
+  if (workarounds().force_int_or_srgb_cube_texture_complete &&
+      texture->target() == GL_TEXTURE_CUBE_MAP &&
+      (GLES2Util::IsIntegerFormat(final_internal_format) ||
+       GLES2Util::GetColorEncodingFromInternalFormat(final_internal_format) ==
+           GL_SRGB)) {
+    TextureManager::DoTexImageArguments args = {
+        target,
+        level,
+        final_internal_format,
+        width,
+        height,
+        1,
+        border,
+        format,
+        type,
+        nullptr,
+        pixels_size,
+        0,
+        TextureManager::DoTexImageArguments::kTexImage2D};
+    texture_manager()->WorkaroundCopyTexImageCubeMap(
+        &texture_state_, &state_, &framebuffer_state_, texture_ref, func_name,
+        args);
+  }
+
   if (src.x() != x || src.y() != y ||
       src.width() != width || src.height() != height) {
     {
@@ -14364,9 +14390,8 @@
 
       std::unique_ptr<char[]> zero(new char[pixels_size]);
       memset(zero.get(), 0, pixels_size);
-      glTexImage2D(target, level, TextureManager::AdjustTexInternalFormat(
-        feature_info_.get(), internal_format),
-        width, height, border, format, type, zero.get());
+      glTexImage2D(target, level, final_internal_format, width, height, border,
+                   format, type, zero.get());
     }
 
     if (!src.IsEmpty()) {
@@ -14385,8 +14410,6 @@
       }
     }
   } else {
-    GLenum final_internal_format = TextureManager::AdjustTexInternalFormat(
-        feature_info_.get(), internal_format);
     if (workarounds().init_two_cube_map_levels_before_copyteximage &&
         texture->target() == GL_TEXTURE_CUBE_MAP &&
         target != GL_TEXTURE_CUBE_MAP_POSITIVE_X) {
diff --git a/gpu/command_buffer/service/texture_manager.cc b/gpu/command_buffer/service/texture_manager.cc
index 68cd6bf..3a65582 100644
--- a/gpu/command_buffer/service/texture_manager.cc
+++ b/gpu/command_buffer/service/texture_manager.cc
@@ -395,6 +395,24 @@
 
 }  // namespace anonymous
 
+DecoderTextureState::DecoderTextureState(
+    const GpuDriverBugWorkarounds& workarounds)
+    : tex_image_failed(false),
+      texture_upload_count(0),
+      texsubimage_faster_than_teximage(
+          workarounds.texsubimage_faster_than_teximage),
+      force_cube_map_positive_x_allocation(
+          workarounds.force_cube_map_positive_x_allocation),
+      force_cube_complete(workarounds.force_cube_complete),
+      force_int_or_srgb_cube_texture_complete(
+          workarounds.force_int_or_srgb_cube_texture_complete),
+      unpack_alignment_workaround_with_unpack_buffer(
+          workarounds.unpack_alignment_workaround_with_unpack_buffer),
+      unpack_overlapping_rows_separately_unpack_buffer(
+          workarounds.unpack_overlapping_rows_separately_unpack_buffer),
+      unpack_image_height_workaround_with_unpack_buffer(
+          workarounds.unpack_image_height_workaround_with_unpack_buffer) {}
+
 TextureManager::DestructionObserver::DestructionObserver() {}
 
 TextureManager::DestructionObserver::~DestructionObserver() {}
@@ -2527,7 +2545,8 @@
 
   std::vector<GLenum> undefined_faces;
   Texture* texture = texture_ref->texture();
-  if (texture_state->force_cube_complete) {
+  if (texture_state->force_cube_complete ||
+      texture_state->force_int_or_srgb_cube_texture_complete) {
     int width = 0;
     int height = 0;
     for (unsigned i = 0; i < 6; i++) {
@@ -2585,6 +2604,15 @@
       (texture_state->force_cube_complete ||
        (texture_state->force_cube_map_positive_x_allocation &&
         args.target != GL_TEXTURE_CUBE_MAP_POSITIVE_X));
+  // Force integer or srgb cube map texture complete, see crbug.com/712117.
+  need_cube_map_workaround =
+      need_cube_map_workaround ||
+      (texture->target() == GL_TEXTURE_CUBE_MAP &&
+       texture_state->force_int_or_srgb_cube_texture_complete &&
+       (GLES2Util::IsIntegerFormat(args.internal_format) ||
+        GLES2Util::GetColorEncodingFromInternalFormat(args.internal_format) ==
+            GL_SRGB));
+
   if (need_cube_map_workaround && !buffer) {
     DoCubeMapWorkaround(texture_state, state, framebuffer_state,
                         texture_ref, function_name, args);
diff --git a/gpu/command_buffer/service/texture_manager.h b/gpu/command_buffer/service/texture_manager.h
index 8cf2edb..b89a8afc 100644
--- a/gpu/command_buffer/service/texture_manager.h
+++ b/gpu/command_buffer/service/texture_manager.h
@@ -698,20 +698,7 @@
 struct DecoderTextureState {
   // total_texture_upload_time automatically initialized to 0 in default
   // constructor.
-  explicit DecoderTextureState(const GpuDriverBugWorkarounds& workarounds)
-      : tex_image_failed(false),
-        texture_upload_count(0),
-        texsubimage_faster_than_teximage(
-            workarounds.texsubimage_faster_than_teximage),
-        force_cube_map_positive_x_allocation(
-            workarounds.force_cube_map_positive_x_allocation),
-        force_cube_complete(workarounds.force_cube_complete),
-        unpack_alignment_workaround_with_unpack_buffer(
-            workarounds.unpack_alignment_workaround_with_unpack_buffer),
-        unpack_overlapping_rows_separately_unpack_buffer(
-            workarounds.unpack_overlapping_rows_separately_unpack_buffer),
-        unpack_image_height_workaround_with_unpack_buffer(
-            workarounds.unpack_image_height_workaround_with_unpack_buffer) {}
+  explicit DecoderTextureState(const GpuDriverBugWorkarounds& workarounds);
 
   // This indicates all the following texSubImage*D calls that are part of the
   // failed texImage*D call should be ignored. The client calls have a lock
@@ -726,6 +713,7 @@
   bool texsubimage_faster_than_teximage;
   bool force_cube_map_positive_x_allocation;
   bool force_cube_complete;
+  bool force_int_or_srgb_cube_texture_complete;
   bool unpack_alignment_workaround_with_unpack_buffer;
   bool unpack_overlapping_rows_separately_unpack_buffer;
   bool unpack_image_height_workaround_with_unpack_buffer;
diff --git a/gpu/config/gpu_driver_bug_list.json b/gpu/config/gpu_driver_bug_list.json
index a7399a4c..7d5d1e7 100644
--- a/gpu/config/gpu_driver_bug_list.json
+++ b/gpu/config/gpu_driver_bug_list.json
@@ -1,6 +1,6 @@
 {
   "name": "gpu driver bug list",
-  "version": "10.3",
+  "version": "10.4",
   "entries": [
     {
       "id": 1,
@@ -2362,6 +2362,18 @@
       "features": [
         "disable_software_to_accelerated_canvas_upgrade"
       ]
+    },
+    {
+      "id": 223,
+      "description": "Force integer or srgb cube map texture complete on Linux AMD",
+      "cr_bugs": [712117],
+      "os": {
+        "type": "linux"
+      },
+      "vendor_id": "0x1002",
+      "features": [
+        "force_int_or_srgb_cube_texture_complete"
+      ]
     }
   ],
   "comment": [
diff --git a/gpu/config/gpu_driver_bug_workaround_type.h b/gpu/config/gpu_driver_bug_workaround_type.h
index 2fb5625..fc021b3 100644
--- a/gpu/config/gpu_driver_bug_workaround_type.h
+++ b/gpu/config/gpu_driver_bug_workaround_type.h
@@ -105,6 +105,8 @@
          force_discrete_gpu)                                 \
   GPU_OP(FORCE_INTEGRATED_GPU,                               \
          force_integrated_gpu)                               \
+  GPU_OP(FORCE_INT_OR_SRGB_CUBE_TEXTURE_COMPLETE,            \
+         force_int_or_srgb_cube_texture_complete)            \
   GPU_OP(FORCE_UPDATE_SCISSOR_STATE_WHEN_BINDING_FBO0,       \
          force_update_scissor_state_when_binding_fbo0)       \
   GPU_OP(GET_FRAG_DATA_INFO_BUG,                             \
diff --git a/gpu/perftests/texture_upload_perftest.cc b/gpu/perftests/texture_upload_perftest.cc
index b562f3f36..9ab6887 100644
--- a/gpu/perftests/texture_upload_perftest.cc
+++ b/gpu/perftests/texture_upload_perftest.cc
@@ -9,7 +9,7 @@
 #include <memory>
 #include <vector>
 
-#include "base/containers/small_map.h"
+#include "base/containers/flat_map.h"
 #include "base/logging.h"
 #include "base/memory/ref_counted.h"
 #include "base/strings/stringprintf.h"
@@ -401,8 +401,7 @@
                                      const GLenum format,
                                      const bool subimage) {
     std::vector<uint8_t> pixels;
-    base::SmallMap<std::map<std::string, Measurement>>
-        aggregates;  // indexed by name
+    base::flat_map<std::string, Measurement> aggregates;  // indexed by name
     int successful_runs = 0;
     GLuint texture_id = CreateGLTexture(format, size, subimage);
     for (int i = 0; i < kUploadPerfWarmupRuns + kUploadPerfTestRuns; ++i) {
diff --git a/headless/BUILD.gn b/headless/BUILD.gn
index d40ba6a8..55afe1f 100644
--- a/headless/BUILD.gn
+++ b/headless/BUILD.gn
@@ -342,9 +342,7 @@
     "//url",
   ]
 
-  if (is_mac) {
-    deps += [ ":mac_helpers" ]
-  } else {
+  if (!is_mac) {
     deps += [ "//ui/aura" ]
   }
 
@@ -434,22 +432,6 @@
   ]
 }
 
-if (is_mac) {
-  copy("mac_helpers") {
-    sources = [
-      "$root_out_dir/crashpad_handler",
-    ]
-
-    deps = [
-      "//third_party/crashpad/crashpad/handler:crashpad_handler",
-    ]
-
-    outputs = [
-      "$root_out_dir/Helpers/{{source_file_part}}",
-    ]
-  }
-}
-
 test("headless_browsertests") {
   sources = [
     "lib/embedder_mojo_browsertest.cc",
diff --git a/headless/lib/headless_browser_browsertest.cc b/headless/lib/headless_browser_browsertest.cc
index 8b6e20c7..52931af4 100644
--- a/headless/lib/headless_browser_browsertest.cc
+++ b/headless/lib/headless_browser_browsertest.cc
@@ -717,13 +717,12 @@
   base::FilePath crash_dumps_dir_;
 };
 
-// TODO(skyostil): Minidump generation currently is only supported on Linux and
-// Mac.
-#if defined(HEADLESS_USE_BREAKPAD) || defined(OS_MACOSX)
+// TODO(skyostil): Minidump generation currently is only supported on Linux.
+#if defined(HEADLESS_USE_BREAKPAD)
 #define MAYBE_GenerateMinidump GenerateMinidump
 #else
 #define MAYBE_GenerateMinidump DISABLED_GenerateMinidump
-#endif  // defined(HEADLESS_USE_BREAKPAD) || defined(OS_MACOSX)
+#endif  // defined(HEADLESS_USE_BREAKPAD)
 IN_PROC_BROWSER_TEST_F(CrashReporterTest, MAYBE_GenerateMinidump) {
   // Navigates a tab to chrome://crash and checks that a minidump is generated.
   // Note that we only test renderer crashes here -- browser crashes need to be
@@ -745,10 +744,6 @@
 
   // Check that one minidump got created.
   {
-#if defined(OS_MACOSX)
-    // Mac outputs dumps in the 'completed' directory.
-    crash_dumps_dir_ = crash_dumps_dir_.Append("completed");
-#endif
     base::ThreadRestrictions::SetIOAllowed(true);
     base::FileEnumerator it(crash_dumps_dir_, /* recursive */ false,
                             base::FileEnumerator::FILES);
diff --git a/headless/lib/headless_content_main_delegate.cc b/headless/lib/headless_content_main_delegate.cc
index 9428908..067ef27 100644
--- a/headless/lib/headless_content_main_delegate.cc
+++ b/headless/lib/headless_content_main_delegate.cc
@@ -33,10 +33,6 @@
 #include "headless/embedded_resource_pak.h"
 #endif
 
-#if defined(OS_MACOSX)
-#include "components/crash/content/app/crashpad.h"
-#endif
-
 namespace headless {
 namespace {
 // Keep in sync with content/common/content_constants_internal.h.
@@ -163,17 +159,16 @@
   g_headless_crash_client.Pointer()->set_crash_dumps_dir(
       browser_->options()->crash_dumps_dir);
 
-#if defined(HEADLESS_USE_BREAKPAD)
+#if !defined(OS_MACOSX)
   if (!browser_->options()->enable_crash_reporter) {
     DCHECK(!breakpad::IsCrashReporterEnabled());
     return;
   }
+#if defined(HEADLESS_USE_BREAKPAD)
   if (process_type != switches::kZygoteProcess)
     breakpad::InitCrashReporter(process_type);
-#elif defined(OS_MACOSX)
-  const bool browser_process = process_type.empty();
-  crash_reporter::InitializeCrashpad(browser_process, process_type);
 #endif  // defined(HEADLESS_USE_BREAKPAD)
+#endif  // !defined(OS_MACOSX)
 }
 
 void HeadlessContentMainDelegate::PreSandboxStartup() {
@@ -186,8 +181,10 @@
 #else
   if (command_line.HasSwitch(switches::kEnableLogging))
     InitLogging(command_line);
-#endif  // defined(OS_WIN)
+#endif
+#if !defined(OS_MACOSX)
   InitCrashReporter(command_line);
+#endif
   InitializeResourceBundle();
 }
 
diff --git a/ios/chrome/browser/ui/alert_coordinator/BUILD.gn b/ios/chrome/browser/ui/alert_coordinator/BUILD.gn
index 7a7ff731..091d8653 100644
--- a/ios/chrome/browser/ui/alert_coordinator/BUILD.gn
+++ b/ios/chrome/browser/ui/alert_coordinator/BUILD.gn
@@ -49,6 +49,7 @@
 }
 
 source_set("alert_coordinator_internal") {
+  configs += [ "//build/config/compiler:enable_arc" ]
   sources = [
     "loading_alert_coordinator.h",
     "loading_alert_coordinator.mm",
diff --git a/ios/chrome/browser/ui/alert_coordinator/loading_alert_coordinator.mm b/ios/chrome/browser/ui/alert_coordinator/loading_alert_coordinator.mm
index ac8c1ec..8d9d363 100644
--- a/ios/chrome/browser/ui/alert_coordinator/loading_alert_coordinator.mm
+++ b/ios/chrome/browser/ui/alert_coordinator/loading_alert_coordinator.mm
@@ -7,9 +7,7 @@
 #import <UIKit/UIKit.h>
 
 #include "base/ios/block_types.h"
-#import "base/ios/weak_nsobject.h"
 #import "base/mac/scoped_block.h"
-#import "base/mac/scoped_nsobject.h"
 #include "components/strings/grit/components_strings.h"
 #import "ios/chrome/browser/ui/material_components/activity_indicator.h"
 #import "ios/chrome/browser/ui/uikit_ui_util.h"
@@ -19,6 +17,10 @@
 #import "ios/third_party/material_roboto_font_loader_ios/src/src/MaterialRobotoFontLoader.h"
 #include "ui/base/l10n/l10n_util_mac.h"
 
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
 namespace {
 
 // MDC constraints.
@@ -41,11 +43,11 @@
 
 @interface LoadingAlertCoordinator () {
   // Title of the alert.
-  base::scoped_nsobject<NSString> _title;
+  NSString* _title;
   // Callback for the cancel button.
-  base::mac::ScopedBlock<ProceduralBlock> _cancelHandler;
+  ProceduralBlock _cancelHandler;
   // View Controller which will be displayed on |baseViewController|.
-  base::scoped_nsobject<UIViewController> _presentedViewController;
+  UIViewController* _presentedViewController;
 }
 
 // Callback called when the cancel button is pressed.
@@ -56,13 +58,13 @@
 // View Controller handling the layout of the dialog.
 @interface LoadingViewController : UIViewController {
   // Title of the dialog.
-  base::scoped_nsobject<NSString> _title;
+  NSString* _title;
   // View containing the elements of the dialog.
-  base::scoped_nsobject<UIView> _contentView;
+  UIView* _contentView;
   // Coordinator used for the cancel callback.
-  base::WeakNSObject<LoadingAlertCoordinator> _coordinator;
+  __weak LoadingAlertCoordinator* _coordinator;
   // Transitioning delegate for this ViewController.
-  base::scoped_nsobject<MDCDialogTransitionController> _transitionDelegate;
+  MDCDialogTransitionController* _transitionDelegate;
 }
 
 // Initializes with the |title| of the dialog and the |coordinator| which will
@@ -84,10 +86,10 @@
                   coordinator:(LoadingAlertCoordinator*)coordinator {
   self = [super initWithNibName:nil bundle:nil];
   if (self) {
-    _title.reset([title copy]);
-    _coordinator.reset(coordinator);
+    _title = [title copy];
+    _coordinator = coordinator;
     self.modalPresentationStyle = UIModalPresentationCustom;
-    _transitionDelegate.reset([[MDCDialogTransitionController alloc] init]);
+    _transitionDelegate = [[MDCDialogTransitionController alloc] init];
     self.transitioningDelegate = _transitionDelegate;
   }
   return self;
@@ -101,7 +103,7 @@
 
   // Cancel button.
   NSString* cancelTitle = l10n_util::GetNSString(IDS_CANCEL);
-  MDCFlatButton* cancelButton = [[[MDCFlatButton alloc] init] autorelease];
+  MDCFlatButton* cancelButton = [[MDCFlatButton alloc] init];
   [cancelButton sizeToFit];
   [cancelButton setCustomTitleColor:[UIColor blackColor]];
   [cancelButton setTitle:cancelTitle forState:UIControlStateNormal];
@@ -110,8 +112,8 @@
          forControlEvents:UIControlEventTouchUpInside];
 
   // Activity indicator.
-  base::scoped_nsobject<MDCActivityIndicator> activityIndicator(
-      [[MDCActivityIndicator alloc] initWithFrame:CGRectZero]);
+  MDCActivityIndicator* activityIndicator =
+      [[MDCActivityIndicator alloc] initWithFrame:CGRectZero];
   [activityIndicator setCycleColors:ActivityIndicatorBrandedCycleColors()];
   [activityIndicator startAnimating];
 
@@ -123,15 +125,15 @@
   [attrsDictionary setObject:UIColorFromRGB(kTitleLabelFontColor)
                       forKey:NSForegroundColorAttributeName];
 
-  NSMutableAttributedString* string = [[[NSMutableAttributedString alloc]
-      initWithString:_title
-          attributes:attrsDictionary] autorelease];
+  NSMutableAttributedString* string =
+      [[NSMutableAttributedString alloc] initWithString:_title
+                                             attributes:attrsDictionary];
 
-  UILabel* title = [[[UILabel alloc] initWithFrame:CGRectZero] autorelease];
+  UILabel* title = [[UILabel alloc] initWithFrame:CGRectZero];
   title.attributedText = string;
 
   // Content view.
-  _contentView.reset([[UIView alloc] initWithFrame:CGRectZero]);
+  _contentView = [[UIView alloc] initWithFrame:CGRectZero];
 
   // Constraints.
   [activityIndicator setTranslatesAutoresizingMaskIntoConstraints:NO];
@@ -149,7 +151,7 @@
     @"spinner" : activityIndicator,
     @"cancel" : cancelButton,
     @"title" : title,
-    @"contentView" : _contentView.get()
+    @"contentView" : _contentView
   };
   NSDictionary* metrics = @{
     @"padding" : @(kMDCPadding),
@@ -208,15 +210,15 @@
                              cancelHandler:(ProceduralBlock)cancelHandler {
   self = [super initWithBaseViewController:viewController];
   if (self) {
-    _title.reset([title copy]);
-    _cancelHandler.reset(cancelHandler, base::scoped_policy::RETAIN);
+    _title = [title copy];
+    _cancelHandler = cancelHandler;
   }
   return self;
 }
 
 - (void)start {
-  _presentedViewController.reset(
-      [[LoadingViewController alloc] initWithTitle:_title coordinator:self]);
+  _presentedViewController =
+      [[LoadingViewController alloc] initWithTitle:_title coordinator:self];
   [self.baseViewController presentViewController:_presentedViewController
                                         animated:YES
                                       completion:nil];
@@ -226,13 +228,13 @@
   [[_presentedViewController presentingViewController]
       dismissViewControllerAnimated:NO
                          completion:nil];
-  _presentedViewController.reset();
-  _cancelHandler.reset();
+  _presentedViewController = nil;
+  _cancelHandler = nil;
 }
 
 - (void)cancelCallback {
   if (_cancelHandler)
-    _cancelHandler.get()();
+    _cancelHandler();
   [self stop];
 }
 
diff --git a/ios/public/provider/chrome/browser/distribution/BUILD.gn b/ios/public/provider/chrome/browser/distribution/BUILD.gn
index f758584..e9af459 100644
--- a/ios/public/provider/chrome/browser/distribution/BUILD.gn
+++ b/ios/public/provider/chrome/browser/distribution/BUILD.gn
@@ -3,6 +3,7 @@
 # found in the LICENSE file.
 
 source_set("distribution") {
+  configs += [ "//build/config/compiler:enable_arc" ]
   sources = [
     "app_distribution_provider.h",
     "app_distribution_provider.mm",
@@ -13,6 +14,7 @@
 }
 
 source_set("test_support") {
+  configs += [ "//build/config/compiler:enable_arc" ]
   testonly = true
   sources = [
     "test_app_distribution_provider.h",
diff --git a/ios/public/provider/chrome/browser/distribution/app_distribution_provider.mm b/ios/public/provider/chrome/browser/distribution/app_distribution_provider.mm
index ad41879..0b586b92 100644
--- a/ios/public/provider/chrome/browser/distribution/app_distribution_provider.mm
+++ b/ios/public/provider/chrome/browser/distribution/app_distribution_provider.mm
@@ -4,6 +4,10 @@
 
 #import "ios/public/provider/chrome/browser/distribution/app_distribution_provider.h"
 
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
 AppDistributionProvider::AppDistributionProvider() {}
 
 AppDistributionProvider::~AppDistributionProvider() {}
diff --git a/ios/public/provider/chrome/browser/distribution/test_app_distribution_provider.mm b/ios/public/provider/chrome/browser/distribution/test_app_distribution_provider.mm
index 0ab9c26c..47938e4 100644
--- a/ios/public/provider/chrome/browser/distribution/test_app_distribution_provider.mm
+++ b/ios/public/provider/chrome/browser/distribution/test_app_distribution_provider.mm
@@ -4,6 +4,10 @@
 
 #import "ios/public/provider/chrome/browser/distribution/test_app_distribution_provider.h"
 
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
 TestAppDistributionProvider::TestAppDistributionProvider() {}
 
 TestAppDistributionProvider::~TestAppDistributionProvider() {}
diff --git a/ios/public/provider/chrome/browser/images/BUILD.gn b/ios/public/provider/chrome/browser/images/BUILD.gn
index 3d96d7f..df3142b 100644
--- a/ios/public/provider/chrome/browser/images/BUILD.gn
+++ b/ios/public/provider/chrome/browser/images/BUILD.gn
@@ -3,6 +3,7 @@
 # found in the LICENSE file.
 
 source_set("images") {
+  configs += [ "//build/config/compiler:enable_arc" ]
   sources = [
     "branded_image_provider.h",
     "branded_image_provider.mm",
@@ -14,6 +15,7 @@
 }
 
 source_set("test_support") {
+  configs += [ "//build/config/compiler:enable_arc" ]
   testonly = true
   sources = [
     "test_branded_image_provider.h",
diff --git a/ios/public/provider/chrome/browser/images/branded_image_provider.mm b/ios/public/provider/chrome/browser/images/branded_image_provider.mm
index 8d52a43..28b1954 100644
--- a/ios/public/provider/chrome/browser/images/branded_image_provider.mm
+++ b/ios/public/provider/chrome/browser/images/branded_image_provider.mm
@@ -6,6 +6,10 @@
 
 #import <Foundation/Foundation.h>
 
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
 BrandedImageProvider::BrandedImageProvider() {}
 
 BrandedImageProvider::~BrandedImageProvider() {}
diff --git a/ios/public/provider/chrome/browser/images/test_branded_image_provider.mm b/ios/public/provider/chrome/browser/images/test_branded_image_provider.mm
index 257ca25..c509ed8 100644
--- a/ios/public/provider/chrome/browser/images/test_branded_image_provider.mm
+++ b/ios/public/provider/chrome/browser/images/test_branded_image_provider.mm
@@ -6,6 +6,10 @@
 
 #import <Foundation/Foundation.h>
 
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
 TestBrandedImageProvider::TestBrandedImageProvider() {}
 
 TestBrandedImageProvider::~TestBrandedImageProvider() {}
diff --git a/ios/public/provider/chrome/browser/omaha/BUILD.gn b/ios/public/provider/chrome/browser/omaha/BUILD.gn
index 06f5238..6dabf3e8 100644
--- a/ios/public/provider/chrome/browser/omaha/BUILD.gn
+++ b/ios/public/provider/chrome/browser/omaha/BUILD.gn
@@ -3,6 +3,7 @@
 # found in the LICENSE file.
 
 source_set("omaha") {
+  configs += [ "//build/config/compiler:enable_arc" ]
   sources = [
     "omaha_service_provider.h",
     "omaha_service_provider.mm",
@@ -15,6 +16,7 @@
 }
 
 source_set("test_support") {
+  configs += [ "//build/config/compiler:enable_arc" ]
   testonly = true
   sources = [
     "test_omaha_service_provider.h",
diff --git a/ios/public/provider/chrome/browser/omaha/omaha_service_provider.mm b/ios/public/provider/chrome/browser/omaha/omaha_service_provider.mm
index a15b0abf..e9e0b970 100644
--- a/ios/public/provider/chrome/browser/omaha/omaha_service_provider.mm
+++ b/ios/public/provider/chrome/browser/omaha/omaha_service_provider.mm
@@ -4,6 +4,10 @@
 
 #import "ios/public/provider/chrome/browser/omaha/omaha_service_provider.h"
 
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
 OmahaServiceProvider::OmahaServiceProvider() {}
 
 OmahaServiceProvider::~OmahaServiceProvider() {}
diff --git a/ios/public/provider/chrome/browser/omaha/test_omaha_service_provider.mm b/ios/public/provider/chrome/browser/omaha/test_omaha_service_provider.mm
index f7ccadf..420df54b 100644
--- a/ios/public/provider/chrome/browser/omaha/test_omaha_service_provider.mm
+++ b/ios/public/provider/chrome/browser/omaha/test_omaha_service_provider.mm
@@ -4,6 +4,10 @@
 
 #import "ios/public/provider/chrome/browser/omaha/test_omaha_service_provider.h"
 
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
 namespace {
 
 const char kTestUpdateServerURL[] = "https://iosupdatetest.chromium.org";
diff --git a/ipc/ipc_message_utils.h b/ipc/ipc_message_utils.h
index 2d51c98..5f036068 100644
--- a/ipc/ipc_message_utils.h
+++ b/ipc/ipc_message_utils.h
@@ -17,10 +17,12 @@
 #include <tuple>
 #include <vector>
 
+#include "base/containers/flat_map.h"
 #include "base/containers/small_map.h"
 #include "base/containers/stack_container.h"
 #include "base/files/file.h"
 #include "base/format_macros.h"
+#include "base/numerics/safe_conversions.h"
 #include "base/optional.h"
 #include "base/strings/string16.h"
 #include "base/strings/string_util.h"
@@ -899,10 +901,10 @@
           int kArraySize,
           typename EqualKey,
           typename MapInit>
-struct ParamTraits<base::SmallMap<NormalMap, kArraySize, EqualKey, MapInit> > {
-  typedef base::SmallMap<NormalMap, kArraySize, EqualKey, MapInit> param_type;
-  typedef typename param_type::key_type K;
-  typedef typename param_type::data_type V;
+struct ParamTraits<base::small_map<NormalMap, kArraySize, EqualKey, MapInit>> {
+  using param_type = base::small_map<NormalMap, kArraySize, EqualKey, MapInit>;
+  using K = typename param_type::key_type;
+  using V = typename param_type::data_type;
   static void GetSize(base::PickleSizer* sizer, const param_type& p) {
     GetParamSize(sizer, static_cast<int>(p.size()));
     typename param_type::const_iterator iter;
@@ -936,7 +938,53 @@
     return true;
   }
   static void Log(const param_type& p, std::string* l) {
-    l->append("<base::SmallMap>");
+    l->append("<base::small_map>");
+  }
+};
+
+template <class Key, class Mapped, class Compare>
+struct ParamTraits<base::flat_map<Key, Mapped, Compare>> {
+  using param_type = base::flat_map<Key, Mapped, Compare>;
+  static void GetSize(base::PickleSizer* sizer, const param_type& p) {
+    DCHECK(base::IsValueInRangeForNumericType<int>(p.size()));
+    GetParamSize(sizer, static_cast<int>(p.size()));
+    for (const auto& iter : p) {
+      GetParamSize(sizer, iter.first);
+      GetParamSize(sizer, iter.second);
+    }
+  }
+  static void Write(base::Pickle* m, const param_type& p) {
+    DCHECK(base::IsValueInRangeForNumericType<int>(p.size()));
+    WriteParam(m, static_cast<int>(p.size()));
+    for (const auto& iter : p) {
+      WriteParam(m, iter.first);
+      WriteParam(m, iter.second);
+    }
+  }
+  static bool Read(const base::Pickle* m,
+                   base::PickleIterator* iter,
+                   param_type* r) {
+    int size;
+    if (!iter->ReadLength(&size))
+      return false;
+
+    // Construct by creating in a vector and moving into the flat_map. Properly
+    // serialized flat_maps will be in-order so this will be O(n). Incorrectly
+    // serialized ones will still be handled properly.
+    std::vector<typename param_type::value_type> vect;
+    vect.resize(size);
+    for (int i = 0; i < size; ++i) {
+      if (!ReadParam(m, iter, &vect[i].first))
+        return false;
+      if (!ReadParam(m, iter, &vect[i].second))
+        return false;
+    }
+
+    *r = param_type(std::move(vect), base::KEEP_FIRST_OF_DUPES);
+    return true;
+  }
+  static void Log(const param_type& p, std::string* l) {
+    l->append("<base::flat_map>");
   }
 };
 
diff --git a/ipc/ipc_message_utils_unittest.cc b/ipc/ipc_message_utils_unittest.cc
index 4a10d34e..4561c2e 100644
--- a/ipc/ipc_message_utils_unittest.cc
+++ b/ipc/ipc_message_utils_unittest.cc
@@ -215,5 +215,25 @@
   EXPECT_EQ(token, deserialized_token);
 }
 
+TEST(IPCMessageUtilsTest, FlatMap) {
+  base::flat_map<std::string, int> input;
+  input["foo"] = 42;
+  input["bar"] = 96;
+
+  base::Pickle pickle;
+  IPC::WriteParam(&pickle, input);
+
+  base::PickleSizer sizer;
+  IPC::GetParamSize(&sizer, input);
+
+  EXPECT_EQ(sizer.payload_size(), pickle.payload_size());
+
+  base::PickleIterator iter(pickle);
+  base::flat_map<std::string, int> output;
+  EXPECT_TRUE(IPC::ReadParam(&pickle, &iter, &output));
+
+  EXPECT_EQ(input, output);
+}
+
 }  // namespace
 }  // namespace IPC
diff --git a/net/quic/platform/impl/quic_containers_impl.h b/net/quic/platform/impl/quic_containers_impl.h
index 287b73b3..360ae4c 100644
--- a/net/quic/platform/impl/quic_containers_impl.h
+++ b/net/quic/platform/impl/quic_containers_impl.h
@@ -21,7 +21,7 @@
 // unique key-value-pair elements, and upgrades itself to unordered_map when
 // runs out of space.
 template <typename Key, typename Value, int Size>
-using QuicSmallMapImpl = base::SmallMap<std::unordered_map<Key, Value>, Size>;
+using QuicSmallMapImpl = base::small_map<std::unordered_map<Key, Value>, Size>;
 
 // A data structure used to represent a sorted set of non-empty, non-adjacent,
 // and mutually disjoint intervals.
diff --git a/storage/browser/blob/blob_memory_controller.cc b/storage/browser/blob/blob_memory_controller.cc
index 298037c..eae2ac4 100644
--- a/storage/browser/blob/blob_memory_controller.cc
+++ b/storage/browser/blob/blob_memory_controller.cc
@@ -245,7 +245,7 @@
         unreserved_file_items,
     std::vector<uint64_t>* file_sizes_output) {
   uint64_t total_size_output = 0;
-  base::SmallMap<std::map<uint64_t, uint64_t>> file_id_to_sizes;
+  base::small_map<std::map<uint64_t, uint64_t>> file_id_to_sizes;
   for (const auto& item : unreserved_file_items) {
     const DataElement& element = item->item()->data_element();
     uint64_t file_id = BlobDataBuilder::GetFutureFileID(element);
diff --git a/third_party/WebKit/LayoutTests/TestExpectations b/third_party/WebKit/LayoutTests/TestExpectations
index 391a03c..ddcc19e 100644
--- a/third_party/WebKit/LayoutTests/TestExpectations
+++ b/third_party/WebKit/LayoutTests/TestExpectations
@@ -269,8 +269,8 @@
 # LayoutNG - is a new layout system for Blink.
 
 ### external/wpt/css/CSS2/positioning
-#### Passed: 264 50%
-#### Skipped: 259
+#### Passed: 263 50%
+#### Skipped: 260
 crbug.com/635619 virtual/layout_ng/external/wpt/css/CSS2/positioning/absolute-non-replaced-height-002.xht [ Skip ]
 crbug.com/635619 virtual/layout_ng/external/wpt/css/CSS2/positioning/absolute-non-replaced-height-003.xht [ Skip ]
 crbug.com/635619 virtual/layout_ng/external/wpt/css/CSS2/positioning/absolute-non-replaced-height-004.xht [ Skip ]
@@ -397,6 +397,7 @@
 crbug.com/635619 virtual/layout_ng/external/wpt/css/CSS2/positioning/position-absolute-001.xht [ Skip ]
 crbug.com/635619 virtual/layout_ng/external/wpt/css/CSS2/positioning/position-absolute-004.xht [ Skip ]
 crbug.com/635619 virtual/layout_ng/external/wpt/css/CSS2/positioning/position-absolute-006.xht [ Skip ]
+crbug.com/635619 virtual/layout_ng/external/wpt/css/CSS2/positioning/position-absolute-007.xht [ Skip ]
 crbug.com/635619 virtual/layout_ng/external/wpt/css/CSS2/positioning/position-applies-to-001.xht [ Skip ]
 crbug.com/635619 virtual/layout_ng/external/wpt/css/CSS2/positioning/position-applies-to-002.xht [ Skip ]
 crbug.com/635619 virtual/layout_ng/external/wpt/css/CSS2/positioning/position-applies-to-003.xht [ Skip ]
@@ -3283,3 +3284,5 @@
 crbug.com/710850 virtual/mojo-loading/http/tests/feature-policy/vibrate-enabledforall.php [ Skip ]
 crbug.com/710850 virtual/mojo-loading/http/tests/feature-policy/vibrate-enabledforself.php [ Skip ]
 crbug.com/710850 virtual/mojo-loading/http/tests/feature-policy/vibrate-disabled.php [ Skip ]
+
+crbug.com/713462 [ Mac ] virtual/gpu/fast/canvas/canvas-ImageData-neutered-source.html [ Failure Pass ]
diff --git a/third_party/WebKit/LayoutTests/VirtualTestSuites b/third_party/WebKit/LayoutTests/VirtualTestSuites
index 81e8dc63..dd747fc3 100644
--- a/third_party/WebKit/LayoutTests/VirtualTestSuites
+++ b/third_party/WebKit/LayoutTests/VirtualTestSuites
@@ -126,10 +126,10 @@
   {
     "prefix": "android",
     "base": "fullscreen",
-    "args": ["--enable-features=OverlayScrollbar", "--enable-threaded-compositing",
+    "args": ["--enable-threaded-compositing",
              "--enable-fixed-position-compositing", "--enable-prefer-compositing-to-lcd-text",
              "--enable-composited-scrolling-for-frames", "--enable-gesture-tap-highlight", "--enable-pinch",
-             "--force-overlay-fullscreen-video", "--enable-overscroll-notifications",
+             "--force-overlay-fullscreen-video", "--enable-overlay-scrollbars", "--enable-overscroll-notifications",
              "--enable-fixed-layout", "--enable-viewport", "--disable-canvas-aa",
              "--disable-composited-antialiasing"]
   },
@@ -474,16 +474,6 @@
   },
   {
     "prefix": "layout_ng",
-    "base": "external/wpt/css/CSS2/normal-flow",
-    "args": ["--enable-blink-features=LayoutNG"]
-  },
-  {
-    "prefix": "layout_ng",
-    "base": "external/wpt/css/CSS2/abspos",
-    "args": ["--enable-blink-features=LayoutNG"]
-  },
-  {
-    "prefix": "layout_ng",
     "base": "external/wpt/css/CSS2/positioning",
     "args": ["--enable-blink-features=LayoutNG"]
   },
diff --git a/third_party/WebKit/LayoutTests/animations/animations-parsing-expected.txt b/third_party/WebKit/LayoutTests/animations/animations-parsing-expected.txt
index a253ba01..fb43a84 100644
--- a/third_party/WebKit/LayoutTests/animations/animations-parsing-expected.txt
+++ b/third_party/WebKit/LayoutTests/animations/animations-parsing-expected.txt
@@ -176,6 +176,10 @@
 PASS computedStyle.animationTimingFunction is 'steps(5, start)'
 PASS style.webkitAnimationTimingFunction is 'steps(5, start)'
 PASS computedStyle.webkitAnimationTimingFunction is 'steps(5, start)'
+PASS style.animationTimingFunction is 'frames(5)'
+PASS computedStyle.animationTimingFunction is 'frames(5)'
+PASS style.webkitAnimationTimingFunction is 'frames(5)'
+PASS computedStyle.webkitAnimationTimingFunction is 'frames(5)'
 PASS style.animationTimingFunction is 'ease-in-out, ease-in'
 PASS computedStyle.animationTimingFunction is 'ease-in-out, ease-in'
 PASS style.webkitAnimationTimingFunction is 'ease-in-out, ease-in'
@@ -249,6 +253,18 @@
 PASS computedStyle.animationTimingFunction is 'ease'
 PASS style.webkitAnimationTimingFunction is ''
 PASS computedStyle.webkitAnimationTimingFunction is 'ease'
+PASS style.animationTimingFunction is ''
+PASS computedStyle.animationTimingFunction is 'ease'
+PASS style.webkitAnimationTimingFunction is ''
+PASS computedStyle.webkitAnimationTimingFunction is 'ease'
+PASS style.animationTimingFunction is ''
+PASS computedStyle.animationTimingFunction is 'ease'
+PASS style.webkitAnimationTimingFunction is ''
+PASS computedStyle.webkitAnimationTimingFunction is 'ease'
+PASS style.animationTimingFunction is ''
+PASS computedStyle.animationTimingFunction is 'ease'
+PASS style.webkitAnimationTimingFunction is ''
+PASS computedStyle.webkitAnimationTimingFunction is 'ease'
 PASS style.animationTimingFunction is 'step-middle'
 PASS computedStyle.animationTimingFunction is 'ease'
 PASS style.webkitAnimationTimingFunction is 'step-middle'
diff --git a/third_party/WebKit/LayoutTests/animations/animations-parsing.html b/third_party/WebKit/LayoutTests/animations/animations-parsing.html
index ddf4648..ef3b6c6 100644
--- a/third_party/WebKit/LayoutTests/animations/animations-parsing.html
+++ b/third_party/WebKit/LayoutTests/animations/animations-parsing.html
@@ -288,6 +288,12 @@
 shouldBe("style.webkitAnimationTimingFunction", "'steps(5, start)'");
 shouldBe("computedStyle.webkitAnimationTimingFunction", "'steps(5, start)'");
 
+style.animationTimingFunction = "frames(5)";
+shouldBe("style.animationTimingFunction", "'frames(5)'");
+shouldBe("computedStyle.animationTimingFunction", "'frames(5)'");
+shouldBe("style.webkitAnimationTimingFunction", "'frames(5)'");
+shouldBe("computedStyle.webkitAnimationTimingFunction", "'frames(5)'");
+
 style.animationName = "anim1, anim2";
 
 style.animationTimingFunction = "ease-in-out, ease-in";
@@ -378,6 +384,24 @@
 shouldBe("style.webkitAnimationTimingFunction", "''");
 shouldBe("computedStyle.webkitAnimationTimingFunction", "'ease'");
 
+style.animationTimingFunction = "frame(5)";
+shouldBe("style.animationTimingFunction", "''");
+shouldBe("computedStyle.animationTimingFunction", "'ease'");
+shouldBe("style.webkitAnimationTimingFunction", "''");
+shouldBe("computedStyle.webkitAnimationTimingFunction", "'ease'");
+
+style.animationTimingFunction = "frames(1)";
+shouldBe("style.animationTimingFunction", "''");
+shouldBe("computedStyle.animationTimingFunction", "'ease'");
+shouldBe("style.webkitAnimationTimingFunction", "''");
+shouldBe("computedStyle.webkitAnimationTimingFunction", "'ease'");
+
+style.animationTimingFunction = "frames(2, 3)";
+shouldBe("style.animationTimingFunction", "''");
+shouldBe("computedStyle.animationTimingFunction", "'ease'");
+shouldBe("style.webkitAnimationTimingFunction", "''");
+shouldBe("computedStyle.webkitAnimationTimingFunction", "'ease'");
+
 style.animationTimingFunction = "red";
 shouldBe("style.animationTimingFunction", "''");
 shouldBe("computedStyle.animationTimingFunction", "'ease'");
diff --git a/third_party/WebKit/LayoutTests/animations/timing-functions-expected.txt b/third_party/WebKit/LayoutTests/animations/timing-functions-expected.txt
index 10d97f1..8e55c254 100644
--- a/third_party/WebKit/LayoutTests/animations/timing-functions-expected.txt
+++ b/third_party/WebKit/LayoutTests/animations/timing-functions-expected.txt
@@ -6,8 +6,9 @@
 PASS - "left" property for "box6" element at 0.25s saw something close to: 100
 PASS - "left" property for "box7" element at 0.25s saw something close to: 133
 PASS - "left" property for "box8" element at 0.25s saw something close to: 100
-PASS - "left" property for "box9" element at 0.25s saw something close to: 141
-PASS - "left" property for "box10" element at 0.25s saw something close to: 141
+PASS - "left" property for "box9" element at 0.25s saw something close to: 100
+PASS - "left" property for "middlebox1" element at 0.25s saw something close to: 141
+PASS - "left" property for "middlebox2" element at 0.25s saw something close to: 141
 PASS - "left" property for "box1" element at 0.5s saw something close to: 180
 PASS - "left" property for "box2" element at 0.5s saw something close to: 180
 PASS - "left" property for "box3" element at 0.5s saw something close to: 150
@@ -16,8 +17,9 @@
 PASS - "left" property for "box6" element at 0.5s saw something close to: 133
 PASS - "left" property for "box7" element at 0.5s saw something close to: 166
 PASS - "left" property for "box8" element at 0.5s saw something close to: 133
-PASS - "left" property for "box9" element at 0.5s saw something close to: 180
-PASS - "left" property for "box10" element at 0.5s saw something close to: 180
+PASS - "left" property for "box9" element at 0.5s saw something close to: 150
+PASS - "left" property for "middlebox1" element at 0.5s saw something close to: 180
+PASS - "left" property for "middlebox2" element at 0.5s saw something close to: 180
 PASS - "left" property for "box1" element at 0.75s saw something close to: 196
 PASS - "left" property for "box2" element at 0.75s saw something close to: 196
 PASS - "left" property for "box3" element at 0.75s saw something close to: 175
@@ -26,6 +28,7 @@
 PASS - "left" property for "box6" element at 0.75s saw something close to: 166
 PASS - "left" property for "box7" element at 0.75s saw something close to: 200
 PASS - "left" property for "box8" element at 0.75s saw something close to: 166
-PASS - "left" property for "box9" element at 0.75s saw something close to: 196
-PASS - "left" property for "box10" element at 0.75s saw something close to: 196
+PASS - "left" property for "box9" element at 0.75s saw something close to: 200
+PASS - "left" property for "middlebox1" element at 0.75s saw something close to: 196
+PASS - "left" property for "middlebox2" element at 0.75s saw something close to: 196
 
diff --git a/third_party/WebKit/LayoutTests/animations/timing-functions.html b/third_party/WebKit/LayoutTests/animations/timing-functions.html
index 5f7721fc..cb03f81 100644
--- a/third_party/WebKit/LayoutTests/animations/timing-functions.html
+++ b/third_party/WebKit/LayoutTests/animations/timing-functions.html
@@ -65,14 +65,17 @@
     #box8 {
       animation-timing-function: steps(3, end);
     }
+    #box9 {
+      animation-timing-function: frames(3);
+    }
     /*
      * The step-middle functions are invalid except through the Web Animations API
      * and should behave like 'ease', unless step-middle has been added to the CSS specification.
      */
-    #box9 {
+    #middlebox1 {
       animation-timing-function: steps(3, middle);
     }
-    #box10 {
+    #middlebox2 {
       animation-timing-function: step-middle;
     }
     
@@ -106,12 +109,15 @@
       [0.25, "box8", "left", 100, 5],
       [0.50, "box8", "left", 133, 5],
       [0.75, "box8", "left", 166, 5],
-      [0.25, "box9", "left", 141, 5],
-      [0.50, "box9", "left", 180, 5],
-      [0.75, "box9", "left", 196, 5],
-      [0.25, "box10", "left", 141, 5],
-      [0.50, "box10", "left", 180, 5],
-      [0.75, "box10", "left", 196, 5],
+      [0.25, "box9", "left", 100, 5],
+      [0.50, "box9", "left", 150, 5],
+      [0.75, "box9", "left", 200, 5],
+      [0.25, "middlebox1", "left", 141, 5],
+      [0.50, "middlebox1", "left", 180, 5],
+      [0.75, "middlebox1", "left", 196, 5],
+      [0.25, "middlebox2", "left", 141, 5],
+      [0.50, "middlebox2", "left", 180, 5],
+      [0.75, "middlebox2", "left", 196, 5],
     ];
     
     runAnimationTest(expectedValues);
@@ -137,9 +143,11 @@
 </div>
 <div class="box" id="box8">
 </div>
-<div class="box-step-middle" id="box9">
+<div class="box" id="box9">
 </div>
-<div class="box-step-middle" id="box10">
+<div class="box-step-middle" id="middlebox1">
+</div>
+<div class="box-step-middle" id="middlebox2">
 </div>
 <div id="result">
 </div>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css-timing-1/frames-timing-functions-output-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/css-timing-1/frames-timing-functions-output-expected.txt
deleted file mode 100644
index 0ddd60ff..0000000
--- a/third_party/WebKit/LayoutTests/external/wpt/css-timing-1/frames-timing-functions-output-expected.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-This is a testharness.js-based test.
-FAIL For an input progress of 0.0, the output of a frames timing function is the first frame assert_equals: expected "0px" but got "auto"
-FAIL At a frame boundary, the output of a frames timing function is the next frame assert_equals: expected "0px" but got "auto"
-FAIL For an input progress of 1.0, the output of a frames timing function is the final frame assert_equals: expected "100px" but got "auto"
-FAIL The number of frames is correctly reflected in the frames timing function output assert_equals: expected "0px" but got "auto"
-FAIL The number of frames is correctly reflected in the frames timing function output on CSS Transitions assert_equals: expected "0px" but got "100px"
-FAIL frames easing with input progress greater than 1 Failed to execute 'animate' on 'Element': 'frames(2)' is not a valid value for easing
-FAIL frames easing with input progress greater than 1.5 Failed to execute 'animate' on 'Element': 'frames(2)' is not a valid value for easing
-FAIL frames easing with input progress less than 0 Failed to execute 'animate' on 'Element': 'frames(2)' is not a valid value for easing
-Harness: the test ran to completion.
-
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css-timing-1/frames-timing-functions-syntax-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/css-timing-1/frames-timing-functions-syntax-expected.txt
deleted file mode 100644
index 6b236698..0000000
--- a/third_party/WebKit/LayoutTests/external/wpt/css-timing-1/frames-timing-functions-syntax-expected.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-This is a testharness.js-based test.
-PASS The number of frames must be a positive integer greater than 1, or we fallback to the previously-set easing 
-FAIL The serialization of frames is 'frames(n)', n is the number of frames assert_equals: expected "frames(2)" but got "ease"
-Harness: the test ran to completion.
-
diff --git a/third_party/WebKit/LayoutTests/external/wpt/web-animations/interfaces/AnimationEffectTiming/easing-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/web-animations/interfaces/AnimationEffectTiming/easing-expected.txt
deleted file mode 100644
index ef6e612..0000000
--- a/third_party/WebKit/LayoutTests/external/wpt/web-animations/interfaces/AnimationEffectTiming/easing-expected.txt
+++ /dev/null
@@ -1,56 +0,0 @@
-This is a testharness.js-based test.
-Found 52 tests; 50 PASS, 2 FAIL, 0 TIMEOUT, 0 NOTRUN.
-PASS Test default value 
-PASS step-start function 
-PASS steps(1, start) function 
-PASS steps(2, start) function 
-PASS step-end function 
-PASS steps(1) function 
-PASS steps(1, end) function 
-PASS steps(2, end) function 
-FAIL frames function Failed to set the 'easing' property on 'AnimationEffectTiming': 'frames(5)' is not a valid value for easing
-PASS linear function 
-PASS ease function 
-PASS ease-in function 
-PASS ease-in-out function 
-PASS ease-out function 
-PASS easing function which produces values greater than 1 
-PASS easing function which produces values less than 1 
-PASS Invalid effect easing value test: '' 
-PASS Invalid effect easing value test: '7' 
-PASS Invalid effect easing value test: 'test' 
-PASS Invalid effect easing value test: 'initial' 
-PASS Invalid effect easing value test: 'inherit' 
-PASS Invalid effect easing value test: 'unset' 
-PASS Invalid effect easing value test: 'cubic-bezier(1.1, 0, 1, 1)' 
-PASS Invalid effect easing value test: 'cubic-bezier(0, 0, 1.1, 1)' 
-PASS Invalid effect easing value test: 'cubic-bezier(-0.1, 0, 1, 1)' 
-PASS Invalid effect easing value test: 'cubic-bezier(0, 0, -0.1, 1)' 
-PASS Invalid effect easing value test: 'cubic-bezier(0.1, 0, 4, 0.4)' 
-PASS Invalid effect easing value test: 'steps(-1, start)' 
-PASS Invalid effect easing value test: 'steps(0.1, start)' 
-PASS Invalid effect easing value test: 'steps(3, nowhere)' 
-PASS Invalid effect easing value test: 'steps(-3, end)' 
-PASS Invalid effect easing value test: 'function (a){return a}' 
-PASS Invalid effect easing value test: 'function (x){return x}' 
-PASS Invalid effect easing value test: 'function(x, y){return 0.3}' 
-PASS Invalid effect easing value test: 'frames(1)' 
-PASS Invalid effect easing value test: 'frames' 
-PASS Invalid effect easing value test: 'frames()' 
-PASS Invalid effect easing value test: 'frames(,)' 
-PASS Invalid effect easing value test: 'frames(a)' 
-PASS Invalid effect easing value test: 'frames(2.0)' 
-PASS Invalid effect easing value test: 'frames(2.5)' 
-PASS Invalid effect easing value test: 'frames(2 3)' 
-PASS Canonical easing 'ease' is returned as set 
-PASS Canonical easing 'linear' is returned as set 
-PASS Canonical easing 'ease-in' is returned as set 
-PASS Canonical easing 'ease-out' is returned as set 
-PASS Canonical easing 'ease-in-out' is returned as set 
-PASS Canonical easing 'cubic-bezier(0.1, 5, 0.23, 0)' is returned as set 
-PASS Canonical easing 'steps(3, start)' is returned as set 
-PASS Canonical easing 'steps(3)' is returned as set 
-FAIL Canonical easing 'frames(3)' is returned as set Failed to set the 'easing' property on 'AnimationEffectTiming': 'frames(3)' is not a valid value for easing
-PASS Change the easing while the animation is running 
-Harness: the test ran to completion.
-
diff --git a/third_party/WebKit/LayoutTests/external/wpt/web-animations/timing-model/time-transformations/transformed-progress-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/web-animations/timing-model/time-transformations/transformed-progress-expected.txt
index 6a9484af..a3b7903 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/web-animations/timing-model/time-transformations/transformed-progress-expected.txt
+++ b/third_party/WebKit/LayoutTests/external/wpt/web-animations/timing-model/time-transformations/transformed-progress-expected.txt
@@ -6,7 +6,7 @@
 PASS Transformed progress for steps(1) function 
 PASS Transformed progress for steps(1, end) function 
 PASS Transformed progress for steps(2, end) function 
-FAIL Transformed progress for frames function Failed to execute 'animate' on 'Element': 'frames(5)' is not a valid value for easing
+PASS Transformed progress for frames function 
 PASS Transformed progress for linear function 
 FAIL Transformed progress for ease function assert_approx_equals: The progress should be approximately 0.40851059137130497 at 250ms expected 0.40851059137130497 +/- 0.01 but got 0.41869212962962965
 PASS Transformed progress for ease-in function 
@@ -25,7 +25,7 @@
 PASS Test bounds point of step-end easing 
 FAIL Test bounds point of step-end easing with iterationStart and delay assert_equals: Progress at 0ms expected 0 but got 0.5
 PASS Test bounds point of step-end easing with iterationStart not at a transition point 
-FAIL Test bounds point of frames easing Failed to execute 'animate' on 'Element': 'frames(2)' is not a valid value for easing
-FAIL Test bounds point of frames easing with iterationStart and delay Failed to execute 'animate' on 'Element': 'frames(2)' is not a valid value for easing
+PASS Test bounds point of frames easing 
+PASS Test bounds point of frames easing with iterationStart and delay 
 Harness: the test ran to completion.
 
diff --git a/third_party/WebKit/LayoutTests/fast/css/fontfaceset-check-platform-fonts.html b/third_party/WebKit/LayoutTests/fast/css/fontfaceset-check-platform-fonts.html
index 5263db6..81473a4 100644
--- a/third_party/WebKit/LayoutTests/fast/css/fontfaceset-check-platform-fonts.html
+++ b/third_party/WebKit/LayoutTests/fast/css/fontfaceset-check-platform-fonts.html
@@ -4,11 +4,11 @@
 <script>
 
 test(() => {
-  assert_true(document.fonts.check('10px Arial'));
-  assert_false(document.fonts.check('10px Nonexistent'));
-  assert_true(document.fonts.check('10px sans-serif'));
-  assert_true(document.fonts.check('10px Nonexistent, monospace'));
-  assert_false(document.fonts.check('10px Nonexistent1, Nonexistent2'));
+  assert_true(document.fonts.check('10px Arial'), 'Arial');
+  assert_false(document.fonts.check('10px Nonexistent'), 'Nonexistent');
+  assert_true(document.fonts.check('10px sans-serif'), 'sans-serif');
+  assert_true(document.fonts.check('10px Nonexistent, monospace'), 'Nonexistent, monospace');
+  assert_false(document.fonts.check('10px Nonexistent1, Nonexistent2'), 'Nonexistent1, Nonexistent2');
 }, 'Tests FontFaceSet#check() returns true for platform fonts');
 
 </script>
diff --git a/third_party/WebKit/LayoutTests/fast/css/media-attr-non-matching-dynamic.html b/third_party/WebKit/LayoutTests/fast/css/media-attr-non-matching-dynamic.html
new file mode 100644
index 0000000..86fbd88c
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/css/media-attr-non-matching-dynamic.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<style>
+  iframe {
+    height: 50px;
+    width: 50px;
+  }
+</style>
+<iframe></iframe>
+<script>
+  test(() => {
+    var iframe = document.querySelector("iframe");
+    var doc = iframe.contentDocument;
+    var style = doc.createElement("style");
+    style.setAttribute("media", "(min-width: 100px)");
+    style.textContent = "body { background: green }";
+    doc.head.appendChild(style);
+    assert_equals(doc.defaultView.getComputedStyle(doc.body).backgroundColor, "rgba(0, 0, 0, 0)");
+    iframe.setAttribute("style", "height: 100px; width: 100px");
+    assert_equals(doc.defaultView.getComputedStyle(doc.body).backgroundColor, "rgb(0, 128, 0)");
+  }, "Sheet with initially non-matching viewport media query applies after resize");
+</script>
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector/file-system-project-mapping-expected.txt b/third_party/WebKit/LayoutTests/http/tests/inspector/file-system-project-mapping-expected.txt
deleted file mode 100644
index 92abcdc..0000000
--- a/third_party/WebKit/LayoutTests/http/tests/inspector/file-system-project-mapping-expected.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-Tests file system project mappings.
-
-
-Running: testProjectBasedMapping
-Adding file system.
-UISourceCode uri to url mappings:
-    file:///var/www/html/foo.js -> http://127.0.0.1:8000/inspector/resources/html/foo.js
-    file:///var/www/html2/bar.js -> http://127.0.0.1:8000/inspector/resources/html2/bar.js
-
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector/file-system-project-mapping.html b/third_party/WebKit/LayoutTests/http/tests/inspector/file-system-project-mapping.html
deleted file mode 100644
index 76a7ab2e..0000000
--- a/third_party/WebKit/LayoutTests/http/tests/inspector/file-system-project-mapping.html
+++ /dev/null
@@ -1,66 +0,0 @@
-<html>
-<head>
-<script src="inspector-test.js"></script>
-<script src="debugger-test.js"></script>
-<script src="persistence/persistence-test.js"></script>
-<script src="isolated-filesystem-test.js"></script>
-<script>
-function addScript(url)
-{
-    var script = document.createElement("script");
-    script.setAttribute("src", url);
-    document.head.appendChild(script);
-}
-
-function test()
-{
-    InspectorTest.forceUseDefaultMapping();
-    var target = InspectorTest.mainTarget;
-    var fileSystemProjectId = Persistence.FileSystemWorkspaceBinding.projectId("file:///var/www");
-
-    function dumpFileSystemUISourceCodesMappings()
-    {
-        var uiSourceCodes = Workspace.workspace.project(fileSystemProjectId).uiSourceCodes();
-        InspectorTest.addResult("UISourceCode uri to url mappings:");
-        for (var uiSourceCode of uiSourceCodes) {
-            var binding = Persistence.persistence.binding(uiSourceCode);
-            var url = binding ? binding.network.url() : "";
-            InspectorTest.addResult("    " + uiSourceCode.url() + " -> " + url);
-        }
-    }
-
-    InspectorTest.runTestSuite([
-        function testProjectBasedMapping(next)
-        {
-            InspectorTest.addResult("Adding file system.");
-            var fs = new InspectorTest.TestFileSystem("file:///var/www");
-            fs.root.mkdir("html").addFile("foo.js", "var foo = 1;");
-            fs.root.mkdir("html2").addFile("bar.js", "var bar = 2;");
-            fs.root.addFile(".devtools", JSON.stringify({ mappings: [ { folder: "/html/", url: "http://127.0.0.1:8000/inspector/resources/html/" }, { folder: "/html2/", url: "http://127.0.0.1:8000/inspector/resources/html2/" } ]}));
-            fs.reportCreated(fileSystemCreated);
-
-            function fileSystemCreated()
-            {
-                InspectorTest.evaluateInPage("addScript('resources/html/foo.js')");
-                InspectorTest.evaluateInPage("addScript('resources/html2/bar.js')");
-                Promise.all([
-                    InspectorTest.waitForBinding("foo.js"),
-                    InspectorTest.waitForBinding("bar.js")
-                ]).then(onBindings);
-            }
-
-            function onBindings()
-            {
-                dumpFileSystemUISourceCodesMappings();
-                fs.reportRemoved();
-                next();
-            }
-        }
-    ]);
-};
-</script>
-</head>
-<body onload="runTest()">
-<p>Tests file system project mappings.</p>
-</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/http/tests/serviceworker/chromium/resources/respondwith-fetch-worker.js b/third_party/WebKit/LayoutTests/http/tests/serviceworker/chromium/resources/respondwith-fetch-worker.js
new file mode 100644
index 0000000..887aa01
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/serviceworker/chromium/resources/respondwith-fetch-worker.js
@@ -0,0 +1 @@
+addEventListener('fetch', e => e.respondWith(fetch(e.request)));
diff --git a/third_party/WebKit/LayoutTests/http/tests/serviceworker/chromium/resources/slow-fetch-and-stop-worker-iframe.html b/third_party/WebKit/LayoutTests/http/tests/serviceworker/chromium/resources/slow-fetch-and-stop-worker-iframe.html
new file mode 100644
index 0000000..04b2113
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/serviceworker/chromium/resources/slow-fetch-and-stop-worker-iframe.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<script>
+addEventListener('message', e => {
+    fetch('./slow-response.php')
+      .then(response => {
+          internals.terminateServiceWorker(navigator.serviceWorker.controller);
+          return response.text();
+        })
+      .then(() => {
+          e.source.postMessage(
+            'Ugr.. Canceled respondWith resolves the promise somehow.',
+            e.origin);
+        })
+      .catch(() => {
+          e.source.postMessage('Rejected successfully', e.origin);
+        });
+  });
+</script>
diff --git a/third_party/WebKit/LayoutTests/http/tests/serviceworker/chromium/resources/slow-response.php b/third_party/WebKit/LayoutTests/http/tests/serviceworker/chromium/resources/slow-response.php
new file mode 100644
index 0000000..1b772caf
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/serviceworker/chromium/resources/slow-response.php
@@ -0,0 +1,19 @@
+<?php
+function put_chunk($txt) {
+  echo sprintf("%x\r\n", strlen($txt));
+  echo "$txt\r\n";
+}
+
+header("Content-type: text/html; charset=UTF-8");
+header("Transfer-encoding: chunked");
+flush();
+
+for ($i = 0; $i < 10000; $i++) {
+  put_chunk("$i<br>");
+  ob_flush();
+  flush();
+  usleep(1000000);
+}
+echo "0\r\n\r\n";
+
+?>
diff --git a/third_party/WebKit/LayoutTests/http/tests/serviceworker/chromium/stop-worker-during-respond-with.html b/third_party/WebKit/LayoutTests/http/tests/serviceworker/chromium/stop-worker-during-respond-with.html
new file mode 100644
index 0000000..fc564301
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/serviceworker/chromium/stop-worker-during-respond-with.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<!-- This is in chromium/ because it relies on the internals API to terminate a
+  service worker. -->
+<title>Service Worker: Stopping the worker during responding to a fetch</title>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script src="../resources/test-helpers.js"></script>
+<script>
+
+promise_test(t => {
+    var SCOPE = 'resources/slow-fetch-and-stop-worker-iframe.html';
+    var SCRIPT = 'resources/respondwith-fetch-worker.js';
+    return service_worker_unregister_and_register(t, SCRIPT, SCOPE)
+      .then(r => {
+          add_completion_callback(() => r.unregister());
+          return wait_for_state(t, r.installing, 'activated');
+        })
+      .then(() => { return with_iframe(SCOPE, {auto_remove: true}); })
+      .then(f => {
+          return new Promise(resolve => {
+              addEventListener('message', resolve);
+              f.contentWindow.postMessage('run', '*');
+            })
+        })
+      .then(e => {
+          assert_equals(e.data, 'Rejected successfully');
+        });
+  }, 'Stopping the worker during responding fetch event cancels the request.');
+
+</script>
diff --git a/third_party/WebKit/LayoutTests/inspector/file-system-mapping-expected.txt b/third_party/WebKit/LayoutTests/inspector/file-system-mapping-expected.txt
index 8e770c5..abf42de 100644
--- a/third_party/WebKit/LayoutTests/inspector/file-system-mapping-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector/file-system-mapping-expected.txt
@@ -36,7 +36,7 @@
      - file:///home/username/projects/bar
      - file:///home/username/project/build
      - file:///www/site1
-         - {"fileSystemPath":"file:///www/site1","urlPrefix":"http://localhost/","pathPrefix":"/","configurable":true}
+         - {"fileSystemPath":"file:///www/site1","urlPrefix":"http://localhost/","pathPrefix":"/"}
 
 Adding file mapping (file:///www/site1, http://www.foo.com/, /foo/)
 Testing file system mapping.
@@ -45,44 +45,44 @@
      - file:///home/username/projects/bar
      - file:///home/username/project/build
      - file:///www/site1
-         - {"fileSystemPath":"file:///www/site1","urlPrefix":"http://localhost/","pathPrefix":"/","configurable":true}
-         - {"fileSystemPath":"file:///www/site1","urlPrefix":"http://www.foo.com/","pathPrefix":"/foo/","configurable":true}
+         - {"fileSystemPath":"file:///www/site1","urlPrefix":"http://localhost/","pathPrefix":"/"}
+         - {"fileSystemPath":"file:///www/site1","urlPrefix":"http://www.foo.com/","pathPrefix":"/foo/"}
 
 Adding file mapping (file:///home/username/projects/foo, http://www.example.com/bar/, /foo/)
 Testing file system mapping.
     file system paths:
      - file:///home/username/projects/foo
-         - {"fileSystemPath":"file:///home/username/projects/foo","urlPrefix":"http://www.example.com/bar/","pathPrefix":"/foo/","configurable":true}
+         - {"fileSystemPath":"file:///home/username/projects/foo","urlPrefix":"http://www.example.com/bar/","pathPrefix":"/foo/"}
      - file:///home/username/projects/bar
      - file:///home/username/project/build
      - file:///www/site1
-         - {"fileSystemPath":"file:///www/site1","urlPrefix":"http://localhost/","pathPrefix":"/","configurable":true}
-         - {"fileSystemPath":"file:///www/site1","urlPrefix":"http://www.foo.com/","pathPrefix":"/foo/","configurable":true}
+         - {"fileSystemPath":"file:///www/site1","urlPrefix":"http://localhost/","pathPrefix":"/"}
+         - {"fileSystemPath":"file:///www/site1","urlPrefix":"http://www.foo.com/","pathPrefix":"/foo/"}
 
 Adding file mapping for resource (http://www.bar.com/foo/folder/42.js, file:///home/username/projects/foo, file:///home/username/projects/foo/baz/folder/42.js)
 Testing file system mapping.
     file system paths:
      - file:///home/username/projects/foo
-         - {"fileSystemPath":"file:///home/username/projects/foo","urlPrefix":"http://www.example.com/bar/","pathPrefix":"/foo/","configurable":true}
-         - {"fileSystemPath":"file:///home/username/projects/foo","urlPrefix":"http://www.bar.com/foo/","pathPrefix":"/baz/","configurable":true}
+         - {"fileSystemPath":"file:///home/username/projects/foo","urlPrefix":"http://www.example.com/bar/","pathPrefix":"/foo/"}
+         - {"fileSystemPath":"file:///home/username/projects/foo","urlPrefix":"http://www.bar.com/foo/","pathPrefix":"/baz/"}
      - file:///home/username/projects/bar
      - file:///home/username/project/build
      - file:///www/site1
-         - {"fileSystemPath":"file:///www/site1","urlPrefix":"http://localhost/","pathPrefix":"/","configurable":true}
-         - {"fileSystemPath":"file:///www/site1","urlPrefix":"http://www.foo.com/","pathPrefix":"/foo/","configurable":true}
+         - {"fileSystemPath":"file:///www/site1","urlPrefix":"http://localhost/","pathPrefix":"/"}
+         - {"fileSystemPath":"file:///www/site1","urlPrefix":"http://www.foo.com/","pathPrefix":"/foo/"}
 
 Adding file mapping for resource (http://localhost:3333/build/layout.css, file:///home/username/project/build, file:///home/username/project/build/layout.css)
 Testing file system mapping.
     file system paths:
      - file:///home/username/projects/foo
-         - {"fileSystemPath":"file:///home/username/projects/foo","urlPrefix":"http://www.example.com/bar/","pathPrefix":"/foo/","configurable":true}
-         - {"fileSystemPath":"file:///home/username/projects/foo","urlPrefix":"http://www.bar.com/foo/","pathPrefix":"/baz/","configurable":true}
+         - {"fileSystemPath":"file:///home/username/projects/foo","urlPrefix":"http://www.example.com/bar/","pathPrefix":"/foo/"}
+         - {"fileSystemPath":"file:///home/username/projects/foo","urlPrefix":"http://www.bar.com/foo/","pathPrefix":"/baz/"}
      - file:///home/username/projects/bar
      - file:///home/username/project/build
-         - {"fileSystemPath":"file:///home/username/project/build","urlPrefix":"http://localhost:3333/build/","pathPrefix":"/","configurable":true}
+         - {"fileSystemPath":"file:///home/username/project/build","urlPrefix":"http://localhost:3333/build/","pathPrefix":"/"}
      - file:///www/site1
-         - {"fileSystemPath":"file:///www/site1","urlPrefix":"http://localhost/","pathPrefix":"/","configurable":true}
-         - {"fileSystemPath":"file:///www/site1","urlPrefix":"http://www.foo.com/","pathPrefix":"/foo/","configurable":true}
+         - {"fileSystemPath":"file:///www/site1","urlPrefix":"http://localhost/","pathPrefix":"/"}
+         - {"fileSystemPath":"file:///www/site1","urlPrefix":"http://www.foo.com/","pathPrefix":"/foo/"}
 
 Testing mappings for url:
     Has mapping for 'http://www.bar.com/foo/folder/42.js': true
@@ -115,46 +115,46 @@
 Testing file system mapping.
     file system paths:
      - file:///home/username/projects/foo
-         - {"fileSystemPath":"file:///home/username/projects/foo","urlPrefix":"http://www.example.com/bar/","pathPrefix":"/foo/","configurable":true}
-         - {"fileSystemPath":"file:///home/username/projects/foo","urlPrefix":"http://www.bar.com/foo/","pathPrefix":"/baz/","configurable":true}
+         - {"fileSystemPath":"file:///home/username/projects/foo","urlPrefix":"http://www.example.com/bar/","pathPrefix":"/foo/"}
+         - {"fileSystemPath":"file:///home/username/projects/foo","urlPrefix":"http://www.bar.com/foo/","pathPrefix":"/baz/"}
      - file:///home/username/projects/bar
      - file:///home/username/project/build
-         - {"fileSystemPath":"file:///home/username/project/build","urlPrefix":"http://localhost:3333/build/","pathPrefix":"/","configurable":true}
+         - {"fileSystemPath":"file:///home/username/project/build","urlPrefix":"http://localhost:3333/build/","pathPrefix":"/"}
      - file:///www/site1
-         - {"fileSystemPath":"file:///www/site1","urlPrefix":"http://localhost/","pathPrefix":"/","configurable":true}
-         - {"fileSystemPath":"file:///www/site1","urlPrefix":"http://www.foo.com/","pathPrefix":"/foo/","configurable":true}
+         - {"fileSystemPath":"file:///www/site1","urlPrefix":"http://localhost/","pathPrefix":"/"}
+         - {"fileSystemPath":"file:///www/site1","urlPrefix":"http://www.foo.com/","pathPrefix":"/foo/"}
 
 Removing file mapping for url http://www.bar.com/foo/folder/42.js
 Testing file system mapping.
     file system paths:
      - file:///home/username/projects/foo
-         - {"fileSystemPath":"file:///home/username/projects/foo","urlPrefix":"http://www.example.com/bar/","pathPrefix":"/foo/","configurable":true}
+         - {"fileSystemPath":"file:///home/username/projects/foo","urlPrefix":"http://www.example.com/bar/","pathPrefix":"/foo/"}
      - file:///home/username/projects/bar
      - file:///home/username/project/build
-         - {"fileSystemPath":"file:///home/username/project/build","urlPrefix":"http://localhost:3333/build/","pathPrefix":"/","configurable":true}
+         - {"fileSystemPath":"file:///home/username/project/build","urlPrefix":"http://localhost:3333/build/","pathPrefix":"/"}
      - file:///www/site1
-         - {"fileSystemPath":"file:///www/site1","urlPrefix":"http://localhost/","pathPrefix":"/","configurable":true}
-         - {"fileSystemPath":"file:///www/site1","urlPrefix":"http://www.foo.com/","pathPrefix":"/foo/","configurable":true}
+         - {"fileSystemPath":"file:///www/site1","urlPrefix":"http://localhost/","pathPrefix":"/"}
+         - {"fileSystemPath":"file:///www/site1","urlPrefix":"http://www.foo.com/","pathPrefix":"/foo/"}
 
 Removing file mapping (file:///www/site1, http://localhost/, /)
 Testing file system mapping.
     file system paths:
      - file:///home/username/projects/foo
-         - {"fileSystemPath":"file:///home/username/projects/foo","urlPrefix":"http://www.example.com/bar/","pathPrefix":"/foo/","configurable":true}
+         - {"fileSystemPath":"file:///home/username/projects/foo","urlPrefix":"http://www.example.com/bar/","pathPrefix":"/foo/"}
      - file:///home/username/projects/bar
      - file:///home/username/project/build
-         - {"fileSystemPath":"file:///home/username/project/build","urlPrefix":"http://localhost:3333/build/","pathPrefix":"/","configurable":true}
+         - {"fileSystemPath":"file:///home/username/project/build","urlPrefix":"http://localhost:3333/build/","pathPrefix":"/"}
      - file:///www/site1
-         - {"fileSystemPath":"file:///www/site1","urlPrefix":"http://www.foo.com/","pathPrefix":"/foo/","configurable":true}
+         - {"fileSystemPath":"file:///www/site1","urlPrefix":"http://www.foo.com/","pathPrefix":"/foo/"}
 
 Removing file mapping (file:///www/site1, http://www.foo.com/, /foo/)
 Testing file system mapping.
     file system paths:
      - file:///home/username/projects/foo
-         - {"fileSystemPath":"file:///home/username/projects/foo","urlPrefix":"http://www.example.com/bar/","pathPrefix":"/foo/","configurable":true}
+         - {"fileSystemPath":"file:///home/username/projects/foo","urlPrefix":"http://www.example.com/bar/","pathPrefix":"/foo/"}
      - file:///home/username/projects/bar
      - file:///home/username/project/build
-         - {"fileSystemPath":"file:///home/username/project/build","urlPrefix":"http://localhost:3333/build/","pathPrefix":"/","configurable":true}
+         - {"fileSystemPath":"file:///home/username/project/build","urlPrefix":"http://localhost:3333/build/","pathPrefix":"/"}
      - file:///www/site1
 
 Removing file mapping (file:///home/username/projects/foo, http://www.example.com/bar/, /foo/)
@@ -163,7 +163,7 @@
      - file:///home/username/projects/foo
      - file:///home/username/projects/bar
      - file:///home/username/project/build
-         - {"fileSystemPath":"file:///home/username/project/build","urlPrefix":"http://localhost:3333/build/","pathPrefix":"/","configurable":true}
+         - {"fileSystemPath":"file:///home/username/project/build","urlPrefix":"http://localhost:3333/build/","pathPrefix":"/"}
      - file:///www/site1
 
 Removing file system file:///www/site1
@@ -172,19 +172,19 @@
      - file:///home/username/projects/foo
      - file:///home/username/projects/bar
      - file:///home/username/project/build
-         - {"fileSystemPath":"file:///home/username/project/build","urlPrefix":"http://localhost:3333/build/","pathPrefix":"/","configurable":true}
+         - {"fileSystemPath":"file:///home/username/project/build","urlPrefix":"http://localhost:3333/build/","pathPrefix":"/"}
 
 Removing file system file:///home/username/projects/foo
 Testing file system mapping.
     file system paths:
      - file:///home/username/projects/bar
      - file:///home/username/project/build
-         - {"fileSystemPath":"file:///home/username/project/build","urlPrefix":"http://localhost:3333/build/","pathPrefix":"/","configurable":true}
+         - {"fileSystemPath":"file:///home/username/project/build","urlPrefix":"http://localhost:3333/build/","pathPrefix":"/"}
 
 Removing file system file:///home/username/projects/bar
 Testing file system mapping.
     file system paths:
      - file:///home/username/project/build
-         - {"fileSystemPath":"file:///home/username/project/build","urlPrefix":"http://localhost:3333/build/","pathPrefix":"/","configurable":true}
+         - {"fileSystemPath":"file:///home/username/project/build","urlPrefix":"http://localhost:3333/build/","pathPrefix":"/"}
 
 
diff --git a/third_party/WebKit/LayoutTests/inspector/file-system-mapping-overrides-expected.txt b/third_party/WebKit/LayoutTests/inspector/file-system-mapping-overrides-expected.txt
deleted file mode 100644
index 884c2d0..0000000
--- a/third_party/WebKit/LayoutTests/inspector/file-system-mapping-overrides-expected.txt
+++ /dev/null
@@ -1,15 +0,0 @@
-Tests FileSystemMapping overrides
-
-
-Running: testFileSystemClashDirectOrder
-http://localhost:1234/right_url/file.txt
-
-Running: testFileSystemClashReversedOrder
-http://localhost:1234/right_url/file.txt
-
-Running: testNetworkClashDirectOrder
-file:///Source/devtools/right/file.txt
-
-Running: testNetworkClashReversedOrder
-file:///Source/devtools/right/file.txt
-
diff --git a/third_party/WebKit/LayoutTests/inspector/file-system-mapping-overrides.html b/third_party/WebKit/LayoutTests/inspector/file-system-mapping-overrides.html
deleted file mode 100644
index ac50e1a..0000000
--- a/third_party/WebKit/LayoutTests/inspector/file-system-mapping-overrides.html
+++ /dev/null
@@ -1,66 +0,0 @@
-<html>
-<head>
-<script src="../http/tests/inspector/inspector-test.js"></script>
-<script>
-function test()
-{
-    InspectorTest.runTestSuite([
-        function testFileSystemClashDirectOrder(next)
-        {
-            var fileSystemMapping = new Workspace.FileSystemMapping(Workspace.isolatedFileSystemManager);
-            fileSystemMapping.addFileSystem("file:///Source/devtools");
-
-            fileSystemMapping.addNonConfigurableFileMapping("file:///Source/devtools", "chrome-devtools://devtools/bundled/wrong_url", "/");
-            fileSystemMapping.addFileMapping("file:///Source/devtools", "http://localhost:1234/right_url", "/");
-
-            InspectorTest.addResult(fileSystemMapping.networkURLForFileSystemURL("file:///Source/devtools", "file:///Source/devtools/file.txt"));
-            fileSystemMapping.dispose();
-            next();
-        },
-
-        function testFileSystemClashReversedOrder(next)
-        {
-            var fileSystemMapping = new Workspace.FileSystemMapping(Workspace.isolatedFileSystemManager);
-            fileSystemMapping.addFileSystem("file:///Source/devtools");
-
-            fileSystemMapping.addFileMapping("file:///Source/devtools", "http://localhost:1234/right_url", "/");
-            fileSystemMapping.addNonConfigurableFileMapping("file:///Source/devtools", "chrome-devtools://devtools/wrong_url", "/");
-
-            InspectorTest.addResult(fileSystemMapping.networkURLForFileSystemURL("file:///Source/devtools", "file:///Source/devtools/file.txt"));
-            fileSystemMapping.dispose();
-            next();
-        },
-
-        function testNetworkClashDirectOrder(next)
-        {
-            var fileSystemMapping = new Workspace.FileSystemMapping(Workspace.isolatedFileSystemManager);
-            fileSystemMapping.addFileSystem("file:///Source/devtools");
-
-            fileSystemMapping.addNonConfigurableFileMapping("file:///Source/devtools", "http://localhost:1234/front_end", "/wrong");
-            fileSystemMapping.addFileMapping("file:///Source/devtools", "http://localhost:1234/front_end", "/right");
-
-            InspectorTest.addResult(fileSystemMapping.fileForURL("http://localhost:1234/front_end/file.txt").fileURL);
-            fileSystemMapping.dispose();
-            next();
-        },
-
-        function testNetworkClashReversedOrder(next)
-        {
-            var fileSystemMapping = new Workspace.FileSystemMapping(Workspace.isolatedFileSystemManager);
-            fileSystemMapping.addFileSystem("file:///Source/devtools");
-
-            fileSystemMapping.addFileMapping("file:///Source/devtools", "http://localhost:1234/front_end", "/right");
-            fileSystemMapping.addNonConfigurableFileMapping("file:///Source/devtools", "http://localhost:1234/front_end", "/wrong");
-
-            InspectorTest.addResult(fileSystemMapping.fileForURL("http://localhost:1234/front_end/file.txt").fileURL);
-            fileSystemMapping.dispose();
-            next();
-        },
-    ]);
-}
-</script>
-</head>
-<body onload="runTest()">
-<p>Tests FileSystemMapping overrides</p>
-</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/inspector/file-system-project-expected.txt b/third_party/WebKit/LayoutTests/inspector/file-system-project-expected.txt
index 90fe56a5..132dc85 100644
--- a/third_party/WebKit/LayoutTests/inspector/file-system-project-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector/file-system-project-expected.txt
@@ -53,13 +53,6 @@
   - file:///var/www3/html/foo.js
   - file:///var/www3/bar.js
 
-Running: testExcludesViaProject
-
--- Excluded /html2/ via .devtools --
-Dumping uiSourceCodes origin URLs:
-  - file:///var/www3/html/foo.js
-  - file:///var/www3/bar.js
-
 Running: testFileAddedExternally
 -- Original tree --
 Dumping uiSourceCodes origin URLs:
diff --git a/third_party/WebKit/LayoutTests/inspector/file-system-project.html b/third_party/WebKit/LayoutTests/inspector/file-system-project.html
index cb27d54..ef42204a 100644
--- a/third_party/WebKit/LayoutTests/inspector/file-system-project.html
+++ b/third_party/WebKit/LayoutTests/inspector/file-system-project.html
@@ -178,26 +178,6 @@
             }
         },
 
-        function testExcludesViaProject(next)
-        {
-            var fs = new InspectorTest.TestFileSystem("file:///var/www3");
-            fs.root.addFile(".devtools", JSON.stringify({excludes:["/html2/"]}));
-            fs.root.mkdir("html").addFile("foo.js", "");
-            fs.root.mkdir(".git").addFile("foogit.js", "");
-            fs.root.addFile("bar.js", "");
-            fs.root.mkdir("html2").addFile("foo.js", "");
-            fs.reportCreated(dumpExcludes);
-
-            function dumpExcludes()
-            {
-                InspectorTest.addResult("");
-                InspectorTest.addResult("-- Excluded /html2/ via .devtools --");
-                dumpWorkspaceUISourceCodes();
-                fs.reportRemoved();
-                next();
-            }
-        },
-
         function testFileAddedExternally(next)
         {
             var fs = new InspectorTest.TestFileSystem("file:///var/www4");
diff --git a/third_party/WebKit/LayoutTests/platform/linux/external/wpt/web-animations/animation-model/keyframe-effects/effect-value-transformed-distance-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/external/wpt/web-animations/animation-model/keyframe-effects/effect-value-transformed-distance-expected.txt
index 6182a61e..22f14c5 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/external/wpt/web-animations/animation-model/keyframe-effects/effect-value-transformed-distance-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/linux/external/wpt/web-animations/animation-model/keyframe-effects/effect-value-transformed-distance-expected.txt
@@ -6,7 +6,7 @@
 PASS A steps(1) function on a keyframe affects the resulting style 
 PASS A steps(1, end) function on a keyframe affects the resulting style 
 PASS A steps(2, end) function on a keyframe affects the resulting style 
-FAIL A frames function on a keyframe affects the resulting style Failed to execute 'animate' on 'Element': 'frames(5)' is not a valid value for easing
+PASS A frames function on a keyframe affects the resulting style 
 PASS A linear function on a keyframe affects the resulting style 
 FAIL A ease function on a keyframe affects the resulting style assert_approx_equals: The width should be approximately 109.47963055884654 at 1100ms expected 109.47963055884654 +/- 0.01 but got 109.312
 PASS A ease-in function on a keyframe affects the resulting style 
@@ -21,7 +21,7 @@
 PASS Linear-equivalent cubic-bezier keyframe easing applied to an effect with a steps(1) function does not alter the result 
 PASS Linear-equivalent cubic-bezier keyframe easing applied to an effect with a steps(1, end) function does not alter the result 
 FAIL Linear-equivalent cubic-bezier keyframe easing applied to an effect with a steps(2, end) function does not alter the result assert_equals: The 'width' of the animated elements should be equal at 500ms expected "50.3438px" but got "50px"
-FAIL Linear-equivalent cubic-bezier keyframe easing applied to an effect with a frames function does not alter the result Failed to execute 'animate' on 'Element': 'frames(5)' is not a valid value for easing
+FAIL Linear-equivalent cubic-bezier keyframe easing applied to an effect with a frames function does not alter the result assert_equals: The 'width' of the animated elements should be equal at 250ms expected "25.0938px" but got "25px"
 FAIL Linear-equivalent cubic-bezier keyframe easing applied to an effect with a linear function does not alter the result assert_equals: The 'width' of the animated elements should be equal at 250ms expected "25.0938px" but got "25px"
 FAIL Linear-equivalent cubic-bezier keyframe easing applied to an effect with a ease function does not alter the result assert_equals: The 'width' of the animated elements should be equal at 250ms expected "41.875px" but got "41.8594px"
 FAIL Linear-equivalent cubic-bezier keyframe easing applied to an effect with a ease-in function does not alter the result assert_equals: The 'width' of the animated elements should be equal at 250ms expected "8.95312px" but got "9.32812px"
diff --git a/third_party/WebKit/LayoutTests/platform/mac/external/wpt/web-animations/animation-model/keyframe-effects/effect-value-transformed-distance-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/external/wpt/web-animations/animation-model/keyframe-effects/effect-value-transformed-distance-expected.txt
index 6182a61e..22f14c5 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/external/wpt/web-animations/animation-model/keyframe-effects/effect-value-transformed-distance-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/mac/external/wpt/web-animations/animation-model/keyframe-effects/effect-value-transformed-distance-expected.txt
@@ -6,7 +6,7 @@
 PASS A steps(1) function on a keyframe affects the resulting style 
 PASS A steps(1, end) function on a keyframe affects the resulting style 
 PASS A steps(2, end) function on a keyframe affects the resulting style 
-FAIL A frames function on a keyframe affects the resulting style Failed to execute 'animate' on 'Element': 'frames(5)' is not a valid value for easing
+PASS A frames function on a keyframe affects the resulting style 
 PASS A linear function on a keyframe affects the resulting style 
 FAIL A ease function on a keyframe affects the resulting style assert_approx_equals: The width should be approximately 109.47963055884654 at 1100ms expected 109.47963055884654 +/- 0.01 but got 109.312
 PASS A ease-in function on a keyframe affects the resulting style 
@@ -21,7 +21,7 @@
 PASS Linear-equivalent cubic-bezier keyframe easing applied to an effect with a steps(1) function does not alter the result 
 PASS Linear-equivalent cubic-bezier keyframe easing applied to an effect with a steps(1, end) function does not alter the result 
 FAIL Linear-equivalent cubic-bezier keyframe easing applied to an effect with a steps(2, end) function does not alter the result assert_equals: The 'width' of the animated elements should be equal at 500ms expected "50.3438px" but got "50px"
-FAIL Linear-equivalent cubic-bezier keyframe easing applied to an effect with a frames function does not alter the result Failed to execute 'animate' on 'Element': 'frames(5)' is not a valid value for easing
+FAIL Linear-equivalent cubic-bezier keyframe easing applied to an effect with a frames function does not alter the result assert_equals: The 'width' of the animated elements should be equal at 250ms expected "25.0938px" but got "25px"
 FAIL Linear-equivalent cubic-bezier keyframe easing applied to an effect with a linear function does not alter the result assert_equals: The 'width' of the animated elements should be equal at 250ms expected "25.0938px" but got "25px"
 FAIL Linear-equivalent cubic-bezier keyframe easing applied to an effect with a ease function does not alter the result assert_equals: The 'width' of the animated elements should be equal at 250ms expected "41.875px" but got "41.8594px"
 FAIL Linear-equivalent cubic-bezier keyframe easing applied to an effect with a ease-in function does not alter the result assert_equals: The 'width' of the animated elements should be equal at 250ms expected "8.95312px" but got "9.32812px"
diff --git a/third_party/WebKit/LayoutTests/platform/win/external/wpt/web-animations/animation-model/keyframe-effects/effect-value-transformed-distance-expected.txt b/third_party/WebKit/LayoutTests/platform/win/external/wpt/web-animations/animation-model/keyframe-effects/effect-value-transformed-distance-expected.txt
index 9cd7b3c..cdbd32e 100644
--- a/third_party/WebKit/LayoutTests/platform/win/external/wpt/web-animations/animation-model/keyframe-effects/effect-value-transformed-distance-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/win/external/wpt/web-animations/animation-model/keyframe-effects/effect-value-transformed-distance-expected.txt
@@ -6,7 +6,7 @@
 PASS A steps(1) function on a keyframe affects the resulting style 
 PASS A steps(1, end) function on a keyframe affects the resulting style 
 PASS A steps(2, end) function on a keyframe affects the resulting style 
-FAIL A frames function on a keyframe affects the resulting style Failed to execute 'animate' on 'Element': 'frames(5)' is not a valid value for easing
+PASS A frames function on a keyframe affects the resulting style 
 PASS A linear function on a keyframe affects the resulting style 
 FAIL A ease function on a keyframe affects the resulting style assert_approx_equals: The width should be approximately 109.47963055884654 at 1100ms expected 109.47963055884654 +/- 0.01 but got 109.313
 PASS A ease-in function on a keyframe affects the resulting style 
@@ -21,7 +21,7 @@
 PASS Linear-equivalent cubic-bezier keyframe easing applied to an effect with a steps(1) function does not alter the result 
 PASS Linear-equivalent cubic-bezier keyframe easing applied to an effect with a steps(1, end) function does not alter the result 
 FAIL Linear-equivalent cubic-bezier keyframe easing applied to an effect with a steps(2, end) function does not alter the result assert_equals: The 'width' of the animated elements should be equal at 500ms expected "50.3438px" but got "50px"
-FAIL Linear-equivalent cubic-bezier keyframe easing applied to an effect with a frames function does not alter the result Failed to execute 'animate' on 'Element': 'frames(5)' is not a valid value for easing
+FAIL Linear-equivalent cubic-bezier keyframe easing applied to an effect with a frames function does not alter the result assert_equals: The 'width' of the animated elements should be equal at 250ms expected "25.0938px" but got "25px"
 FAIL Linear-equivalent cubic-bezier keyframe easing applied to an effect with a linear function does not alter the result assert_equals: The 'width' of the animated elements should be equal at 250ms expected "25.0938px" but got "25px"
 FAIL Linear-equivalent cubic-bezier keyframe easing applied to an effect with a ease function does not alter the result assert_equals: The 'width' of the animated elements should be equal at 250ms expected "41.875px" but got "41.8594px"
 FAIL Linear-equivalent cubic-bezier keyframe easing applied to an effect with a ease-in function does not alter the result assert_equals: The 'width' of the animated elements should be equal at 250ms expected "8.95313px" but got "9.32813px"
diff --git a/third_party/WebKit/LayoutTests/transitions/transitions-parsing-expected.txt b/third_party/WebKit/LayoutTests/transitions/transitions-parsing-expected.txt
index b7b57bd3..2bd1db5 100644
--- a/third_party/WebKit/LayoutTests/transitions/transitions-parsing-expected.txt
+++ b/third_party/WebKit/LayoutTests/transitions/transitions-parsing-expected.txt
@@ -227,6 +227,10 @@
 PASS computedStyle.transitionTimingFunction is 'steps(5, start)'
 PASS style.webkitTransitionTimingFunction is 'steps(5, start)'
 PASS computedStyle.webkitTransitionTimingFunction is 'steps(5, start)'
+PASS style.transitionTimingFunction is 'frames(5)'
+PASS computedStyle.transitionTimingFunction is 'frames(5)'
+PASS style.webkitTransitionTimingFunction is 'frames(5)'
+PASS computedStyle.webkitTransitionTimingFunction is 'frames(5)'
 PASS style.transitionTimingFunction is 'ease-in-out, ease-in'
 PASS computedStyle.transitionTimingFunction is 'ease-in-out, ease-in'
 PASS style.webkitTransitionTimingFunction is 'ease-in-out, ease-in'
@@ -300,6 +304,22 @@
 PASS computedStyle.transitionTimingFunction is 'ease'
 PASS style.webkitTransitionTimingFunction is ''
 PASS computedStyle.webkitTransitionTimingFunction is 'ease'
+PASS style.transitionTimingFunction is ''
+PASS computedStyle.transitionTimingFunction is 'ease'
+PASS style.webkitTransitionTimingFunction is ''
+PASS computedStyle.webkitTransitionTimingFunction is 'ease'
+PASS style.transitionTimingFunction is ''
+PASS computedStyle.transitionTimingFunction is 'ease'
+PASS style.webkitTransitionTimingFunction is ''
+PASS computedStyle.webkitTransitionTimingFunction is 'ease'
+PASS style.transitionTimingFunction is ''
+PASS computedStyle.transitionTimingFunction is 'ease'
+PASS style.webkitTransitionTimingFunction is ''
+PASS computedStyle.webkitTransitionTimingFunction is 'ease'
+PASS style.transitionTimingFunction is ''
+PASS computedStyle.transitionTimingFunction is 'ease'
+PASS style.webkitTransitionTimingFunction is ''
+PASS computedStyle.webkitTransitionTimingFunction is 'ease'
 Valid transition-delay values.
 PASS computedStyle.transitionDelay is '0s'
 PASS computedStyle.webkitTransitionDelay is '0s'
diff --git a/third_party/WebKit/LayoutTests/transitions/transitions-parsing.html b/third_party/WebKit/LayoutTests/transitions/transitions-parsing.html
index 14b566f..4e9c4c2 100644
--- a/third_party/WebKit/LayoutTests/transitions/transitions-parsing.html
+++ b/third_party/WebKit/LayoutTests/transitions/transitions-parsing.html
@@ -372,6 +372,12 @@
 shouldBe("style.webkitTransitionTimingFunction", "'steps(5, start)'");
 shouldBe("computedStyle.webkitTransitionTimingFunction", "'steps(5, start)'");
 
+style.transitionTimingFunction = "frames(5)";
+shouldBe("style.transitionTimingFunction", "'frames(5)'");
+shouldBe("computedStyle.transitionTimingFunction", "'frames(5)'");
+shouldBe("style.webkitTransitionTimingFunction", "'frames(5)'");
+shouldBe("computedStyle.webkitTransitionTimingFunction", "'frames(5)'");
+
 style.transitionProperty = "opacity, width";
 
 style.transitionTimingFunction = "ease-in-out, ease-in";
@@ -462,6 +468,30 @@
 shouldBe("style.webkitTransitionTimingFunction", "''");
 shouldBe("computedStyle.webkitTransitionTimingFunction", "'ease'");
 
+style.transitionTimingFunction = "frame(5)";
+shouldBe("style.transitionTimingFunction", "''");
+shouldBe("computedStyle.transitionTimingFunction", "'ease'");
+shouldBe("style.webkitTransitionTimingFunction", "''");
+shouldBe("computedStyle.webkitTransitionTimingFunction", "'ease'");
+
+style.transitionTimingFunction = "frames()";
+shouldBe("style.transitionTimingFunction", "''");
+shouldBe("computedStyle.transitionTimingFunction", "'ease'");
+shouldBe("style.webkitTransitionTimingFunction", "''");
+shouldBe("computedStyle.webkitTransitionTimingFunction", "'ease'");
+
+style.transitionTimingFunction = "frames(1)";
+shouldBe("style.transitionTimingFunction", "''");
+shouldBe("computedStyle.transitionTimingFunction", "'ease'");
+shouldBe("style.webkitTransitionTimingFunction", "''");
+shouldBe("computedStyle.webkitTransitionTimingFunction", "'ease'");
+
+style.transitionTimingFunction = "frames(5, end)";
+shouldBe("style.transitionTimingFunction", "''");
+shouldBe("computedStyle.transitionTimingFunction", "'ease'");
+shouldBe("style.webkitTransitionTimingFunction", "''");
+shouldBe("computedStyle.webkitTransitionTimingFunction", "'ease'");
+
 style.transitionTimingFunction = "red";
 shouldBe("style.transitionTimingFunction", "''");
 shouldBe("computedStyle.transitionTimingFunction", "'ease'");
diff --git a/third_party/WebKit/Source/build/scripts/make_computed_style_base.py b/third_party/WebKit/Source/build/scripts/make_computed_style_base.py
index fbb0aff..092e0e79 100755
--- a/third_party/WebKit/Source/build/scripts/make_computed_style_base.py
+++ b/third_party/WebKit/Source/build/scripts/make_computed_style_base.py
@@ -132,8 +132,12 @@
             self.is_inherited_method_name = method_name(join_name(name_for_methods, 'is inherited'))
 
         # Method names
+        # TODO(nainar): Method name generation is inconsistent. Fix.
         self.getter_method_name = getter_method_name
         self.setter_method_name = setter_method_name
+        self.internal_getter_method_name = method_name(join_name(getter_method_name, 'Internal'))
+        self.internal_mutable_method_name = method_name(join_name('Mutable', name_for_methods, 'Internal'))
+        self.internal_setter_method_name = method_name(join_name(setter_method_name, 'Internal'))
         self.initial_method_name = initial_method_name
         self.resetter_method_name = method_name(join_name('Reset', name_for_methods))
 
diff --git a/third_party/WebKit/Source/build/scripts/templates/ComputedStyleBase.h.tmpl b/third_party/WebKit/Source/build/scripts/templates/ComputedStyleBase.h.tmpl
index 03a4801..bbd444f 100644
--- a/third_party/WebKit/Source/build/scripts/templates/ComputedStyleBase.h.tmpl
+++ b/third_party/WebKit/Source/build/scripts/templates/ComputedStyleBase.h.tmpl
@@ -81,7 +81,7 @@
 
   {% for field in fields %}
   // {{field.property_name}}
-  {{field_templates[field.field_template].decl_methods(field)|indent(2)}}
+  {{field_templates[field.field_template].decl_public_methods(field)|indent(2)}}
 
   {% endfor %}
  protected:
@@ -93,6 +93,14 @@
   {% endfor %}
   {}
 
+  {% for field in fields %}
+  {% if field.field_template in ('storage_only', 'monotonic_flag', 'external') %}
+  // {{field.property_name}}
+  {{field_templates[field.field_template].decl_protected_methods(field)|indent(2)}}
+
+  {% endif %}
+  {% endfor %}
+
   ~ComputedStyleBase() = default;
 
   // Storage.
diff --git a/third_party/WebKit/Source/build/scripts/templates/fields/base.tmpl b/third_party/WebKit/Source/build/scripts/templates/fields/base.tmpl
index b3c23ae..d0a9ee14 100644
--- a/third_party/WebKit/Source/build/scripts/templates/fields/base.tmpl
+++ b/third_party/WebKit/Source/build/scripts/templates/fields/base.tmpl
@@ -1,12 +1,12 @@
-{% from 'fields/field.tmpl' import encode, decode, return_type %}
-{% macro decl_methods(field) -%}
-inline static {{field.type_name}} {{field.initial_method_name}}() {
+{% from 'fields/field.tmpl' import encode, decode, return_type, argument_type %}
+{% macro decl_public_methods(field) -%}
+inline static {{return_type(field)}} {{field.initial_method_name}}() {
   return {{field.default_value}};
 }
 {{return_type(field)}} {{field.getter_method_name}}() const {
   return {{decode(field, field.name)}};
 }
-void {{field.setter_method_name}}({{field.type_name}} v) {
+void {{field.setter_method_name}}({{argument_type(field)}} v) {
   {{field.name}} = {{encode(field, "v")}};
 }
 inline void {{field.resetter_method_name}}() {
@@ -14,3 +14,11 @@
 }
 {%- endmacro %}
 
+{% macro decl_protected_methods(field) -%}
+{{return_type(field)}} {{field.internal_getter_method_name}}() const {
+  return {{decode(field, field.name)}};
+}
+void {{field.internal_setter_method_name}}({{argument_type(field)}} v) {
+  {{field.name}} = {{encode(field, "v")}};
+}
+{%- endmacro %}
diff --git a/third_party/WebKit/Source/build/scripts/templates/fields/external.tmpl b/third_party/WebKit/Source/build/scripts/templates/fields/external.tmpl
index b530be7..7cfd9967 100644
--- a/third_party/WebKit/Source/build/scripts/templates/fields/external.tmpl
+++ b/third_party/WebKit/Source/build/scripts/templates/fields/external.tmpl
@@ -1,8 +1,14 @@
 {% import 'fields/base.tmpl' as base %}
 {% from 'fields/field.tmpl' import decode %}
-{% macro decl_methods(field) %}
-{{base.decl_methods(field)}}
+{% macro decl_public_methods(field) %}
+{{base.decl_public_methods(field)}}
 void {{field.setter_method_name}}({{field.type_name}}&& v) {
   {{field.name}} = std::move(v);
 }
 {% endmacro %}
+
+{% macro decl_protected_methods(field) -%}
+{{field.type_name}}& {{field.internal_mutable_method_name}}() {
+  return {{decode(field, field.name)}};
+}
+{%- endmacro %}
diff --git a/third_party/WebKit/Source/build/scripts/templates/fields/field.tmpl b/third_party/WebKit/Source/build/scripts/templates/fields/field.tmpl
index 8790c22..685a0ee 100644
--- a/third_party/WebKit/Source/build/scripts/templates/fields/field.tmpl
+++ b/third_party/WebKit/Source/build/scripts/templates/fields/field.tmpl
@@ -18,6 +18,14 @@
 {% if field.is_bit_field -%}
 {{field.type_name}}
 {%- else -%}
-const {{field.type_name}}&
+{{field.type_name}}&
 {%- endif %}
 {% endmacro %}
+
+{% macro argument_type(field) %}
+{% if field.is_bit_field -%}
+{{field.type_name}}
+{%- else -%}
+const {{field.type_name}}&
+{%- endif %}
+{% endmacro %}
\ No newline at end of file
diff --git a/third_party/WebKit/Source/build/scripts/templates/fields/keyword.tmpl b/third_party/WebKit/Source/build/scripts/templates/fields/keyword.tmpl
index 3d6cb329..25c16018 100644
--- a/third_party/WebKit/Source/build/scripts/templates/fields/keyword.tmpl
+++ b/third_party/WebKit/Source/build/scripts/templates/fields/keyword.tmpl
@@ -1,5 +1,7 @@
 {% import 'fields/base.tmpl' as base %}
-{% macro decl_methods(field) -%}
-{{ base.decl_methods(field) }}
+{% macro decl_public_methods(field) -%}
+{{ base.decl_public_methods(field) }}
 {# TODO(shend): Add special keyword methods like cardinality #}
 {%- endmacro %}
+{% macro decl_protected_methods(field) -%}
+{%- endmacro %}
diff --git a/third_party/WebKit/Source/build/scripts/templates/fields/monotonic_flag.tmpl b/third_party/WebKit/Source/build/scripts/templates/fields/monotonic_flag.tmpl
index dfae150..afa6413 100644
--- a/third_party/WebKit/Source/build/scripts/templates/fields/monotonic_flag.tmpl
+++ b/third_party/WebKit/Source/build/scripts/templates/fields/monotonic_flag.tmpl
@@ -1,5 +1,5 @@
-{% from 'fields/field.tmpl' import encode %}
-{% macro decl_methods(field) %}
+{% from 'fields/field.tmpl' import encode, argument_type %}
+{% macro decl_public_methods(field) %}
 bool {{field.getter_method_name}}() const {
   return {{field.name}};
 }
@@ -7,3 +7,8 @@
   {{field.name}} = {{encode(field, "true")}};
 }
 {% endmacro %}
+{% macro decl_protected_methods(field) -%}
+void {{field.internal_setter_method_name}}({{argument_type(field)}} v) {
+  {{field.name}} = {{encode(field, "v")}};
+}
+{%- endmacro %}
diff --git a/third_party/WebKit/Source/build/scripts/templates/fields/primitive.tmpl b/third_party/WebKit/Source/build/scripts/templates/fields/primitive.tmpl
index 30519af..800eb98d 100644
--- a/third_party/WebKit/Source/build/scripts/templates/fields/primitive.tmpl
+++ b/third_party/WebKit/Source/build/scripts/templates/fields/primitive.tmpl
@@ -1,4 +1,6 @@
 {% import 'fields/base.tmpl' as base %}
-{% macro decl_methods(field) -%}
-{{ base.decl_methods(field) }}
+{% macro decl_public_methods(field) -%}
+{{ base.decl_public_methods(field) }}
+{%- endmacro %}
+{% macro decl_protected_methods(field) -%}
 {%- endmacro %}
diff --git a/third_party/WebKit/Source/build/scripts/templates/fields/storage_only.tmpl b/third_party/WebKit/Source/build/scripts/templates/fields/storage_only.tmpl
index 551d2cc1..801fc78 100644
--- a/third_party/WebKit/Source/build/scripts/templates/fields/storage_only.tmpl
+++ b/third_party/WebKit/Source/build/scripts/templates/fields/storage_only.tmpl
@@ -1,3 +1,22 @@
-{% macro decl_methods(field) -%}
+{% from 'fields/field.tmpl' import encode, decode, return_type, argument_type %}
+{% macro decl_public_methods(field) -%}
 // Getters and setters not generated
 {%- endmacro %}
+{% macro decl_protected_methods(field) -%}
+{% if field.is_bit_field %}
+{{return_type(field)}} {{field.internal_getter_method_name}}() const {
+  return {{decode(field, field.name)}};
+}
+{% else %}
+const {{return_type(field)}} {{field.internal_getter_method_name}}() const {
+  return {{decode(field, field.name)}};
+}
+{{return_type(field)}} {{field.internal_mutable_method_name}}() {
+  return {{decode(field, field.name)}};
+}
+{% endif %}
+void {{field.internal_setter_method_name}}({{argument_type(field)}} v) {
+  {{field.name}} = {{encode(field, "v")}};
+}
+{%- endmacro %}
+
diff --git a/third_party/WebKit/Source/core/BUILD.gn b/third_party/WebKit/Source/core/BUILD.gn
index f5db09c3..575e0d43 100644
--- a/third_party/WebKit/Source/core/BUILD.gn
+++ b/third_party/WebKit/Source/core/BUILD.gn
@@ -534,6 +534,7 @@
     "$blink_core_output_dir/css/properties/CSSPropertyAPITextSizeAdjust.h",
     "$blink_core_output_dir/css/properties/CSSPropertyAPITextUnderlinePosition.h",
     "$blink_core_output_dir/css/properties/CSSPropertyAPITouchAction.h",
+    "$blink_core_output_dir/css/properties/CSSPropertyAPITransform.h",
     "$blink_core_output_dir/css/properties/CSSPropertyAPITransformOrigin.h",
     "$blink_core_output_dir/css/properties/CSSPropertyAPITranslate.h",
     "$blink_core_output_dir/css/properties/CSSPropertyAPIVerticalAlign.h",
diff --git a/third_party/WebKit/Source/core/animation/AnimationInputHelpersTest.cpp b/third_party/WebKit/Source/core/animation/AnimationInputHelpersTest.cpp
index 4da3129..ded896e 100644
--- a/third_party/WebKit/Source/core/animation/AnimationInputHelpersTest.cpp
+++ b/third_party/WebKit/Source/core/animation/AnimationInputHelpersTest.cpp
@@ -113,6 +113,7 @@
   TimingFunctionRoundTrips("ease-in", exception_state);
   TimingFunctionRoundTrips("ease-out", exception_state);
   TimingFunctionRoundTrips("ease-in-out", exception_state);
+  TimingFunctionRoundTrips("frames(3)", exception_state);
   TimingFunctionRoundTrips("cubic-bezier(0.1, 5, 0.23, 0)", exception_state);
 
   EXPECT_EQ("steps(1, start)",
@@ -134,6 +135,8 @@
 
   TimingFunctionThrows("steps(3, nowhere)", exception_state);
   TimingFunctionThrows("steps(-3, end)", exception_state);
+  TimingFunctionThrows("frames(3, end)", exception_state);
+  TimingFunctionThrows("frames(1)", exception_state);
   TimingFunctionThrows("cubic-bezier(0.1, 0, 4, 0.4)", exception_state);
 }
 
diff --git a/third_party/WebKit/Source/core/animation/CompositorAnimationsTest.cpp b/third_party/WebKit/Source/core/animation/CompositorAnimationsTest.cpp
index 50c2643..e681582 100644
--- a/third_party/WebKit/Source/core/animation/CompositorAnimationsTest.cpp
+++ b/third_party/WebKit/Source/core/animation/CompositorAnimationsTest.cpp
@@ -66,6 +66,7 @@
   RefPtr<TimingFunction> cubic_ease_timing_function_;
   RefPtr<TimingFunction> cubic_custom_timing_function_;
   RefPtr<TimingFunction> step_timing_function_;
+  RefPtr<TimingFunction> frames_timing_function_;
 
   Timing timing_;
   CompositorAnimations::CompositorTiming compositor_timing_;
@@ -87,6 +88,7 @@
         CubicBezierTimingFunction::Create(1, 2, 3, 4);
     step_timing_function_ =
         StepsTimingFunction::Create(1, StepsTimingFunction::StepPosition::END);
+    frames_timing_function_ = FramesTimingFunction::Create(2);
 
     timing_ = CreateCompositableTiming();
     compositor_timing_ = CompositorAnimations::CompositorTiming();
@@ -610,6 +612,15 @@
 }
 
 TEST_F(AnimationCompositorAnimationsTest,
+       isCandidateForAnimationOnCompositorTimingFunctionFrames) {
+  timing_.timing_function = frames_timing_function_;
+  EXPECT_TRUE(IsCandidateForAnimationOnCompositor(
+      timing_, *keyframe_animation_effect2_));
+  EXPECT_TRUE(IsCandidateForAnimationOnCompositor(
+      timing_, *keyframe_animation_effect5_));
+}
+
+TEST_F(AnimationCompositorAnimationsTest,
        isCandidateForAnimationOnCompositorTimingFunctionChainedLinear) {
   EXPECT_TRUE(IsCandidateForAnimationOnCompositor(
       timing_, *keyframe_animation_effect2_));
@@ -679,23 +690,29 @@
 }
 
 TEST_F(AnimationCompositorAnimationsTest,
-       isCandidateForAnimationOnCompositorTimingFunctionWithStepOkay) {
+       isCandidateForAnimationOnCompositorTimingFunctionWithStepOrFrameOkay) {
   (*keyframe_vector2_)[0]->SetEasing(step_timing_function_.Get());
   keyframe_animation_effect2_ =
       AnimatableValueKeyframeEffectModel::Create(*keyframe_vector2_);
   EXPECT_TRUE(IsCandidateForAnimationOnCompositor(
       timing_, *keyframe_animation_effect2_));
 
+  (*keyframe_vector2_)[0]->SetEasing(frames_timing_function_.Get());
+  keyframe_animation_effect2_ =
+      AnimatableValueKeyframeEffectModel::Create(*keyframe_vector2_);
+  EXPECT_TRUE(IsCandidateForAnimationOnCompositor(
+      timing_, *keyframe_animation_effect2_));
+
   (*keyframe_vector5_)[0]->SetEasing(step_timing_function_.Get());
   (*keyframe_vector5_)[1]->SetEasing(linear_timing_function_.Get());
   (*keyframe_vector5_)[2]->SetEasing(cubic_ease_timing_function_.Get());
-  (*keyframe_vector5_)[3]->SetEasing(linear_timing_function_.Get());
+  (*keyframe_vector5_)[3]->SetEasing(frames_timing_function_.Get());
   keyframe_animation_effect5_ =
       AnimatableValueKeyframeEffectModel::Create(*keyframe_vector5_);
   EXPECT_TRUE(IsCandidateForAnimationOnCompositor(
       timing_, *keyframe_animation_effect5_));
 
-  (*keyframe_vector5_)[0]->SetEasing(linear_timing_function_.Get());
+  (*keyframe_vector5_)[0]->SetEasing(frames_timing_function_.Get());
   (*keyframe_vector5_)[1]->SetEasing(step_timing_function_.Get());
   (*keyframe_vector5_)[2]->SetEasing(cubic_ease_timing_function_.Get());
   (*keyframe_vector5_)[3]->SetEasing(linear_timing_function_.Get());
@@ -705,7 +722,7 @@
       timing_, *keyframe_animation_effect5_));
 
   (*keyframe_vector5_)[0]->SetEasing(linear_timing_function_.Get());
-  (*keyframe_vector5_)[1]->SetEasing(cubic_ease_timing_function_.Get());
+  (*keyframe_vector5_)[1]->SetEasing(frames_timing_function_.Get());
   (*keyframe_vector5_)[2]->SetEasing(cubic_ease_timing_function_.Get());
   (*keyframe_vector5_)[3]->SetEasing(step_timing_function_.Get());
   keyframe_animation_effect5_ =
diff --git a/third_party/WebKit/Source/core/animation/TimingInputTest.cpp b/third_party/WebKit/Source/core/animation/TimingInputTest.cpp
index 630f9e93..5e9f6d8 100644
--- a/third_party/WebKit/Source/core/animation/TimingInputTest.cpp
+++ b/third_party/WebKit/Source/core/animation/TimingInputTest.cpp
@@ -329,6 +329,11 @@
                               success)
            .timing_function);
   EXPECT_TRUE(success);
+  EXPECT_EQ(*FramesTimingFunction::Create(5),
+            *ApplyTimingInputString(scope.GetIsolate(), "easing", "frames(5)",
+                                    success)
+                 .timing_function);
+  EXPECT_TRUE(success);
 
   ApplyTimingInputString(scope.GetIsolate(), "easing", "", success);
   EXPECT_FALSE(success);
@@ -338,6 +343,11 @@
   ApplyTimingInputString(scope.GetIsolate(), "easing",
                          "cubic-bezier(2, 2, 0.3, 0.3)", success);
   EXPECT_FALSE(success);
+  ApplyTimingInputString(scope.GetIsolate(), "easing", "frames(1)", success);
+  EXPECT_FALSE(success);
+  ApplyTimingInputString(scope.GetIsolate(), "easing", "frames(3, start)",
+                         success);
+  EXPECT_FALSE(success);
   ApplyTimingInputString(scope.GetIsolate(), "easing", "rubbish", success);
   EXPECT_FALSE(success);
   ApplyTimingInputNumber(scope.GetIsolate(), "easing", 2, success);
diff --git a/third_party/WebKit/Source/core/css/ActiveStyleSheets.cpp b/third_party/WebKit/Source/core/css/ActiveStyleSheets.cpp
index d4d8639..9030786e 100644
--- a/third_party/WebKit/Source/core/css/ActiveStyleSheets.cpp
+++ b/third_party/WebKit/Source/core/css/ActiveStyleSheets.cpp
@@ -37,6 +37,13 @@
       changed_rule_sets.insert(old_style_sheets[index].second);
   }
 
+  // If we add a sheet for which the media attribute currently doesn't match, we
+  // have a null RuleSet and there's no need to do any style invalidation.
+  // However, we need to tell the StyleEngine to re-collect viewport and device
+  // dependent media query results so that we can correctly update active style
+  // sheets when such media query evaluations change.
+  bool adds_non_matching_mq = false;
+
   if (index == old_style_sheet_count) {
     // The old stylesheet vector is a prefix of the new vector in terms of
     // StyleSheets. If none of the RuleSets changed, we only need to add the new
@@ -45,10 +52,12 @@
     for (; index < new_style_sheet_count; index++) {
       if (new_style_sheets[index].second)
         changed_rule_sets.insert(new_style_sheets[index].second);
+      else if (new_style_sheets[index].first->HasMediaQueryResults())
+        adds_non_matching_mq = true;
     }
     if (rule_sets_changed_in_common_prefix)
       return kActiveSheetsChanged;
-    if (changed_rule_sets.IsEmpty())
+    if (changed_rule_sets.IsEmpty() && !adds_non_matching_mq)
       return kNoActiveSheetsChanged;
     return kActiveSheetsAppended;
   }
@@ -58,9 +67,12 @@
     for (; index < old_style_sheet_count; index++) {
       if (old_style_sheets[index].second)
         changed_rule_sets.insert(old_style_sheets[index].second);
+      else if (old_style_sheets[index].first->HasMediaQueryResults())
+        adds_non_matching_mq = true;
     }
-    return changed_rule_sets.IsEmpty() ? kNoActiveSheetsChanged
-                                       : kActiveSheetsChanged;
+    return changed_rule_sets.IsEmpty() && !adds_non_matching_mq
+               ? kNoActiveSheetsChanged
+               : kActiveSheetsChanged;
   }
 
   DCHECK_LT(index, old_style_sheet_count);
@@ -88,6 +100,8 @@
       // Sheet either removed or inserted.
       if (sheet1.second)
         changed_rule_sets.insert(sheet1.second);
+      else if (sheet1.first->HasMediaQueryResults())
+        adds_non_matching_mq = true;
       continue;
     }
 
@@ -104,8 +118,9 @@
     if (sheet2.second)
       changed_rule_sets.insert(sheet2.second);
   }
-  return changed_rule_sets.IsEmpty() ? kNoActiveSheetsChanged
-                                     : kActiveSheetsChanged;
+  return changed_rule_sets.IsEmpty() && !adds_non_matching_mq
+             ? kNoActiveSheetsChanged
+             : kActiveSheetsChanged;
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/css/ActiveStyleSheetsTest.cpp b/third_party/WebKit/Source/core/css/ActiveStyleSheetsTest.cpp
index af1c54c..1187aa3c 100644
--- a/third_party/WebKit/Source/core/css/ActiveStyleSheetsTest.cpp
+++ b/third_party/WebKit/Source/core/css/ActiveStyleSheetsTest.cpp
@@ -10,6 +10,7 @@
 #include "core/css/StyleSheetContents.h"
 #include "core/css/StyleSheetList.h"
 #include "core/css/parser/CSSParserContext.h"
+#include "core/css/parser/MediaQueryParser.h"
 #include "core/dom/StyleEngine.h"
 #include "core/dom/shadow/ShadowRoot.h"
 #include "core/dom/shadow/ShadowRootInit.h"
@@ -409,6 +410,35 @@
   EXPECT_EQ(2u, changed_rule_sets.size());
 }
 
+TEST_F(ActiveStyleSheetsTest, CompareActiveStyleSheets_AddRemoveNonMatchingMQ) {
+  ActiveStyleSheetVector old_sheets;
+  ActiveStyleSheetVector new_sheets;
+  HeapHashSet<Member<RuleSet>> changed_rule_sets;
+
+  EXPECT_EQ(
+      kNoActiveSheetsChanged,
+      CompareActiveStyleSheets(old_sheets, new_sheets, changed_rule_sets));
+  EXPECT_EQ(0u, changed_rule_sets.size());
+
+  CSSStyleSheet* sheet1 = CreateSheet();
+  MediaQuerySet* mq =
+      MediaQueryParser::ParseMediaQuerySet("(min-width: 9000px)");
+  sheet1->SetMediaQueries(mq);
+  sheet1->MatchesMediaQueries(MediaQueryEvaluator());
+
+  new_sheets.push_back(std::make_pair(sheet1, nullptr));
+
+  EXPECT_EQ(
+      kActiveSheetsAppended,
+      CompareActiveStyleSheets(old_sheets, new_sheets, changed_rule_sets));
+  EXPECT_EQ(0u, changed_rule_sets.size());
+
+  EXPECT_EQ(
+      kActiveSheetsChanged,
+      CompareActiveStyleSheets(new_sheets, old_sheets, changed_rule_sets));
+  EXPECT_EQ(0u, changed_rule_sets.size());
+}
+
 TEST_F(ApplyRulesetsTest, AddUniversalRuleToDocument) {
   GetDocument().View()->UpdateAllLifecyclePhases();
 
diff --git a/third_party/WebKit/Source/core/css/BUILD.gn b/third_party/WebKit/Source/core/css/BUILD.gn
index 12c7181b..b88eac9 100644
--- a/third_party/WebKit/Source/core/css/BUILD.gn
+++ b/third_party/WebKit/Source/core/css/BUILD.gn
@@ -463,6 +463,7 @@
     "properties/CSSPropertyAPITextSizeAdjust.cpp",
     "properties/CSSPropertyAPITextUnderlinePosition.cpp",
     "properties/CSSPropertyAPITouchAction.cpp",
+    "properties/CSSPropertyAPITransform.cpp",
     "properties/CSSPropertyAPITransformOrigin.cpp",
     "properties/CSSPropertyAPITranslate.cpp",
     "properties/CSSPropertyAPIVerticalAlign.cpp",
diff --git a/third_party/WebKit/Source/core/css/CSSProperties.json5 b/third_party/WebKit/Source/core/css/CSSProperties.json5
index b112f80..191cf0eb 100644
--- a/third_party/WebKit/Source/core/css/CSSProperties.json5
+++ b/third_party/WebKit/Source/core/css/CSSProperties.json5
@@ -1917,6 +1917,8 @@
     },
     {
       name: "transform",
+      api_class: true,
+      api_methods: ["parseSingleValue"],
       converter: "ConvertTransformOperations",
       interpolable: true,
       keywords: ["none"],
diff --git a/third_party/WebKit/Source/core/css/CSSStyleSheet.h b/third_party/WebKit/Source/core/css/CSSStyleSheet.h
index d7fc6ad..d14803fd 100644
--- a/third_party/WebKit/Source/core/css/CSSStyleSheet.h
+++ b/third_party/WebKit/Source/core/css/CSSStyleSheet.h
@@ -102,6 +102,10 @@
   const MediaQuerySet* MediaQueries() const { return media_queries_; }
   void SetMediaQueries(MediaQuerySet*);
   bool MatchesMediaQueries(const MediaQueryEvaluator&);
+  bool HasMediaQueryResults() const {
+    return !viewport_dependent_media_query_results_.IsEmpty() ||
+           !device_dependent_media_query_results_.IsEmpty();
+  }
   const MediaQueryResultList& ViewportDependentMediaQueryResults() const {
     return viewport_dependent_media_query_results_;
   }
diff --git a/third_party/WebKit/Source/core/css/CSSTimingFunctionValue.cpp b/third_party/WebKit/Source/core/css/CSSTimingFunctionValue.cpp
index 5bd6c7d8..b1e3d6c 100644
--- a/third_party/WebKit/Source/core/css/CSSTimingFunctionValue.cpp
+++ b/third_party/WebKit/Source/core/css/CSSTimingFunctionValue.cpp
@@ -61,4 +61,13 @@
   return steps_ == other.steps_ && step_position_ == other.step_position_;
 }
 
+String CSSFramesTimingFunctionValue::CustomCSSText() const {
+  return "frames(" + String::Number(frames_) + ")";
+}
+
+bool CSSFramesTimingFunctionValue::Equals(
+    const CSSFramesTimingFunctionValue& other) const {
+  return frames_ == other.frames_;
+}
+
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/css/CSSTimingFunctionValue.h b/third_party/WebKit/Source/core/css/CSSTimingFunctionValue.h
index 3eaa55aa..155dbe0c 100644
--- a/third_party/WebKit/Source/core/css/CSSTimingFunctionValue.h
+++ b/third_party/WebKit/Source/core/css/CSSTimingFunctionValue.h
@@ -106,6 +106,32 @@
 DEFINE_CSS_VALUE_TYPE_CASTS(CSSStepsTimingFunctionValue,
                             IsStepsTimingFunctionValue());
 
+class CSSFramesTimingFunctionValue : public CSSValue {
+ public:
+  static CSSFramesTimingFunctionValue* Create(int frames) {
+    return new CSSFramesTimingFunctionValue(frames);
+  }
+
+  int NumberOfFrames() const { return frames_; }
+
+  String CustomCSSText() const;
+
+  bool Equals(const CSSFramesTimingFunctionValue&) const;
+
+  DEFINE_INLINE_TRACE_AFTER_DISPATCH() {
+    CSSValue::TraceAfterDispatch(visitor);
+  }
+
+ private:
+  CSSFramesTimingFunctionValue(int frames)
+      : CSSValue(kFramesTimingFunctionClass), frames_(frames) {}
+
+  int frames_;
+};
+
+DEFINE_CSS_VALUE_TYPE_CASTS(CSSFramesTimingFunctionValue,
+                            IsFramesTimingFunctionValue());
+
 }  // namespace blink
 
 #endif
diff --git a/third_party/WebKit/Source/core/css/CSSValue.cpp b/third_party/WebKit/Source/core/css/CSSValue.cpp
index 42a388c..55f8ac0f 100644
--- a/third_party/WebKit/Source/core/css/CSSValue.cpp
+++ b/third_party/WebKit/Source/core/css/CSSValue.cpp
@@ -217,6 +217,8 @@
                                                                    other);
       case kStepsTimingFunctionClass:
         return CompareCSSValues<CSSStepsTimingFunctionValue>(*this, other);
+      case kFramesTimingFunctionClass:
+        return CompareCSSValues<CSSFramesTimingFunctionValue>(*this, other);
       case kUnicodeRangeClass:
         return CompareCSSValues<CSSUnicodeRangeValue>(*this, other);
       case kURIClass:
@@ -314,6 +316,8 @@
       return ToCSSCubicBezierTimingFunctionValue(this)->CustomCSSText();
     case kStepsTimingFunctionClass:
       return ToCSSStepsTimingFunctionValue(this)->CustomCSSText();
+    case kFramesTimingFunctionClass:
+      return ToCSSFramesTimingFunctionValue(this)->CustomCSSText();
     case kUnicodeRangeClass:
       return ToCSSUnicodeRangeValue(this)->CustomCSSText();
     case kURIClass:
@@ -445,6 +449,9 @@
     case kStepsTimingFunctionClass:
       ToCSSStepsTimingFunctionValue(this)->~CSSStepsTimingFunctionValue();
       return;
+    case kFramesTimingFunctionClass:
+      ToCSSFramesTimingFunctionValue(this)->~CSSFramesTimingFunctionValue();
+      return;
     case kUnicodeRangeClass:
       ToCSSUnicodeRangeValue(this)->~CSSUnicodeRangeValue();
       return;
@@ -583,6 +590,9 @@
     case kStepsTimingFunctionClass:
       ToCSSStepsTimingFunctionValue(this)->TraceAfterDispatch(visitor);
       return;
+    case kFramesTimingFunctionClass:
+      ToCSSFramesTimingFunctionValue(this)->TraceAfterDispatch(visitor);
+      return;
     case kUnicodeRangeClass:
       ToCSSUnicodeRangeValue(this)->TraceAfterDispatch(visitor);
       return;
diff --git a/third_party/WebKit/Source/core/css/CSSValue.h b/third_party/WebKit/Source/core/css/CSSValue.h
index 84fca53..78a3beb 100644
--- a/third_party/WebKit/Source/core/css/CSSValue.h
+++ b/third_party/WebKit/Source/core/css/CSSValue.h
@@ -129,6 +129,9 @@
   bool IsStepsTimingFunctionValue() const {
     return class_type_ == kStepsTimingFunctionClass;
   }
+  bool IsFramesTimingFunctionValue() const {
+    return class_type_ == kFramesTimingFunctionClass;
+  }
   bool IsGridTemplateAreasValue() const {
     return class_type_ == kGridTemplateAreasClass;
   }
@@ -202,6 +205,7 @@
     // Timing function classes.
     kCubicBezierTimingFunctionClass,
     kStepsTimingFunctionClass,
+    kFramesTimingFunctionClass,
 
     // Other class types.
     kBorderImageSliceClass,
diff --git a/third_party/WebKit/Source/core/css/CSSValueKeywords.json5 b/third_party/WebKit/Source/core/css/CSSValueKeywords.json5
index e340a8fe..fccff88 100644
--- a/third_party/WebKit/Source/core/css/CSSValueKeywords.json5
+++ b/third_party/WebKit/Source/core/css/CSSValueKeywords.json5
@@ -772,6 +772,7 @@
     "step-middle",
     "step-end",
     "steps",
+    "frames",
     "cubic-bezier",
 
     //
diff --git a/third_party/WebKit/Source/core/css/ComputedStyleCSSValueMapping.cpp b/third_party/WebKit/Source/core/css/ComputedStyleCSSValueMapping.cpp
index c8b49c4d..993b0652 100644
--- a/third_party/WebKit/Source/core/css/ComputedStyleCSSValueMapping.cpp
+++ b/third_party/WebKit/Source/core/css/ComputedStyleCSSValueMapping.cpp
@@ -1350,6 +1350,13 @@
       return CSSIdentifierValue::Create(value_id);
     }
 
+    case TimingFunction::Type::FRAMES: {
+      const FramesTimingFunction* frames_timing_function =
+          ToFramesTimingFunction(timing_function);
+      int frames = frames_timing_function->NumberOfFrames();
+      return CSSFramesTimingFunctionValue::Create(frames);
+    }
+
     default:
       return CSSIdentifierValue::Create(CSSValueLinear);
   }
diff --git a/third_party/WebKit/Source/core/css/parser/CSSPropertyParser.cpp b/third_party/WebKit/Source/core/css/parser/CSSPropertyParser.cpp
index 40c5a14..a0c3702 100644
--- a/third_party/WebKit/Source/core/css/parser/CSSPropertyParser.cpp
+++ b/third_party/WebKit/Source/core/css/parser/CSSPropertyParser.cpp
@@ -400,6 +400,26 @@
   return CSSStepsTimingFunctionValue::Create(steps->GetIntValue(), position);
 }
 
+static CSSValue* ConsumeFrames(CSSParserTokenRange& range) {
+  DCHECK_EQ(range.Peek().FunctionId(), CSSValueFrames);
+  CSSParserTokenRange range_copy = range;
+  CSSParserTokenRange args = ConsumeFunction(range_copy);
+
+  CSSPrimitiveValue* frames = ConsumePositiveInteger(args);
+  if (!frames)
+    return nullptr;
+
+  int frames_int = frames->GetIntValue();
+  if (frames_int <= 1)
+    return nullptr;
+
+  if (!args.AtEnd())
+    return nullptr;
+
+  range = range_copy;
+  return CSSFramesTimingFunctionValue::Create(frames_int);
+}
+
 static CSSValue* ConsumeCubicBezier(CSSParserTokenRange& range) {
   DCHECK_EQ(range.Peek().FunctionId(), CSSValueCubicBezier);
   CSSParserTokenRange range_copy = range;
@@ -429,6 +449,8 @@
   CSSValueID function = range.Peek().FunctionId();
   if (function == CSSValueSteps)
     return ConsumeSteps(range);
+  if (function == CSSValueFrames)
+    return ConsumeFrames(range);
   if (function == CSSValueCubicBezier)
     return ConsumeCubicBezier(range);
   return nullptr;
@@ -750,180 +772,6 @@
   return ConsumeLineWidth(range, css_parser_mode, unitless);
 }
 
-static bool ConsumeTranslate3d(CSSParserTokenRange& args,
-                               CSSParserMode css_parser_mode,
-                               CSSFunctionValue*& transform_value) {
-  unsigned number_of_arguments = 2;
-  CSSValue* parsed_value = nullptr;
-  do {
-    parsed_value =
-        ConsumeLengthOrPercent(args, css_parser_mode, kValueRangeAll);
-    if (!parsed_value)
-      return false;
-    transform_value->Append(*parsed_value);
-    if (!ConsumeCommaIncludingWhitespace(args))
-      return false;
-  } while (--number_of_arguments);
-  parsed_value = ConsumeLength(args, css_parser_mode, kValueRangeAll);
-  if (!parsed_value)
-    return false;
-  transform_value->Append(*parsed_value);
-  return true;
-}
-
-static bool ConsumeNumbers(CSSParserTokenRange& args,
-                           CSSFunctionValue*& transform_value,
-                           unsigned number_of_arguments) {
-  do {
-    CSSValue* parsed_value = ConsumeNumber(args, kValueRangeAll);
-    if (!parsed_value)
-      return false;
-    transform_value->Append(*parsed_value);
-    if (--number_of_arguments && !ConsumeCommaIncludingWhitespace(args))
-      return false;
-  } while (number_of_arguments);
-  return true;
-}
-
-static bool ConsumePerspective(CSSParserTokenRange& args,
-                               const CSSParserContext* context,
-                               CSSFunctionValue*& transform_value,
-                               bool use_legacy_parsing) {
-  CSSPrimitiveValue* parsed_value =
-      ConsumeLength(args, context->Mode(), kValueRangeNonNegative);
-  if (!parsed_value && use_legacy_parsing) {
-    double perspective;
-    if (!ConsumeNumberRaw(args, perspective) || perspective < 0)
-      return false;
-    context->Count(UseCounter::kUnitlessPerspectiveInTransformProperty);
-    parsed_value = CSSPrimitiveValue::Create(
-        perspective, CSSPrimitiveValue::UnitType::kPixels);
-  }
-  if (!parsed_value)
-    return false;
-  transform_value->Append(*parsed_value);
-  return true;
-}
-
-static CSSValue* ConsumeTransformValue(CSSParserTokenRange& range,
-                                       const CSSParserContext* context,
-                                       bool use_legacy_parsing) {
-  CSSValueID function_id = range.Peek().FunctionId();
-  if (function_id == CSSValueInvalid)
-    return nullptr;
-  CSSParserTokenRange args = ConsumeFunction(range);
-  if (args.AtEnd())
-    return nullptr;
-  CSSFunctionValue* transform_value = CSSFunctionValue::Create(function_id);
-  CSSValue* parsed_value = nullptr;
-  switch (function_id) {
-    case CSSValueRotate:
-    case CSSValueRotateX:
-    case CSSValueRotateY:
-    case CSSValueRotateZ:
-    case CSSValueSkewX:
-    case CSSValueSkewY:
-    case CSSValueSkew:
-      parsed_value = ConsumeAngle(args);
-      if (!parsed_value)
-        return nullptr;
-      if (function_id == CSSValueSkew &&
-          ConsumeCommaIncludingWhitespace(args)) {
-        transform_value->Append(*parsed_value);
-        parsed_value = ConsumeAngle(args);
-        if (!parsed_value)
-          return nullptr;
-      }
-      break;
-    case CSSValueScaleX:
-    case CSSValueScaleY:
-    case CSSValueScaleZ:
-    case CSSValueScale:
-      parsed_value = ConsumeNumber(args, kValueRangeAll);
-      if (!parsed_value)
-        return nullptr;
-      if (function_id == CSSValueScale &&
-          ConsumeCommaIncludingWhitespace(args)) {
-        transform_value->Append(*parsed_value);
-        parsed_value = ConsumeNumber(args, kValueRangeAll);
-        if (!parsed_value)
-          return nullptr;
-      }
-      break;
-    case CSSValuePerspective:
-      if (!ConsumePerspective(args, context, transform_value,
-                              use_legacy_parsing))
-        return nullptr;
-      break;
-    case CSSValueTranslateX:
-    case CSSValueTranslateY:
-    case CSSValueTranslate:
-      parsed_value =
-          ConsumeLengthOrPercent(args, context->Mode(), kValueRangeAll);
-      if (!parsed_value)
-        return nullptr;
-      if (function_id == CSSValueTranslate &&
-          ConsumeCommaIncludingWhitespace(args)) {
-        transform_value->Append(*parsed_value);
-        parsed_value =
-            ConsumeLengthOrPercent(args, context->Mode(), kValueRangeAll);
-        if (!parsed_value)
-          return nullptr;
-      }
-      break;
-    case CSSValueTranslateZ:
-      parsed_value = ConsumeLength(args, context->Mode(), kValueRangeAll);
-      break;
-    case CSSValueMatrix:
-    case CSSValueMatrix3d:
-      if (!ConsumeNumbers(args, transform_value,
-                          (function_id == CSSValueMatrix3d) ? 16 : 6))
-        return nullptr;
-      break;
-    case CSSValueScale3d:
-      if (!ConsumeNumbers(args, transform_value, 3))
-        return nullptr;
-      break;
-    case CSSValueRotate3d:
-      if (!ConsumeNumbers(args, transform_value, 3) ||
-          !ConsumeCommaIncludingWhitespace(args))
-        return nullptr;
-      parsed_value = ConsumeAngle(args);
-      if (!parsed_value)
-        return nullptr;
-      break;
-    case CSSValueTranslate3d:
-      if (!ConsumeTranslate3d(args, context->Mode(), transform_value))
-        return nullptr;
-      break;
-    default:
-      return nullptr;
-  }
-  if (parsed_value)
-    transform_value->Append(*parsed_value);
-  if (!args.AtEnd())
-    return nullptr;
-  return transform_value;
-}
-
-static CSSValue* ConsumeTransform(CSSParserTokenRange& range,
-                                  const CSSParserContext* context,
-                                  bool use_legacy_parsing) {
-  if (range.Peek().Id() == CSSValueNone)
-    return ConsumeIdent(range);
-
-  CSSValueList* list = CSSValueList::CreateSpaceSeparated();
-  do {
-    CSSValue* parsed_transform_value =
-        ConsumeTransformValue(range, context, use_legacy_parsing);
-    if (!parsed_transform_value)
-      return nullptr;
-    list->Append(*parsed_transform_value);
-  } while (!range.AtEnd());
-
-  return list;
-}
-
 static CSSValue* ConsumeNoneOrURI(CSSParserTokenRange& range,
                                   const CSSParserContext* context) {
   if (range.Peek().Id() == CSSValueNone)
@@ -1784,6 +1632,7 @@
 const CSSValue* CSSPropertyParser::ParseSingleValue(
     CSSPropertyID unresolved_property,
     CSSPropertyID current_shorthand) {
+  DCHECK(context_);
   CSSPropertyID property = resolveCSSPropertyID(unresolved_property);
   if (CSSParserFastPaths::IsKeywordPropertyID(property)) {
     if (!CSSParserFastPaths::IsValidKeywordPropertyAndValue(
@@ -1801,7 +1650,6 @@
   const CSSPropertyDescriptor& css_property_desc =
       CSSPropertyDescriptor::Get(property);
   if (css_property_desc.parseSingleValue) {
-    DCHECK(context_);
     return css_property_desc.parseSingleValue(range_, *context_,
                                               unresolved_property);
   }
@@ -1814,13 +1662,13 @@
     case CSSPropertyMaxWidth:
     case CSSPropertyMaxHeight:
       return CSSPropertyLengthUtils::ConsumeMaxWidthOrHeight(
-          range_, context_, UnitlessQuirk::kAllow);
+          range_, *context_, UnitlessQuirk::kAllow);
     case CSSPropertyMinWidth:
     case CSSPropertyMinHeight:
     case CSSPropertyWidth:
     case CSSPropertyHeight:
       return CSSPropertyLengthUtils::ConsumeWidthOrHeight(
-          range_, context_, UnitlessQuirk::kAllow);
+          range_, *context_, UnitlessQuirk::kAllow);
     case CSSPropertyInlineSize:
     case CSSPropertyBlockSize:
     case CSSPropertyMinInlineSize:
@@ -1829,7 +1677,7 @@
     case CSSPropertyWebkitMinLogicalHeight:
     case CSSPropertyWebkitLogicalWidth:
     case CSSPropertyWebkitLogicalHeight:
-      return CSSPropertyLengthUtils::ConsumeWidthOrHeight(range_, context_);
+      return CSSPropertyLengthUtils::ConsumeWidthOrHeight(range_, *context_);
     case CSSPropertyScrollSnapDestination:
     case CSSPropertyObjectPosition:
     case CSSPropertyPerspectiveOrigin:
@@ -1908,10 +1756,6 @@
     case CSSPropertyOffsetRotate:
     case CSSPropertyOffsetRotation:
       return ConsumeOffsetRotate(range_);
-    case CSSPropertyTransform:
-      return ConsumeTransform(
-          range_, context_,
-          unresolved_property == CSSPropertyAliasWebkitTransform);
     case CSSPropertyWebkitTransformOriginX:
     case CSSPropertyWebkitPerspectiveOriginX:
       return CSSPropertyPositionUtils::ConsumePositionLonghand<CSSValueLeft,
diff --git a/third_party/WebKit/Source/core/css/properties/CSSPropertyAPITransform.cpp b/third_party/WebKit/Source/core/css/properties/CSSPropertyAPITransform.cpp
new file mode 100644
index 0000000..712a4a4c
--- /dev/null
+++ b/third_party/WebKit/Source/core/css/properties/CSSPropertyAPITransform.cpp
@@ -0,0 +1,207 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "core/css/properties/CSSPropertyAPITransform.h"
+
+#include "core/css/CSSFunctionValue.h"
+#include "core/css/CSSValueList.h"
+#include "core/css/parser/CSSParserContext.h"
+#include "core/css/parser/CSSPropertyParserHelpers.h"
+#include "platform/Length.h"
+
+namespace blink {
+
+namespace {
+
+bool ConsumeNumbers(CSSParserTokenRange& args,
+                    CSSFunctionValue*& transform_value,
+                    unsigned number_of_arguments) {
+  do {
+    CSSValue* parsed_value =
+        CSSPropertyParserHelpers::ConsumeNumber(args, kValueRangeAll);
+    if (!parsed_value)
+      return false;
+    transform_value->Append(*parsed_value);
+    if (--number_of_arguments &&
+        !CSSPropertyParserHelpers::ConsumeCommaIncludingWhitespace(args)) {
+      return false;
+    }
+  } while (number_of_arguments);
+  return true;
+}
+
+bool ConsumePerspective(CSSParserTokenRange& args,
+                        const CSSParserContext* context,
+                        CSSFunctionValue*& transform_value,
+                        bool use_legacy_parsing) {
+  CSSPrimitiveValue* parsed_value = CSSPropertyParserHelpers::ConsumeLength(
+      args, context->Mode(), kValueRangeNonNegative);
+  if (!parsed_value && use_legacy_parsing) {
+    double perspective;
+    if (!CSSPropertyParserHelpers::ConsumeNumberRaw(args, perspective) ||
+        perspective < 0) {
+      return false;
+    }
+    context->Count(UseCounter::kUnitlessPerspectiveInTransformProperty);
+    parsed_value = CSSPrimitiveValue::Create(
+        perspective, CSSPrimitiveValue::UnitType::kPixels);
+  }
+  if (!parsed_value)
+    return false;
+  transform_value->Append(*parsed_value);
+  return true;
+}
+
+bool ConsumeTranslate3d(CSSParserTokenRange& args,
+                        CSSParserMode css_parser_mode,
+                        CSSFunctionValue*& transform_value) {
+  unsigned number_of_arguments = 2;
+  CSSValue* parsed_value = nullptr;
+  do {
+    parsed_value = CSSPropertyParserHelpers::ConsumeLengthOrPercent(
+        args, css_parser_mode, kValueRangeAll);
+    if (!parsed_value)
+      return false;
+    transform_value->Append(*parsed_value);
+    if (!CSSPropertyParserHelpers::ConsumeCommaIncludingWhitespace(args))
+      return false;
+  } while (--number_of_arguments);
+  parsed_value = CSSPropertyParserHelpers::ConsumeLength(args, css_parser_mode,
+                                                         kValueRangeAll);
+  if (!parsed_value)
+    return false;
+  transform_value->Append(*parsed_value);
+  return true;
+}
+
+CSSValue* ConsumeTransformValue(CSSParserTokenRange& range,
+                                const CSSParserContext* context,
+                                bool use_legacy_parsing) {
+  CSSValueID function_id = range.Peek().FunctionId();
+  if (function_id == CSSValueInvalid)
+    return nullptr;
+  CSSParserTokenRange args = CSSPropertyParserHelpers::ConsumeFunction(range);
+  if (args.AtEnd())
+    return nullptr;
+  CSSFunctionValue* transform_value = CSSFunctionValue::Create(function_id);
+  CSSValue* parsed_value = nullptr;
+  switch (function_id) {
+    case CSSValueRotate:
+    case CSSValueRotateX:
+    case CSSValueRotateY:
+    case CSSValueRotateZ:
+    case CSSValueSkewX:
+    case CSSValueSkewY:
+    case CSSValueSkew:
+      parsed_value = CSSPropertyParserHelpers::ConsumeAngle(args);
+      if (!parsed_value)
+        return nullptr;
+      if (function_id == CSSValueSkew &&
+          CSSPropertyParserHelpers::ConsumeCommaIncludingWhitespace(args)) {
+        transform_value->Append(*parsed_value);
+        parsed_value = CSSPropertyParserHelpers::ConsumeAngle(args);
+        if (!parsed_value)
+          return nullptr;
+      }
+      break;
+    case CSSValueScaleX:
+    case CSSValueScaleY:
+    case CSSValueScaleZ:
+    case CSSValueScale:
+      parsed_value =
+          CSSPropertyParserHelpers::ConsumeNumber(args, kValueRangeAll);
+      if (!parsed_value)
+        return nullptr;
+      if (function_id == CSSValueScale &&
+          CSSPropertyParserHelpers::ConsumeCommaIncludingWhitespace(args)) {
+        transform_value->Append(*parsed_value);
+        parsed_value =
+            CSSPropertyParserHelpers::ConsumeNumber(args, kValueRangeAll);
+        if (!parsed_value)
+          return nullptr;
+      }
+      break;
+    case CSSValuePerspective:
+      if (!ConsumePerspective(args, context, transform_value,
+                              use_legacy_parsing)) {
+        return nullptr;
+      }
+      break;
+    case CSSValueTranslateX:
+    case CSSValueTranslateY:
+    case CSSValueTranslate:
+      parsed_value = CSSPropertyParserHelpers::ConsumeLengthOrPercent(
+          args, context->Mode(), kValueRangeAll);
+      if (!parsed_value)
+        return nullptr;
+      if (function_id == CSSValueTranslate &&
+          CSSPropertyParserHelpers::ConsumeCommaIncludingWhitespace(args)) {
+        transform_value->Append(*parsed_value);
+        parsed_value = CSSPropertyParserHelpers::ConsumeLengthOrPercent(
+            args, context->Mode(), kValueRangeAll);
+        if (!parsed_value)
+          return nullptr;
+      }
+      break;
+    case CSSValueTranslateZ:
+      parsed_value = CSSPropertyParserHelpers::ConsumeLength(
+          args, context->Mode(), kValueRangeAll);
+      break;
+    case CSSValueMatrix:
+    case CSSValueMatrix3d:
+      if (!ConsumeNumbers(args, transform_value,
+                          (function_id == CSSValueMatrix3d) ? 16 : 6)) {
+        return nullptr;
+      }
+      break;
+    case CSSValueScale3d:
+      if (!ConsumeNumbers(args, transform_value, 3))
+        return nullptr;
+      break;
+    case CSSValueRotate3d:
+      if (!ConsumeNumbers(args, transform_value, 3) ||
+          !CSSPropertyParserHelpers::ConsumeCommaIncludingWhitespace(args)) {
+        return nullptr;
+      }
+      parsed_value = CSSPropertyParserHelpers::ConsumeAngle(args);
+      if (!parsed_value)
+        return nullptr;
+      break;
+    case CSSValueTranslate3d:
+      if (!ConsumeTranslate3d(args, context->Mode(), transform_value))
+        return nullptr;
+      break;
+    default:
+      return nullptr;
+  }
+  if (parsed_value)
+    transform_value->Append(*parsed_value);
+  if (!args.AtEnd())
+    return nullptr;
+  return transform_value;
+}
+
+}  // namespace
+
+const CSSValue* CSSPropertyAPITransform::parseSingleValue(
+    CSSParserTokenRange& range,
+    const CSSParserContext& context,
+    CSSPropertyID unresolved_property) {
+  if (range.Peek().Id() == CSSValueNone)
+    return CSSPropertyParserHelpers::ConsumeIdent(range);
+
+  CSSValueList* list = CSSValueList::CreateSpaceSeparated();
+  do {
+    CSSValue* parsed_transform_value = ConsumeTransformValue(
+        range, &context,
+        unresolved_property == CSSPropertyAliasWebkitTransform);
+    if (!parsed_transform_value)
+      return nullptr;
+    list->Append(*parsed_transform_value);
+  } while (!range.AtEnd());
+
+  return list;
+}
+
+}  // namespace blink
diff --git a/third_party/WebKit/Source/core/css/properties/CSSPropertyAPIWebkitMaxLogicalWidthOrHeight.cpp b/third_party/WebKit/Source/core/css/properties/CSSPropertyAPIWebkitMaxLogicalWidthOrHeight.cpp
index 368c13b4..d7c51772 100644
--- a/third_party/WebKit/Source/core/css/properties/CSSPropertyAPIWebkitMaxLogicalWidthOrHeight.cpp
+++ b/third_party/WebKit/Source/core/css/properties/CSSPropertyAPIWebkitMaxLogicalWidthOrHeight.cpp
@@ -12,7 +12,7 @@
     CSSParserTokenRange& range,
     const CSSParserContext& context,
     CSSPropertyID) {
-  return CSSPropertyLengthUtils::ConsumeMaxWidthOrHeight(range, &context);
+  return CSSPropertyLengthUtils::ConsumeMaxWidthOrHeight(range, context);
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/css/properties/CSSPropertyLengthUtils.cpp b/third_party/WebKit/Source/core/css/properties/CSSPropertyLengthUtils.cpp
index 9d0c3f0..88248843 100644
--- a/third_party/WebKit/Source/core/css/properties/CSSPropertyLengthUtils.cpp
+++ b/third_party/WebKit/Source/core/css/properties/CSSPropertyLengthUtils.cpp
@@ -12,23 +12,23 @@
 
 namespace {
 
-bool ValidWidthOrHeightKeyword(CSSValueID id, const CSSParserContext* context) {
+bool ValidWidthOrHeightKeyword(CSSValueID id, const CSSParserContext& context) {
   if (id == CSSValueWebkitMinContent || id == CSSValueWebkitMaxContent ||
       id == CSSValueWebkitFillAvailable || id == CSSValueWebkitFitContent ||
       id == CSSValueMinContent || id == CSSValueMaxContent ||
       id == CSSValueFitContent) {
     switch (id) {
       case CSSValueWebkitMinContent:
-        context->Count(UseCounter::kCSSValuePrefixedMinContent);
+        context.Count(UseCounter::kCSSValuePrefixedMinContent);
         break;
       case CSSValueWebkitMaxContent:
-        context->Count(UseCounter::kCSSValuePrefixedMaxContent);
+        context.Count(UseCounter::kCSSValuePrefixedMaxContent);
         break;
       case CSSValueWebkitFillAvailable:
-        context->Count(UseCounter::kCSSValuePrefixedFillAvailable);
+        context.Count(UseCounter::kCSSValuePrefixedFillAvailable);
         break;
       case CSSValueWebkitFitContent:
-        context->Count(UseCounter::kCSSValuePrefixedFitContent);
+        context.Count(UseCounter::kCSSValuePrefixedFitContent);
         break;
       default:
         break;
@@ -42,24 +42,24 @@
 
 CSSValue* CSSPropertyLengthUtils::ConsumeMaxWidthOrHeight(
     CSSParserTokenRange& range,
-    const CSSParserContext* context,
+    const CSSParserContext& context,
     CSSPropertyParserHelpers::UnitlessQuirk unitless) {
   if (range.Peek().Id() == CSSValueNone ||
       ValidWidthOrHeightKeyword(range.Peek().Id(), context))
     return CSSPropertyParserHelpers::ConsumeIdent(range);
   return CSSPropertyParserHelpers::ConsumeLengthOrPercent(
-      range, context->Mode(), kValueRangeNonNegative, unitless);
+      range, context.Mode(), kValueRangeNonNegative, unitless);
 }
 
 CSSValue* CSSPropertyLengthUtils::ConsumeWidthOrHeight(
     CSSParserTokenRange& range,
-    const CSSParserContext* context,
+    const CSSParserContext& context,
     CSSPropertyParserHelpers::UnitlessQuirk unitless) {
   if (range.Peek().Id() == CSSValueAuto ||
       ValidWidthOrHeightKeyword(range.Peek().Id(), context))
     return CSSPropertyParserHelpers::ConsumeIdent(range);
   return CSSPropertyParserHelpers::ConsumeLengthOrPercent(
-      range, context->Mode(), kValueRangeNonNegative, unitless);
+      range, context.Mode(), kValueRangeNonNegative, unitless);
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/css/properties/CSSPropertyLengthUtils.h b/third_party/WebKit/Source/core/css/properties/CSSPropertyLengthUtils.h
index 4abf0da1d..e29fc68 100644
--- a/third_party/WebKit/Source/core/css/properties/CSSPropertyLengthUtils.h
+++ b/third_party/WebKit/Source/core/css/properties/CSSPropertyLengthUtils.h
@@ -19,12 +19,12 @@
 
   static CSSValue* ConsumeMaxWidthOrHeight(
       CSSParserTokenRange&,
-      const CSSParserContext*,
+      const CSSParserContext&,
       CSSPropertyParserHelpers::UnitlessQuirk =
           CSSPropertyParserHelpers::UnitlessQuirk::kForbid);
   static CSSValue* ConsumeWidthOrHeight(
       CSSParserTokenRange&,
-      const CSSParserContext*,
+      const CSSParserContext&,
       CSSPropertyParserHelpers::UnitlessQuirk =
           CSSPropertyParserHelpers::UnitlessQuirk::kForbid);
 };
diff --git a/third_party/WebKit/Source/core/css/resolver/CSSToStyleMap.cpp b/third_party/WebKit/Source/core/css/resolver/CSSToStyleMap.cpp
index 12dc5c7..5687d25 100644
--- a/third_party/WebKit/Source/core/css/resolver/CSSToStyleMap.cpp
+++ b/third_party/WebKit/Source/core/css/resolver/CSSToStyleMap.cpp
@@ -459,6 +459,13 @@
   if (value.IsInitialValue())
     return CSSTimingData::InitialTimingFunction();
 
+  if (value.IsFramesTimingFunctionValue()) {
+    const CSSFramesTimingFunctionValue& frames_timing_function =
+        ToCSSFramesTimingFunctionValue(value);
+    return FramesTimingFunction::Create(
+        frames_timing_function.NumberOfFrames());
+  }
+
   const CSSStepsTimingFunctionValue& steps_timing_function =
       ToCSSStepsTimingFunctionValue(value);
   if (steps_timing_function.GetStepPosition() ==
diff --git a/third_party/WebKit/Source/core/dom/StyleEngine.cpp b/third_party/WebKit/Source/core/dom/StyleEngine.cpp
index ebcc74e4..81a37da9 100644
--- a/third_party/WebKit/Source/core/dom/StyleEngine.cpp
+++ b/third_party/WebKit/Source/core/dom/StyleEngine.cpp
@@ -1099,6 +1099,9 @@
     return;
   }
 
+  if (changed_rule_sets.IsEmpty())
+    return;
+
   if (changed_rule_flags & kKeyframesRules)
     ScopedStyleResolver::KeyframesRulesAdded(tree_scope);
 
diff --git a/third_party/WebKit/Source/core/editing/markers/DocumentMarkerController.cpp b/third_party/WebKit/Source/core/editing/markers/DocumentMarkerController.cpp
index 6773d52d..8fd8a11 100644
--- a/third_party/WebKit/Source/core/editing/markers/DocumentMarkerController.cpp
+++ b/third_party/WebKit/Source/core/editing/markers/DocumentMarkerController.cpp
@@ -322,25 +322,8 @@
       dst_markers->at(marker_list_index) = new MarkerList;
     MarkerList* dst_list = dst_markers->at(marker_list_index);
 
-    unsigned end_offset = length - 1;
-    MarkerList::iterator it;
-    for (it = src_list->begin(); it != src_list->end(); ++it) {
-      DocumentMarker* marker = it->Get();
-
-      // stop if we are now past the specified range
-      if (marker->StartOffset() > end_offset)
-        break;
-
-      // pin the marker to the specified range
+    if (DocumentMarkerListEditor::MoveMarkers(src_list, length, dst_list))
       doc_dirty = true;
-      if (marker->EndOffset() > end_offset)
-        marker->SetEndOffset(end_offset);
-
-      DocumentMarkerListEditor::AddMarker(dst_list, marker);
-    }
-
-    // Remove the range of markers that were moved to dst_node
-    src_list->erase(0, it - src_list->begin());
   }
 
   // repaint the affected node
@@ -350,6 +333,35 @@
   }
 }
 
+// TODO(rlanday): move DocumentMarkerListEditor into its own .h/.cpp files
+bool DocumentMarkerListEditor::MoveMarkers(MarkerList* src_list,
+                                           int length,
+                                           MarkerList* dst_list) {
+  DCHECK_GT(length, 0);
+  bool doc_dirty = false;
+  const unsigned end_offset = length - 1;
+  MarkerList::iterator it;
+  for (it = src_list->begin(); it != src_list->end(); ++it) {
+    DocumentMarker* marker = it->Get();
+
+    // stop if we are now past the specified range
+    if (marker->StartOffset() > end_offset)
+      break;
+
+    // pin the marker to the specified range
+    doc_dirty = true;
+    if (marker->EndOffset() > end_offset)
+      marker->SetEndOffset(end_offset);
+
+    DocumentMarkerListEditor::AddMarker(dst_list, marker);
+  }
+
+  // Remove the range of markers that were moved to dst_node
+  src_list->erase(0, it - src_list->begin());
+
+  return doc_dirty;
+}
+
 void DocumentMarkerController::RemoveMarkers(
     Node* node,
     unsigned start_offset,
diff --git a/third_party/WebKit/Source/core/editing/markers/DocumentMarkerController.h b/third_party/WebKit/Source/core/editing/markers/DocumentMarkerController.h
index 8d2c9b5f..8214cc2 100644
--- a/third_party/WebKit/Source/core/editing/markers/DocumentMarkerController.h
+++ b/third_party/WebKit/Source/core/editing/markers/DocumentMarkerController.h
@@ -59,6 +59,11 @@
 
   static void AddMarker(MarkerList*, const DocumentMarker*);
 
+  // Returns true if a marker was moved, false otherwise.
+  static bool MoveMarkers(MarkerList* src_list,
+                          int length,
+                          MarkerList* dst_list);
+
   // Returns true if a marker was removed, false otherwise.
   static bool RemoveMarkers(MarkerList*, unsigned start_offset, int length);
 
diff --git a/third_party/WebKit/Source/core/layout/ng/inline/ng_inline_layout_algorithm.cc b/third_party/WebKit/Source/core/layout/ng/inline/ng_inline_layout_algorithm.cc
index 922c983..c81a44a 100644
--- a/third_party/WebKit/Source/core/layout/ng/inline/ng_inline_layout_algorithm.cc
+++ b/third_party/WebKit/Source/core/layout/ng/inline/ng_inline_layout_algorithm.cc
@@ -285,8 +285,8 @@
 
   NGLogicalOffset origin_point =
       GetOriginPointForFloats(ConstraintSpace(), content_size_);
-  PositionPendingFloats(origin_point.block_offset, MutableConstraintSpace(),
-                        &container_builder_);
+  PositionPendingFloats(origin_point.block_offset, &container_builder_,
+                        MutableConstraintSpace());
   FindNextLayoutOpportunity();
   return true;
 }
@@ -383,9 +383,9 @@
       float_does_not_fit) {
     container_builder_.AddUnpositionedFloat(floating_object);
   } else {
-    NGLogicalOffset offset =
+    floating_object->logical_offset =
         PositionFloat(floating_object.Get(), MutableConstraintSpace());
-    container_builder_.AddFloatingObject(floating_object, offset);
+    container_builder_.MutablePositionedFloats().push_back(floating_object);
     FindNextLayoutOpportunity();
   }
 }
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_block_layout_algorithm.cc b/third_party/WebKit/Source/core/layout/ng/ng_block_layout_algorithm.cc
index f7f23b06..e4f148a 100644
--- a/third_party/WebKit/Source/core/layout/ng/ng_block_layout_algorithm.cc
+++ b/third_party/WebKit/Source/core/layout/ng/ng_block_layout_algorithm.cc
@@ -7,7 +7,6 @@
 #include "core/layout/ng/inline/ng_inline_node.h"
 #include "core/layout/ng/ng_absolute_utils.h"
 #include "core/layout/ng/ng_block_child_iterator.h"
-#include "core/layout/ng/ng_box_fragment.h"
 #include "core/layout/ng/ng_constraint_space.h"
 #include "core/layout/ng/ng_constraint_space_builder.h"
 #include "core/layout/ng/ng_floats_utils.h"
@@ -84,6 +83,19 @@
   }
 }
 
+void PositionPendingFloats(LayoutUnit origin_block_offset,
+                           NGFragmentBuilder* container_builder,
+                           NGConstraintSpace* space) {
+  DCHECK(container_builder->BfcOffset())
+      << "Parent BFC offset should be known here";
+  const auto& floating_objects = container_builder->UnpositionedFloats();
+  PositionFloats(origin_block_offset,
+                 container_builder->BfcOffset().value().block_offset,
+                 floating_objects, space);
+  container_builder->MutablePositionedFloats().AppendVector(floating_objects);
+  container_builder->MutableUnpositionedFloats().clear();
+}
+
 NGBlockLayoutAlgorithm::NGBlockLayoutAlgorithm(NGBlockNode* node,
                                                NGConstraintSpace* space,
                                                NGBlockBreakToken* break_token)
@@ -130,14 +142,11 @@
 
 NGLogicalOffset NGBlockLayoutAlgorithm::CalculateLogicalOffset(
     const WTF::Optional<NGLogicalOffset>& known_fragment_offset) {
+  if (known_fragment_offset)
+    return known_fragment_offset.value() - ContainerBfcOffset();
   LayoutUnit inline_offset =
       border_and_padding_.inline_start + curr_child_margins_.inline_start;
-  LayoutUnit block_offset = content_size_;
-  if (known_fragment_offset) {
-    block_offset = known_fragment_offset.value().block_offset -
-                   ContainerBfcOffset().block_offset;
-  }
-  return {inline_offset, block_offset};
+  return {inline_offset, content_size_};
 }
 
 RefPtr<NGLayoutResult> NGBlockLayoutAlgorithm::Layout() {
@@ -221,13 +230,16 @@
       }
     }
 
-    PrepareChildLayout(child);
+    NGLogicalOffset child_bfc_offset = PrepareChildLayout(child);
     RefPtr<NGConstraintSpace> child_space =
-        CreateConstraintSpaceForChild(child);
+        CreateConstraintSpaceForChild(child_bfc_offset, child);
     RefPtr<NGLayoutResult> layout_result =
         child->Layout(child_space.Get(), child_break_token);
 
-    FinishChildLayout(child, child_space.Get(), layout_result);
+    if (child->IsFloating())
+      FinishFloatChildLayout(child->Style(), *child_space, layout_result.Get());
+    else
+      FinishChildLayout(child_space.Get(), layout_result.Get());
 
     entry = child_iterator.NextChild();
     child = entry.node;
@@ -261,8 +273,8 @@
     curr_bfc_offset_.block_offset += curr_margin_strut_.Sum();
     MaybeUpdateFragmentBfcOffset(ConstraintSpace(), curr_bfc_offset_,
                                  &container_builder_);
-    PositionPendingFloats(curr_bfc_offset_.block_offset,
-                          MutableConstraintSpace(), &container_builder_);
+    PositionPendingFloats(curr_bfc_offset_.block_offset, &container_builder_,
+                          MutableConstraintSpace());
   }
 
   // Margins collapsing:
@@ -283,25 +295,22 @@
   return container_builder_.ToBoxFragment();
 }
 
-void NGBlockLayoutAlgorithm::PrepareChildLayout(NGLayoutInputNode* child) {
+NGLogicalOffset NGBlockLayoutAlgorithm::PrepareChildLayout(
+    NGLayoutInputNode* child) {
   DCHECK(child);
 
+  curr_bfc_offset_ = container_builder_.BfcOffset()
+                         ? container_builder_.BfcOffset().value()
+                         : ConstraintSpace().BfcOffset();
+  curr_bfc_offset_.block_offset += content_size_;
+
   // Calculate margins in parent's writing mode.
   curr_child_margins_ = CalculateMargins(
       child, *space_builder_.ToConstraintSpace(
                  FromPlatformWritingMode(Style().GetWritingMode())));
 
-  // Set estimated BFC offset to the next child's constraint space.
-  curr_bfc_offset_ = container_builder_.BfcOffset()
-                         ? container_builder_.BfcOffset().value()
-                         : ConstraintSpace().BfcOffset();
-  curr_bfc_offset_.block_offset += content_size_;
-  curr_bfc_offset_.inline_offset += border_and_padding_.inline_start;
-
-  bool is_floating = child->IsBlock() && child->Style().IsFloating();
-
   bool should_position_pending_floats =
-      child->IsBlock() && !is_floating &&
+      !child->IsFloating() &&
       !IsNewFormattingContextForBlockLevelChild(Style(), *child) &&
       ClearanceMayAffectLayout(ConstraintSpace(),
                                container_builder_.UnpositionedFloats(),
@@ -316,106 +325,61 @@
         ConstraintSpace(),
         {curr_bfc_offset_.inline_offset, origin_point_block_offset},
         &container_builder_);
-    PositionPendingFloats(origin_point_block_offset, MutableConstraintSpace(),
-                          &container_builder_);
+    PositionPendingFloats(origin_point_block_offset, &container_builder_,
+                          MutableConstraintSpace());
   }
 
-  bool is_inflow = child->IsInline() || !is_floating;
+  bool is_inflow = child->IsInline() || !child->IsFloating();
 
+  NGLogicalOffset child_bfc_offset = curr_bfc_offset_;
+  child_bfc_offset.inline_offset += border_and_padding_.inline_start;
   // Only inflow children (e.g. not floats) are included in the child's margin
   // strut as they do not participate in margin collapsing.
   if (is_inflow) {
-    curr_bfc_offset_.inline_offset += curr_child_margins_.inline_start;
+    child_bfc_offset.inline_offset += curr_child_margins_.inline_start;
     // Append the current margin strut with child's block start margin.
-    // Non empty border/padding use cases are handled inside of the child's
-    // layout.
-    curr_margin_strut_.Append(curr_child_margins_.block_start);
+    // Non empty border/padding, and new FC use cases are handled inside of the
+    // child's layout.
+    if (!IsNewFormattingContextForBlockLevelChild(Style(), *child))
+      curr_margin_strut_.Append(curr_child_margins_.block_start);
   }
 
-  // TODO(ikilpatrick): Children which establish new formatting contexts need
-  // to be placed using the opportunity iterator before we can collapse margins.
-  // If the child is placed at the block_start of this fragment, then its
-  // margins do impact the position of its parent, if not (its placed below a
-  // float for example) it doesn't. \o/
-
-  bool should_collapse_margins =
-      child->IsInline() ||
-      (!is_floating &&
-       IsNewFormattingContextForBlockLevelChild(Style(), *child));
-
-  // Inline children or children which establish a block formatting context
-  // collapse margins and position themselves immediately as they need to know
-  // their BFC offset for fragmentation purposes.
-  if (should_collapse_margins) {
+  // Should collapse margins if inline.
+  if (child->IsInline()) {
     curr_bfc_offset_.block_offset += curr_margin_strut_.Sum();
     MaybeUpdateFragmentBfcOffset(ConstraintSpace(), curr_bfc_offset_,
                                  &container_builder_);
-    PositionPendingFloats(curr_bfc_offset_.block_offset,
-                          MutableConstraintSpace(), &container_builder_);
+    PositionPendingFloats(curr_bfc_offset_.block_offset, &container_builder_,
+                          MutableConstraintSpace());
     curr_margin_strut_ = {};
   }
+  child_bfc_offset.block_offset = curr_bfc_offset_.block_offset;
+  return child_bfc_offset;
 }
 
 void NGBlockLayoutAlgorithm::FinishChildLayout(
-    NGLayoutInputNode* child,
-    NGConstraintSpace* child_space,
-    RefPtr<NGLayoutResult> layout_result) {
-  NGBoxFragment fragment(
-      ConstraintSpace().WritingMode(),
-      ToNGPhysicalBoxFragment(layout_result->PhysicalFragment().Get()));
-
+    const NGConstraintSpace* child_space,
+    NGLayoutResult* layout_result) {
   // Pull out unpositioned floats to the current fragment. This may needed if
   // for example the child fragment could not position its floats because it's
   // empty and therefore couldn't determine its position in space.
   container_builder_.MutableUnpositionedFloats().AppendVector(
       layout_result->UnpositionedFloats());
 
-  if (child->IsBlock() && child->Style().IsFloating()) {
-    NGLogicalOffset origin_offset = constraint_space_->BfcOffset();
-    origin_offset.inline_offset += border_and_padding_.inline_start;
-    RefPtr<NGFloatingObject> floating_object = NGFloatingObject::Create(
-        child->Style(), child_space->WritingMode(),
-        child_space->AvailableSize(), origin_offset,
-        constraint_space_->BfcOffset(), curr_child_margins_,
-        layout_result->PhysicalFragment().Get());
-    container_builder_.AddUnpositionedFloat(floating_object);
-    // No need to postpone the positioning if we know the correct offset.
-    if (container_builder_.BfcOffset()) {
-      NGLogicalOffset origin_point = curr_bfc_offset_;
-      // Adjust origin point to the margins of the last child.
-      // Example: <div style="margin-bottom: 20px"><float></div>
-      //          <div style="margin-bottom: 30px"></div>
-      origin_point.block_offset += curr_margin_strut_.Sum();
-      PositionPendingFloats(origin_point.block_offset, MutableConstraintSpace(),
-                            &container_builder_);
-    }
-    return;
-  }
+  NGBoxFragment fragment(
+      ConstraintSpace().WritingMode(),
+      ToNGPhysicalBoxFragment(layout_result->PhysicalFragment().Get()));
 
-  // Determine the fragment's position in the parent space either by using
-  // content_size_ or known fragment's BFC offset.
-  WTF::Optional<NGLogicalOffset> bfc_offset;
-  if (child_space->IsNewFormattingContext()) {
-    bfc_offset = curr_bfc_offset_;
-  } else if (fragment.BfcOffset()) {
-    // Fragment that knows its offset can be used to set parent's BFC position.
-    curr_bfc_offset_.block_offset = fragment.BfcOffset().value().block_offset;
-    MaybeUpdateFragmentBfcOffset(ConstraintSpace(), curr_bfc_offset_,
-                                 &container_builder_);
-    PositionPendingFloats(curr_bfc_offset_.block_offset,
-                          MutableConstraintSpace(), &container_builder_);
-    bfc_offset = curr_bfc_offset_;
-  } else if (container_builder_.BfcOffset()) {
-    // Fragment doesn't know its offset but we can still calculate its BFC
-    // position because the parent fragment's BFC is known.
-    // Example:
-    // BFC Offset is known here because of the padding.
-    // <div style="padding: 1px">
-    //   <div id="empty-div" style="margins: 1px"></div>
-    bfc_offset = curr_bfc_offset_;
-    bfc_offset.value().block_offset += curr_margin_strut_.Sum();
-  }
-  NGLogicalOffset logical_offset = CalculateLogicalOffset(bfc_offset);
+  // Determine the fragment's position in the parent space.
+  WTF::Optional<NGLogicalOffset> child_bfc_offset;
+  if (child_space->IsNewFormattingContext())
+    child_bfc_offset = PositionNewFc(fragment, *child_space);
+  else if (fragment.BfcOffset())
+    child_bfc_offset = PositionWithBfcOffset(fragment);
+  else if (container_builder_.BfcOffset())
+    child_bfc_offset = PositionWithParentBfc();
+
+  NGLogicalOffset logical_offset = CalculateLogicalOffset(child_bfc_offset);
 
   // Update margin strut.
   curr_margin_strut_ = fragment.EndMarginStrut();
@@ -438,6 +402,88 @@
   container_builder_.AddChild(layout_result, logical_offset);
 }
 
+NGLogicalOffset NGBlockLayoutAlgorithm::PositionNewFc(
+    const NGBoxFragment& fragment,
+    const NGConstraintSpace& child_space) {
+  // 1. Position all pending floats to a temporary space.
+  RefPtr<NGConstraintSpace> tmp_space =
+      NGConstraintSpaceBuilder(&child_space)
+          .SetIsNewFormattingContext(false)
+          .ToConstraintSpace(child_space.WritingMode());
+  PositionFloats(curr_bfc_offset_.block_offset, curr_bfc_offset_.block_offset,
+                 container_builder_.UnpositionedFloats(), tmp_space.Get());
+
+  // 2. Find an estimated layout opportunity for our fragment.
+  NGLayoutOpportunity opportunity = FindLayoutOpportunityForFragment(
+      tmp_space->Exclusions().get(), child_space.AvailableSize(),
+      curr_bfc_offset_, curr_child_margins_, fragment);
+
+  // 3. If the found opportunity lies on the same line with our estimated
+  //    child's BFC offset then merge fragment's margins with the current
+  //    MarginStrut.
+  if (opportunity.offset.block_offset == curr_bfc_offset_.block_offset)
+    curr_margin_strut_.Append(curr_child_margins_.block_start);
+  curr_bfc_offset_.block_offset += curr_margin_strut_.Sum();
+  curr_margin_strut_ = {};
+
+  // 4. The child's BFC block offset is known here.
+  MaybeUpdateFragmentBfcOffset(ConstraintSpace(), curr_bfc_offset_,
+                               &container_builder_);
+  PositionPendingFloats(curr_bfc_offset_.block_offset, &container_builder_,
+                        MutableConstraintSpace());
+
+  // 5. Find the final layout opportunity for the fragment after all pending
+  // floats are positioned at the correct BFC block's offset.
+  opportunity = FindLayoutOpportunityForFragment(
+      MutableConstraintSpace()->Exclusions().get(), child_space.AvailableSize(),
+      curr_bfc_offset_, curr_child_margins_, fragment);
+
+  curr_bfc_offset_ = opportunity.offset;
+  return curr_bfc_offset_;
+}
+
+NGLogicalOffset NGBlockLayoutAlgorithm::PositionWithBfcOffset(
+    const NGBoxFragment& fragment) {
+  DCHECK(fragment.BfcOffset());
+  curr_bfc_offset_.block_offset = fragment.BfcOffset().value().block_offset;
+  MaybeUpdateFragmentBfcOffset(ConstraintSpace(), curr_bfc_offset_,
+                               &container_builder_);
+  PositionPendingFloats(curr_bfc_offset_.block_offset, &container_builder_,
+                        MutableConstraintSpace());
+  return fragment.BfcOffset().value();
+}
+
+NGLogicalOffset NGBlockLayoutAlgorithm::PositionWithParentBfc() {
+  curr_bfc_offset_ +=
+      {border_and_padding_.inline_start + curr_child_margins_.inline_start,
+       curr_margin_strut_.Sum()};
+  return curr_bfc_offset_;
+}
+
+void NGBlockLayoutAlgorithm::FinishFloatChildLayout(
+    const ComputedStyle& child_style,
+    const NGConstraintSpace& child_space,
+    const NGLayoutResult* layout_result) {
+  NGLogicalOffset origin_offset = constraint_space_->BfcOffset();
+  origin_offset.inline_offset += border_and_padding_.inline_start;
+  RefPtr<NGFloatingObject> floating_object = NGFloatingObject::Create(
+      child_style, child_space.WritingMode(), child_space.AvailableSize(),
+      origin_offset, constraint_space_->BfcOffset(), curr_child_margins_,
+      layout_result->PhysicalFragment().Get());
+  container_builder_.AddUnpositionedFloat(floating_object);
+
+  // No need to postpone the positioning if we know the correct offset.
+  if (container_builder_.BfcOffset()) {
+    NGLogicalOffset origin_point = curr_bfc_offset_;
+    // Adjust origin point to the margins of the last child.
+    // Example: <div style="margin-bottom: 20px"><float></div>
+    //          <div style="margin-bottom: 30px"></div>
+    origin_point.block_offset += curr_margin_strut_.Sum();
+    PositionPendingFloats(origin_point.block_offset, &container_builder_,
+                          MutableConstraintSpace());
+  }
+}
+
 void NGBlockLayoutAlgorithm::FinalizeForFragmentation() {
   LayoutUnit used_block_size =
       BreakToken() ? BreakToken()->UsedBlockSize() : LayoutUnit();
@@ -480,6 +526,8 @@
     NGLayoutInputNode* child,
     const NGConstraintSpace& space) {
   DCHECK(child);
+  if (child->IsInline())
+    return {};
   const ComputedStyle& child_style = child->Style();
 
   WTF::Optional<MinMaxContentSize> sizes;
@@ -490,25 +538,25 @@
       ComputeInlineSizeForFragment(space, child_style, sizes);
   NGBoxStrut margins = ComputeMargins(space, child_style, space.WritingMode(),
                                       space.Direction());
-  if (!child_style.IsFloating()) {
+  if (!child->IsFloating()) {
     ApplyAutoMargins(space, child_style, child_inline_size, &margins);
   }
   return margins;
 }
 
 RefPtr<NGConstraintSpace> NGBlockLayoutAlgorithm::CreateConstraintSpaceForChild(
+    const NGLogicalOffset& child_bfc_offset,
     NGLayoutInputNode* child) {
   DCHECK(child);
 
   const ComputedStyle& child_style = child->Style();
   bool is_new_bfc = IsNewFormattingContextForBlockLevelChild(Style(), *child);
   space_builder_.SetIsNewFormattingContext(is_new_bfc)
-      .SetBfcOffset(curr_bfc_offset_);
+      .SetBfcOffset(child_bfc_offset);
 
   if (child->IsInline()) {
     // TODO(kojii): Setup space_builder_ appropriately for inline child.
-    space_builder_.SetBfcOffset(curr_bfc_offset_)
-        .SetClearanceOffset(ConstraintSpace().ClearanceOffset());
+    space_builder_.SetClearanceOffset(ConstraintSpace().ClearanceOffset());
     return space_builder_.ToConstraintSpace(
         FromPlatformWritingMode(Style().GetWritingMode()));
   }
@@ -522,8 +570,8 @@
   // Float's margins are not included in child's space because:
   // 1) Floats do not participate in margins collapsing.
   // 2) Floats margins are used separately to calculate floating exclusions.
-  space_builder_.SetMarginStrut(child_style.IsFloating() ? NGMarginStrut()
-                                                         : curr_margin_strut_);
+  space_builder_.SetMarginStrut(child->IsFloating() ? NGMarginStrut()
+                                                    : curr_margin_strut_);
 
   LayoutUnit space_available;
   if (constraint_space_->HasBlockFragmentation()) {
@@ -532,7 +580,7 @@
     // position in the formatting context, and are able to adjust the
     // fragmentation line.
     if (is_new_bfc) {
-      space_available -= curr_bfc_offset_.block_offset;
+      space_available -= child_bfc_offset.block_offset;
     }
   }
   space_builder_.SetFragmentainerSpaceAvailable(space_available);
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_block_layout_algorithm.h b/third_party/WebKit/Source/core/layout/ng/ng_block_layout_algorithm.h
index 2e6f19c..eba0950 100644
--- a/third_party/WebKit/Source/core/layout/ng/ng_block_layout_algorithm.h
+++ b/third_party/WebKit/Source/core/layout/ng/ng_block_layout_algorithm.h
@@ -9,6 +9,7 @@
 #include "core/layout/ng/geometry/ng_margin_strut.h"
 #include "core/layout/ng/ng_block_break_token.h"
 #include "core/layout/ng/ng_block_node.h"
+#include "core/layout/ng/ng_box_fragment.h"
 #include "core/layout/ng/ng_constraint_space_builder.h"
 #include "core/layout/ng/ng_layout_algorithm.h"
 #include "platform/wtf/RefPtr.h"
@@ -23,6 +24,12 @@
                                   const NGLogicalOffset&,
                                   NGFragmentBuilder* builder);
 
+// Positions pending floats starting from {@origin_block_offset} and relative
+// to container's BFC offset.
+void PositionPendingFloats(LayoutUnit origin_block_offset,
+                           NGFragmentBuilder* container_builder,
+                           NGConstraintSpace* space);
+
 // A class for general block layout (e.g. a <div> with no special style).
 // Lays out the children in sequence.
 class CORE_EXPORT NGBlockLayoutAlgorithm
@@ -45,11 +52,52 @@
                               const NGConstraintSpace& space);
 
   // Creates a new constraint space for the current child.
-  RefPtr<NGConstraintSpace> CreateConstraintSpaceForChild(NGLayoutInputNode*);
-  void PrepareChildLayout(NGLayoutInputNode*);
-  void FinishChildLayout(NGLayoutInputNode*,
-                         NGConstraintSpace*,
-                         RefPtr<NGLayoutResult>);
+  RefPtr<NGConstraintSpace> CreateConstraintSpaceForChild(
+      const NGLogicalOffset& child_bfc_offset,
+      NGLayoutInputNode*);
+
+  // @return Estimated BFC offset for the "to be layout" child.
+  NGLogicalOffset PrepareChildLayout(NGLayoutInputNode*);
+
+  void FinishChildLayout(const NGConstraintSpace*, NGLayoutResult*);
+
+  // Positions the fragment that establishes a new formatting context.
+  //
+  // This uses Layout Opportunity iterator to position the fragment.
+  // That's because an element that establishes a new block formatting context
+  // must not overlap the margin box of any floats in the same block formatting
+  // context as the element itself.
+  //
+  // So if necessary, we clear the new BFC by placing it below any preceding
+  // floats or place it adjacent to such floats if there is sufficient space.
+  //
+  // Example:
+  // <div id="container">
+  //   <div id="float"></div>
+  //   <div id="new-fc" style="margin-top: 20px;"></div>
+  // </div>
+  // 1) If #new-fc is small enough to fit the available space right from #float
+  //    then it will be placed there and we collapse its margin.
+  // 2) If #new-fc is too big then we need to clear its position and place it
+  //    below #float ignoring its vertical margin.
+  NGLogicalOffset PositionNewFc(const NGBoxFragment&,
+                                const NGConstraintSpace& child_space);
+
+  // Positions the fragment that knows its BFC offset.
+  NGLogicalOffset PositionWithBfcOffset(const NGBoxFragment&);
+
+  // Positions using the parent BFC offset.
+  // Fragment doesn't know its offset but we can still calculate its BFC
+  // position because the parent fragment's BFC is known.
+  // Example:
+  //   BFC Offset is known here because of the padding.
+  //   <div style="padding: 1px">
+  //     <div id="empty-div" style="margins: 1px"></div>
+  NGLogicalOffset PositionWithParentBfc();
+
+  void FinishFloatChildLayout(const ComputedStyle&,
+                              const NGConstraintSpace&,
+                              const NGLayoutResult*);
 
   // Final adjustments before fragment creation. We need to prevent the
   // fragment from crossing fragmentainer boundaries, and rather create a break
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_block_layout_algorithm_test.cc b/third_party/WebKit/Source/core/layout/ng/ng_block_layout_algorithm_test.cc
index 6f72f77..73f8eabf 100644
--- a/third_party/WebKit/Source/core/layout/ng/ng_block_layout_algorithm_test.cc
+++ b/third_party/WebKit/Source/core/layout/ng/ng_block_layout_algorithm_test.cc
@@ -1906,8 +1906,8 @@
   ASSERT_EQ(1UL, floating_objects.size());
   auto floating_object = floating_objects.TakeFirst();
   // left-float's margin = 15.
-  EXPECT_THAT(LayoutUnit(15), floating_object->X());
-  EXPECT_THAT(LayoutUnit(15), floating_object->Y());
+  EXPECT_THAT(floating_object->X(), LayoutUnit(15));
+  EXPECT_THAT(floating_object->Y(), LayoutUnit(15));
 
   RefPtr<const NGPhysicalBoxFragment> html_fragment;
   std::tie(html_fragment, std::ignore) = RunBlockLayoutAlgorithmForElement(
@@ -1919,14 +1919,14 @@
   auto* empty_block1 =
       ToNGPhysicalBoxFragment(container_fragment->Children()[0].Get());
   // empty-block1's margin == 8
-  EXPECT_THAT(NGPhysicalOffset(LayoutUnit(8), LayoutUnit(8)),
-              empty_block1->Offset());
+  EXPECT_THAT(empty_block1->Offset(),
+              NGPhysicalOffset(LayoutUnit(8), LayoutUnit(8)));
 
   auto* empty_block2 =
       ToNGPhysicalBoxFragment(container_fragment->Children()[1].Get());
   // empty-block2's margin == 50
-  EXPECT_THAT(NGPhysicalOffset(LayoutUnit(0), LayoutUnit(50)),
-              empty_block2->Offset());
+  EXPECT_THAT(empty_block2->Offset(),
+              NGPhysicalOffset(LayoutUnit(0), LayoutUnit(50)));
 }
 
 // Verifies that we can correctly position blocks with clearance and
@@ -2270,7 +2270,66 @@
   EXPECT_FALSE(iterator.NextChild());
 }
 
-TEST_F(NGBlockLayoutAlgorithmTest, NewFormattingContextBlock) {}
+// Verifies that we correctly position a new FC block with the Layout
+// Opportunity iterator.
+TEST_F(NGBlockLayoutAlgorithmTest,
+       NewFcBlockWithAdjoiningFloatCollapsesMargins) {
+  SetBodyInnerHTML(R"HTML(
+    <!DOCTYPE html>
+    <style>
+      #container {
+        width: 200px; outline: solid purple 1px;
+      }
+      #float {
+        float: left; width: 100px; height: 30px; background: red;
+      }
+      #new-fc {
+        contain: paint; margin-top: 20px; background: purple;
+        height: 50px;
+      }
+    </style>
+    <div id="container">
+      <div id="float"></div>
+      <div id="new-fc"></div>
+    </div>
+  )HTML");
 
+  const NGPhysicalBoxFragment* body_fragment;
+  const NGPhysicalBoxFragment* container_fragment;
+  const NGPhysicalBoxFragment* new_fc_fragment;
+  RefPtr<const NGPhysicalBoxFragment> fragment;
+  auto run_test = [&](const Length& block_width) {
+    Element* new_fc_block = GetDocument().GetElementById("new-fc");
+    new_fc_block->MutableComputedStyle()->SetWidth(block_width);
+    std::tie(fragment, std::ignore) = RunBlockLayoutAlgorithmForElement(
+        GetDocument().getElementsByTagName("html")->item(0));
+    ASSERT_EQ(1UL, fragment->Children().size());
+    body_fragment = ToNGPhysicalBoxFragment(fragment->Children()[0].Get());
+    container_fragment =
+        ToNGPhysicalBoxFragment(body_fragment->Children()[0].Get());
+    ASSERT_EQ(1UL, container_fragment->Children().size());
+    new_fc_fragment =
+        ToNGPhysicalBoxFragment(container_fragment->Children()[0].Get());
+  };
+
+  // #new-fc is small enough to fit on the same line with #float.
+  run_test(Length(80, kFixed));
+  // 100 = float's width, 0 = no margin collapsing
+  EXPECT_THAT(new_fc_fragment->Offset(),
+              NGPhysicalOffset(LayoutUnit(100), LayoutUnit(0)));
+  // 8 = body's margins, 20 = new-fc's margin top(20) collapses with
+  // body's margin(8)
+  EXPECT_THAT(body_fragment->Offset(),
+              NGPhysicalOffset(LayoutUnit(8), LayoutUnit(20)));
+
+  // #new-fc is too wide to be positioned on the same line with #float
+  run_test(Length(120, kFixed));
+  // 30 = #float's height
+  EXPECT_THAT(new_fc_fragment->Offset(),
+              NGPhysicalOffset(LayoutUnit(0), LayoutUnit(30)));
+  // 8 = body's margins, no margin collapsing
+  EXPECT_THAT(body_fragment->Offset(),
+              NGPhysicalOffset(LayoutUnit(8), LayoutUnit(8)));
+}
 }  // namespace
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_floating_object.h b/third_party/WebKit/Source/core/layout/ng/ng_floating_object.h
index 29d71d83..29f9fd6 100644
--- a/third_party/WebKit/Source/core/layout/ng/ng_floating_object.h
+++ b/third_party/WebKit/Source/core/layout/ng/ng_floating_object.h
@@ -13,6 +13,7 @@
 #include "core/layout/ng/ng_physical_fragment.h"
 #include "core/style/ComputedStyle.h"
 #include "core/style/ComputedStyleConstants.h"
+#include "platform/wtf/Optional.h"
 #include "platform/wtf/RefPtr.h"
 
 namespace blink {
@@ -50,6 +51,10 @@
   NGLogicalOffset origin_offset;
   NGLogicalOffset from_offset;
 
+  // Calculated logical offset. It's never {@code nullopt} for a positioned
+  // float.
+  WTF::Optional<NGLogicalOffset> logical_offset;
+
   // Writing mode of the float's constraint space.
   NGWritingMode writing_mode;
 
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_floats_utils.cc b/third_party/WebKit/Source/core/layout/ng/ng_floats_utils.cc
index 93dd925e..762b170a 100644
--- a/third_party/WebKit/Source/core/layout/ng/ng_floats_utils.cc
+++ b/third_party/WebKit/Source/core/layout/ng/ng_floats_utils.cc
@@ -5,6 +5,7 @@
 #include "core/layout/ng/ng_floats_utils.h"
 
 #include "core/layout/ng/ng_box_fragment.h"
+#include "core/layout/ng/ng_layout_opportunity_iterator.h"
 
 namespace blink {
 namespace {
@@ -28,42 +29,15 @@
   return adjusted_offset;
 }
 
-// Finds a layout opportunity for the fragment.
-// It iterates over all layout opportunities in the constraint space and returns
-// the first layout opportunity that is wider than the fragment or returns the
-// last one which is always the widest.
-//
-// @param space Constraint space that is used to find layout opportunity for
-//              the fragment.
-// @param fragment Fragment that needs to be placed.
-// @param floating_object Floating object for which we need to find a layout
-//                        opportunity.
-// @return Layout opportunity for the fragment.
-const NGLayoutOpportunity FindLayoutOpportunityForFragment(
+NGLayoutOpportunity FindLayoutOpportunityForFloat(
     const NGConstraintSpace* space,
     const NGFragment& fragment,
     const NGFloatingObject* floating_object) {
   NGLogicalOffset adjusted_origin_point =
       AdjustToTopEdgeAlignmentRule(*space, floating_object->origin_offset);
-
-  NGLayoutOpportunityIterator opportunity_iter(space->Exclusions().get(),
-                                               floating_object->available_size,
-                                               adjusted_origin_point);
-  NGLayoutOpportunity opportunity;
-  NGLayoutOpportunity opportunity_candidate = opportunity_iter.Next();
-
-  NGBoxStrut margins = floating_object->margins;
-  while (!opportunity_candidate.IsEmpty()) {
-    opportunity = opportunity_candidate;
-    // Checking opportunity's block size is not necessary as a float cannot be
-    // positioned on top of another float inside of the same constraint space.
-    auto fragment_inline_size = fragment.InlineSize() + margins.InlineSum();
-    if (opportunity.size.inline_size >= fragment_inline_size)
-      break;
-
-    opportunity_candidate = opportunity_iter.Next();
-  }
-  return opportunity;
+  return FindLayoutOpportunityForFragment(
+      space->Exclusions().get(), floating_object->available_size,
+      adjusted_origin_point, floating_object->margins, fragment);
 }
 
 // Calculates the logical offset for opportunity.
@@ -128,7 +102,7 @@
       ToNGPhysicalBoxFragment(floating_object->fragment.Get()));
 
   // Find a layout opportunity that will fit our float.
-  NGLayoutOpportunity opportunity = FindLayoutOpportunityForFragment(
+  NGLayoutOpportunity opportunity = FindLayoutOpportunityForFloat(
       new_parent_space, float_fragment, floating_object);
 
   // TODO(glebl): This should check for infinite opportunity instead.
@@ -163,21 +137,16 @@
   return logical_offset;
 }
 
-void PositionPendingFloats(const LayoutUnit& origin_block_offset,
-                           NGConstraintSpace* space,
-                           NGFragmentBuilder* builder) {
-  DCHECK(builder) << "Builder cannot be null here";
-  DCHECK(builder->BfcOffset()) << "Parent BFC offset should be known here";
-  LayoutUnit bfc_block_offset = builder->BfcOffset().value().block_offset;
-
-  for (auto& floating_object : builder->UnpositionedFloats()) {
+void PositionFloats(LayoutUnit origin_block_offset,
+                    LayoutUnit from_block_offset,
+                    const Vector<RefPtr<NGFloatingObject>>& floating_objects,
+                    NGConstraintSpace* space) {
+  for (auto& floating_object : floating_objects) {
     floating_object->origin_offset.block_offset = origin_block_offset;
-    floating_object->from_offset.block_offset = bfc_block_offset;
-
-    NGLogicalOffset offset = PositionFloat(floating_object.Get(), space);
-    builder->AddFloatingObject(floating_object, offset);
+    floating_object->from_offset.block_offset = from_block_offset;
+    floating_object->logical_offset =
+        PositionFloat(floating_object.Get(), space);
   }
-  builder->MutableUnpositionedFloats().clear();
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_floats_utils.h b/third_party/WebKit/Source/core/layout/ng/ng_floats_utils.h
index 985b8b5..449b6c5 100644
--- a/third_party/WebKit/Source/core/layout/ng/ng_floats_utils.h
+++ b/third_party/WebKit/Source/core/layout/ng/ng_floats_utils.h
@@ -17,11 +17,11 @@
 CORE_EXPORT NGLogicalOffset PositionFloat(NGFloatingObject*,
                                           NGConstraintSpace* new_parent_space);
 
-// Positions pending floats stored on the fragment builder starting from
-// {@code origin_block_offset}.
-void PositionPendingFloats(const LayoutUnit& origin_block_offset,
-                           NGConstraintSpace* space,
-                           NGFragmentBuilder* builder);
+// Positions the list of floats by updating their {@code logical_offset}.
+void PositionFloats(LayoutUnit origin_block_offset,
+                    LayoutUnit from_block_offset,
+                    const Vector<RefPtr<NGFloatingObject>>&,
+                    NGConstraintSpace*);
 }  // namespace blink
 
 #endif  // NGFloatsUtils_h
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_fragment_builder.cc b/third_party/WebKit/Source/core/layout/ng/ng_fragment_builder.cc
index 6061bdd..d2c03777 100644
--- a/third_party/WebKit/Source/core/layout/ng/ng_fragment_builder.cc
+++ b/third_party/WebKit/Source/core/layout/ng/ng_fragment_builder.cc
@@ -109,14 +109,6 @@
   return *this;
 }
 
-NGFragmentBuilder& NGFragmentBuilder::AddFloatingObject(
-    RefPtr<NGFloatingObject> floating_object,
-    const NGLogicalOffset& floating_object_offset) {
-  positioned_floats_.push_back(floating_object);
-  floating_object_offsets_.push_back(floating_object_offset);
-  return *this;
-}
-
 NGFragmentBuilder& NGFragmentBuilder::SetBfcOffset(
     const NGLogicalOffset& offset) {
   bfc_offset_ = offset;
@@ -204,11 +196,14 @@
     break_token = NGBlockBreakToken::Create(node_.Get());
   }
 
-  for (size_t i = 0; i < positioned_floats_.size(); ++i) {
-    RefPtr<NGFloatingObject>& floating_object = positioned_floats_[i];
+  for (auto& floating_object : positioned_floats_) {
+    DCHECK(floating_object->logical_offset)
+        << "logical_offset should be set for a positioned float.";
     NGPhysicalFragment* floating_fragment = floating_object->fragment.Get();
-    floating_fragment->SetOffset(floating_object_offsets_[i].ConvertToPhysical(
-        writing_mode_, direction_, physical_size, floating_fragment->Size()));
+    floating_fragment->SetOffset(
+        floating_object->logical_offset.value().ConvertToPhysical(
+            writing_mode_, direction_, physical_size,
+            floating_fragment->Size()));
   }
 
   RefPtr<NGPhysicalBoxFragment> fragment = AdoptRef(new NGPhysicalBoxFragment(
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_fragment_builder.h b/third_party/WebKit/Source/core/layout/ng/ng_fragment_builder.h
index 269cba6..0924450 100644
--- a/third_party/WebKit/Source/core/layout/ng/ng_fragment_builder.h
+++ b/third_party/WebKit/Source/core/layout/ng/ng_fragment_builder.h
@@ -38,9 +38,6 @@
   NGFragmentBuilder& AddChild(RefPtr<NGPhysicalFragment>,
                               const NGLogicalOffset&);
 
-  NGFragmentBuilder& AddFloatingObject(RefPtr<NGFloatingObject>,
-                                       const NGLogicalOffset&);
-
   NGFragmentBuilder& SetBfcOffset(const NGLogicalOffset& offset);
 
   NGFragmentBuilder& AddUnpositionedFloat(
@@ -114,6 +111,11 @@
     return unpositioned_floats_;
   }
 
+  // Mutable list of positioned floats, i.e. floats with logical_offset set.
+  Vector<RefPtr<NGFloatingObject>>& MutablePositionedFloats() {
+    return positioned_floats_;
+  }
+
   const WTF::Optional<NGLogicalOffset>& BfcOffset() const {
     return bfc_offset_;
   }
@@ -171,7 +173,6 @@
   // determine its block position in space.
   Vector<RefPtr<NGFloatingObject>> unpositioned_floats_;
 
-  Vector<NGLogicalOffset> floating_object_offsets_;
   Vector<RefPtr<NGFloatingObject>> positioned_floats_;
 
   WTF::Optional<NGLogicalOffset> bfc_offset_;
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_layout_algorithm.h b/third_party/WebKit/Source/core/layout/ng/ng_layout_algorithm.h
index 2317506..be9bcfa5 100644
--- a/third_party/WebKit/Source/core/layout/ng/ng_layout_algorithm.h
+++ b/third_party/WebKit/Source/core/layout/ng/ng_layout_algorithm.h
@@ -6,6 +6,7 @@
 #define NGLayoutAlgorithm_h
 
 #include "core/CoreExport.h"
+#include "core/layout/ng/ng_floats_utils.h"
 #include "core/layout/ng/ng_fragment_builder.h"
 #include "core/layout/ng/ng_min_max_content_size.h"
 #include "platform/wtf/Allocator.h"
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_layout_input_node.h b/third_party/WebKit/Source/core/layout/ng/ng_layout_input_node.h
index be10d879..25b30c7 100644
--- a/third_party/WebKit/Source/core/layout/ng/ng_layout_input_node.h
+++ b/third_party/WebKit/Source/core/layout/ng/ng_layout_input_node.h
@@ -6,11 +6,11 @@
 #define NGLayoutInputNode_h
 
 #include "core/CoreExport.h"
+#include "core/style/ComputedStyle.h"
 #include "platform/heap/Handle.h"
 
 namespace blink {
 
-class ComputedStyle;
 class LayoutObject;
 class NGBreakToken;
 class NGConstraintSpace;
@@ -29,6 +29,8 @@
 
   bool IsBlock() const { return type_ == kLegacyBlock; }
 
+  bool IsFloating() const { return IsBlock() && Style().IsFloating(); }
+
   virtual ~NGLayoutInputNode(){};
 
   // Performs layout on this input node, will return the layout result.
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_layout_opportunity_iterator.cc b/third_party/WebKit/Source/core/layout/ng/ng_layout_opportunity_iterator.cc
index de08081..24e29d3 100644
--- a/third_party/WebKit/Source/core/layout/ng/ng_layout_opportunity_iterator.cc
+++ b/third_party/WebKit/Source/core/layout/ng/ng_layout_opportunity_iterator.cc
@@ -259,6 +259,28 @@
 }
 }  // namespace
 
+NGLayoutOpportunity FindLayoutOpportunityForFragment(
+    const NGExclusions* exclusions,
+    const NGLogicalSize& available_size,
+    const NGLogicalOffset& origin_point,
+    const NGBoxStrut& margins,
+    const NGFragment& fragment) {
+  NGLayoutOpportunityIterator opportunity_iter(exclusions, available_size,
+                                               origin_point);
+  NGLayoutOpportunity opportunity;
+  NGLayoutOpportunity opportunity_candidate = opportunity_iter.Next();
+  while (!opportunity_candidate.IsEmpty()) {
+    opportunity = opportunity_candidate;
+    // Checking opportunity's block size is not necessary as a float cannot be
+    // positioned on top of another float inside of the same constraint space.
+    auto fragment_inline_size = fragment.InlineSize() + margins.InlineSum();
+    if (opportunity.size.inline_size >= fragment_inline_size)
+      break;
+    opportunity_candidate = opportunity_iter.Next();
+  }
+  return opportunity;
+}
+
 NGLayoutOpportunityIterator::NGLayoutOpportunityIterator(
     const NGExclusions* exclusions,
     const NGLogicalSize& available_size,
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_layout_opportunity_iterator.h b/third_party/WebKit/Source/core/layout/ng/ng_layout_opportunity_iterator.h
index 24e8941..6684f93 100644
--- a/third_party/WebKit/Source/core/layout/ng/ng_layout_opportunity_iterator.h
+++ b/third_party/WebKit/Source/core/layout/ng/ng_layout_opportunity_iterator.h
@@ -7,6 +7,7 @@
 
 #include "core/CoreExport.h"
 #include "core/layout/ng/ng_exclusion.h"
+#include "core/layout/ng/ng_fragment.h"
 #include "core/layout/ng/ng_layout_opportunity_tree_node.h"
 #include "platform/wtf/Vector.h"
 #include "platform/wtf/text/StringBuilder.h"
@@ -16,6 +17,13 @@
 typedef NGLogicalRect NGLayoutOpportunity;
 typedef Vector<NGLayoutOpportunity> NGLayoutOpportunities;
 
+NGLayoutOpportunity FindLayoutOpportunityForFragment(
+    const NGExclusions* exclusions,
+    const NGLogicalSize& size,
+    const NGLogicalOffset& origin_point,
+    const NGBoxStrut& margins,
+    const NGFragment& fragment);
+
 class CORE_EXPORT NGLayoutOpportunityIterator final {
  public:
   // Default constructor.
diff --git a/third_party/WebKit/Source/core/loader/PingLoader.cpp b/third_party/WebKit/Source/core/loader/PingLoader.cpp
index 563614afeb..9359f29 100644
--- a/third_party/WebKit/Source/core/loader/PingLoader.cpp
+++ b/third_party/WebKit/Source/core/loader/PingLoader.cpp
@@ -267,7 +267,7 @@
   if (frame->FrameScheduler())
     frame->FrameScheduler()->DidStopLoading(identifier_);
 
-  loader_ = WTF::WrapUnique(Platform::Current()->CreateURLLoader());
+  loader_ = Platform::Current()->CreateURLLoader();
   DCHECK(loader_);
   WrappedResourceRequest wrapped_request(request);
   wrapped_request.SetAllowStoredCredentials(credentials_allowed ==
diff --git a/third_party/WebKit/Source/devtools/.devtools b/third_party/WebKit/Source/devtools/.devtools
deleted file mode 100644
index cf718f46..0000000
--- a/third_party/WebKit/Source/devtools/.devtools
+++ /dev/null
@@ -1,5 +0,0 @@
-{
-    "mappings": [
-        { "folder": "/front_end/", "url": "chrome-devtools://devtools/bundled/" }
-    ]
-}
diff --git a/third_party/WebKit/Source/devtools/front_end/sdk/CSSMetadata.js b/third_party/WebKit/Source/devtools/front_end/sdk/CSSMetadata.js
index b31fb71a..aee9e670 100644
--- a/third_party/WebKit/Source/devtools/front_end/sdk/CSSMetadata.js
+++ b/third_party/WebKit/Source/devtools/front_end/sdk/CSSMetadata.js
@@ -655,12 +655,16 @@
   'transform-origin': {values: ['left', 'center', 'right', 'top', 'bottom']},
   'transform-style': {values: ['flat', 'preserve-3d']},
   'transition-timing-function': {
-    values:
-        ['ease', 'linear', 'ease-in', 'ease-out', 'ease-in-out', 'step-start', 'step-end', 'steps', 'cubic-bezier']
+    values: [
+      'ease', 'linear', 'ease-in', 'ease-out', 'ease-in-out', 'step-start', 'step-end', 'steps', 'frames',
+      'cubic-bezier'
+    ]
   },
   'animation-timing-function': {
-    values:
-        ['ease', 'linear', 'ease-in', 'ease-out', 'ease-in-out', 'step-start', 'step-end', 'steps', 'cubic-bezier']
+    values: [
+      'ease', 'linear', 'ease-in', 'ease-out', 'ease-in-out', 'step-start', 'step-end', 'steps', 'frames',
+      'cubic-bezier'
+    ]
   },
   'animation-direction': {values: ['normal', 'reverse', 'alternate', 'alternate-reverse']},
   'animation-play-state': {values: ['running', 'paused']},
diff --git a/third_party/WebKit/Source/devtools/front_end/settings/EditFileSystemView.js b/third_party/WebKit/Source/devtools/front_end/settings/EditFileSystemView.js
index 1a24b50b..ad02bc0 100644
--- a/third_party/WebKit/Source/devtools/front_end/settings/EditFileSystemView.js
+++ b/third_party/WebKit/Source/devtools/front_end/settings/EditFileSystemView.js
@@ -108,28 +108,13 @@
     this._mappingsList.clear();
     var mappings = Workspace.fileSystemMapping.mappingEntries(this._fileSystemPath);
     for (var entry of mappings) {
-      if (entry.configurable) {
-        this._mappingsList.appendItem(entry, true);
-        this._mappings.push(entry);
-      }
-    }
-    for (var entry of mappings) {
-      if (!entry.configurable) {
-        this._mappingsList.appendItem(entry, false);
-        this._mappings.push(entry);
-      }
-    }
-
-    for (var folder of Workspace.isolatedFileSystemManager.fileSystem(this._fileSystemPath)
-             .nonConfigurableExcludedFolders()
-             .values()) {
-      this._excludedFolders.push(folder);
-      this._excludedFoldersList.appendItem(folder, false);
+      this._mappingsList.appendItem(entry, true);
+      this._mappings.push(entry);
     }
   }
 
   _addMappingButtonClicked() {
-    var entry = new Workspace.FileSystemMapping.Entry(this._fileSystemPath, '', '', true);
+    var entry = new Workspace.FileSystemMapping.Entry(this._fileSystemPath, '', '');
     this._mappingsList.addNewItem(0, entry);
   }
 
@@ -145,14 +130,11 @@
    */
   renderItem(item, editable) {
     var element = createElementWithClass('div', 'file-system-list-item');
-    if (!editable)
-      element.classList.add('locked');
     if (item instanceof Workspace.FileSystemMapping.Entry) {
       var entry = /** @type {!Workspace.FileSystemMapping.Entry} */ (item);
-      var urlPrefix = entry.configurable ? entry.urlPrefix : Common.UIString('%s (via .devtools)', entry.urlPrefix);
       var urlPrefixElement = element.createChild('div', 'file-system-value');
-      urlPrefixElement.textContent = urlPrefix;
-      urlPrefixElement.title = urlPrefix;
+      urlPrefixElement.textContent = entry.urlPrefix;
+      urlPrefixElement.title = entry.urlPrefix;
       element.createChild('div', 'file-system-separator');
       var pathPrefixElement = element.createChild('div', 'file-system-value');
       pathPrefixElement.textContent = entry.pathPrefix;
@@ -163,7 +145,6 @@
       pathPrefixElement.textContent = pathPrefix;
       pathPrefixElement.title = pathPrefix;
     }
-    element.createChild('div', 'file-system-locked').title = Common.UIString('From .devtools file');
     return element;
   }
 
@@ -264,7 +245,7 @@
     function urlPrefixValidator(item, index, input) {
       var prefix = this._normalizePrefix(input.value);
       for (var i = 0; i < this._mappings.length; ++i) {
-        if (i !== index && this._mappings[i].configurable && this._mappings[i].urlPrefix === prefix)
+        if (i !== index && this._mappings[i].urlPrefix === prefix)
           return false;
       }
       return !!prefix;
@@ -280,7 +261,7 @@
     function pathPrefixValidator(item, index, input) {
       var prefix = this._normalizePrefix(input.value);
       for (var i = 0; i < this._mappings.length; ++i) {
-        if (i !== index && this._mappings[i].configurable && this._mappings[i].pathPrefix === prefix)
+        if (i !== index && this._mappings[i].pathPrefix === prefix)
           return false;
       }
       return !!prefix;
diff --git a/third_party/WebKit/Source/devtools/front_end/settings/editFileSystemView.css b/third_party/WebKit/Source/devtools/front_end/settings/editFileSystemView.css
index 30f3ec2..7bf411c 100644
--- a/third_party/WebKit/Source/devtools/front_end/settings/editFileSystemView.css
+++ b/third_party/WebKit/Source/devtools/front_end/settings/editFileSystemView.css
@@ -75,16 +75,3 @@
     width: 100%;
     text-align: inherit;
 }
-
-.file-system-locked {
-    flex: none;
-    visibility: hidden;
-}
-
-.file-system-locked:after {
-    content: "\1F512";
-}
-
-.file-system-list-item.locked .file-system-locked {
-    visibility: visible;
-}
diff --git a/third_party/WebKit/Source/devtools/front_end/workspace/FileSystemMapping.js b/third_party/WebKit/Source/devtools/front_end/workspace/FileSystemMapping.js
index e2224779f4..3f6b8672 100644
--- a/third_party/WebKit/Source/devtools/front_end/workspace/FileSystemMapping.js
+++ b/third_party/WebKit/Source/devtools/front_end/workspace/FileSystemMapping.js
@@ -56,7 +56,7 @@
    */
   _fileSystemsLoaded(fileSystems) {
     for (var fileSystem of fileSystems)
-      this._addMappingsForFilesystem(fileSystem);
+      this.addFileSystem(fileSystem.path());
   }
 
   /**
@@ -64,26 +64,7 @@
    */
   _fileSystemAdded(event) {
     var fileSystem = /** @type {!Workspace.IsolatedFileSystem} */ (event.data);
-    this._addMappingsForFilesystem(fileSystem);
-  }
-
-  /**
-   * @param {!Workspace.IsolatedFileSystem} fileSystem
-   */
-  _addMappingsForFilesystem(fileSystem) {
     this.addFileSystem(fileSystem.path());
-
-    var mappings = fileSystem.projectProperty('mappings');
-    for (var i = 0; Array.isArray(mappings) && i < mappings.length; ++i) {
-      var mapping = mappings[i];
-      if (!mapping || typeof mapping !== 'object')
-        continue;
-      var folder = mapping['folder'];
-      var url = mapping['url'];
-      if (typeof folder !== 'string' || typeof url !== 'string')
-        continue;
-      this.addNonConfigurableFileMapping(fileSystem.path(), url, folder);
-    }
   }
 
   /**
@@ -105,8 +86,7 @@
 
       for (var i = 0; i < savedFileSystemMappings.length; ++i) {
         var savedEntry = savedFileSystemMappings[i];
-        var entry =
-            new Workspace.FileSystemMapping.Entry(fileSystemPath, savedEntry.urlPrefix, savedEntry.pathPrefix, true);
+        var entry = new Workspace.FileSystemMapping.Entry(fileSystemPath, savedEntry.urlPrefix, savedEntry.pathPrefix);
         fileSystemMappings.push(entry);
       }
     }
@@ -119,10 +99,8 @@
     for (var fileSystemPath in this._fileSystemMappings) {
       setting[fileSystemPath] = [];
       var entries = this._fileSystemMappings[fileSystemPath];
-      for (var entry of entries) {
-        if (entry.configurable)
-          setting[fileSystemPath].push(entry);
-      }
+      for (var entry of entries)
+        setting[fileSystemPath].push(entry);
     }
     this._fileSystemMappingSetting.set(setting);
   }
@@ -135,9 +113,6 @@
       var fileSystemMapping = this._fileSystemMappings[fileSystemPath];
       for (var i = 0; i < fileSystemMapping.length; ++i) {
         var entry = fileSystemMapping[i];
-        // Resolve conflict in favor of configurable mapping.
-        if (this._mappingForURLPrefix[entry.urlPrefix] && !entry.configurable)
-          continue;
         this._mappingForURLPrefix[entry.urlPrefix] = entry;
         if (this._urlPrefixes.indexOf(entry.urlPrefix) === -1)
           this._urlPrefixes.push(entry.urlPrefix);
@@ -180,7 +155,7 @@
       pathPrefix += '/';
     if (!pathPrefix.startsWith('/'))
       pathPrefix = '/' + pathPrefix;
-    this._innerAddFileMapping(fileSystemPath, urlPrefix, pathPrefix, true);
+    this._innerAddFileMapping(fileSystemPath, urlPrefix, pathPrefix);
     this._saveToSettings();
   }
 
@@ -189,18 +164,8 @@
    * @param {string} urlPrefix
    * @param {string} pathPrefix
    */
-  addNonConfigurableFileMapping(fileSystemPath, urlPrefix, pathPrefix) {
-    this._innerAddFileMapping(fileSystemPath, urlPrefix, pathPrefix, false);
-  }
-
-  /**
-   * @param {string} fileSystemPath
-   * @param {string} urlPrefix
-   * @param {string} pathPrefix
-   * @param {boolean} configurable
-   */
-  _innerAddFileMapping(fileSystemPath, urlPrefix, pathPrefix, configurable) {
-    var entry = new Workspace.FileSystemMapping.Entry(fileSystemPath, urlPrefix, pathPrefix, configurable);
+  _innerAddFileMapping(fileSystemPath, urlPrefix, pathPrefix) {
+    var entry = new Workspace.FileSystemMapping.Entry(fileSystemPath, urlPrefix, pathPrefix);
     this._fileSystemMappings[fileSystemPath].push(entry);
     this._rebuildIndexes();
     this.dispatchEventToListeners(Workspace.FileSystemMapping.Events.FileMappingAdded, entry);
@@ -247,8 +212,6 @@
     var entry = null;
     for (var i = 0; i < entries.length; ++i) {
       var pathPrefix = entries[i].pathPrefix;
-      if (entry && entry.configurable && !entries[i].configurable)
-        continue;
       // We are looking for the longest pathPrefix match.
       if (entry && entry.pathPrefix.length > pathPrefix.length)
         continue;
@@ -266,7 +229,7 @@
   _configurableMappingEntryForPathPrefix(fileSystemPath, pathPrefix) {
     var entries = this._fileSystemMappings[fileSystemPath];
     for (var i = 0; i < entries.length; ++i) {
-      if (entries[i].configurable && pathPrefix === entries[i].pathPrefix)
+      if (pathPrefix === entries[i].pathPrefix)
         return entries[i];
     }
     return null;
@@ -320,7 +283,7 @@
    */
   removeMappingForURL(url) {
     var entry = this._mappingEntryForURL(url);
-    if (!entry || !entry.configurable)
+    if (!entry)
       return;
     this._fileSystemMappings[entry.fileSystemPath].remove(entry);
     this._saveToSettings();
@@ -374,13 +337,11 @@
    * @param {string} fileSystemPath
    * @param {string} urlPrefix
    * @param {string} pathPrefix
-   * @param {boolean} configurable
    */
-  constructor(fileSystemPath, urlPrefix, pathPrefix, configurable) {
+  constructor(fileSystemPath, urlPrefix, pathPrefix) {
     this.fileSystemPath = fileSystemPath;
     this.urlPrefix = urlPrefix;
     this.pathPrefix = pathPrefix;
-    this.configurable = configurable;
   }
 };
 
diff --git a/third_party/WebKit/Source/devtools/front_end/workspace/IsolatedFileSystem.js b/third_party/WebKit/Source/devtools/front_end/workspace/IsolatedFileSystem.js
index d1410582..594f826a 100644
--- a/third_party/WebKit/Source/devtools/front_end/workspace/IsolatedFileSystem.js
+++ b/third_party/WebKit/Source/devtools/front_end/workspace/IsolatedFileSystem.js
@@ -46,8 +46,6 @@
     this._excludedFoldersSetting = Common.settings.createLocalSetting('workspaceExcludedFolders', {});
     /** @type {!Set<string>} */
     this._excludedFolders = new Set(this._excludedFoldersSetting.get()[path] || []);
-    /** @type {!Set<string>} */
-    this._nonConfigurableExcludedFolders = new Set();
 
     /** @type {!Set<string>} */
     this._initialFilePaths = new Set();
@@ -69,27 +67,9 @@
       return Promise.resolve(/** @type {?Workspace.IsolatedFileSystem} */ (null));
 
     var fileSystem = new Workspace.IsolatedFileSystem(manager, path, embedderPath, domFileSystem);
-    var fileContentPromise = fileSystem.requestFileContentPromise('.devtools');
-    return fileContentPromise.then(onConfigAvailable)
+    return fileSystem._initializeFilePaths()
         .then(() => fileSystem)
         .catchException(/** @type {?Workspace.IsolatedFileSystem} */ (null));
-
-    /**
-     * @param {?string} projectText
-     * @return {!Promise}
-     */
-    function onConfigAvailable(projectText) {
-      if (projectText) {
-        try {
-          var projectObject = JSON.parse(projectText);
-          fileSystem._initializeProject(
-              typeof projectObject === 'object' ? /** @type {!Object} */ (projectObject) : null);
-        } catch (e) {
-          Common.console.error('Invalid project file: ' + projectText);
-        }
-      }
-      return fileSystem._initializeFilePaths();
-    }
   }
 
   /**
@@ -156,29 +136,6 @@
   }
 
   /**
-   * @param {?Object} projectObject
-   */
-  _initializeProject(projectObject) {
-    this._projectObject = projectObject;
-
-    var projectExcludes = this.projectProperty('excludes');
-    if (Array.isArray(projectExcludes)) {
-      for (var folder of /** @type {!Array<*>} */ (projectExcludes)) {
-        if (typeof folder === 'string')
-          this._nonConfigurableExcludedFolders.add(folder);
-      }
-    }
-  }
-
-  /**
-   * @param {string} key
-   * @return {*}
-   */
-  projectProperty(key) {
-    return this._projectObject ? this._projectObject[key] : null;
-  }
-
-  /**
    * @return {!Promise}
    */
   _initializeFilePaths() {
@@ -574,7 +531,7 @@
    * @return {boolean}
    */
   _isFileExcluded(folderPath) {
-    if (this._nonConfigurableExcludedFolders.has(folderPath) || this._excludedFolders.has(folderPath))
+    if (this._excludedFolders.has(folderPath))
       return true;
     var regex = this._manager.workspaceFolderExcludePatternSetting().asRegExp();
     return !!(regex && regex.test(folderPath));
@@ -588,13 +545,6 @@
   }
 
   /**
-   * @return {!Set<string>}
-   */
-  nonConfigurableExcludedFolders() {
-    return this._nonConfigurableExcludedFolders;
-  }
-
-  /**
    * @param {string} query
    * @param {!Common.Progress} progress
    * @param {function(!Array.<string>)} callback
diff --git a/third_party/WebKit/Source/modules/fetch/BodyStreamBuffer.cpp b/third_party/WebKit/Source/modules/fetch/BodyStreamBuffer.cpp
index 0ab6b66..e5f5b43 100644
--- a/third_party/WebKit/Source/modules/fetch/BodyStreamBuffer.cpp
+++ b/third_party/WebKit/Source/modules/fetch/BodyStreamBuffer.cpp
@@ -52,9 +52,9 @@
     client_->DidFetchDataLoadedString(string);
   }
 
-  void DidFetchDataLoadedStream() override {
+  void DidFetchDataLoadedDataPipe() override {
     buffer_->EndLoading();
-    client_->DidFetchDataLoadedStream();
+    client_->DidFetchDataLoadedDataPipe();
   }
 
   void DidFetchDataLoadedCustomFormat() override {
diff --git a/third_party/WebKit/Source/modules/fetch/DEPS b/third_party/WebKit/Source/modules/fetch/DEPS
index c6e03118..0aa29185 100644
--- a/third_party/WebKit/Source/modules/fetch/DEPS
+++ b/third_party/WebKit/Source/modules/fetch/DEPS
@@ -4,4 +4,6 @@
     "+modules/ModulesExport.h",
     "+modules/fetch",
     "+modules/credentialmanager",
+    "+mojo/public/cpp/system/data_pipe.h",
+    "+mojo/public/cpp/system/simple_watcher.h",
 ]
diff --git a/third_party/WebKit/Source/modules/fetch/FetchDataLoader.cpp b/third_party/WebKit/Source/modules/fetch/FetchDataLoader.cpp
index 1953256..ac601a3 100644
--- a/third_party/WebKit/Source/modules/fetch/FetchDataLoader.cpp
+++ b/third_party/WebKit/Source/modules/fetch/FetchDataLoader.cpp
@@ -7,6 +7,8 @@
 #include <memory>
 #include "core/html/parser/TextResourceDecoder.h"
 #include "modules/fetch/BytesConsumer.h"
+#include "mojo/public/cpp/system/simple_watcher.h"
+#include "platform/wtf/Functional.h"
 #include "platform/wtf/PtrUtil.h"
 #include "platform/wtf/text/StringBuilder.h"
 #include "platform/wtf/text/WTFString.h"
@@ -231,39 +233,61 @@
   StringBuilder builder_;
 };
 
-class FetchDataLoaderAsStream final : public FetchDataLoader,
-                                      public BytesConsumer::Client {
-  USING_GARBAGE_COLLECTED_MIXIN(FetchDataLoaderAsStream);
+class FetchDataLoaderAsDataPipe final : public FetchDataLoader,
+                                        public BytesConsumer::Client {
+  USING_GARBAGE_COLLECTED_MIXIN(FetchDataLoaderAsDataPipe);
 
  public:
-  explicit FetchDataLoaderAsStream(Stream* out_stream)
-      : out_stream_(out_stream) {}
+  explicit FetchDataLoaderAsDataPipe(
+      mojo::ScopedDataPipeProducerHandle out_data_pipe)
+      : out_data_pipe_(std::move(out_data_pipe)),
+        data_pipe_watcher_(FROM_HERE,
+                           mojo::SimpleWatcher::ArmingPolicy::MANUAL) {}
+  ~FetchDataLoaderAsDataPipe() override {}
 
   void Start(BytesConsumer* consumer,
              FetchDataLoader::Client* client) override {
     DCHECK(!client_);
     DCHECK(!consumer_);
+    data_pipe_watcher_.Watch(
+        out_data_pipe_.get(), MOJO_HANDLE_SIGNAL_WRITABLE,
+        ConvertToBaseCallback(WTF::Bind(&FetchDataLoaderAsDataPipe::OnWritable,
+                                        WrapWeakPersistent(this))));
+    data_pipe_watcher_.ArmOrNotify();
     client_ = client;
     consumer_ = consumer;
     consumer_->SetClient(this);
-    OnStateChange();
   }
 
+  void OnWritable(MojoResult) { OnStateChange(); }
+
+  // Implements BytesConsumer::Client.
   void OnStateChange() override {
-    bool need_to_flush = false;
-    while (true) {
+    bool should_wait = false;
+    while (!should_wait) {
       const char* buffer;
       size_t available;
       auto result = consumer_->BeginRead(&buffer, &available);
-      if (result == BytesConsumer::Result::kShouldWait) {
-        if (need_to_flush)
-          out_stream_->Flush();
+      if (result == BytesConsumer::Result::kShouldWait)
         return;
-      }
       if (result == BytesConsumer::Result::kOk) {
-        out_stream_->AddData(buffer, available);
-        need_to_flush = true;
-        result = consumer_->EndRead(available);
+        DCHECK_GT(available, 0UL);
+        uint32_t num_bytes = available;
+        MojoResult mojo_result =
+            mojo::WriteDataRaw(out_data_pipe_.get(), buffer, &num_bytes,
+                               MOJO_WRITE_DATA_FLAG_NONE);
+        if (mojo_result == MOJO_RESULT_OK) {
+          result = consumer_->EndRead(num_bytes);
+        } else if (mojo_result == MOJO_RESULT_SHOULD_WAIT) {
+          result = consumer_->EndRead(0);
+          should_wait = true;
+          data_pipe_watcher_.ArmOrNotify();
+        } else {
+          result = consumer_->EndRead(0);
+          StopInternal();
+          client_->DidFetchDataLoadFailed();
+          return;
+        }
       }
       switch (result) {
         case BytesConsumer::Result::kOk:
@@ -272,37 +296,38 @@
           NOTREACHED();
           return;
         case BytesConsumer::Result::kDone:
-          if (need_to_flush)
-            out_stream_->Flush();
-          out_stream_->Finalize();
-          client_->DidFetchDataLoadedStream();
+          StopInternal();
+          client_->DidFetchDataLoadedDataPipe();
           return;
         case BytesConsumer::Result::kError:
-          // If the stream is aborted soon after the stream is registered
-          // to the StreamRegistry, ServiceWorkerURLRequestJob may not
-          // notice the error and continue waiting forever.
-          // TODO(yhirano): Add new message to report the error to the
-          // browser process.
-          out_stream_->Abort();
+          StopInternal();
           client_->DidFetchDataLoadFailed();
           return;
       }
     }
   }
 
-  void Cancel() override { consumer_->Cancel(); }
+  void Cancel() override { StopInternal(); }
 
   DEFINE_INLINE_TRACE() {
     visitor->Trace(consumer_);
     visitor->Trace(client_);
-    visitor->Trace(out_stream_);
     FetchDataLoader::Trace(visitor);
     BytesConsumer::Client::Trace(visitor);
   }
 
+ private:
+  void StopInternal() {
+    consumer_->Cancel();
+    data_pipe_watcher_.Cancel();
+    out_data_pipe_.reset();
+  }
+
   Member<BytesConsumer> consumer_;
   Member<FetchDataLoader::Client> client_;
-  Member<Stream> out_stream_;
+
+  mojo::ScopedDataPipeProducerHandle out_data_pipe_;
+  mojo::SimpleWatcher data_pipe_watcher_;
 };
 
 }  // namespace
@@ -320,8 +345,9 @@
   return new FetchDataLoaderAsString();
 }
 
-FetchDataLoader* FetchDataLoader::CreateLoaderAsStream(Stream* out_stream) {
-  return new FetchDataLoaderAsStream(out_stream);
+FetchDataLoader* FetchDataLoader::CreateLoaderAsDataPipe(
+    mojo::ScopedDataPipeProducerHandle out_data_pipe) {
+  return new FetchDataLoaderAsDataPipe(std::move(out_data_pipe));
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/modules/fetch/FetchDataLoader.h b/third_party/WebKit/Source/modules/fetch/FetchDataLoader.h
index 62065b1..d379e5e 100644
--- a/third_party/WebKit/Source/modules/fetch/FetchDataLoader.h
+++ b/third_party/WebKit/Source/modules/fetch/FetchDataLoader.h
@@ -8,6 +8,7 @@
 #include "core/dom/DOMArrayBuffer.h"
 #include "core/streams/Stream.h"
 #include "modules/ModulesExport.h"
+#include "mojo/public/cpp/system/data_pipe.h"
 #include "platform/blob/BlobData.h"
 #include "platform/heap/Handle.h"
 #include "platform/wtf/Forward.h"
@@ -42,8 +43,8 @@
     }
     virtual void DidFetchDataLoadedString(const String&) { NOTREACHED(); }
     // This is called after all data are read from |handle| and written
-    // to |outStream|, and |outStream| is closed or aborted.
-    virtual void DidFetchDataLoadedStream() { NOTREACHED(); }
+    // to |out_data_pipe|, and |out_data_pipe| is closed or aborted.
+    virtual void DidFetchDataLoadedDataPipe() { NOTREACHED(); }
 
     // This function is called when a "custom" FetchDataLoader (none of the
     // ones listed above) finishes loading.
@@ -57,7 +58,9 @@
   static FetchDataLoader* CreateLoaderAsBlobHandle(const String& mime_type);
   static FetchDataLoader* CreateLoaderAsArrayBuffer();
   static FetchDataLoader* CreateLoaderAsString();
-  static FetchDataLoader* CreateLoaderAsStream(Stream*);
+  static FetchDataLoader* CreateLoaderAsDataPipe(
+      mojo::ScopedDataPipeProducerHandle out_data_pipe);
+
   virtual ~FetchDataLoader() {}
 
   // |consumer| must not have a client when called.
diff --git a/third_party/WebKit/Source/modules/serviceworkers/DEPS b/third_party/WebKit/Source/modules/serviceworkers/DEPS
index 3e20685..1421587 100644
--- a/third_party/WebKit/Source/modules/serviceworkers/DEPS
+++ b/third_party/WebKit/Source/modules/serviceworkers/DEPS
@@ -5,4 +5,5 @@
     "+modules/ModulesExport.h",
     "+modules/fetch",
     "+modules/serviceworkers",
+    "+mojo/public/cpp/system/data_pipe.h",
 ]
diff --git a/third_party/WebKit/Source/modules/serviceworkers/FetchRespondWithObserver.cpp b/third_party/WebKit/Source/modules/serviceworkers/FetchRespondWithObserver.cpp
index e2a70db..af03e8e9 100644
--- a/third_party/WebKit/Source/modules/serviceworkers/FetchRespondWithObserver.cpp
+++ b/third_party/WebKit/Source/modules/serviceworkers/FetchRespondWithObserver.cpp
@@ -113,17 +113,26 @@
          request_context == WebURLRequest::kRequestContextWorker;
 }
 
-class NoopLoaderClient final
-    : public GarbageCollectedFinalized<NoopLoaderClient>,
+// Notifies the result of FetchDataLoader to |handle_|. |handle_| pass through
+// the result to its observer which is outside of blink.
+class FetchLoaderClient final
+    : public GarbageCollectedFinalized<FetchLoaderClient>,
       public FetchDataLoader::Client {
-  WTF_MAKE_NONCOPYABLE(NoopLoaderClient);
-  USING_GARBAGE_COLLECTED_MIXIN(NoopLoaderClient);
+  WTF_MAKE_NONCOPYABLE(FetchLoaderClient);
+  USING_GARBAGE_COLLECTED_MIXIN(FetchLoaderClient);
 
  public:
-  NoopLoaderClient() = default;
-  void DidFetchDataLoadedStream() override {}
-  void DidFetchDataLoadFailed() override {}
+  explicit FetchLoaderClient(
+      std::unique_ptr<WebServiceWorkerStreamHandle> handle)
+      : handle_(std::move(handle)) {}
+
+  void DidFetchDataLoadedDataPipe() override { handle_->Completed(); }
+  void DidFetchDataLoadFailed() override { handle_->Aborted(); }
+
   DEFINE_INLINE_TRACE() { FetchDataLoader::Client::Trace(visitor); }
+
+ private:
+  std::unique_ptr<WebServiceWorkerStreamHandle> handle_;
 };
 
 }  // namespace
@@ -222,13 +231,29 @@
     RefPtr<BlobDataHandle> blob_data_handle = buffer->DrainAsBlobDataHandle(
         BytesConsumer::BlobSizePolicy::kAllowBlobWithInvalidSize);
     if (blob_data_handle) {
+      // Handle the blob response.
       web_response.SetBlobDataHandle(blob_data_handle);
-    } else {
-      Stream* out_stream = Stream::Create(GetExecutionContext(), "");
-      web_response.SetStreamURL(out_stream->Url());
-      buffer->StartLoading(FetchDataLoader::CreateLoaderAsStream(out_stream),
-                           new NoopLoaderClient);
+      ServiceWorkerGlobalScopeClient::From(GetExecutionContext())
+          ->RespondToFetchEvent(event_id_, web_response, event_dispatch_time_);
+      return;
     }
+    // Handle the stream response.
+    mojo::DataPipe data_pipe;
+    DCHECK(data_pipe.producer_handle.is_valid());
+    DCHECK(data_pipe.consumer_handle.is_valid());
+
+    std::unique_ptr<WebServiceWorkerStreamHandle> body_stream_handle =
+        WTF::MakeUnique<WebServiceWorkerStreamHandle>(
+            std::move(data_pipe.consumer_handle));
+    ServiceWorkerGlobalScopeClient::From(GetExecutionContext())
+        ->RespondToFetchEventWithResponseStream(event_id_, web_response,
+                                                body_stream_handle.get(),
+                                                event_dispatch_time_);
+
+    buffer->StartLoading(FetchDataLoader::CreateLoaderAsDataPipe(
+                             std::move(data_pipe.producer_handle)),
+                         new FetchLoaderClient(std::move(body_stream_handle)));
+    return;
   }
   ServiceWorkerGlobalScopeClient::From(GetExecutionContext())
       ->RespondToFetchEvent(event_id_, web_response, event_dispatch_time_);
@@ -236,7 +261,7 @@
 
 void FetchRespondWithObserver::OnNoResponse() {
   ServiceWorkerGlobalScopeClient::From(GetExecutionContext())
-      ->RespondToFetchEvent(event_id_, event_dispatch_time_);
+      ->RespondToFetchEventWithNoResponse(event_id_, event_dispatch_time_);
 }
 
 FetchRespondWithObserver::FetchRespondWithObserver(
diff --git a/third_party/WebKit/Source/modules/serviceworkers/ServiceWorkerGlobalScopeClient.h b/third_party/WebKit/Source/modules/serviceworkers/ServiceWorkerGlobalScopeClient.h
index 62e04e1a..5373e4a0 100644
--- a/third_party/WebKit/Source/modules/serviceworkers/ServiceWorkerGlobalScopeClient.h
+++ b/third_party/WebKit/Source/modules/serviceworkers/ServiceWorkerGlobalScopeClient.h
@@ -42,6 +42,7 @@
 #include "public/platform/modules/serviceworker/WebServiceWorkerClientsInfo.h"
 #include "public/platform/modules/serviceworker/WebServiceWorkerEventResult.h"
 #include "public/platform/modules/serviceworker/WebServiceWorkerSkipWaitingCallbacks.h"
+#include "public/platform/modules/serviceworker/WebServiceWorkerStreamHandle.h"
 
 namespace blink {
 
@@ -96,13 +97,17 @@
   virtual void DidHandleExtendableMessageEvent(int event_id,
                                                WebServiceWorkerEventResult,
                                                double event_dispatch_time) = 0;
-  // Calling respondToFetchEvent without response means no response was
-  // provided by the service worker in the fetch events, so fallback to native.
-  virtual void RespondToFetchEvent(int fetch_event_id,
-                                   double event_dispatch_time) = 0;
+  virtual void RespondToFetchEventWithNoResponse(
+      int fetch_event_id,
+      double event_dispatch_time) = 0;
   virtual void RespondToFetchEvent(int fetch_event_id,
                                    const WebServiceWorkerResponse&,
                                    double event_dispatch_time) = 0;
+  virtual void RespondToFetchEventWithResponseStream(
+      int fetch_event_id,
+      const WebServiceWorkerResponse&,
+      WebServiceWorkerStreamHandle*,
+      double event_dispatch_time) = 0;
   virtual void RespondToPaymentRequestEvent(int event_id,
                                             const WebPaymentAppResponse&,
                                             double event_dispatch_time) = 0;
diff --git a/third_party/WebKit/Source/platform/BUILD.gn b/third_party/WebKit/Source/platform/BUILD.gn
index 55013198..038a88c 100644
--- a/third_party/WebKit/Source/platform/BUILD.gn
+++ b/third_party/WebKit/Source/platform/BUILD.gn
@@ -570,6 +570,7 @@
     "exported/WebServiceWorkerProxy.cpp",
     "exported/WebServiceWorkerRequest.cpp",
     "exported/WebServiceWorkerResponse.cpp",
+    "exported/WebServiceWorkerStreamHandle.cpp",
     "exported/WebSpeechSynthesisUtterance.cpp",
     "exported/WebSpeechSynthesisVoice.cpp",
     "exported/WebSpeechSynthesizerClientImpl.cpp",
diff --git a/third_party/WebKit/Source/platform/animation/TimingFunction.cpp b/third_party/WebKit/Source/platform/animation/TimingFunction.cpp
index 179f91b7..86e7cdc 100644
--- a/third_party/WebKit/Source/platform/animation/TimingFunction.cpp
+++ b/third_party/WebKit/Source/platform/animation/TimingFunction.cpp
@@ -108,6 +108,27 @@
   return steps_->Clone();
 }
 
+String FramesTimingFunction::ToString() const {
+  StringBuilder builder;
+  builder.Append("frames(");
+  builder.Append(String::NumberToStringECMAScript(this->NumberOfFrames()));
+  builder.Append(")");
+  return builder.ToString();
+}
+
+void FramesTimingFunction::Range(double* min_value, double* max_value) const {
+  *min_value = 0;
+  *max_value = 1;
+}
+
+double FramesTimingFunction::Evaluate(double fraction, double) const {
+  return frames_->GetPreciseValue(fraction);
+}
+
+std::unique_ptr<cc::TimingFunction> FramesTimingFunction::CloneToCC() const {
+  return frames_->Clone();
+}
+
 PassRefPtr<TimingFunction> CreateCompositorTimingFunctionFromCC(
     const cc::TimingFunction* timing_function) {
   if (!timing_function)
@@ -169,6 +190,14 @@
          (lhs.GetStepPosition() == stf.GetStepPosition());
 }
 
+bool operator==(const FramesTimingFunction& lhs, const TimingFunction& rhs) {
+  if (rhs.GetType() != TimingFunction::Type::FRAMES)
+    return false;
+
+  const FramesTimingFunction& ftf = ToFramesTimingFunction(rhs);
+  return lhs.NumberOfFrames() == ftf.NumberOfFrames();
+}
+
 // The generic operator== *must* come after the
 // non-generic operator== otherwise it will end up calling itself.
 bool operator==(const TimingFunction& lhs, const TimingFunction& rhs) {
@@ -185,6 +214,10 @@
       const StepsTimingFunction& step = ToStepsTimingFunction(lhs);
       return (step == rhs);
     }
+    case TimingFunction::Type::FRAMES: {
+      const FramesTimingFunction& frame = ToFramesTimingFunction(lhs);
+      return (frame == rhs);
+    }
     default:
       ASSERT_NOT_REACHED();
   }
diff --git a/third_party/WebKit/Source/platform/animation/TimingFunction.h b/third_party/WebKit/Source/platform/animation/TimingFunction.h
index 5e52324..55758d6 100644
--- a/third_party/WebKit/Source/platform/animation/TimingFunction.h
+++ b/third_party/WebKit/Source/platform/animation/TimingFunction.h
@@ -222,6 +222,30 @@
   std::unique_ptr<cc::StepsTimingFunction> steps_;
 };
 
+class PLATFORM_EXPORT FramesTimingFunction final : public TimingFunction {
+ public:
+  static PassRefPtr<FramesTimingFunction> Create(int frames) {
+    return AdoptRef(new FramesTimingFunction(frames));
+  }
+
+  ~FramesTimingFunction() override {}
+
+  // TimingFunction implementation.
+  String ToString() const override;
+  double Evaluate(double fraction, double) const override;
+  void Range(double* min_value, double* max_value) const override;
+  std::unique_ptr<cc::TimingFunction> CloneToCC() const override;
+
+  int NumberOfFrames() const { return frames_->frames(); }
+
+ private:
+  FramesTimingFunction(int frames)
+      : TimingFunction(Type::FRAMES),
+        frames_(cc::FramesTimingFunction::Create(frames)) {}
+
+  std::unique_ptr<cc::FramesTimingFunction> frames_;
+};
+
 PLATFORM_EXPORT PassRefPtr<TimingFunction> CreateCompositorTimingFunctionFromCC(
     const cc::TimingFunction*);
 
@@ -231,6 +255,8 @@
                                 const TimingFunction&);
 PLATFORM_EXPORT bool operator==(const StepsTimingFunction&,
                                 const TimingFunction&);
+PLATFORM_EXPORT bool operator==(const FramesTimingFunction&,
+                                const TimingFunction&);
 
 PLATFORM_EXPORT bool operator==(const TimingFunction&, const TimingFunction&);
 PLATFORM_EXPORT bool operator!=(const TimingFunction&, const TimingFunction&);
@@ -243,6 +269,7 @@
 DEFINE_TIMING_FUNCTION_TYPE_CASTS(Linear, LINEAR);
 DEFINE_TIMING_FUNCTION_TYPE_CASTS(CubicBezier, CUBIC_BEZIER);
 DEFINE_TIMING_FUNCTION_TYPE_CASTS(Steps, STEPS);
+DEFINE_TIMING_FUNCTION_TYPE_CASTS(Frames, FRAMES);
 
 }  // namespace blink
 
diff --git a/third_party/WebKit/Source/platform/animation/TimingFunctionTest.cpp b/third_party/WebKit/Source/platform/animation/TimingFunctionTest.cpp
index 7872782..9c2bc03 100644
--- a/third_party/WebKit/Source/platform/animation/TimingFunctionTest.cpp
+++ b/third_party/WebKit/Source/platform/animation/TimingFunctionTest.cpp
@@ -117,6 +117,11 @@
   EXPECT_EQ("steps(5)", step_timing_custom_end->ToString());
 }
 
+TEST_F(TimingFunctionTest, FrameToString) {
+  RefPtr<TimingFunction> frame_timing = FramesTimingFunction::Create(3);
+  EXPECT_EQ("frames(3)", frame_timing->ToString());
+}
+
 TEST_F(TimingFunctionTest, BaseOperatorEq) {
   RefPtr<TimingFunction> linear_timing = LinearTimingFunction::Shared();
   RefPtr<TimingFunction> cubic_timing1 = CubicBezierTimingFunction::Preset(
@@ -127,6 +132,7 @@
       StepsTimingFunction::Preset(StepsTimingFunction::StepPosition::END);
   RefPtr<TimingFunction> steps_timing2 =
       StepsTimingFunction::Create(5, StepsTimingFunction::StepPosition::START);
+  RefPtr<TimingFunction> frames_timing = FramesTimingFunction::Create(5);
 
   Vector<std::pair<std::string, RefPtr<TimingFunction>>> v;
   v.push_back(std::make_pair("linearTiming", linear_timing));
@@ -134,6 +140,7 @@
   v.push_back(std::make_pair("cubicTiming2", cubic_timing2));
   v.push_back(std::make_pair("stepsTiming1", steps_timing1));
   v.push_back(std::make_pair("stepsTiming2", steps_timing2));
+  v.push_back(std::make_pair("framesTiming", frames_timing));
   NotEqualHelperLoop(v);
 }
 
@@ -253,6 +260,19 @@
   EXPECT_EQ(*steps_b, *steps_a);
 }
 
+TEST_F(TimingFunctionTest, FramesOperatorEq) {
+  RefPtr<TimingFunction> frames_timing1 = FramesTimingFunction::Create(5);
+  RefPtr<TimingFunction> frames_timing2 = FramesTimingFunction::Create(7);
+
+  EXPECT_EQ(*FramesTimingFunction::Create(5), *frames_timing1);
+  EXPECT_EQ(*FramesTimingFunction::Create(7), *frames_timing2);
+
+  Vector<std::pair<std::string, RefPtr<TimingFunction>>> v;
+  v.push_back(std::make_pair("framesTiming1", frames_timing1));
+  v.push_back(std::make_pair("framesTiming2", frames_timing2));
+  NotEqualHelperLoop(v);
+}
+
 TEST_F(TimingFunctionTest, LinearEvaluate) {
   RefPtr<TimingFunction> linear_timing = LinearTimingFunction::Shared();
   EXPECT_EQ(0.2, linear_timing->Evaluate(0.2, 0));
@@ -291,6 +311,21 @@
   EXPECT_NEAR(1, end, 0.01);
 }
 
+TEST_F(TimingFunctionTest, FrameRange) {
+  double start = 0;
+  double end = 1;
+  RefPtr<TimingFunction> frames = FramesTimingFunction::Create(4);
+  frames->Range(&start, &end);
+  EXPECT_NEAR(0, start, 0.01);
+  EXPECT_NEAR(1, end, 0.01);
+
+  start = -1;
+  end = 10;
+  frames->Range(&start, &end);
+  EXPECT_NEAR(0, start, 0.01);
+  EXPECT_NEAR(1, end, 0.01);
+}
+
 TEST_F(TimingFunctionTest, CubicRange) {
   double start = 0;
   double end = 1;
@@ -484,6 +519,22 @@
   EXPECT_EQ(2.00, steps_timing_custom_end->Evaluate(2.00, 0));
 }
 
+TEST_F(TimingFunctionTest, FramesEvaluate) {
+  RefPtr<TimingFunction> frames_timing = FramesTimingFunction::Create(5);
+  EXPECT_EQ(-2.50, frames_timing->Evaluate(-2.00, 0));
+  EXPECT_EQ(0.00, frames_timing->Evaluate(0.00, 0));
+  EXPECT_EQ(0.00, frames_timing->Evaluate(0.19, 0));
+  EXPECT_EQ(0.25, frames_timing->Evaluate(0.20, 0));
+  EXPECT_EQ(0.25, frames_timing->Evaluate(0.39, 0));
+  EXPECT_EQ(0.50, frames_timing->Evaluate(0.40, 0));
+  EXPECT_EQ(0.50, frames_timing->Evaluate(0.59, 0));
+  EXPECT_EQ(0.75, frames_timing->Evaluate(0.60, 0));
+  EXPECT_EQ(0.75, frames_timing->Evaluate(0.79, 0));
+  EXPECT_EQ(1.00, frames_timing->Evaluate(0.80, 0));
+  EXPECT_EQ(1.00, frames_timing->Evaluate(1.00, 0));
+  EXPECT_EQ(3.75, frames_timing->Evaluate(3.00, 0));
+}
+
 }  // namespace
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/platform/exported/WebServiceWorkerResponse.cpp b/third_party/WebKit/Source/platform/exported/WebServiceWorkerResponse.cpp
index f0011dc..a322c00 100644
--- a/third_party/WebKit/Source/platform/exported/WebServiceWorkerResponse.cpp
+++ b/third_party/WebKit/Source/platform/exported/WebServiceWorkerResponse.cpp
@@ -26,7 +26,6 @@
   WebServiceWorkerResponseType response_type;
   HTTPHeaderMap headers;
   RefPtr<BlobDataHandle> blob_data_handle;
-  WebURL stream_url;
   WebServiceWorkerResponseError error;
   int64_t response_time;
   WebString cache_storage_cache_name;
@@ -128,14 +127,6 @@
   return private_->blob_data_handle->size();
 }
 
-void WebServiceWorkerResponse::SetStreamURL(const WebURL& url) {
-  private_->stream_url = url;
-}
-
-const WebURL& WebServiceWorkerResponse::StreamURL() const {
-  return private_->stream_url;
-}
-
 void WebServiceWorkerResponse::SetError(WebServiceWorkerResponseError error) {
   private_->error = error;
 }
diff --git a/third_party/WebKit/Source/platform/exported/WebServiceWorkerStreamHandle.cpp b/third_party/WebKit/Source/platform/exported/WebServiceWorkerStreamHandle.cpp
new file mode 100644
index 0000000..d08046c
--- /dev/null
+++ b/third_party/WebKit/Source/platform/exported/WebServiceWorkerStreamHandle.cpp
@@ -0,0 +1,30 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "public/platform/modules/serviceworker/WebServiceWorkerStreamHandle.h"
+
+namespace blink {
+
+void WebServiceWorkerStreamHandle::SetListener(
+    std::unique_ptr<Listener> listener) {
+  DCHECK(!listener_);
+  listener_ = std::move(listener);
+}
+
+void WebServiceWorkerStreamHandle::Aborted() {
+  DCHECK(listener_);
+  listener_->OnAborted();
+}
+
+void WebServiceWorkerStreamHandle::Completed() {
+  DCHECK(listener_);
+  listener_->OnCompleted();
+}
+
+mojo::ScopedDataPipeConsumerHandle
+WebServiceWorkerStreamHandle::DrainStreamDataPipe() {
+  return std::move(stream_);
+}
+
+}  // namespace blink
diff --git a/third_party/WebKit/Source/platform/graphics/DEPS b/third_party/WebKit/Source/platform/graphics/DEPS
index fc51aa12..7dcab9c 100644
--- a/third_party/WebKit/Source/platform/graphics/DEPS
+++ b/third_party/WebKit/Source/platform/graphics/DEPS
@@ -11,7 +11,6 @@
     "+gpu/command_buffer/client/gles2_interface.h",
     "+gpu/command_buffer/client/gpu_memory_buffer_manager.h",
     "+gpu/command_buffer/common/capabilities.h",
-    "+gpu/command_buffer/common/gpu_memory_buffer_support.h",
     "+gpu/command_buffer/common/mailbox.h",
     "+gpu/command_buffer/common/sync_token.h",
 ]
diff --git a/third_party/WebKit/Source/platform/graphics/gpu/DrawingBuffer.cpp b/third_party/WebKit/Source/platform/graphics/gpu/DrawingBuffer.cpp
index 97ce92b..15555873 100644
--- a/third_party/WebKit/Source/platform/graphics/gpu/DrawingBuffer.cpp
+++ b/third_party/WebKit/Source/platform/graphics/gpu/DrawingBuffer.cpp
@@ -37,7 +37,6 @@
 #include "gpu/command_buffer/client/gles2_interface.h"
 #include "gpu/command_buffer/client/gpu_memory_buffer_manager.h"
 #include "gpu/command_buffer/common/capabilities.h"
-#include "gpu/command_buffer/common/gpu_memory_buffer_support.h"
 #include "platform/RuntimeEnabledFeatures.h"
 #include "platform/graphics/AcceleratedStaticBitmapImage.h"
 #include "platform/graphics/GraphicsLayer.h"
@@ -532,22 +531,15 @@
   parameters.target = GC3D_TEXTURE_RECTANGLE_ARB;
 
   if (want_alpha_channel_) {
-    parameters.creation_internal_color_format = GL_RGBA;
-    parameters.internal_color_format = GL_RGBA;
+    parameters.allocate_alpha_channel = true;
   } else if (ContextProvider()
                  ->GetCapabilities()
                  .chromium_image_rgb_emulation) {
-    parameters.creation_internal_color_format = GL_RGB;
-    parameters.internal_color_format = GL_RGBA;
+    parameters.allocate_alpha_channel = false;
   } else {
-    GLenum format =
-        DefaultBufferRequiresAlphaChannelToBePreserved() ? GL_RGBA : GL_RGB;
-    parameters.creation_internal_color_format = format;
-    parameters.internal_color_format = format;
+    parameters.allocate_alpha_channel =
+        DefaultBufferRequiresAlphaChannelToBePreserved();
   }
-
-  // Unused when CHROMIUM_image is being used.
-  parameters.color_format = 0;
   return parameters;
 #else
   return TextureColorBufferParameters();
@@ -559,21 +551,14 @@
   ColorBufferParameters parameters;
   parameters.target = GL_TEXTURE_2D;
   if (want_alpha_channel_) {
-    parameters.internal_color_format = GL_RGBA;
-    parameters.creation_internal_color_format = GL_RGBA;
-    parameters.color_format = GL_RGBA;
+    parameters.allocate_alpha_channel = true;
   } else if (ContextProvider()
                  ->GetCapabilities()
                  .emulate_rgb_buffer_with_rgba) {
-    parameters.internal_color_format = GL_RGBA;
-    parameters.creation_internal_color_format = GL_RGBA;
-    parameters.color_format = GL_RGBA;
+    parameters.allocate_alpha_channel = true;
   } else {
-    GLenum format =
-        DefaultBufferRequiresAlphaChannelToBePreserved() ? GL_RGBA : GL_RGB;
-    parameters.creation_internal_color_format = format;
-    parameters.internal_color_format = format;
-    parameters.color_format = format;
+    parameters.allocate_alpha_channel =
+        DefaultBufferRequiresAlphaChannelToBePreserved();
   }
   return parameters;
 }
@@ -1156,17 +1141,24 @@
       Platform::Current()->GetGpuMemoryBufferManager();
   if (ShouldUseChromiumImage() && gpu_memory_buffer_manager) {
     parameters = GpuMemoryBufferColorBufferParameters();
-    gfx::BufferFormat buffer_format = gpu::DefaultBufferFormatForImageFormat(
-        parameters.creation_internal_color_format);
+    gfx::BufferFormat buffer_format;
+    GLenum gl_format = GL_NONE;
+    if (parameters.allocate_alpha_channel) {
+      buffer_format = gfx::BufferFormat::RGBA_8888;
+      gl_format = GL_RGBA;
+    } else {
+      buffer_format = gfx::BufferFormat::BGRX_8888;
+      gl_format = GL_RGB;
+    }
     gpu_memory_buffer = gpu_memory_buffer_manager->CreateGpuMemoryBuffer(
         gfx::Size(size), buffer_format, gfx::BufferUsage::SCANOUT,
         gpu::kNullSurfaceHandle);
     if (gpu_memory_buffer) {
       if (RuntimeEnabledFeatures::colorCorrectRenderingEnabled())
         gpu_memory_buffer->SetColorSpaceForScanout(color_space_);
-      image_id = gl_->CreateImageCHROMIUM(
-          gpu_memory_buffer->AsClientBuffer(), size.Width(), size.Height(),
-          parameters.creation_internal_color_format);
+      image_id =
+          gl_->CreateImageCHROMIUM(gpu_memory_buffer->AsClientBuffer(),
+                                   size.Width(), size.Height(), gl_format);
       if (!image_id)
         gpu_memory_buffer.reset();
     }
@@ -1191,21 +1183,14 @@
     gl_->BindTexImage2DCHROMIUM(parameters.target, image_id);
   } else {
     if (storage_texture_supported_) {
-      GLenum internal_storage_format = GL_NONE;
-      if (parameters.creation_internal_color_format == GL_RGB) {
-        internal_storage_format = GL_RGB8;
-      } else if (parameters.creation_internal_color_format == GL_RGBA) {
-        internal_storage_format = GL_RGBA8;
-      } else {
-        NOTREACHED();
-      }
+      GLenum internal_storage_format =
+          parameters.allocate_alpha_channel ? GL_RGBA8 : GL_RGB8;
       gl_->TexStorage2DEXT(GL_TEXTURE_2D, 1, internal_storage_format,
                            size.Width(), size.Height());
     } else {
-      gl_->TexImage2D(parameters.target, 0,
-                      parameters.creation_internal_color_format, size.Width(),
-                      size.Height(), 0, parameters.color_format,
-                      GL_UNSIGNED_BYTE, 0);
+      GLenum gl_format = parameters.allocate_alpha_channel ? GL_RGBA : GL_RGB;
+      gl_->TexImage2D(parameters.target, 0, gl_format, size.Width(),
+                      size.Height(), 0, gl_format, GL_UNSIGNED_BYTE, 0);
     }
   }
 
diff --git a/third_party/WebKit/Source/platform/graphics/gpu/DrawingBuffer.h b/third_party/WebKit/Source/platform/graphics/gpu/DrawingBuffer.h
index 938c0c3..56f1eea 100644
--- a/third_party/WebKit/Source/platform/graphics/gpu/DrawingBuffer.h
+++ b/third_party/WebKit/Source/platform/graphics/gpu/DrawingBuffer.h
@@ -293,13 +293,7 @@
   struct ColorBufferParameters {
     DISALLOW_NEW();
     GLenum target = 0;
-    GLenum internal_color_format = 0;
-
-    // The internal color format used when allocating storage for the
-    // texture. This may be different from internalColorFormat if RGB
-    // emulation is required.
-    GLenum creation_internal_color_format = 0;
-    GLenum color_format = 0;
+    bool allocate_alpha_channel = false;
   };
 
   struct ColorBuffer : public RefCounted<ColorBuffer> {
diff --git a/third_party/WebKit/Source/platform/loader/fetch/ResourceLoader.cpp b/third_party/WebKit/Source/platform/loader/fetch/ResourceLoader.cpp
index 6a6cf8b..8d2c54e 100644
--- a/third_party/WebKit/Source/platform/loader/fetch/ResourceLoader.cpp
+++ b/third_party/WebKit/Source/platform/loader/fetch/ResourceLoader.cpp
@@ -83,7 +83,7 @@
     return;
   }
 
-  loader_ = WTF::WrapUnique(Platform::Current()->CreateURLLoader());
+  loader_ = Platform::Current()->CreateURLLoader();
   DCHECK(loader_);
   loader_->SetDefersLoading(Context().DefersLoading());
   loader_->SetLoadingTaskRunner(Context().LoadingTaskRunner().Get());
diff --git a/third_party/WebKit/Source/platform/loader/testing/FetchTestingPlatformSupport.cpp b/third_party/WebKit/Source/platform/loader/testing/FetchTestingPlatformSupport.cpp
index db47027e..f3013640 100644
--- a/third_party/WebKit/Source/platform/loader/testing/FetchTestingPlatformSupport.cpp
+++ b/third_party/WebKit/Source/platform/loader/testing/FetchTestingPlatformSupport.cpp
@@ -45,7 +45,7 @@
   return url_loader_mock_factory_.get();
 }
 
-WebURLLoader* FetchTestingPlatformSupport::CreateURLLoader() {
+std::unique_ptr<WebURLLoader> FetchTestingPlatformSupport::CreateURLLoader() {
   return url_loader_mock_factory_->CreateURLLoader(nullptr);
 }
 
diff --git a/third_party/WebKit/Source/platform/loader/testing/FetchTestingPlatformSupport.h b/third_party/WebKit/Source/platform/loader/testing/FetchTestingPlatformSupport.h
index e346232..16e6e5b 100644
--- a/third_party/WebKit/Source/platform/loader/testing/FetchTestingPlatformSupport.h
+++ b/third_party/WebKit/Source/platform/loader/testing/FetchTestingPlatformSupport.h
@@ -24,7 +24,7 @@
   // Platform:
   WebURLError CancelledError(const WebURL&) const override;
   WebURLLoaderMockFactory* GetURLLoaderMockFactory() override;
-  WebURLLoader* CreateURLLoader() override;
+  std::unique_ptr<WebURLLoader> CreateURLLoader() override;
 
  private:
   class FetchTestingWebURLLoaderMockFactory;
diff --git a/third_party/WebKit/Source/platform/testing/TestingPlatformSupport.cpp b/third_party/WebKit/Source/platform/testing/TestingPlatformSupport.cpp
index 98e489f2..076244cc 100644
--- a/third_party/WebKit/Source/platform/testing/TestingPlatformSupport.cpp
+++ b/third_party/WebKit/Source/platform/testing/TestingPlatformSupport.cpp
@@ -190,7 +190,7 @@
   return old_platform_ ? old_platform_->GetURLLoaderMockFactory() : nullptr;
 }
 
-WebURLLoader* TestingPlatformSupport::CreateURLLoader() {
+std::unique_ptr<WebURLLoader> TestingPlatformSupport::CreateURLLoader() {
   return old_platform_ ? old_platform_->CreateURLLoader() : nullptr;
 }
 
diff --git a/third_party/WebKit/Source/platform/testing/TestingPlatformSupport.h b/third_party/WebKit/Source/platform/testing/TestingPlatformSupport.h
index 5b5d1edb..8e057f8 100644
--- a/third_party/WebKit/Source/platform/testing/TestingPlatformSupport.h
+++ b/third_party/WebKit/Source/platform/testing/TestingPlatformSupport.h
@@ -113,7 +113,7 @@
   WebFileUtilities* GetFileUtilities() override;
   WebIDBFactory* IdbFactory() override;
   WebURLLoaderMockFactory* GetURLLoaderMockFactory() override;
-  blink::WebURLLoader* CreateURLLoader() override;
+  std::unique_ptr<blink::WebURLLoader> CreateURLLoader() override;
   WebData LoadResource(const char* name) override;
   WebURLError CancelledError(const WebURL&) const override;
   InterfaceProvider* GetInterfaceProvider() override;
diff --git a/third_party/WebKit/Source/platform/testing/weburl_loader_mock.cc b/third_party/WebKit/Source/platform/testing/weburl_loader_mock.cc
index d60e983..02a1d7c 100644
--- a/third_party/WebKit/Source/platform/testing/weburl_loader_mock.cc
+++ b/third_party/WebKit/Source/platform/testing/weburl_loader_mock.cc
@@ -25,9 +25,9 @@
 }  // namespace
 
 WebURLLoaderMock::WebURLLoaderMock(WebURLLoaderMockFactoryImpl* factory,
-                                   WebURLLoader* default_loader)
+                                   std::unique_ptr<WebURLLoader> default_loader)
     : factory_(factory),
-      default_loader_(WTF::WrapUnique(default_loader)),
+      default_loader_(std::move(default_loader)),
       weak_factory_(this) {}
 
 WebURLLoaderMock::~WebURLLoaderMock() {
diff --git a/third_party/WebKit/Source/platform/testing/weburl_loader_mock.h b/third_party/WebKit/Source/platform/testing/weburl_loader_mock.h
index b531b2a..58d64b06 100644
--- a/third_party/WebKit/Source/platform/testing/weburl_loader_mock.h
+++ b/third_party/WebKit/Source/platform/testing/weburl_loader_mock.h
@@ -30,7 +30,7 @@
  public:
   // This object becomes the owner of |default_loader|.
   WebURLLoaderMock(WebURLLoaderMockFactoryImpl* factory,
-                   WebURLLoader* default_loader);
+                   std::unique_ptr<WebURLLoader> default_loader);
   ~WebURLLoaderMock() override;
 
   // Simulates the asynchronous request being served.
diff --git a/third_party/WebKit/Source/platform/testing/weburl_loader_mock_factory_impl.cc b/third_party/WebKit/Source/platform/testing/weburl_loader_mock_factory_impl.cc
index c933a00e..b441fd5 100644
--- a/third_party/WebKit/Source/platform/testing/weburl_loader_mock_factory_impl.cc
+++ b/third_party/WebKit/Source/platform/testing/weburl_loader_mock_factory_impl.cc
@@ -7,6 +7,7 @@
 #include <stdint.h>
 
 #include "base/files/file_util.h"
+#include "base/memory/ptr_util.h"
 #include "base/run_loop.h"
 #include "platform/loader/fetch/MemoryCache.h"
 #include "platform/testing/TestingPlatformSupport.h"
@@ -32,9 +33,9 @@
 
 WebURLLoaderMockFactoryImpl::~WebURLLoaderMockFactoryImpl() {}
 
-WebURLLoader* WebURLLoaderMockFactoryImpl::CreateURLLoader(
-    WebURLLoader* default_loader) {
-  return new WebURLLoaderMock(this, default_loader);
+std::unique_ptr<WebURLLoader> WebURLLoaderMockFactoryImpl::CreateURLLoader(
+    std::unique_ptr<WebURLLoader> default_loader) {
+  return base::MakeUnique<WebURLLoaderMock>(this, std::move(default_loader));
 }
 
 void WebURLLoaderMockFactoryImpl::RegisterURL(const WebURL& url,
diff --git a/third_party/WebKit/Source/platform/testing/weburl_loader_mock_factory_impl.h b/third_party/WebKit/Source/platform/testing/weburl_loader_mock_factory_impl.h
index 8b054c8..8fe9d11 100644
--- a/third_party/WebKit/Source/platform/testing/weburl_loader_mock_factory_impl.h
+++ b/third_party/WebKit/Source/platform/testing/weburl_loader_mock_factory_impl.h
@@ -36,7 +36,8 @@
   ~WebURLLoaderMockFactoryImpl() override;
 
   // WebURLLoaderMockFactory:
-  WebURLLoader* CreateURLLoader(WebURLLoader* default_loader) override;
+  std::unique_ptr<WebURLLoader> CreateURLLoader(
+      std::unique_ptr<WebURLLoader> default_loader) override;
   void RegisterURL(const WebURL& url,
                    const WebURLResponse& response,
                    const WebString& file_path = WebString()) override;
diff --git a/third_party/WebKit/Source/web/LocalFrameClientImpl.cpp b/third_party/WebKit/Source/web/LocalFrameClientImpl.cpp
index 5a0e67e..cce0081 100644
--- a/third_party/WebKit/Source/web/LocalFrameClientImpl.cpp
+++ b/third_party/WebKit/Source/web/LocalFrameClientImpl.cpp
@@ -654,7 +654,7 @@
     const Vector<String>& added_selectors,
     const Vector<String>& removed_selectors) {
   if (WebFrameClient* client = web_frame_->Client()) {
-    client->DidMatchCSS(web_frame_, WebVector<WebString>(added_selectors),
+    client->DidMatchCSS(WebVector<WebString>(added_selectors),
                         WebVector<WebString>(removed_selectors));
   }
 }
diff --git a/third_party/WebKit/Source/web/ServiceWorkerGlobalScopeClientImpl.cpp b/third_party/WebKit/Source/web/ServiceWorkerGlobalScopeClientImpl.cpp
index e45583cb..36f288c 100644
--- a/third_party/WebKit/Source/web/ServiceWorkerGlobalScopeClientImpl.cpp
+++ b/third_party/WebKit/Source/web/ServiceWorkerGlobalScopeClientImpl.cpp
@@ -127,17 +127,11 @@
                                           event_dispatch_time);
 }
 
-void ServiceWorkerGlobalScopeClientImpl::RespondToFetchEvent(
+void ServiceWorkerGlobalScopeClientImpl::RespondToFetchEventWithNoResponse(
     int fetch_event_id,
     double event_dispatch_time) {
-  client_.RespondToFetchEvent(fetch_event_id, event_dispatch_time);
-}
-
-void ServiceWorkerGlobalScopeClientImpl::RespondToPaymentRequestEvent(
-    int event_id,
-    const WebPaymentAppResponse& response,
-    double event_dispatch_time) {
-  client_.RespondToPaymentRequestEvent(event_id, response, event_dispatch_time);
+  client_.RespondToFetchEventWithNoResponse(fetch_event_id,
+                                            event_dispatch_time);
 }
 
 void ServiceWorkerGlobalScopeClientImpl::RespondToFetchEvent(
@@ -147,6 +141,22 @@
   client_.RespondToFetchEvent(fetch_event_id, response, event_dispatch_time);
 }
 
+void ServiceWorkerGlobalScopeClientImpl::RespondToFetchEventWithResponseStream(
+    int fetch_event_id,
+    const WebServiceWorkerResponse& response,
+    WebServiceWorkerStreamHandle* stream_handle,
+    double event_dispatch_time) {
+  client_.RespondToFetchEventWithResponseStream(
+      fetch_event_id, response, stream_handle, event_dispatch_time);
+}
+
+void ServiceWorkerGlobalScopeClientImpl::RespondToPaymentRequestEvent(
+    int event_id,
+    const WebPaymentAppResponse& response,
+    double event_dispatch_time) {
+  client_.RespondToPaymentRequestEvent(event_id, response, event_dispatch_time);
+}
+
 void ServiceWorkerGlobalScopeClientImpl::DidHandleFetchEvent(
     int fetch_event_id,
     WebServiceWorkerEventResult result,
diff --git a/third_party/WebKit/Source/web/ServiceWorkerGlobalScopeClientImpl.h b/third_party/WebKit/Source/web/ServiceWorkerGlobalScopeClientImpl.h
index 9928ce7..ddf2ea7 100644
--- a/third_party/WebKit/Source/web/ServiceWorkerGlobalScopeClientImpl.h
+++ b/third_party/WebKit/Source/web/ServiceWorkerGlobalScopeClientImpl.h
@@ -81,12 +81,17 @@
   void DidHandleExtendableMessageEvent(int event_id,
                                        WebServiceWorkerEventResult,
                                        double event_dispatch_time) override;
-  void RespondToFetchEvent(int response_id,
-                           double event_dispatch_time) override;
-  void RespondToFetchEvent(int response_id,
+  void RespondToFetchEventWithNoResponse(int fetch_event_id,
+                                         double event_dispatch_time) override;
+  void RespondToFetchEvent(int fetch_event_id,
                            const WebServiceWorkerResponse&,
                            double event_dispatch_time) override;
-  void RespondToPaymentRequestEvent(int response_id,
+  void RespondToFetchEventWithResponseStream(
+      int fetch_event_id,
+      const WebServiceWorkerResponse&,
+      WebServiceWorkerStreamHandle*,
+      double event_dispatch_time) override;
+  void RespondToPaymentRequestEvent(int event_id,
                                     const WebPaymentAppResponse&,
                                     double event_dispatch_time) override;
   void DidHandleFetchEvent(int fetch_event_id,
diff --git a/third_party/WebKit/Source/web/ServiceWorkerGlobalScopeProxy.cpp b/third_party/WebKit/Source/web/ServiceWorkerGlobalScopeProxy.cpp
index c7e8b5b3..02ba59e 100644
--- a/third_party/WebKit/Source/web/ServiceWorkerGlobalScopeProxy.cpp
+++ b/third_party/WebKit/Source/web/ServiceWorkerGlobalScopeProxy.cpp
@@ -331,7 +331,7 @@
     // TODO(mek): Ideally the browser wouldn't even start the service worker
     // if its tokens have expired.
     ServiceWorkerGlobalScopeClient::From(WorkerGlobalScope())
-        ->RespondToFetchEvent(fetch_event_id, WTF::CurrentTime());
+        ->RespondToFetchEventWithNoResponse(fetch_event_id, WTF::CurrentTime());
     ServiceWorkerGlobalScopeClient::From(WorkerGlobalScope())
         ->DidHandleFetchEvent(fetch_event_id,
                               kWebServiceWorkerEventResultCompleted,
diff --git a/third_party/WebKit/Source/web/WebLocalFrameImpl.cpp b/third_party/WebKit/Source/web/WebLocalFrameImpl.cpp
index a2d5180..cd9cd7b 100644
--- a/third_party/WebKit/Source/web/WebLocalFrameImpl.cpp
+++ b/third_party/WebKit/Source/web/WebLocalFrameImpl.cpp
@@ -1896,7 +1896,7 @@
     plugin->DidFailLoading(error);
 
   if (was_provisional)
-    Client()->DidFailProvisionalLoad(this, web_error, web_commit_type);
+    Client()->DidFailProvisionalLoad(web_error, web_commit_type);
   else
     Client()->DidFailLoad(web_error, web_commit_type);
 }
diff --git a/third_party/WebKit/Source/web/tests/FrameTestHelpers.cpp b/third_party/WebKit/Source/web/tests/FrameTestHelpers.cpp
index a3e3c78..b7503c62 100644
--- a/third_party/WebKit/Source/web/tests/FrameTestHelpers.cpp
+++ b/third_party/WebKit/Source/web/tests/FrameTestHelpers.cpp
@@ -226,6 +226,7 @@
       WebTreeScopeType::kDocument, web_frame_client,
       web_frame_client->GetInterfaceProvider(), nullptr, opener);
   web_view_->SetMainFrame(frame);
+  web_frame_client->SetFrame(frame);
   // TODO(dcheng): The main frame widget currently has a special case.
   // Eliminate this once WebView is no longer a WebWidget.
   blink::WebFrameWidget::Create(web_widget_client, web_view_, frame);
diff --git a/third_party/WebKit/Source/web/tests/FrameTestHelpers.h b/third_party/WebKit/Source/web/tests/FrameTestHelpers.h
index a3e35fc2..9837e2ca 100644
--- a/third_party/WebKit/Source/web/tests/FrameTestHelpers.h
+++ b/third_party/WebKit/Source/web/tests/FrameTestHelpers.h
@@ -252,6 +252,9 @@
  public:
   TestWebFrameClient();
 
+  WebLocalFrame* Frame() const { return frame_; }
+  void SetFrame(WebLocalFrame* frame) { frame_ = frame; }
+
   void FrameDetached(WebLocalFrame*, DetachType) override;
   WebLocalFrame* CreateChildFrame(WebLocalFrame* parent,
                                   WebTreeScopeType,
@@ -270,6 +273,9 @@
 
  private:
   int loads_in_progress_ = 0;
+
+  // This is null from when the client is created until it is initialized.
+  WebLocalFrame* frame_;
 };
 
 // Minimal implementation of WebRemoteFrameClient needed for unit tests that
diff --git a/third_party/WebKit/Source/web/tests/WebFrameTest.cpp b/third_party/WebKit/Source/web/tests/WebFrameTest.cpp
index cb87e9a3..cc1f59d2 100644
--- a/third_party/WebKit/Source/web/tests/WebFrameTest.cpp
+++ b/third_party/WebKit/Source/web/tests/WebFrameTest.cpp
@@ -715,7 +715,6 @@
  public:
   CSSCallbackWebFrameClient() : update_count_(0) {}
   void DidMatchCSS(
-      WebLocalFrame*,
       const WebVector<WebString>& newly_matching_selectors,
       const WebVector<WebString>& stopped_matching_selectors) override;
 
@@ -724,11 +723,10 @@
 };
 
 void CSSCallbackWebFrameClient::DidMatchCSS(
-    WebLocalFrame* frame,
     const WebVector<WebString>& newly_matching_selectors,
     const WebVector<WebString>& stopped_matching_selectors) {
   ++update_count_;
-  std::set<std::string>& frame_selectors = matched_selectors_[frame];
+  std::set<std::string>& frame_selectors = matched_selectors_[Frame()];
   for (size_t i = 0; i < newly_matching_selectors.size(); ++i) {
     std::string selector = newly_matching_selectors[i].Utf8();
     EXPECT_EQ(0U, frame_selectors.count(selector)) << selector;
@@ -747,6 +745,7 @@
     frame_ = helper_.InitializeAndLoad("about:blank", true, &client_)
                  ->MainFrame()
                  ->ToWebLocalFrame();
+    client_.SetFrame(frame_);
   }
 
   ~WebFrameCSSCallbackTest() {
@@ -6332,17 +6331,17 @@
  public:
   TestSubstituteDataWebFrameClient() : commit_called_(false) {}
 
-  virtual void DidFailProvisionalLoad(WebLocalFrame* frame,
-                                      const WebURLError& error,
+  virtual void DidFailProvisionalLoad(const WebURLError& error,
                                       WebHistoryCommitType) {
-    frame->LoadHTMLString("This should appear",
-                          ToKURL("data:text/html,chromewebdata"),
-                          error.unreachable_url, true);
+    Frame()->LoadHTMLString("This should appear",
+                            ToKURL("data:text/html,chromewebdata"),
+                            error.unreachable_url, true);
   }
 
   virtual void DidCommitProvisionalLoad(WebLocalFrame* frame,
                                         const WebHistoryItem&,
                                         WebHistoryCommitType) {
+    ASSERT_EQ(Frame(), frame);
     if (frame->DataSource()->GetResponse().Url() !=
         WebURL(URLTestHelpers::ToKURL("about:blank")))
       commit_called_ = true;
@@ -11711,8 +11710,7 @@
       did_call_did_stop_loading_ = true;
     }
 
-    void DidFailProvisionalLoad(WebLocalFrame*,
-                                const WebURLError&,
+    void DidFailProvisionalLoad(const WebURLError&,
                                 WebHistoryCommitType) override {
       EXPECT_TRUE(false) << "The load should not have failed.";
     }
diff --git a/third_party/WebKit/public/BUILD.gn b/third_party/WebKit/public/BUILD.gn
index f5900542..172531f6 100644
--- a/third_party/WebKit/public/BUILD.gn
+++ b/third_party/WebKit/public/BUILD.gn
@@ -707,6 +707,7 @@
     "platform/modules/presentation/presentation.mojom",
     "platform/modules/sensitive_input_visibility/sensitive_input_visibility_service.mojom",
     "platform/modules/serviceworker/service_worker_event_status.mojom",
+    "platform/modules/serviceworker/service_worker_stream_handle.mojom",
     "platform/modules/websockets/websocket.mojom",
     "platform/referrer.mojom",
     "platform/site_engagement.mojom",
diff --git a/third_party/WebKit/public/platform/Platform.h b/third_party/WebKit/public/platform/Platform.h
index 28b05e6..eee72390 100644
--- a/third_party/WebKit/public/platform/Platform.h
+++ b/third_party/WebKit/public/platform/Platform.h
@@ -53,6 +53,7 @@
 #include "WebStorageQuotaType.h"
 #include "WebString.h"
 #include "WebURLError.h"
+#include "WebURLLoader.h"
 #include "WebVector.h"
 #include "base/metrics/user_metrics_action.h"
 #include "cc/resources/shared_bitmap.h"
@@ -127,7 +128,6 @@
 class WebThemeEngine;
 class WebThread;
 class WebTrialTokenValidator;
-class WebURLLoader;
 class WebURLLoaderMockFactory;
 class WebURLResponse;
 class WebURLResponse;
@@ -334,7 +334,7 @@
   // Network -------------------------------------------------------------
 
   // Returns a new WebURLLoader instance.
-  virtual WebURLLoader* CreateURLLoader() { return nullptr; }
+  virtual std::unique_ptr<WebURLLoader> CreateURLLoader() { return nullptr; }
 
   // May return null.
   virtual WebPrescientNetworking* PrescientNetworking() { return nullptr; }
diff --git a/third_party/WebKit/public/platform/WebURLLoaderMockFactory.h b/third_party/WebKit/public/platform/WebURLLoaderMockFactory.h
index 4c071626..76321e62 100644
--- a/third_party/WebKit/public/platform/WebURLLoaderMockFactory.h
+++ b/third_party/WebKit/public/platform/WebURLLoaderMockFactory.h
@@ -28,7 +28,8 @@
   // Create a WebURLLoader that takes care of mocked requests.
   // Non-mocked request are forwarded to given loader which should not
   // be nullptr.
-  virtual WebURLLoader* CreateURLLoader(WebURLLoader*) = 0;
+  virtual std::unique_ptr<WebURLLoader> CreateURLLoader(
+      std::unique_ptr<WebURLLoader>) = 0;
 
   // Registers a response and the file to be served when the specified URL
   // is loaded. If no file is specified then the response content will be empty.
diff --git a/third_party/WebKit/public/platform/modules/serviceworker/WebServiceWorkerResponse.h b/third_party/WebKit/public/platform/modules/serviceworker/WebServiceWorkerResponse.h
index 69ac3fb..8241b96 100644
--- a/third_party/WebKit/public/platform/modules/serviceworker/WebServiceWorkerResponse.h
+++ b/third_party/WebKit/public/platform/modules/serviceworker/WebServiceWorkerResponse.h
@@ -70,9 +70,6 @@
   WebString BlobUUID() const;
   uint64_t BlobSize() const;
 
-  void SetStreamURL(const WebURL&);
-  const WebURL& StreamURL() const;
-
   // Provides a more detailed error when status() is zero.
   void SetError(WebServiceWorkerResponseError);
   WebServiceWorkerResponseError GetError() const;
diff --git a/third_party/WebKit/public/platform/modules/serviceworker/WebServiceWorkerStreamHandle.h b/third_party/WebKit/public/platform/modules/serviceworker/WebServiceWorkerStreamHandle.h
new file mode 100644
index 0000000..e079e44
--- /dev/null
+++ b/third_party/WebKit/public/platform/modules/serviceworker/WebServiceWorkerStreamHandle.h
@@ -0,0 +1,43 @@
+// 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 WebServiceWorkerStreamHandle_h
+#define WebServiceWorkerStreamHandle_h
+
+#include <memory>
+
+#include "mojo/public/cpp/system/data_pipe.h"
+#include "public/platform/WebCommon.h"
+
+namespace blink {
+
+// Contains the info to send back a body to the page over Mojo's data pipe.
+class BLINK_PLATFORM_EXPORT WebServiceWorkerStreamHandle {
+ public:
+  // Listener can observe whether the data pipe is successfully closed at the
+  // end of the body or it has accidentally finished.
+  class Listener {
+   public:
+    virtual ~Listener(){};
+    virtual void OnAborted() = 0;
+    virtual void OnCompleted() = 0;
+  };
+
+  void SetListener(std::unique_ptr<Listener>);
+  mojo::ScopedDataPipeConsumerHandle DrainStreamDataPipe();
+
+#if INSIDE_BLINK
+  WebServiceWorkerStreamHandle(mojo::ScopedDataPipeConsumerHandle stream)
+      : stream_(std::move(stream)) {}
+  void Aborted();
+  void Completed();
+#endif
+
+ private:
+  mojo::ScopedDataPipeConsumerHandle stream_;
+  std::unique_ptr<Listener> listener_;
+};
+}
+
+#endif  // WebServiceWorkerStreamHandle_h
diff --git a/third_party/WebKit/public/platform/modules/serviceworker/service_worker_stream_handle.mojom b/third_party/WebKit/public/platform/modules/serviceworker/service_worker_stream_handle.mojom
new file mode 100644
index 0000000..968a6e8a
--- /dev/null
+++ b/third_party/WebKit/public/platform/modules/serviceworker/service_worker_stream_handle.mojom
@@ -0,0 +1,17 @@
+// 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.
+
+module blink.mojom;
+
+// The renderer uses this interface to notify the browser that the stream
+// response passed to respondWith() ended.
+interface ServiceWorkerStreamCallback {
+  OnCompleted();
+  OnAborted();
+};
+
+struct ServiceWorkerStreamHandle {
+  handle<data_pipe_consumer> stream;
+  ServiceWorkerStreamCallback& callback_request;
+};
\ No newline at end of file
diff --git a/third_party/WebKit/public/web/WebFrameClient.h b/third_party/WebKit/public/web/WebFrameClient.h
index 30e98c0..75f6bbf 100644
--- a/third_party/WebKit/public/web/WebFrameClient.h
+++ b/third_party/WebKit/public/web/WebFrameClient.h
@@ -251,7 +251,6 @@
 
   // Called when a watched CSS selector matches or stops matching.
   virtual void DidMatchCSS(
-      WebLocalFrame*,
       const WebVector<WebString>& newly_matching_selectors,
       const WebVector<WebString>& stopped_matching_selectors) {}
 
@@ -362,8 +361,7 @@
 
   // The provisional load failed. The WebHistoryCommitType is the commit type
   // that would have been used had the load succeeded.
-  virtual void DidFailProvisionalLoad(WebLocalFrame*,
-                                      const WebURLError&,
+  virtual void DidFailProvisionalLoad(const WebURLError&,
                                       WebHistoryCommitType) {}
 
   // The provisional datasource is now committed.  The first part of the
diff --git a/third_party/WebKit/public/web/modules/serviceworker/WebServiceWorkerContextClient.h b/third_party/WebKit/public/web/modules/serviceworker/WebServiceWorkerContextClient.h
index 4b8c565ed..63062cf 100644
--- a/third_party/WebKit/public/web/modules/serviceworker/WebServiceWorkerContextClient.h
+++ b/third_party/WebKit/public/web/modules/serviceworker/WebServiceWorkerContextClient.h
@@ -39,6 +39,7 @@
 #include "public/platform/modules/serviceworker/WebServiceWorkerClientsInfo.h"
 #include "public/platform/modules/serviceworker/WebServiceWorkerEventResult.h"
 #include "public/platform/modules/serviceworker/WebServiceWorkerSkipWaitingCallbacks.h"
+#include "public/platform/modules/serviceworker/WebServiceWorkerStreamHandle.h"
 #include "public/web/WebDevToolsAgentClient.h"
 #include "v8/include/v8.h"
 
@@ -162,17 +163,28 @@
       WebServiceWorkerEventResult result,
       double event_dispatch_time) {}
 
-  // ServiceWorker specific methods. respondFetchEvent will be called after
+  // ServiceWorker specific methods. RespondToFetchEvent* will be called after
   // FetchEvent returns a response by the ServiceWorker's script context, and
-  // didHandleFetchEvent will be called after the end of FetchEvent's
+  // DidHandleFetchEvent will be called after the end of FetchEvent's
   // lifecycle. When no response is provided, the browser should fallback to
-  // native fetch. EventIDs are the same with the ids passed from
-  // dispatchFetchEvent respectively.
-  virtual void RespondToFetchEvent(int fetch_event_id,
-                                   double event_dispatch_time) {}
+  // native fetch. |fetch_event_id|s are the same with the ids passed from
+  // DispatchFetchEvent respectively.
+
+  // Used when respondWith() is not called. Tells the browser to fall back to
+  // native fetch.
+  virtual void RespondToFetchEventWithNoResponse(int fetch_event_id,
+                                                 double event_dispatch_time) {}
+  // Responds to the fetch event with |response|.
   virtual void RespondToFetchEvent(int fetch_event_id,
                                    const WebServiceWorkerResponse& response,
                                    double event_dispatch_time) {}
+  // Responds to the fetch event with |response|, where body is
+  // |body_as_stream|.
+  virtual void RespondToFetchEventWithResponseStream(
+      int fetch_event_id,
+      const WebServiceWorkerResponse& response,
+      WebServiceWorkerStreamHandle* body_as_stream,
+      double event_dispatch_time) {}
   virtual void RespondToPaymentRequestEvent(
       int event_id,
       const WebPaymentAppResponse& response,
diff --git a/tools/gn/value.h b/tools/gn/value.h
index 3ce0117f..3103509b 100644
--- a/tools/gn/value.h
+++ b/tools/gn/value.h
@@ -120,7 +120,7 @@
  private:
   // This are a lot of objects associated with every Value that need
   // initialization and tear down every time. It might be more efficient to
-  // create a union of ManualConstructor objects (see SmallMap) and only
+  // create a union of ManualConstructor objects (see small_map) and only
   // use the one we care about.
   Type type_;
   std::string string_value_;
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 2322d5c..456522f 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -103743,7 +103743,6 @@
   <int value="1694854500" label="disable-save-password-bubble"/>
   <int value="1696139514" label="enable-ble-advertising-in-apps"/>
   <int value="1697189972" label="WebPaymentsSingleAppUiSkip:disabled"/>
-  <int value="1700394127" label="OverlayScrollbar:disabled"/>
   <int value="1701972870" label="NTPSnippetsIncreasedVisibility:enabled"/>
   <int value="1702821235" label="WebAssembly:enabled"/>
   <int value="1705724232" label="use-android-midi-api"/>
@@ -103804,7 +103803,6 @@
   <int value="1900529524" label="disable-touch-drag-drop"/>
   <int value="1905465678" label="ContextualSearchSingleActions:enabled"/>
   <int value="1906942630" label="enable-easy-unlock"/>
-  <int value="1913298816" label="OverlayScrollbar:enabled"/>
   <int value="1915178511" label="disable-blink-features"/>
   <int value="1927259098" label="TranslateLanguageByULP:enabled"/>
   <int value="1928407249" label="NewPhotoPicker:enabled"/>
diff --git a/tools/perf/benchmarks/media.py b/tools/perf/benchmarks/media.py
index f37ce44..5c0c2b5 100644
--- a/tools/perf/benchmarks/media.py
+++ b/tools/perf/benchmarks/media.py
@@ -114,7 +114,7 @@
   test = media.Media
   tag = 'android'
   page_set = page_sets.ToughVideoCasesPageSet
-  options = {'story_tag_filter_exclude': 'is_4k,is_50fps'}
+  options = {'story_tag_filter_exclude': 'is_4k,is_50fps,theora'}
 
   @classmethod
   def ShouldDisable(cls, possible_browser):
@@ -136,7 +136,7 @@
   Will eventually replace MediaAndroidToughVideoCases class."""
 
   tag = 'android'
-  options = {'story_tag_filter_exclude': 'is_4k,is_50fps'}
+  options = {'story_tag_filter_exclude': 'is_4k,is_50fps,theora'}
 
   @classmethod
   def ShouldDisable(cls, possible_browser):
diff --git a/ui/base/webui/OWNERS b/ui/base/webui/OWNERS
index 8624e36..89558c26 100644
--- a/ui/base/webui/OWNERS
+++ b/ui/base/webui/OWNERS
@@ -1,4 +1,5 @@
-dbeam@chromium.org
-estade@chromium.org
+file://ui/webui/PLATFORM_OWNERS
+
+dschuyler@chromium.org
 
 # COMPONENT: UI>Browser>WebUI
diff --git a/ui/latency/latency_info.h b/ui/latency/latency_info.h
index 600f874..11cb6c71 100644
--- a/ui/latency/latency_info.h
+++ b/ui/latency/latency_info.h
@@ -12,7 +12,7 @@
 #include <utility>
 #include <vector>
 
-#include "base/containers/small_map.h"
+#include "base/containers/flat_map.h"
 #include "base/time/time.h"
 #include "ui/gfx/geometry/point_f.h"
 
@@ -130,16 +130,12 @@
     base::TimeTicks last_event_time;
   };
 
-  // Empirically determined constant based on a typical scroll sequence.
-  enum { kTypicalMaxComponentsPerLatencyInfo = 10 };
-
   enum : size_t { kMaxInputCoordinates = 2 };
 
   // Map a Latency Component (with a component-specific int64_t id) to a
   // component info.
-  typedef base::SmallMap<
-      std::map<std::pair<LatencyComponentType, int64_t>, LatencyComponent>,
-      kTypicalMaxComponentsPerLatencyInfo> LatencyMap;
+  using LatencyMap = base::flat_map<std::pair<LatencyComponentType, int64_t>,
+                                    LatencyComponent>;
 
   LatencyInfo();
   LatencyInfo(const LatencyInfo& other);
diff --git a/ui/native_theme/BUILD.gn b/ui/native_theme/BUILD.gn
index 5f436ac4..174e477 100644
--- a/ui/native_theme/BUILD.gn
+++ b/ui/native_theme/BUILD.gn
@@ -15,12 +15,12 @@
     "native_theme_android.h",
     "native_theme_base.cc",
     "native_theme_base.h",
-    "native_theme_features.cc",
-    "native_theme_features.h",
     "native_theme_mac.h",
     "native_theme_mac.mm",
     "native_theme_observer.cc",
     "native_theme_observer.h",
+    "native_theme_switches.cc",
+    "native_theme_switches.h",
   ]
 
   if (use_aura) {
diff --git a/ui/native_theme/native_theme_aura.cc b/ui/native_theme/native_theme_aura.cc
index 31d5186f..d2cfc1fb 100644
--- a/ui/native_theme/native_theme_aura.cc
+++ b/ui/native_theme/native_theme_aura.cc
@@ -24,7 +24,7 @@
 #include "ui/gfx/path.h"
 #include "ui/gfx/skia_util.h"
 #include "ui/native_theme/common_theme.h"
-#include "ui/native_theme/native_theme_features.h"
+#include "ui/native_theme/native_theme_switches.h"
 #include "ui/native_theme/overlay_scrollbar_constants_aura.h"
 
 namespace ui {
diff --git a/ui/native_theme/native_theme_features.cc b/ui/native_theme/native_theme_features.cc
deleted file mode 100644
index 25cf50a4..0000000
--- a/ui/native_theme/native_theme_features.cc
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ui/native_theme/native_theme_features.h"
-
-namespace features {
-
-#if defined(OS_ANDROID) || defined(OS_CHROMEOS)
-constexpr base::FeatureState kOverlayScrollbarFeatureState =
-    base::FEATURE_ENABLED_BY_DEFAULT;
-#else
-constexpr base::FeatureState kOverlayScrollbarFeatureState =
-    base::FEATURE_DISABLED_BY_DEFAULT;
-#endif
-
-// Enables or disables overlay scrollbars in Blink (i.e. web content) on Aura
-// or Linux.  The status of native UI overlay scrollbars is determined in
-// PlatformStyle::CreateScrollBar. Does nothing on Mac.
-const base::Feature kOverlayScrollbar{"OverlayScrollbar",
-                                      kOverlayScrollbarFeatureState};
-
-}  // namespace features
-
-namespace ui {
-
-bool IsOverlayScrollbarEnabled() {
-  return base::FeatureList::IsEnabled(features::kOverlayScrollbar);
-}
-
-}  // namespace ui
diff --git a/ui/native_theme/native_theme_features.h b/ui/native_theme/native_theme_features.h
deleted file mode 100644
index 85e8985..0000000
--- a/ui/native_theme/native_theme_features.h
+++ /dev/null
@@ -1,25 +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.
-
-// Defines all the command-line switches used by native theme
-
-#ifndef UI_NATIVE_THEME_NATIVE_THEME_FEATURES_H_
-#define UI_NATIVE_THEME_NATIVE_THEME_FEATURES_H_
-
-#include "base/feature_list.h"
-#include "ui/native_theme/native_theme_export.h"
-
-namespace features {
-
-NATIVE_THEME_EXPORT extern const base::Feature kOverlayScrollbar;
-
-}  // namespace features
-
-namespace ui {
-
-NATIVE_THEME_EXPORT bool IsOverlayScrollbarEnabled();
-
-}  // namespace ui
-
-#endif  // UI_NATIVE_THEME_NATIVE_THEME_FEATURES_H_
diff --git a/ui/native_theme/native_theme_switches.cc b/ui/native_theme/native_theme_switches.cc
new file mode 100644
index 0000000..1d8f8b3
--- /dev/null
+++ b/ui/native_theme/native_theme_switches.cc
@@ -0,0 +1,34 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/command_line.h"
+#include "ui/native_theme/native_theme_switches.h"
+
+namespace switches {
+
+// Enables or disables overlay scrollbars in Blink (i.e. web content) on Aura
+// or Linux.  The status of native UI overlay scrollbars are determined in
+// PlatformStyle::CreateScrollBar. Does nothing on Mac.
+const char kEnableOverlayScrollbar[] = "enable-overlay-scrollbar";
+const char kDisableOverlayScrollbar[] = "disable-overlay-scrollbar";
+
+}  // namespace switches
+
+namespace ui {
+
+bool IsOverlayScrollbarEnabled() {
+  const base::CommandLine& command_line =
+      *base::CommandLine::ForCurrentProcess();
+
+  if (command_line.HasSwitch(switches::kDisableOverlayScrollbar))
+    return false;
+
+#if defined(OS_CHROMEOS)
+  return true;
+#else
+  return command_line.HasSwitch(switches::kEnableOverlayScrollbar);
+#endif
+}
+
+}  // namespace ui
diff --git a/ui/native_theme/native_theme_switches.h b/ui/native_theme/native_theme_switches.h
new file mode 100644
index 0000000..026ee70
--- /dev/null
+++ b/ui/native_theme/native_theme_switches.h
@@ -0,0 +1,25 @@
+// 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.
+
+// Defines all the command-line switches used by native theme
+
+#ifndef UI_NATIVE_THEME_NATIVE_THEME_SWITCHES_H_
+#define UI_NATIVE_THEME_NATIVE_THEME_SWITCHES_H_
+
+#include "ui/native_theme/native_theme_export.h"
+
+namespace switches {
+
+NATIVE_THEME_EXPORT extern const char kDisableOverlayScrollbar[];
+NATIVE_THEME_EXPORT extern const char kEnableOverlayScrollbar[];
+
+}  // namespace switches
+
+namespace ui {
+
+NATIVE_THEME_EXPORT bool IsOverlayScrollbarEnabled();
+
+}  // namespace ui
+
+#endif  // UI_NATIVE_THEME_NATIVE_THEME_SWITCHES_H_
diff --git a/ui/ozone/platform/drm/common/drm_util.cc b/ui/ozone/platform/drm/common/drm_util.cc
index 59944118..7d35a2a6 100644
--- a/ui/ozone/platform/drm/common/drm_util.cc
+++ b/ui/ozone/platform/drm/common/drm_util.cc
@@ -13,7 +13,7 @@
 #include <algorithm>
 #include <utility>
 
-#include "base/containers/small_map.h"
+#include "base/containers/flat_map.h"
 #include "base/memory/ptr_util.h"
 #include "ui/display/util/edid_parser.h"
 
@@ -236,8 +236,7 @@
       available_connectors.push_back(std::move(connector));
   }
 
-  base::SmallMap<std::map<ScopedDrmConnectorPtr::element_type*, int>>
-      connector_crtcs;
+  base::flat_map<ScopedDrmConnectorPtr::element_type*, int> connector_crtcs;
   for (auto& c : available_connectors) {
     uint32_t possible_crtcs = 0;
     for (int i = 0; i < c->count_encoders; ++i) {
diff --git a/ui/webui/OWNERS b/ui/webui/OWNERS
index 9fcad090..8c48ccd 100644
--- a/ui/webui/OWNERS
+++ b/ui/webui/OWNERS
@@ -1,13 +1,5 @@
-# Please use more specific OWNERS when possible.
-bauerb@chromium.org
-dbeam@chromium.org
-dpapad@chromium.org
-michaelpg@chromium.org
-pam@chromium.org
-tommycli@chromium.org
-xiyuan@chromium.org
+file://ui/webui/PLATFORM_OWNERS
 
-# Emeritus
-estade@chromium.org
+fukino@chromium.org
 
 # COMPONENT: UI>Browser>WebUI
diff --git a/ui/webui/PLATFORM_OWNERS b/ui/webui/PLATFORM_OWNERS
new file mode 100644
index 0000000..249ebb89
--- /dev/null
+++ b/ui/webui/PLATFORM_OWNERS
@@ -0,0 +1,9 @@
+# Please use more specific OWNERS when possible.
+bauerb@chromium.org
+calamity@chromium.org
+dbeam@chromium.org
+dpapad@chromium.org
+michaelpg@chromium.org
+pam@chromium.org
+tommycli@chromium.org
+xiyuan@chromium.org