diff --git a/DEPS b/DEPS
index ad94b24..ad26068 100644
--- a/DEPS
+++ b/DEPS
@@ -40,7 +40,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': '580ffa0fb17bc4e924776eafd941bf1fab397cde',
+  'skia_revision': '9c10df3b60f4a7d50c1070a5d8c4aaadb79ba9b7',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
@@ -96,7 +96,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling catapult
   # and whatever else without interference from each other.
-  'catapult_revision': 'b06826430a44dbf653ce3c0e6116f6dc8469a6f0',
+  'catapult_revision': '7ef761733d291aabe77a456fe91083a966aa3caa',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
diff --git a/ash/BUILD.gn b/ash/BUILD.gn
index 67f0b83b..1e85a0ce 100644
--- a/ash/BUILD.gn
+++ b/ash/BUILD.gn
@@ -1029,7 +1029,6 @@
 
   deps = [
     ":ash",
-    "//ash/common/test:test_support",
     "//ash/public/cpp:ash_public_cpp",
     "//ash/test:test_support_without_content",
     "//base",
@@ -1110,7 +1109,6 @@
   deps = [
     ":ash",
     ":ash_with_content",
-    "//ash/common/test:test_support",
     "//ash/public/cpp:ash_public_cpp",
     "//ash/test:ash_with_aura_test_support",
     "//ash/test:test_support_with_content",
@@ -1237,7 +1235,6 @@
   ]
   deps = [
     "//ash",
-    "//ash/common/test:test_support",
     "//ash/public/cpp:ash_public_cpp",
     "//ash/resources/vector_icons",
     "//ash/test:test_support_without_content",
@@ -1390,7 +1387,6 @@
     ":common_unittests",
     "//ash/autoclick/common:autoclick",
     "//ash/common/strings",
-    "//ash/common/test:test_support",
     "//ash/public/cpp:ash_public_cpp",
     "//ash/resources",
     "//ash/resources/vector_icons",
diff --git a/ash/common/shelf/shelf_button.cc b/ash/common/shelf/shelf_button.cc
index ff2e786a..b39fa9c2 100644
--- a/ash/common/shelf/shelf_button.cc
+++ b/ash/common/shelf/shelf_button.cc
@@ -21,6 +21,7 @@
 #include "ui/gfx/canvas.h"
 #include "ui/gfx/geometry/vector2d.h"
 #include "ui/gfx/image/image_skia_operations.h"
+#include "ui/gfx/scoped_canvas.h"
 #include "ui/gfx/skbitmap_operations.h"
 #include "ui/views/animation/ink_drop_impl.h"
 #include "ui/views/animation/square_ink_drop_ripple.h"
@@ -28,18 +29,12 @@
 
 namespace {
 
-// Size of the bar. This is along the opposite axis of the shelf. For example,
-// if the shelf is aligned horizontally then this is the height of the bar.
-const int kBarSize = 3;
 const int kIconSize = 32;
 const int kAttentionThrobDurationMS = 800;
 const int kMaxAnimationSeconds = 10;
 const int kIndicatorOffsetFromBottom = 2;
-const int kIndicatorRadius = 2;
+const int kIndicatorRadiusDip = 2;
 const SkColor kIndicatorColor = SK_ColorWHITE;
-// Canvas scale to ensure that the activity indicator is not pixelated even at
-// the highest possible device scale factors.
-const int kIndicatorCanvasScale = 5;
 
 // Shelf item ripple constants.
 const int kInkDropSmallSize = 48;
@@ -50,17 +45,6 @@
 const int kIconPaddingHorizontal = 7;
 const int kIconPaddingVertical = 8;
 
-// Paints an activity indicator on |canvas| whose |size| is specified in DIP.
-void PaintIndicatorOnCanvas(gfx::Canvas* canvas, const gfx::Size& size) {
-  cc::PaintFlags flags;
-  flags.setAntiAlias(true);
-  flags.setColor(kIndicatorColor);
-  canvas->DrawCircle(
-      gfx::Point(size.width() / 2,
-                 size.height() - kIndicatorOffsetFromBottom - kIndicatorRadius),
-      kIndicatorRadius, flags);
-}
-
 // Simple AnimationDelegate that owns a single ThrobAnimation instance to
 // keep all Draw Attention animations in sync.
 class ShelfButtonAnimation : public gfx::AnimationDelegate {
@@ -86,7 +70,14 @@
       animation_.Stop();
   }
 
-  int GetAlpha() { return GetThrobAnimation().CurrentValueBetween(0, 255); }
+  bool HasObserver(Observer* observer) const {
+    return observers_.HasObserver(observer);
+  }
+
+  SkAlpha GetAlpha() {
+    return GetThrobAnimation().CurrentValueBetween(SK_AlphaTRANSPARENT,
+                                                   SK_AlphaOPAQUE);
+  }
 
   double GetAnimation() { return GetThrobAnimation().GetCurrentValue(); }
 
@@ -127,111 +118,71 @@
 namespace ash {
 
 ////////////////////////////////////////////////////////////////////////////////
-// ShelfButton::BarView
+// ShelfButton::AppStatusIndicatorView
 
-class ShelfButton::BarView : public views::ImageView,
-                             public ShelfButtonAnimation::Observer {
+class ShelfButton::AppStatusIndicatorView
+    : public views::View,
+      public ShelfButtonAnimation::Observer {
  public:
-  BarView(WmShelf* wm_shelf)
-      : wm_shelf_(wm_shelf),
-        show_attention_(false),
-        animation_end_time_(base::TimeTicks()),
-        animating_(false) {
+  AppStatusIndicatorView()
+      : show_attention_(false), animation_end_time_(base::TimeTicks()) {
     // Make sure the events reach the parent view for handling.
     set_can_process_events_within_subtree(false);
   }
 
-  ~BarView() override {
-    if (show_attention_)
-      ShelfButtonAnimation::GetInstance()->RemoveObserver(this);
+  ~AppStatusIndicatorView() override {
+    ShelfButtonAnimation::GetInstance()->RemoveObserver(this);
   }
 
   // views::View:
   void OnPaint(gfx::Canvas* canvas) override {
+    gfx::ScopedCanvas scoped(canvas);
     if (show_attention_) {
-      int alpha =
-          animating_ ? ShelfButtonAnimation::GetInstance()->GetAlpha() : 255;
+      SkAlpha alpha = ShelfButtonAnimation::GetInstance()->HasObserver(this)
+                          ? ShelfButtonAnimation::GetInstance()->GetAlpha()
+                          : SK_AlphaOPAQUE;
       canvas->SaveLayerAlpha(alpha);
-      views::ImageView::OnPaint(canvas);
-      canvas->Restore();
-    } else {
-      views::ImageView::OnPaint(canvas);
     }
+
+    DCHECK_EQ(width(), height());
+    DCHECK_EQ(kIndicatorRadiusDip, width() / 2);
+    cc::PaintFlags flags;
+    flags.setColor(kIndicatorColor);
+    flags.setFlags(cc::PaintFlags::kAntiAlias_Flag);
+    canvas->DrawCircle(gfx::Point(width() / 2, height() / 2),
+                       kIndicatorRadiusDip, flags);
   }
 
   // ShelfButtonAnimation::Observer
   void AnimationProgressed() override {
-    UpdateBounds();
+    UpdateAnimating();
     SchedulePaint();
   }
 
-  void SetBarBoundsRect(const gfx::Rect& bounds) {
-    base_bounds_ = bounds;
-    UpdateBounds();
-  }
-
   void ShowAttention(bool show) {
-    if (show_attention_ != show) {
-      show_attention_ = show;
-      if (show_attention_) {
-        animating_ = true;
-        animation_end_time_ =
-            base::TimeTicks::Now() +
-            base::TimeDelta::FromSeconds(kMaxAnimationSeconds);
-        ShelfButtonAnimation::GetInstance()->AddObserver(this);
-      } else {
-        animating_ = false;
-        ShelfButtonAnimation::GetInstance()->RemoveObserver(this);
-      }
-    }
-    UpdateBounds();
-  }
-
- private:
-  void UpdateBounds() {
-    gfx::Rect bounds = base_bounds_;
-    if (show_attention_) {
-      // Scale from .35 to 1.0 of the total width (which is wider than the
-      // visible width of the image), so the animation "rests" briefly at full
-      // visible width.  Cap bounds length at kIconSize to prevent visual
-      // flutter while centering bar within further expanding bounds.
-      double animation =
-          animating_ ? ShelfButtonAnimation::GetInstance()->GetAnimation()
-                     : 1.0;
-      double scale = .35 + .65 * animation;
-      if (wm_shelf_->IsHorizontalAlignment()) {
-        int width = base_bounds_.width() * scale;
-        bounds.set_width(std::min(width, kIconSize));
-        int x_offset = (base_bounds_.width() - bounds.width()) / 2;
-        bounds.set_x(base_bounds_.x() + x_offset);
-        UpdateAnimating(bounds.width() == kIconSize);
-      } else {
-        int height = base_bounds_.height() * scale;
-        bounds.set_height(std::min(height, kIconSize));
-        int y_offset = (base_bounds_.height() - bounds.height()) / 2;
-        bounds.set_y(base_bounds_.y() + y_offset);
-        UpdateAnimating(bounds.height() == kIconSize);
-      }
-    }
-    SetBoundsRect(bounds);
-  }
-
-  void UpdateAnimating(bool max_length) {
-    if (!max_length)
+    if (show_attention_ == show)
       return;
-    if (base::TimeTicks::Now() > animation_end_time_) {
-      animating_ = false;
+
+    show_attention_ = show;
+    if (show_attention_) {
+      animation_end_time_ = base::TimeTicks::Now() +
+                            base::TimeDelta::FromSeconds(kMaxAnimationSeconds);
+      ShelfButtonAnimation::GetInstance()->AddObserver(this);
+    } else {
       ShelfButtonAnimation::GetInstance()->RemoveObserver(this);
     }
   }
 
-  WmShelf* wm_shelf_;
+ private:
+  void UpdateAnimating() {
+    if (base::TimeTicks::Now() > animation_end_time_)
+      ShelfButtonAnimation::GetInstance()->RemoveObserver(this);
+  }
+
   bool show_attention_;
   base::TimeTicks animation_end_time_;  // For attention throbbing underline.
-  bool animating_;  // Is time-limited attention animation running?
-  gfx::Rect base_bounds_;
 
-  DISALLOW_COPY_AND_ASSIGN(BarView);
+  DISALLOW_COPY_AND_ASSIGN(AppStatusIndicatorView);
 };
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -245,7 +196,7 @@
       listener_(listener),
       shelf_view_(shelf_view),
       icon_view_(new views::ImageView()),
-      bar_(new BarView(shelf_view->wm_shelf())),
+      indicator_(new AppStatusIndicatorView()),
       state_(STATE_NORMAL),
       destroyed_flag_(nullptr) {
   SetFocusBehavior(FocusBehavior::ACCESSIBLE_ONLY);
@@ -268,7 +219,7 @@
   // Do not make this interactive, so that events are sent to ShelfView.
   icon_view_->set_can_process_events_within_subtree(false);
 
-  AddChildView(bar_);
+  AddChildView(indicator_);
   AddChildView(icon_view_);
 }
 
@@ -317,7 +268,7 @@
     state_ |= state;
     Layout();
     if (state & STATE_ATTENTION)
-      bar_->ShowAttention(true);
+      indicator_->ShowAttention(true);
   }
 }
 
@@ -326,7 +277,7 @@
     state_ &= ~state;
     Layout();
     if (state & STATE_ATTENTION)
-      bar_->ShowAttention(false);
+      indicator_->ShowAttention(false);
   }
 }
 
@@ -406,17 +357,11 @@
   if (SHELF_ALIGNMENT_LEFT == wm_shelf->GetAlignment())
     x_offset = button_bounds.width() - (kIconSize + icon_pad);
 
-  // Center icon with respect to the secondary axis, and ensure
-  // that the icon doesn't occlude the bar highlight.
-  if (is_horizontal_shelf) {
+  // Center icon with respect to the secondary axis.
+  if (is_horizontal_shelf)
     x_offset = std::max(0, button_bounds.width() - icon_width) / 2;
-    if (y_offset + icon_height + kBarSize > button_bounds.height())
-      icon_height = button_bounds.height() - (y_offset + kBarSize);
-  } else {
+  else
     y_offset = std::max(0, button_bounds.height() - icon_height) / 2;
-    if (x_offset + icon_width + kBarSize > button_bounds.width())
-      icon_width = button_bounds.width() - (x_offset + kBarSize);
-  }
 
   // Expand bounds to include shadows.
   gfx::Insets insets_shadows = gfx::ShadowValue::GetMargin(icon_shadows_);
@@ -426,6 +371,8 @@
   gfx::Rect icon_view_bounds =
       gfx::Rect(button_bounds.x() + x_offset, button_bounds.y() + y_offset,
                 icon_width, icon_height);
+  // The indicator should be aligned with the icon, not the icon + shadow.
+  gfx::Point indicator_midpoint = icon_view_bounds.CenterPoint();
   icon_view_bounds.Inset(insets_shadows);
   icon_view_bounds.AdjustToFit(gfx::Rect(size()));
   icon_view_->SetBoundsRect(icon_view_bounds);
@@ -436,7 +383,25 @@
   DCHECK_LE(icon_width, kIconSize);
   DCHECK_LE(icon_height, kIconSize);
 
-  bar_->SetBarBoundsRect(button_bounds);
+  switch (wm_shelf->GetAlignment()) {
+    case SHELF_ALIGNMENT_BOTTOM:
+    case SHELF_ALIGNMENT_BOTTOM_LOCKED:
+      indicator_midpoint.set_y(button_bounds.bottom() - kIndicatorRadiusDip -
+                               kIndicatorOffsetFromBottom);
+      break;
+    case SHELF_ALIGNMENT_LEFT:
+      indicator_midpoint.set_x(button_bounds.x() + kIndicatorRadiusDip +
+                               kIndicatorOffsetFromBottom);
+      break;
+    case SHELF_ALIGNMENT_RIGHT:
+      indicator_midpoint.set_x(button_bounds.right() - kIndicatorRadiusDip -
+                               kIndicatorOffsetFromBottom);
+      break;
+  }
+
+  gfx::Rect indicator_bounds(indicator_midpoint, gfx::Size());
+  indicator_bounds.Inset(gfx::Insets(-kIndicatorRadiusDip));
+  indicator_->SetBoundsRect(indicator_bounds);
 
   UpdateState();
 }
@@ -519,7 +484,10 @@
 }
 
 void ShelfButton::UpdateState() {
-  UpdateBar();
+  indicator_->SetVisible(!(state_ & STATE_HIDDEN) &&
+                         (state_ & STATE_ACTIVE || state_ & STATE_ATTENTION ||
+                          state_ & STATE_RUNNING));
+
   const bool is_horizontal_shelf =
       shelf_view_->wm_shelf()->IsHorizontalAlignment();
   icon_view_->SetHorizontalAlignment(is_horizontal_shelf
@@ -531,47 +499,4 @@
   SchedulePaint();
 }
 
-void ShelfButton::UpdateBar() {
-  bool draw_bar = !(state_ & STATE_HIDDEN) &&
-                  (state_ & STATE_ACTIVE || state_ & STATE_ATTENTION ||
-                   state_ & STATE_RUNNING);
-
-  if (draw_bar) {
-    WmShelf* wm_shelf = shelf_view_->wm_shelf();
-    gfx::ImageSkia image;
-    if (wm_shelf->IsVisible()) {
-      gfx::Size size(kShelfButtonSize, GetShelfConstant(SHELF_SIZE));
-      gfx::Canvas canvas(size, kIndicatorCanvasScale, true /* is_opaque */);
-      PaintIndicatorOnCanvas(&canvas, size);
-      image = gfx::ImageSkia(canvas.ExtractImageRep());
-    }
-    ShelfAlignment shelf_alignment = wm_shelf->GetAlignment();
-    if (!wm_shelf->IsHorizontalAlignment()) {
-      image = gfx::ImageSkiaOperations::CreateRotatedImage(
-          image, shelf_alignment == SHELF_ALIGNMENT_LEFT
-                     ? SkBitmapOperations::ROTATION_90_CW
-                     : SkBitmapOperations::ROTATION_270_CW);
-    }
-    bar_->SetImage(image);
-    switch (shelf_alignment) {
-      case SHELF_ALIGNMENT_BOTTOM:
-      case SHELF_ALIGNMENT_BOTTOM_LOCKED:
-        bar_->SetHorizontalAlignment(views::ImageView::CENTER);
-        bar_->SetVerticalAlignment(views::ImageView::TRAILING);
-        break;
-      case SHELF_ALIGNMENT_LEFT:
-        bar_->SetHorizontalAlignment(views::ImageView::LEADING);
-        bar_->SetVerticalAlignment(views::ImageView::CENTER);
-        break;
-      case SHELF_ALIGNMENT_RIGHT:
-        bar_->SetHorizontalAlignment(views::ImageView::TRAILING);
-        bar_->SetVerticalAlignment(views::ImageView::CENTER);
-        break;
-    }
-    bar_->SchedulePaint();
-  }
-
-  bar_->SetVisible(draw_bar);
-}
-
 }  // namespace ash
diff --git a/ash/common/shelf/shelf_button.h b/ash/common/shelf/shelf_button.h
index 8e2bc3d..4f828ea18 100644
--- a/ash/common/shelf/shelf_button.h
+++ b/ash/common/shelf/shelf_button.h
@@ -94,15 +94,12 @@
   void SetShadowedImage(const gfx::ImageSkia& bitmap);
 
  private:
-  class BarView;
+  class AppStatusIndicatorView;
 
   // Updates the parts of the button to reflect the current |state_| and
   // alignment. This may add or remove views, layout and paint.
   void UpdateState();
 
-  // Updates the status bar (bitmap, orientation, visibility).
-  void UpdateBar();
-
   InkDropButtonListener* listener_;
 
   // The shelf view hosting this button.
@@ -111,8 +108,9 @@
   // The icon part of a button can be animated independently of the rest.
   views::ImageView* icon_view_;
 
-  // Draws a bar underneath the image to represent the state of the application.
-  BarView* bar_;
+  // Draws an indicator underneath the image to represent the state of the
+  // application.
+  AppStatusIndicatorView* indicator_;
 
   // The current application state, a bitfield of State enum values.
   int state_;
diff --git a/ash/common/test/BUILD.gn b/ash/common/test/BUILD.gn
deleted file mode 100644
index b4b4d37..0000000
--- a/ash/common/test/BUILD.gn
+++ /dev/null
@@ -1,46 +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.
-
-static_library("test_support") {
-  testonly = true
-  sources = [
-    "ash_test.cc",
-    "ash_test.h",
-    "ash_test_impl.h",
-    "test_palette_delegate.cc",
-    "test_palette_delegate.h",
-    "test_session_state_delegate.cc",
-    "test_session_state_delegate.h",
-    "test_shelf_delegate.cc",
-    "test_shelf_delegate.h",
-    "test_shelf_item_delegate.cc",
-    "test_shelf_item_delegate.h",
-    "test_system_tray_delegate.cc",
-    "test_system_tray_delegate.h",
-    "wm_shell_test_api.cc",
-    "wm_shell_test_api.h",
-    "workspace_event_handler_test_helper.cc",
-    "workspace_event_handler_test_helper.h",
-  ]
-  configs += [ "//build/config:precompiled_headers" ]
-
-  public_deps = [
-    "//testing/gtest",
-  ]
-  deps = [
-    "//ash",
-    "//ash/public/cpp:ash_public_cpp",
-    "//base",
-    "//components/signin/core/account_id",
-    "//components/user_manager",
-
-    # TODO: this is for ui/wm/public/window_types.h, which is in aura.
-    # http://crbug.com/654078.
-    "//ui/aura",
-    "//ui/display/manager",
-    "//ui/gfx/geometry",
-    "//ui/views",
-    "//ui/wm",
-  ]
-}
diff --git a/ash/mus/BUILD.gn b/ash/mus/BUILD.gn
index ae44e0d8..8130f9b4 100644
--- a/ash/mus/BUILD.gn
+++ b/ash/mus/BUILD.gn
@@ -207,7 +207,6 @@
     ":lib",
     ":resources",
     "//ash",
-    "//ash/common/test:test_support",
     "//ash/public/cpp:ash_public_cpp",
     "//ash/test:test_support_without_content",
     "//base",
diff --git a/ash/test/BUILD.gn b/ash/test/BUILD.gn
index cff635e8..96d7d5d 100644
--- a/ash/test/BUILD.gn
+++ b/ash/test/BUILD.gn
@@ -14,7 +14,6 @@
   deps = [
     ":test_support_common",
     "//ash",
-    "//ash/common/test:test_support",
     "//base",
     "//skia",
     "//ui/aura:test_support",
@@ -68,6 +67,24 @@
   testonly = true
   visibility = [ ":*" ]
   sources = [
+    "../common/test/ash_test.cc",
+    "../common/test/ash_test.h",
+    "../common/test/ash_test_impl.h",
+    "../common/test/test_palette_delegate.cc",
+    "../common/test/test_palette_delegate.h",
+    "../common/test/test_session_state_delegate.cc",
+    "../common/test/test_session_state_delegate.h",
+    "../common/test/test_shelf_delegate.cc",
+    "../common/test/test_shelf_delegate.h",
+    "../common/test/test_shelf_item_delegate.cc",
+    "../common/test/test_shelf_item_delegate.h",
+    "../common/test/test_system_tray_delegate.cc",
+    "../common/test/test_system_tray_delegate.h",
+    "../common/test/wm_shell_test_api.cc",
+    "../common/test/wm_shell_test_api.h",
+    "../common/test/workspace_event_handler_test_helper.cc",
+    "../common/test/workspace_event_handler_test_helper.h",
+
     # TODO(jamescook): Move these files into ash/test.
     "../laser/laser_pointer_controller_test_api.cc",
     "../laser/laser_pointer_controller_test_api.h",
@@ -135,10 +152,11 @@
 
   public_deps = [
     "//ash",
+    "//testing/gtest",
     "//ui/display:display_manager_test_api",
   ]
   deps = [
-    "//ash/common/test:test_support",
+    "//ash",
     "//ash/mus:lib",
     "//ash/public/cpp:ash_public_cpp",
     "//ash/resources",
diff --git a/base/strings/string_util.cc b/base/strings/string_util.cc
index 49ae943..71ae894 100644
--- a/base/strings/string_util.cc
+++ b/base/strings/string_util.cc
@@ -875,13 +875,20 @@
 template <typename list_type, typename string_type>
 static string_type JoinStringT(const list_type& parts,
                                BasicStringPiece<string_type> sep) {
-  auto iter = parts.begin();
-  // Early-exit to avoid bad access to the first element.
-  if (iter == parts.end())
+  if (parts.size() == 0)
     return string_type();
 
-  // Begin constructing the result from the first element.
-  string_type result(iter->data(), iter->size());
+  // Pre-allocate the eventual size of the string. Start with the size of all of
+  // the separators (note that this *assumes* parts.size() > 0).
+  size_t total_size = (parts.size() - 1) * sep.size();
+  for (const auto& part : parts)
+    total_size += part.size();
+  string_type result;
+  result.reserve(total_size);
+
+  auto iter = parts.begin();
+  DCHECK(iter != parts.end());
+  AppendToString(&result, *iter);
   ++iter;
 
   for (; iter != parts.end(); ++iter) {
@@ -892,6 +899,9 @@
     AppendToString(&result, *iter);
   }
 
+  // Sanity-check that we pre-allocated correctly.
+  DCHECK_EQ(total_size, result.size());
+
   return result;
 }
 
diff --git a/chrome/browser/chromeos/file_manager/zip_file_creator.cc b/chrome/browser/chromeos/file_manager/zip_file_creator.cc
index 19887d92..0cbfc599 100644
--- a/chrome/browser/chromeos/file_manager/zip_file_creator.cc
+++ b/chrome/browser/chromeos/file_manager/zip_file_creator.cc
@@ -75,12 +75,8 @@
 void ZipFileCreator::ReportDone(bool success) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
-  // The current user of this class holds no reference to |this| so resetting
-  // the |utility_process_mojo_client_| here could release the last reference
-  // and delete |this|. So save |callback_| before resetting the client.
-  auto callback = base::ResetAndReturn(&callback_);
   utility_process_mojo_client_.reset();
-  callback.Run(success);
+  base::ResetAndReturn(&callback_).Run(success);
 }
 
 }  // namespace file_manager
diff --git a/chrome/browser/devtools/devtools_window.cc b/chrome/browser/devtools/devtools_window.cc
index a2d355e..e32cc60 100644
--- a/chrome/browser/devtools/devtools_window.cc
+++ b/chrome/browser/devtools/devtools_window.cc
@@ -770,7 +770,8 @@
     WebContents* contents) {
   DevToolsWindow* window =
       DevToolsWindow::GetInstanceForInspectedWebContents(contents);
-  return window && !window->intercepted_page_beforeunload_;
+  return window && !window->intercepted_page_beforeunload_ &&
+         window->life_stage_ == kLoadCompleted;
 }
 
 // static
diff --git a/chrome/browser/extensions/extension_service_sync_unittest.cc b/chrome/browser/extensions/extension_service_sync_unittest.cc
index 5cfc387..b19b087 100644
--- a/chrome/browser/extensions/extension_service_sync_unittest.cc
+++ b/chrome/browser/extensions/extension_service_sync_unittest.cc
@@ -1401,13 +1401,21 @@
   PackCRXAndUpdateExtension(id, path, pem_path, ENABLED);
 }
 
-TEST_F(ExtensionServiceSyncTest, ProcessSyncDataPermissionApproval) {
-  // This is the update URL specified in the test extension. Setting it here is
-  // necessary to make it considered syncable.
-  base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
-      switches::kAppsGalleryUpdateURL,
-      "http://localhost/autoupdate/updates.xml");
+class ExtensionServiceSyncCustomGalleryTest : public ExtensionServiceSyncTest {
+ public:
+  void SetUp() override {
+    ExtensionServiceSyncTest::SetUp();
 
+    // This is the update URL specified in the permissions test extension.
+    // Setting it here is necessary to make the extension considered syncable.
+    base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
+        switches::kAppsGalleryUpdateURL,
+        "http://localhost/autoupdate/updates.xml");
+  }
+};
+
+TEST_F(ExtensionServiceSyncCustomGalleryTest,
+       ProcessSyncDataPermissionApproval) {
   InitializeEmptyExtensionService();
   extension_sync_service()->MergeDataAndStartSyncing(
       syncer::EXTENSIONS, syncer::SyncDataList(),
@@ -1559,26 +1567,17 @@
 
 #if BUILDFLAG(ENABLE_SUPERVISED_USERS)
 
-class ExtensionServiceTestSupervised : public ExtensionServiceSyncTest,
-                                       public SupervisedUserService::Delegate {
+class ExtensionServiceTestSupervised
+    : public ExtensionServiceSyncCustomGalleryTest,
+      public SupervisedUserService::Delegate {
  public:
   ExtensionServiceTestSupervised()
       : field_trial_list_(base::MakeUnique<base::MockEntropyProvider>()) {}
 
-  void SetUp() override {
-    ExtensionServiceSyncTest::SetUp();
-
-    // This is the update URL specified in the permissions test extension.
-    // Setting it here is necessary to make the extension considered syncable.
-    base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
-        switches::kAppsGalleryUpdateURL,
-        "http://localhost/autoupdate/updates.xml");
-  }
-
   void TearDown() override {
     supervised_user_service()->SetDelegate(nullptr);
 
-    ExtensionServiceSyncTest::TearDown();
+    ExtensionServiceSyncCustomGalleryTest::TearDown();
   }
 
  protected:
diff --git a/chrome/browser/lifetime/browser_close_manager_browsertest.cc b/chrome/browser/lifetime/browser_close_manager_browsertest.cc
index 5016975..2e18057c 100644
--- a/chrome/browser/lifetime/browser_close_manager_browsertest.cc
+++ b/chrome/browser/lifetime/browser_close_manager_browsertest.cc
@@ -27,6 +27,7 @@
 #include "chrome/browser/prefs/session_startup_pref.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/sessions/tab_restore_service_factory.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_commands.h"
 #include "chrome/browser/ui/browser_list.h"
@@ -38,6 +39,8 @@
 #include "chrome/test/base/ui_test_utils.h"
 #include "components/app_modal/javascript_app_modal_dialog.h"
 #include "components/app_modal/native_app_modal_dialog.h"
+#include "components/sessions/core/tab_restore_service.h"
+#include "components/sessions/core/tab_restore_service_observer.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/download_item.h"
 #include "content/public/browser/download_manager.h"
@@ -45,6 +48,7 @@
 #include "content/public/browser/render_view_host.h"
 #include "content/public/browser/render_widget_host.h"
 #include "content/public/browser/web_contents.h"
+#include "content/public/test/browser_test_utils.h"
 #include "content/public/test/download_test_observer.h"
 #include "content/public/test/test_navigation_observer.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
@@ -113,6 +117,39 @@
   DISALLOW_COPY_AND_ASSIGN(RepeatedNotificationObserver);
 };
 
+class TabRestoreServiceChangesObserver
+    : public sessions::TabRestoreServiceObserver {
+ public:
+  explicit TabRestoreServiceChangesObserver(Profile* profile)
+      : service_(TabRestoreServiceFactory::GetForProfile(profile)) {
+    if (service_)
+      service_->AddObserver(this);
+  }
+
+  ~TabRestoreServiceChangesObserver() override {
+    if (service_)
+      service_->RemoveObserver(this);
+  }
+
+  size_t changes_count() const { return changes_count_; }
+
+ private:
+  // sessions::TabRestoreServiceObserver:
+  void TabRestoreServiceChanged(sessions::TabRestoreService*) override {
+    changes_count_++;
+  }
+
+  // sessions::TabRestoreServiceObserver:
+  void TabRestoreServiceDestroyed(sessions::TabRestoreService*) override {
+    service_ = nullptr;
+  }
+
+  sessions::TabRestoreService* service_ = nullptr;
+  size_t changes_count_ = 0;
+
+  DISALLOW_COPY_AND_ASSIGN(TabRestoreServiceChangesObserver);
+};
+
 class TestBrowserCloseManager : public BrowserCloseManager {
  public:
   enum UserChoice {
@@ -708,6 +745,103 @@
 }
 
 IN_PROC_BROWSER_TEST_P(BrowserCloseManagerBrowserTest,
+                       AddBeforeUnloadDuringClosing) {
+  // TODO(crbug.com/250305): Currently FastUnloadController is broken.
+  // And it is difficult to fix this issue without fixing that one.
+  if (GetParam())
+    return;
+
+  ASSERT_TRUE(embedded_test_server()->Start());
+  ASSERT_NO_FATAL_FAILURE(ui_test_utils::NavigateToURL(
+      browser(), embedded_test_server()->GetURL("/title1.html")));
+
+  // Open second window.
+  ui_test_utils::NavigateToURLWithDisposition(
+      browser(), embedded_test_server()->GetURL("/beforeunload.html"),
+      WindowOpenDisposition::NEW_WINDOW,
+      ui_test_utils::BROWSER_TEST_WAIT_FOR_BROWSER);
+  EXPECT_EQ(2u, BrowserList::GetInstance()->size());
+  auto* browser2 = BrowserList::GetInstance()->get(0) != browser()
+                       ? BrowserList::GetInstance()->get(0)
+                       : BrowserList::GetInstance()->get(1);
+  content::WaitForLoadStop(browser2->tab_strip_model()->GetWebContentsAt(0));
+
+  // Let's work with second window only.
+  // This page has beforeunload handler already.
+  EXPECT_TRUE(browser2->tab_strip_model()
+                  ->GetWebContentsAt(0)
+                  ->NeedToFireBeforeUnload());
+  // This page doesn't have beforeunload handler. Yet.
+  ui_test_utils::NavigateToURLWithDisposition(
+      browser2, embedded_test_server()->GetURL("/title2.html"),
+      WindowOpenDisposition::NEW_FOREGROUND_TAB,
+      ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
+  content::WaitForLoadStop(browser2->tab_strip_model()->GetWebContentsAt(1));
+  EXPECT_FALSE(browser2->tab_strip_model()
+                   ->GetWebContentsAt(1)
+                   ->NeedToFireBeforeUnload());
+  EXPECT_EQ(2, browser2->tab_strip_model()->count());
+
+  DisableHangMonitor(browser2);
+
+  // The test.
+
+  TabRestoreServiceChangesObserver restore_observer(browser2->profile());
+  content::WindowedNotificationObserver observer(
+      chrome::NOTIFICATION_BROWSER_CLOSED,
+      content::NotificationService::AllSources());
+  chrome::CloseWindow(browser2);
+  // Just to be sure CloseWindow doesn't have asynchronous tasks
+  // that could have an impact.
+  content::RunAllPendingInMessageLoop();
+
+  // Closing browser shouldn't happen because of beforeunload handler.
+  EXPECT_EQ(2u, BrowserList::GetInstance()->size());
+  // Add beforeunload handler for the 2nd (title2.html) tab which haven't had it
+  // yet.
+  ASSERT_TRUE(content::ExecuteScript(
+      browser2->tab_strip_model()->GetWebContentsAt(1)->GetRenderViewHost(),
+      "window.addEventListener('beforeunload', "
+      "function(event) { event.returnValue = 'Foo'; });"));
+  EXPECT_TRUE(browser2->tab_strip_model()
+                  ->GetWebContentsAt(1)
+                  ->NeedToFireBeforeUnload());
+  // Accept closing the first tab.
+  ASSERT_NO_FATAL_FAILURE(AcceptClose());
+  // Just to be sure accepting a dialog doesn't have asynchronous tasks
+  // that could have an impact.
+  content::RunAllPendingInMessageLoop();
+  // It shouldn't close the whole window/browser.
+  EXPECT_EQ(2u, BrowserList::GetInstance()->size());
+  EXPECT_EQ(2, browser2->tab_strip_model()->count());
+  // Accept closing the second tab.
+  ASSERT_NO_FATAL_FAILURE(AcceptClose());
+  observer.Wait();
+  // Now the second window/browser should be closed.
+  EXPECT_EQ(1u, BrowserList::GetInstance()->size());
+  EXPECT_EQ(browser(), BrowserList::GetInstance()->get(0));
+  EXPECT_EQ(1u, restore_observer.changes_count());
+
+  // Restore the closed browser.
+  content::WindowedNotificationObserver open_window_observer(
+      chrome::NOTIFICATION_BROWSER_OPENED,
+      content::NotificationService::AllSources());
+  chrome::OpenWindowWithRestoredTabs(browser()->profile());
+  open_window_observer.Wait();
+  EXPECT_EQ(2u, BrowserList::GetInstance()->size());
+  browser2 = BrowserList::GetInstance()->get(0) != browser()
+                 ? BrowserList::GetInstance()->get(0)
+                 : BrowserList::GetInstance()->get(1);
+
+  // Check the restored browser contents.
+  EXPECT_EQ(2, browser2->tab_strip_model()->count());
+  EXPECT_EQ(embedded_test_server()->GetURL("/beforeunload.html"),
+            browser2->tab_strip_model()->GetWebContentsAt(0)->GetURL());
+  EXPECT_EQ(embedded_test_server()->GetURL("/title2.html"),
+            browser2->tab_strip_model()->GetWebContentsAt(1)->GetURL());
+}
+
+IN_PROC_BROWSER_TEST_P(BrowserCloseManagerBrowserTest,
                        TestCloseTabDuringShutdown) {
   ASSERT_TRUE(embedded_test_server()->Start());
   ASSERT_NO_FATAL_FAILURE(ui_test_utils::NavigateToURL(
diff --git a/chrome/browser/resources/OWNERS b/chrome/browser/resources/OWNERS
index b2fdca59..407277e 100644
--- a/chrome/browser/resources/OWNERS
+++ b/chrome/browser/resources/OWNERS
@@ -1,13 +1,4 @@
-bauerb@chromium.org
-dbeam@chromium.org
-dpapad@chromium.org
-michaelpg@chromium.org
-pam@chromium.org
-tommycli@chromium.org
-xiyuan@chromium.org
-
-# Emeritus
-estade@chromium.org
+file://ui/webui/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/ssl/ssl_browser_tests.cc b/chrome/browser/ssl/ssl_browser_tests.cc
index 67ff717..821b5ae 100644
--- a/chrome/browser/ssl/ssl_browser_tests.cc
+++ b/chrome/browser/ssl/ssl_browser_tests.cc
@@ -4104,13 +4104,8 @@
  public:
   SSLUICaptivePortalListResourceBundleTest()
       : CertVerifierBrowserTest(),
-        https_server_(net::EmbeddedTestServer::TYPE_HTTPS),
-        https_server_mismatched_(net::EmbeddedTestServer::TYPE_HTTPS) {
+        https_server_(net::EmbeddedTestServer::TYPE_HTTPS) {
     https_server_.ServeFilesFromSourceDirectory(base::FilePath(kDocRoot));
-
-    https_server_mismatched_.SetSSLConfig(
-        net::EmbeddedTestServer::CERT_MISMATCHED_NAME);
-    https_server_mismatched_.AddDefaultHandlers(base::FilePath(kDocRoot));
   }
 
   void SetUp() override {
@@ -4125,6 +4120,39 @@
   }
 
  protected:
+  // Checks that a captive portal interstitial isn't displayed, even though the
+  // server's certificate is marked as a captive portal certificate.
+  void TestNoCaptivePortalInterstitial(net::CertStatus cert_status,
+                                       int net_error) {
+    ASSERT_TRUE(https_server()->Start());
+    base::HistogramTester histograms;
+
+    // Mark the server's cert as a captive portal cert.
+    SetUpCertVerifier(cert_status, net_error, kCaptivePortalSPKI);
+
+    // Navigate to an unsafe page on the server. CaptivePortalCertificateList
+    // feature is enabled but either the error is not name-mismatch, or it's not
+    // the only error, so a generic SSL interstitial should be displayed.
+    WebContents* tab = browser()->tab_strip_model()->GetActiveWebContents();
+    SSLInterstitialTimerObserver interstitial_timer_observer(tab);
+    ui_test_utils::NavigateToURL(browser(), https_server()->GetURL("/"));
+    content::WaitForInterstitialAttach(tab);
+
+    InterstitialPage* interstitial_page = tab->GetInterstitialPage();
+    ASSERT_EQ(SSLBlockingPage::kTypeForTesting,
+              interstitial_page->GetDelegateForTesting()->GetTypeForTesting());
+    EXPECT_TRUE(interstitial_timer_observer.timer_started());
+
+    // Check that the histogram for the captive portal cert was recorded.
+    histograms.ExpectTotalCount(SSLErrorHandler::GetHistogramNameForTesting(),
+                                2);
+    histograms.ExpectBucketCount(SSLErrorHandler::GetHistogramNameForTesting(),
+                                 SSLErrorHandler::HANDLE_ALL, 1);
+    histograms.ExpectBucketCount(
+        SSLErrorHandler::GetHistogramNameForTesting(),
+        SSLErrorHandler::SHOW_SSL_INTERSTITIAL_OVERRIDABLE, 1);
+  }
+
   void SetUpCertVerifier(net::CertStatus cert_status,
                          int net_result,
                          const std::string& spki_hash) {
@@ -4145,13 +4173,9 @@
   }
 
   net::EmbeddedTestServer* https_server() { return &https_server_; }
-  net::EmbeddedTestServer* https_server_mismatched() {
-    return &https_server_mismatched_;
-  }
 
  private:
   net::EmbeddedTestServer https_server_;
-  net::EmbeddedTestServer https_server_mismatched_;
 };
 
 }  // namespace
@@ -4322,34 +4346,30 @@
   scoped_feature_list.InitFromCommandLine(
       "CaptivePortalCertificateList" /* enabled */,
       std::string() /* disabled */);
-  ASSERT_TRUE(https_server()->Start());
-  base::HistogramTester histograms;
 
-  // Mark the server's cert as a captive portal cert, but with an
-  // authority-invalid error.
-  SetUpCertVerifier(net::CERT_STATUS_AUTHORITY_INVALID,
-                    net::ERR_CERT_AUTHORITY_INVALID, kCaptivePortalSPKI);
+  TestNoCaptivePortalInterstitial(net::CERT_STATUS_AUTHORITY_INVALID,
+                                  net::ERR_CERT_AUTHORITY_INVALID);
+}
 
-  // Navigate to an unsafe page on the server. CaptivePortalCertificateList
-  // feature is enabled but the error is not a name mismatch, so a generic SSL
-  // interstitial should be displayed.
-  WebContents* tab = browser()->tab_strip_model()->GetActiveWebContents();
-  SSLInterstitialTimerObserver interstitial_timer_observer(tab);
-  ui_test_utils::NavigateToURL(browser(), https_server()->GetURL("/"));
-  content::WaitForInterstitialAttach(tab);
+// Same as SSLUICaptivePortalListResourceBundleTest.Enabled_AuthorityInvalid,
+// but this time there are two errors (name mismatch + weak key). Captive portal
+// interstitial should not be shown when name mismatch isn't the only error.
+IN_PROC_BROWSER_TEST_F(SSLUICaptivePortalListResourceBundleTest,
+                       Enabled_NameMismatchAndWeakKey) {
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitFromCommandLine(
+      "CaptivePortalCertificateList" /* enabled */,
+      std::string() /* disabled */);
 
-  InterstitialPage* interstitial_page = tab->GetInterstitialPage();
-  ASSERT_EQ(SSLBlockingPage::kTypeForTesting,
-            interstitial_page->GetDelegateForTesting()->GetTypeForTesting());
-  EXPECT_TRUE(interstitial_timer_observer.timer_started());
-
-  // Check that the histogram for the captive portal cert was recorded.
-  histograms.ExpectTotalCount(SSLErrorHandler::GetHistogramNameForTesting(), 2);
-  histograms.ExpectBucketCount(SSLErrorHandler::GetHistogramNameForTesting(),
-                               SSLErrorHandler::HANDLE_ALL, 1);
-  histograms.ExpectBucketCount(
-      SSLErrorHandler::GetHistogramNameForTesting(),
-      SSLErrorHandler::SHOW_SSL_INTERSTITIAL_OVERRIDABLE, 1);
+  const net::CertStatus cert_status =
+      net::CERT_STATUS_COMMON_NAME_INVALID | net::CERT_STATUS_WEAK_KEY;
+  // Sanity check that COMMON_NAME_INVALID is seen as the net error, since the
+  // test is designed to verify that SSLErrorHandler notices other errors in the
+  // CertStatus even when COMMON_NAME_INVALID is the net error.
+  ASSERT_EQ(net::ERR_CERT_COMMON_NAME_INVALID,
+            net::MapCertStatusToNetError(cert_status));
+  TestNoCaptivePortalInterstitial(cert_status,
+                                  net::ERR_CERT_COMMON_NAME_INVALID);
 }
 
 #else
diff --git a/chrome/browser/ssl/ssl_error_handler.cc b/chrome/browser/ssl/ssl_error_handler.cc
index 0b852f40..22c1ba6 100644
--- a/chrome/browser/ssl/ssl_error_handler.cc
+++ b/chrome/browser/ssl/ssl_error_handler.cc
@@ -547,9 +547,20 @@
     return;
   }
 
-#if BUILDFLAG(ENABLE_CAPTIVE_PORTAL_DETECTION)
-  if (base::FeatureList::IsEnabled(kCaptivePortalCertificateList) &&
+  const net::CertStatus non_name_mismatch_errors =
+      ssl_info_.cert_status ^ net::CERT_STATUS_COMMON_NAME_INVALID;
+  const bool only_error_is_name_mismatch =
       cert_error_ == net::ERR_CERT_COMMON_NAME_INVALID &&
+      (!net::IsCertStatusError(non_name_mismatch_errors) ||
+       net::IsCertStatusMinorError(ssl_info_.cert_status));
+
+#if BUILDFLAG(ENABLE_CAPTIVE_PORTAL_DETECTION)
+  // Check known captive portal certificate list if the only error is
+  // name-mismatch. If there are multiple errors, it indicates that the captive
+  // portal landing page itself will have SSL errors, and so it's not a very
+  // helpful place to direct the user to go.
+  if (base::FeatureList::IsEnabled(kCaptivePortalCertificateList) &&
+      only_error_is_name_mismatch &&
       g_config.Pointer()->IsKnownCaptivePortalCert(ssl_info_)) {
     RecordUMA(CAPTIVE_PORTAL_CERT_FOUND);
     ShowCaptivePortalInterstitial(
@@ -567,14 +578,11 @@
       delegate_->IsErrorOverridable() &&
       delegate_->GetSuggestedUrl(dns_names, &suggested_url)) {
     RecordUMA(WWW_MISMATCH_FOUND);
-    net::CertStatus extra_cert_errors =
-        ssl_info_.cert_status ^ net::CERT_STATUS_COMMON_NAME_INVALID;
 
-    // Show the SSL intersitial if |CERT_STATUS_COMMON_NAME_INVALID| is not
+    // Show the SSL interstitial if |CERT_STATUS_COMMON_NAME_INVALID| is not
     // the only error. Need not check for captive portal in this case.
     // (See the comment below).
-    if (net::IsCertStatusError(extra_cert_errors) &&
-        !net::IsCertStatusMinorError(ssl_info_.cert_status)) {
+    if (!only_error_is_name_mismatch) {
       ShowSSLInterstitial();
       return;
     }
diff --git a/chrome/browser/ssl/ssl_error_handler_unittest.cc b/chrome/browser/ssl/ssl_error_handler_unittest.cc
index 0beff0a..19471ae 100644
--- a/chrome/browser/ssl/ssl_error_handler_unittest.cc
+++ b/chrome/browser/ssl/ssl_error_handler_unittest.cc
@@ -191,11 +191,11 @@
 
 }  // namespace
 
-template <net::CertStatus cert_status>
-class SSLErrorHandlerCertStatusTestBase
-    : public ChromeRenderViewHostTestHarness {
+// A class to test name mismatch errors. Creates an error handler with a name
+// mismatch error.
+class SSLErrorHandlerNameMismatchTest : public ChromeRenderViewHostTestHarness {
  public:
-  SSLErrorHandlerCertStatusTestBase() : field_trial_list_(nullptr) {}
+  SSLErrorHandlerNameMismatchTest() : field_trial_list_(nullptr) {}
 
   void SetUp() override {
     ChromeRenderViewHostTestHarness::SetUp();
@@ -203,7 +203,7 @@
     SSLErrorHandler::SetInterstitialDelayForTesting(base::TimeDelta());
     ssl_info_.cert =
         net::ImportCertFromFile(net::GetTestCertsDirectory(), "ok_cert.pem");
-    ssl_info_.cert_status = cert_status;
+    ssl_info_.cert_status = net::CERT_STATUS_COMMON_NAME_INVALID;
     ssl_info_.public_key_hashes.push_back(
         net::HashValue(kCertPublicKeyHashValue));
 
@@ -215,13 +215,6 @@
         ssl_info_,
         GURL(),  // request_url
         base::Callback<void(content::CertificateRequestResultType)>()));
-
-    // Enable finch experiment for captive portal interstitials.
-    ASSERT_TRUE(base::FieldTrialList::CreateFieldTrial(
-                    "CaptivePortalInterstitial", "Enabled"));
-    // Enable finch experiment for SSL common name mismatch handling.
-    ASSERT_TRUE(base::FieldTrialList::CreateFieldTrial(
-                    "SSLCommonNameMismatchHandling", "Enabled"));
   }
 
   void TearDown() override {
@@ -242,13 +235,128 @@
   TestSSLErrorHandlerDelegate* delegate_;
   base::FieldTrialList field_trial_list_;
 
-  DISALLOW_COPY_AND_ASSIGN(SSLErrorHandlerCertStatusTestBase);
+  DISALLOW_COPY_AND_ASSIGN(SSLErrorHandlerNameMismatchTest);
 };
 
-using SSLErrorHandlerNameMismatchTest =
-    SSLErrorHandlerCertStatusTestBase<net::CERT_STATUS_COMMON_NAME_INVALID>;
-using SSLErrorHandlerAuthorityInvalidTest =
-    SSLErrorHandlerCertStatusTestBase<net::CERT_STATUS_AUTHORITY_INVALID>;
+// A class to test the captive portal certificate list feature. Creates an error
+// handler with a name mismatch error by default. The error handler can be
+// recreated by calling ResetErrorHandler() with an appropriate cert status.
+class SSLErrorHandlerCaptivePortalCertListTest
+    : public ChromeRenderViewHostTestHarness {
+ public:
+  SSLErrorHandlerCaptivePortalCertListTest() : field_trial_list_(nullptr) {}
+
+  void SetUp() override {
+    ChromeRenderViewHostTestHarness::SetUp();
+    SSLErrorHandler::ResetConfigForTesting();
+    SSLErrorHandler::SetInterstitialDelayForTesting(base::TimeDelta());
+    ResetErrorHandler(net::CERT_STATUS_COMMON_NAME_INVALID);
+  }
+
+  void TearDown() override {
+    EXPECT_FALSE(error_handler()->IsTimerRunningForTesting());
+    error_handler_.reset(nullptr);
+    SSLErrorHandler::ResetConfigForTesting();
+    ChromeRenderViewHostTestHarness::TearDown();
+  }
+
+  TestSSLErrorHandler* error_handler() { return error_handler_.get(); }
+  TestSSLErrorHandlerDelegate* delegate() { return delegate_; }
+
+  const net::SSLInfo& ssl_info() { return ssl_info_; }
+
+ protected:
+  void SetFeatureEnabled(bool enabled) {
+    if (enabled) {
+      scoped_feature_list_.InitFromCommandLine(
+          "CaptivePortalCertificateList" /* enabled */,
+          std::string() /* disabled */);
+    } else {
+      scoped_feature_list_.InitFromCommandLine(
+          std::string(), "CaptivePortalCertificateList" /* disabled */);
+    }
+  }
+
+  // Deletes the current error handler and creates a new one with the given
+  // |cert_status|.
+  void ResetErrorHandler(net::CertStatus cert_status) {
+    ssl_info_.Reset();
+    ssl_info_.cert =
+        net::ImportCertFromFile(net::GetTestCertsDirectory(), "ok_cert.pem");
+    ssl_info_.cert_status = cert_status;
+    ssl_info_.public_key_hashes.push_back(
+        net::HashValue(kCertPublicKeyHashValue));
+
+    delegate_ =
+        new TestSSLErrorHandlerDelegate(profile(), web_contents(), ssl_info_);
+    error_handler_.reset(new TestSSLErrorHandler(
+        std::unique_ptr<SSLErrorHandler::Delegate>(delegate_), web_contents(),
+        profile(), net::MapCertStatusToNetError(ssl_info_.cert_status),
+        ssl_info_,
+        GURL(),  // request_url
+        base::Callback<void(content::CertificateRequestResultType)>()));
+
+    // Enable finch experiment for captive portal interstitials.
+    ASSERT_TRUE(base::FieldTrialList::CreateFieldTrial(
+        "CaptivePortalInterstitial", "Enabled"));
+    // Enable finch experiment for SSL common name mismatch handling.
+    ASSERT_TRUE(base::FieldTrialList::CreateFieldTrial(
+        "SSLCommonNameMismatchHandling", "Enabled"));
+  }
+
+  void TestNoCaptivePortalInterstitial() {
+    base::HistogramTester histograms;
+
+    EXPECT_FALSE(error_handler()->IsTimerRunningForTesting());
+    EXPECT_EQ(1u, ssl_info().public_key_hashes.size());
+
+    auto config_proto =
+        base::MakeUnique<chrome_browser_ssl::SSLErrorAssistantConfig>();
+    config_proto->add_captive_portal_cert()->set_sha256_hash(
+        "sha256/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
+    config_proto->add_captive_portal_cert()->set_sha256_hash(
+        ssl_info().public_key_hashes[0].ToString());
+    config_proto->add_captive_portal_cert()->set_sha256_hash(
+        "sha256/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb");
+    SSLErrorHandler::SetErrorAssistantProto(std::move(config_proto));
+
+    error_handler()->StartHandlingError();
+
+    // Timer should start for captive portal detection.
+    EXPECT_TRUE(error_handler()->IsTimerRunningForTesting());
+    EXPECT_TRUE(delegate()->captive_portal_checked());
+    EXPECT_FALSE(delegate()->ssl_interstitial_shown());
+    EXPECT_FALSE(delegate()->captive_portal_interstitial_shown());
+    EXPECT_FALSE(delegate()->suggested_url_checked());
+
+    base::RunLoop().RunUntilIdle();
+
+    EXPECT_FALSE(error_handler()->IsTimerRunningForTesting());
+    EXPECT_TRUE(delegate()->captive_portal_checked());
+    EXPECT_TRUE(delegate()->ssl_interstitial_shown());
+    EXPECT_FALSE(delegate()->captive_portal_interstitial_shown());
+    EXPECT_FALSE(delegate()->suggested_url_checked());
+
+    // Check that the histogram for the captive portal cert was recorded.
+    histograms.ExpectTotalCount(SSLErrorHandler::GetHistogramNameForTesting(),
+                                2);
+    histograms.ExpectBucketCount(SSLErrorHandler::GetHistogramNameForTesting(),
+                                 SSLErrorHandler::HANDLE_ALL, 1);
+    histograms.ExpectBucketCount(
+        SSLErrorHandler::GetHistogramNameForTesting(),
+        SSLErrorHandler::SHOW_SSL_INTERSTITIAL_OVERRIDABLE, 1);
+  }
+
+ private:
+  net::SSLInfo ssl_info_;
+  std::unique_ptr<TestSSLErrorHandler> error_handler_;
+  TestSSLErrorHandlerDelegate* delegate_;
+  base::FieldTrialList field_trial_list_;
+  base::test::ScopedFeatureList scoped_feature_list_;
+
+  DISALLOW_COPY_AND_ASSIGN(SSLErrorHandlerCaptivePortalCertListTest);
+};
+
 
 class SSLErrorHandlerDateInvalidTest : public ChromeRenderViewHostTestHarness {
  public:
@@ -703,11 +811,8 @@
 
 // Tests that a certificate marked as a known captive portal certificate causes
 // the captive portal interstitial to be shown.
-TEST_F(SSLErrorHandlerNameMismatchTest, CaptivePortalCertificateList_Enabled) {
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitFromCommandLine(
-      "CaptivePortalCertificateList" /* enabled */,
-      std::string() /* disabled */);
+TEST_F(SSLErrorHandlerCaptivePortalCertListTest, Enabled) {
+  SetFeatureEnabled(true);
 
   EXPECT_FALSE(error_handler()->IsTimerRunningForTesting());
   EXPECT_EQ(1u, ssl_info().public_key_hashes.size());
@@ -756,66 +861,66 @@
 // Tests that a certificate marked as a known captive portal certificate does
 // not cause the captive portal interstitial to be shown, if the feature is
 // disabled.
-TEST_F(SSLErrorHandlerNameMismatchTest, CaptivePortalCertificateList_Disabled) {
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitFromCommandLine(
-      std::string() /* enabled */,
-      "CaptivePortalCertificateList" /* disabled */);
+TEST_F(SSLErrorHandlerCaptivePortalCertListTest, Disabled) {
+  SetFeatureEnabled(false);
 
-  base::HistogramTester histograms;
-
-  EXPECT_FALSE(error_handler()->IsTimerRunningForTesting());
-  EXPECT_EQ(1u, ssl_info().public_key_hashes.size());
-
-  auto config_proto =
-      base::MakeUnique<chrome_browser_ssl::SSLErrorAssistantConfig>();
-  config_proto->add_captive_portal_cert()->set_sha256_hash(
-      "sha256/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
-  config_proto->add_captive_portal_cert()->set_sha256_hash(
-      ssl_info().public_key_hashes[0].ToString());
-  config_proto->add_captive_portal_cert()->set_sha256_hash(
-      "sha256/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb");
-  SSLErrorHandler::SetErrorAssistantProto(std::move(config_proto));
-
-  error_handler()->StartHandlingError();
-
-  // Timer should start since captive portal certificate list feature is
-  // disabled.
-  EXPECT_TRUE(error_handler()->IsTimerRunningForTesting());
-  EXPECT_TRUE(delegate()->captive_portal_checked());
-  EXPECT_FALSE(delegate()->ssl_interstitial_shown());
-  EXPECT_FALSE(delegate()->captive_portal_interstitial_shown());
-  EXPECT_FALSE(delegate()->suggested_url_checked());
-
-  // A buggy SSL error handler might have incorrectly started the timer. Run to
-  // completion to ensure the timer is expired.
-  base::RunLoop().RunUntilIdle();
-
-  EXPECT_FALSE(error_handler()->IsTimerRunningForTesting());
-  EXPECT_TRUE(delegate()->captive_portal_checked());
-  EXPECT_TRUE(delegate()->ssl_interstitial_shown());
-  EXPECT_FALSE(delegate()->captive_portal_interstitial_shown());
-  EXPECT_FALSE(delegate()->suggested_url_checked());
-
-  // Check that the histogram for the captive portal cert was recorded.
-  histograms.ExpectTotalCount(SSLErrorHandler::GetHistogramNameForTesting(), 2);
-  histograms.ExpectBucketCount(SSLErrorHandler::GetHistogramNameForTesting(),
-                               SSLErrorHandler::HANDLE_ALL, 1);
-  histograms.ExpectBucketCount(
-      SSLErrorHandler::GetHistogramNameForTesting(),
-      SSLErrorHandler::SHOW_SSL_INTERSTITIAL_OVERRIDABLE, 1);
+  // Default error for SSLErrorHandlerNameMismatchTest tests is name mismatch.
+  TestNoCaptivePortalInterstitial();
 }
 
 // Tests that an error other than name mismatch does not cause a captive portal
 // interstitial to be shown, even if the certificate is marked as a known
 // captive portal certificate.
-TEST_F(SSLErrorHandlerAuthorityInvalidTest,
-       CaptivePortalCertificateList_ShouldShowGenericInterstitial) {
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitFromCommandLine(
-      "CaptivePortalCertificateList" /* enabled */,
-      std::string() /* disabled */);
+TEST_F(SSLErrorHandlerCaptivePortalCertListTest, AuthorityInvalid) {
+  SetFeatureEnabled(true);
 
+  ResetErrorHandler(net::CERT_STATUS_AUTHORITY_INVALID);
+  TestNoCaptivePortalInterstitial();
+}
+
+// Tests that an authority invalid error in addition to name mismatch error does
+// not cause a captive portal interstitial to be shown, even if the certificate
+// is marked as a known captive portal certificate. The resulting error is
+// authority-invalid.
+TEST_F(SSLErrorHandlerCaptivePortalCertListTest,
+       NameMismatchAndAuthorityInvalid) {
+  SetFeatureEnabled(true);
+
+  const net::CertStatus cert_status =
+      net::CERT_STATUS_COMMON_NAME_INVALID | net::CERT_STATUS_AUTHORITY_INVALID;
+  // Sanity check that AUTHORITY_INVALID is seen as the net error.
+  ASSERT_EQ(net::ERR_CERT_AUTHORITY_INVALID,
+            net::MapCertStatusToNetError(cert_status));
+  ResetErrorHandler(cert_status);
+  TestNoCaptivePortalInterstitial();
+}
+
+// Tests that another error in addition to name mismatch error does not cause a
+// captive portal interstitial to be shown, even if the certificate is marked as
+// a known captive portal certificate. Similar to
+// NameMismatchAndAuthorityInvalid, except the resulting error is name mismatch.
+TEST_F(SSLErrorHandlerCaptivePortalCertListTest, NameMismatchAndWeakKey) {
+  SetFeatureEnabled(true);
+
+  const net::CertStatus cert_status =
+      net::CERT_STATUS_COMMON_NAME_INVALID | net::CERT_STATUS_WEAK_KEY;
+  // Sanity check that COMMON_NAME_INVALID is seen as the net error, since the
+  // test is designed to verify that SSLErrorHandler notices other errors in the
+  // CertStatus even when COMMON_NAME_INVALID is the net error.
+  ASSERT_EQ(net::ERR_CERT_COMMON_NAME_INVALID,
+            net::MapCertStatusToNetError(cert_status));
+  ResetErrorHandler(cert_status);
+  TestNoCaptivePortalInterstitial();
+}
+
+#else
+
+TEST_F(SSLErrorHandlerCaptivePortalCertListTest, DisabledByBuild) {
+  SetFeatureEnabled(true);
+
+  // Default error for SSLErrorHandlerNameMismatchTest tests is name mismatch,
+  // but the feature is disabled by build so a generic SSL interstitial will be
+  // displayed.
   base::HistogramTester histograms;
 
   EXPECT_FALSE(error_handler()->IsTimerRunningForTesting());
@@ -833,22 +938,20 @@
 
   error_handler()->StartHandlingError();
 
-  // Timer should start for captive portal detection.
-  EXPECT_TRUE(error_handler()->IsTimerRunningForTesting());
-  EXPECT_TRUE(delegate()->captive_portal_checked());
-  EXPECT_FALSE(delegate()->ssl_interstitial_shown());
+  EXPECT_FALSE(error_handler()->IsTimerRunningForTesting());
+  EXPECT_FALSE(delegate()->captive_portal_checked());
+  EXPECT_TRUE(delegate()->ssl_interstitial_shown());
   EXPECT_FALSE(delegate()->captive_portal_interstitial_shown());
   EXPECT_FALSE(delegate()->suggested_url_checked());
 
   base::RunLoop().RunUntilIdle();
 
   EXPECT_FALSE(error_handler()->IsTimerRunningForTesting());
-  EXPECT_TRUE(delegate()->captive_portal_checked());
+  EXPECT_FALSE(delegate()->captive_portal_checked());
   EXPECT_TRUE(delegate()->ssl_interstitial_shown());
   EXPECT_FALSE(delegate()->captive_portal_interstitial_shown());
   EXPECT_FALSE(delegate()->suggested_url_checked());
 
-  // Check that the histogram for the captive portal cert was recorded.
   histograms.ExpectTotalCount(SSLErrorHandler::GetHistogramNameForTesting(), 2);
   histograms.ExpectBucketCount(SSLErrorHandler::GetHistogramNameForTesting(),
                                SSLErrorHandler::HANDLE_ALL, 1);
diff --git a/chrome/browser/ui/unload_controller.cc b/chrome/browser/ui/unload_controller.cc
index 8da5988..7a30244f 100644
--- a/chrome/browser/ui/unload_controller.cc
+++ b/chrome/browser/ui/unload_controller.cc
@@ -291,6 +291,9 @@
 }
 
 void UnloadController::ProcessPendingTabs() {
+  // Cancel posted/queued ProcessPendingTabs task if there is any.
+  weak_factory_.InvalidateWeakPtrs();
+
   if (!is_attempting_to_close_browser_) {
     // Because we might invoke this after a delay it's possible for the value of
     // is_attempting_to_close_browser_ to have changed since we scheduled the
@@ -298,7 +301,7 @@
     return;
   }
 
-  if (HasCompletedUnloadProcessing()) {
+  if (HasCompletedUnloadProcessing() && !TabsNeedBeforeUnloadFired()) {
     // We've finished all the unload events and can proceed to close the
     // browser.
     browser_->OnWindowClosing();
@@ -379,6 +382,9 @@
     if (process_now) {
       ProcessPendingTabs();
     } else {
+      // Do not post a new task if there is already any.
+      if (weak_factory_.HasWeakPtrs())
+        return;
       base::ThreadTaskRunnerHandle::Get()->PostTask(
           FROM_HERE, base::Bind(&UnloadController::ProcessPendingTabs,
                                 weak_factory_.GetWeakPtr()));
diff --git a/chrome/browser/ui/webui/OWNERS b/chrome/browser/ui/webui/OWNERS
index 016c0a8..e0f6aed 100644
--- a/chrome/browser/ui/webui/OWNERS
+++ b/chrome/browser/ui/webui/OWNERS
@@ -1,13 +1,4 @@
-bauerb@chromium.org
-dbeam@chromium.org
-dpapad@chromium.org
-michaelpg@chromium.org
-pam@chromium.org
-tommycli@chromium.org
-xiyuan@chromium.org
-
-# Emeritus
-estade@chromium.org
+file://ui/webui/OWNERS
 
 per-file devtools_ui*=dgozman@chromium.org
 per-file devtools_ui*=pfeldman@chromium.org
diff --git a/chrome/browser/web_applications/web_app.cc b/chrome/browser/web_applications/web_app.cc
index d0da854..298dcb9 100644
--- a/chrome/browser/web_applications/web_app.cc
+++ b/chrome/browser/web_applications/web_app.cc
@@ -90,15 +90,14 @@
     const base::Closure& callback,
     std::unique_ptr<web_app::ShortcutInfo> shortcut_info) {
   base::FilePath shortcut_data_dir = GetShortcutDataDir(*shortcut_info);
-  base::Closure task = base::Bind(&web_app::internals::UpdatePlatformShortcuts,
-                                  shortcut_data_dir, old_app_title,
-                                  base::Passed(&shortcut_info));
-  if (callback.is_null()) {
-    BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, task);
-  } else {
-    BrowserThread::PostTaskAndReply(BrowserThread::FILE, FROM_HERE, task,
-                                    callback);
-  }
+  const web_app::ShortcutInfo& shortcut_info_ref = *shortcut_info;
+  BrowserThread::PostTaskAndReply(
+      BrowserThread::FILE, FROM_HERE,
+      base::Bind(&web_app::internals::UpdatePlatformShortcuts,
+                 shortcut_data_dir, old_app_title,
+                 base::ConstRef(shortcut_info_ref)),
+      base::Bind(&web_app::internals::DeleteShortcutInfoOnUIThread,
+                 base::Passed(&shortcut_info), callback));
 }
 
 void OnImageLoaded(std::unique_ptr<web_app::ShortcutInfo> shortcut_info,
@@ -130,11 +129,16 @@
     const web_app::ShortcutLocations& locations,
     std::unique_ptr<web_app::ShortcutInfo> shortcut_info) {
   base::FilePath shortcut_data_dir = GetShortcutDataDir(*shortcut_info);
-  BrowserThread::PostTask(
+
+  const web_app::ShortcutInfo& shortcut_info_ref = *shortcut_info;
+  BrowserThread::PostTaskAndReply(
       BrowserThread::FILE, FROM_HERE,
       base::Bind(
           base::IgnoreResult(&web_app::internals::CreatePlatformShortcuts),
-          shortcut_data_dir, base::Passed(&shortcut_info), locations, reason));
+          shortcut_data_dir, base::ConstRef(shortcut_info_ref), locations,
+          reason),
+      base::Bind(&web_app::internals::DeleteShortcutInfoOnUIThread,
+                 base::Passed(&shortcut_info), base::Closure()));
 }
 
 }  // namespace
@@ -161,10 +165,21 @@
   return base::FilePath(file_name);
 }
 
+void DeleteShortcutInfoOnUIThread(
+    std::unique_ptr<web_app::ShortcutInfo> shortcut_info,
+    const base::Closure& callback) {
+  shortcut_info.reset();
+  if (callback)
+    callback.Run();
+}
+
 }  // namespace internals
 
 ShortcutInfo::ShortcutInfo() {}
-ShortcutInfo::~ShortcutInfo() {}
+
+ShortcutInfo::~ShortcutInfo() {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+}
 
 ShortcutLocations::ShortcutLocations()
     : on_desktop(false),
@@ -430,10 +445,14 @@
   std::unique_ptr<ShortcutInfo> shortcut_info(
       ShortcutInfoForExtensionAndProfile(app, profile));
   base::FilePath shortcut_data_dir = GetShortcutDataDir(*shortcut_info);
-  BrowserThread::PostTask(
+  const web_app::ShortcutInfo& shortcut_info_ref = *shortcut_info;
+
+  BrowserThread::PostTaskAndReply(
       BrowserThread::FILE, FROM_HERE,
       base::Bind(&web_app::internals::DeletePlatformShortcuts,
-                 shortcut_data_dir, base::Passed(&shortcut_info)));
+                 shortcut_data_dir, base::ConstRef(shortcut_info_ref)),
+      base::Bind(&web_app::internals::DeleteShortcutInfoOnUIThread,
+                 base::Passed(&shortcut_info), base::Closure()));
 }
 
 void UpdateAllShortcuts(const base::string16& old_app_title,
diff --git a/chrome/browser/web_applications/web_app.h b/chrome/browser/web_applications/web_app.h
index 1e42349..f8ba2643 100644
--- a/chrome/browser/web_applications/web_app.h
+++ b/chrome/browser/web_applications/web_app.h
@@ -58,8 +58,9 @@
 
  private:
   // ShortcutInfo must not be copied; generally it is passed around via
-  // scoped_ptrs. This is to allow passing ShortcutInfos between threads,
-  // despite ImageFamily having a non-thread-safe reference count.
+  // unique_ptr. Since ImageFamily has a non-thread-safe reference count in
+  // its member and is bound to UI thread, destroy ShortcutInfo instance
+  // on UI thread.
   DISALLOW_COPY_AND_ASSIGN(ShortcutInfo);
 };
 
@@ -229,24 +230,23 @@
 // shortcut, and is also used as the UserDataDir for platform app shortcuts.
 // |shortcut_info| contains info about the shortcut to create, and
 // |creation_locations| contains information about where to create them.
-bool CreatePlatformShortcuts(
-    const base::FilePath& shortcut_data_path,
-    std::unique_ptr<ShortcutInfo> shortcut_info,
-    const ShortcutLocations& creation_locations,
-    ShortcutCreationReason creation_reason);
+bool CreatePlatformShortcuts(const base::FilePath& shortcut_data_path,
+                             const ShortcutInfo& shortcut_info,
+                             const ShortcutLocations& creation_locations,
+                             ShortcutCreationReason creation_reason);
 
 // Delete all the shortcuts we have added for this extension. This is the
 // platform specific implementation of the DeleteAllShortcuts function, and
 // is executed on the FILE thread.
 void DeletePlatformShortcuts(const base::FilePath& shortcut_data_path,
-                             std::unique_ptr<ShortcutInfo> shortcut_info);
+                             const ShortcutInfo& shortcut_info);
 
 // Updates all the shortcuts we have added for this extension. This is the
 // platform specific implementation of the UpdateAllShortcuts function, and
 // is executed on the FILE thread.
 void UpdatePlatformShortcuts(const base::FilePath& shortcut_data_path,
                              const base::string16& old_app_title,
-                             std::unique_ptr<ShortcutInfo> shortcut_info);
+                             const ShortcutInfo& shortcut_info);
 
 // Delete all the shortcuts for an entire profile.
 // This is executed on the FILE thread.
@@ -256,6 +256,10 @@
 // on-disk file name .
 base::FilePath GetSanitizedFileName(const base::string16& name);
 
+// Clears |shortcut_info| and invokes |callback| unless it's null.
+void DeleteShortcutInfoOnUIThread(std::unique_ptr<ShortcutInfo> shortcut_info,
+                                  const base::Closure& callback);
+
 }  // namespace internals
 
 }  // namespace web_app
diff --git a/chrome/browser/web_applications/web_app_chromeos.cc b/chrome/browser/web_applications/web_app_chromeos.cc
index 8fcd2f1..f90fc9f 100644
--- a/chrome/browser/web_applications/web_app_chromeos.cc
+++ b/chrome/browser/web_applications/web_app_chromeos.cc
@@ -13,20 +13,19 @@
 
 namespace internals {
 
-bool CreatePlatformShortcuts(
-    const base::FilePath& web_app_path,
-    std::unique_ptr<ShortcutInfo> shortcut_info,
-    const ShortcutLocations& creation_locations,
-    ShortcutCreationReason creation_reason) {
+bool CreatePlatformShortcuts(const base::FilePath& web_app_path,
+                             const ShortcutInfo& shortcut_info,
+                             const ShortcutLocations& creation_locations,
+                             ShortcutCreationReason creation_reason) {
   return true;
 }
 
 void DeletePlatformShortcuts(const base::FilePath& web_app_path,
-                             std::unique_ptr<ShortcutInfo> shortcut_info) {}
+                             const ShortcutInfo& shortcut_info) {}
 
 void UpdatePlatformShortcuts(const base::FilePath& web_app_path,
                              const base::string16& old_app_title,
-                             std::unique_ptr<ShortcutInfo> shortcut_info) {}
+                             const ShortcutInfo& shortcut_info) {}
 
 void DeleteAllShortcutsForProfile(const base::FilePath& profile_path) {}
 
diff --git a/chrome/browser/web_applications/web_app_linux.cc b/chrome/browser/web_applications/web_app_linux.cc
index 893173b..b9b929f 100644
--- a/chrome/browser/web_applications/web_app_linux.cc
+++ b/chrome/browser/web_applications/web_app_linux.cc
@@ -21,14 +21,13 @@
 
 namespace internals {
 
-bool CreatePlatformShortcuts(
-    const base::FilePath& web_app_path,
-    std::unique_ptr<ShortcutInfo> shortcut_info,
-    const ShortcutLocations& creation_locations,
-    ShortcutCreationReason /*creation_reason*/) {
+bool CreatePlatformShortcuts(const base::FilePath& web_app_path,
+                             const ShortcutInfo& shortcut_info,
+                             const ShortcutLocations& creation_locations,
+                             ShortcutCreationReason /*creation_reason*/) {
 #if !defined(OS_CHROMEOS)
   DCHECK_CURRENTLY_ON(content::BrowserThread::FILE);
-  return shell_integration_linux::CreateDesktopShortcut(*shortcut_info,
+  return shell_integration_linux::CreateDesktopShortcut(shortcut_info,
                                                         creation_locations);
 #else
   return false;
@@ -36,16 +35,16 @@
 }
 
 void DeletePlatformShortcuts(const base::FilePath& web_app_path,
-                             std::unique_ptr<ShortcutInfo> shortcut_info) {
+                             const ShortcutInfo& shortcut_info) {
 #if !defined(OS_CHROMEOS)
-  shell_integration_linux::DeleteDesktopShortcuts(shortcut_info->profile_path,
-                                                  shortcut_info->extension_id);
+  shell_integration_linux::DeleteDesktopShortcuts(shortcut_info.profile_path,
+                                                  shortcut_info.extension_id);
 #endif
 }
 
 void UpdatePlatformShortcuts(const base::FilePath& web_app_path,
                              const base::string16& /*old_app_title*/,
-                             std::unique_ptr<ShortcutInfo> shortcut_info) {
+                             const ShortcutInfo& shortcut_info) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::FILE);
 
   std::unique_ptr<base::Environment> env(base::Environment::Create());
@@ -53,7 +52,7 @@
   // Find out whether shortcuts are already installed.
   ShortcutLocations creation_locations =
       shell_integration_linux::GetExistingShortcutLocations(
-          env.get(), shortcut_info->profile_path, shortcut_info->extension_id);
+          env.get(), shortcut_info.profile_path, shortcut_info.extension_id);
 
   // Always create a hidden shortcut in applications if a visible one is not
   // being created. This allows the operating system to identify the app, but
@@ -61,8 +60,8 @@
   if (creation_locations.applications_menu_location == APP_MENU_LOCATION_NONE)
     creation_locations.applications_menu_location = APP_MENU_LOCATION_HIDDEN;
 
-  CreatePlatformShortcuts(web_app_path, std::move(shortcut_info),
-                          creation_locations, SHORTCUT_CREATION_AUTOMATED);
+  CreatePlatformShortcuts(web_app_path, shortcut_info, creation_locations,
+                          SHORTCUT_CREATION_AUTOMATED);
 }
 
 void DeleteAllShortcutsForProfile(const base::FilePath& profile_path) {
diff --git a/chrome/browser/web_applications/web_app_mac.mm b/chrome/browser/web_applications/web_app_mac.mm
index 1c46707..6da7719 100644
--- a/chrome/browser/web_applications/web_app_mac.mm
+++ b/chrome/browser/web_applications/web_app_mac.mm
@@ -214,11 +214,10 @@
       user_data_dir.value(), base::CompareCase::SENSITIVE);
 }
 
-void LaunchShimOnFileThread(
-    std::unique_ptr<web_app::ShortcutInfo> shortcut_info,
-    bool launched_after_rebuild) {
+void LaunchShimOnFileThread(const web_app::ShortcutInfo& shortcut_info,
+                            bool launched_after_rebuild) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::FILE);
-  base::FilePath shim_path = web_app::GetAppInstallPath(*shortcut_info);
+  base::FilePath shim_path = web_app::GetAppInstallPath(shortcut_info);
 
   if (shim_path.empty() ||
       !base::PathExists(shim_path) ||
@@ -226,7 +225,7 @@
     // The user may have deleted the copy in the Applications folder, use the
     // one in the web app's |app_data_dir_|.
     base::FilePath app_data_dir = web_app::GetWebAppDataDirectory(
-        shortcut_info->profile_path, shortcut_info->extension_id, GURL());
+        shortcut_info.profile_path, shortcut_info.extension_id, GURL());
     shim_path = app_data_dir.Append(shim_path.BaseName());
   }
 
@@ -266,18 +265,22 @@
 }
 
 void UpdateAndLaunchShimOnFileThread(
-    std::unique_ptr<web_app::ShortcutInfo> shortcut_info) {
+    const web_app::ShortcutInfo& shortcut_info) {
   base::FilePath shortcut_data_dir = web_app::GetWebAppDataDirectory(
-      shortcut_info->profile_path, shortcut_info->extension_id, GURL());
+      shortcut_info.profile_path, shortcut_info.extension_id, GURL());
   UpdatePlatformShortcutsInternal(shortcut_data_dir, base::string16(),
-                                  *shortcut_info);
-  LaunchShimOnFileThread(std::move(shortcut_info), true);
+                                  shortcut_info);
+  LaunchShimOnFileThread(shortcut_info, true);
 }
 
 void UpdateAndLaunchShim(std::unique_ptr<web_app::ShortcutInfo> shortcut_info) {
-  content::BrowserThread::PostTask(content::BrowserThread::FILE, FROM_HERE,
-                                   base::Bind(&UpdateAndLaunchShimOnFileThread,
-                                              base::Passed(&shortcut_info)));
+  const web_app::ShortcutInfo& shortcut_info_ref = *shortcut_info;
+  content::BrowserThread::PostTaskAndReply(
+      content::BrowserThread::FILE, FROM_HERE,
+      base::Bind(&UpdateAndLaunchShimOnFileThread,
+                 base::ConstRef(shortcut_info_ref)),
+      base::Bind(&web_app::internals::DeleteShortcutInfoOnUIThread,
+                 base::Passed(&shortcut_info), base::Closure()));
 }
 
 void RebuildAppAndLaunch(std::unique_ptr<web_app::ShortcutInfo> shortcut_info) {
@@ -526,10 +529,9 @@
 }
 
 void RevealAppShimInFinderForAppOnFileThread(
-    std::unique_ptr<web_app::ShortcutInfo> shortcut_info,
+    const web_app::ShortcutInfo& shortcut_info,
     const base::FilePath& app_path) {
-  web_app::WebAppShortcutCreator shortcut_creator(app_path,
-                                                  shortcut_info.get());
+  web_app::WebAppShortcutCreator shortcut_creator(app_path, &shortcut_info);
   shortcut_creator.RevealAppShimInFinder();
 }
 
@@ -977,9 +979,13 @@
     return;
   }
 
-  content::BrowserThread::PostTask(
+  const web_app::ShortcutInfo& shortcut_info_ref = *shortcut_info;
+  content::BrowserThread::PostTaskAndReply(
       content::BrowserThread::FILE, FROM_HERE,
-      base::Bind(&LaunchShimOnFileThread, base::Passed(&shortcut_info), false));
+      base::Bind(&LaunchShimOnFileThread, base::ConstRef(shortcut_info_ref),
+                 false),
+      base::Bind(&web_app::internals::DeleteShortcutInfoOnUIThread,
+                 base::Passed(&shortcut_info), base::Closure()));
 }
 
 bool MaybeRebuildShortcut(const base::CommandLine& command_line) {
@@ -1022,38 +1028,40 @@
                                  const extensions::Extension* app) {
   std::unique_ptr<web_app::ShortcutInfo> shortcut_info =
       ShortcutInfoForExtensionAndProfile(app, profile);
-  content::BrowserThread::PostTask(
+  const web_app::ShortcutInfo& shortcut_info_ref = *shortcut_info;
+  content::BrowserThread::PostTaskAndReply(
       content::BrowserThread::FILE, FROM_HERE,
       base::Bind(&RevealAppShimInFinderForAppOnFileThread,
-                 base::Passed(&shortcut_info), app->path()));
+                 base::ConstRef(shortcut_info_ref), app->path()),
+      base::Bind(&web_app::internals::DeleteShortcutInfoOnUIThread,
+                 base::Passed(&shortcut_info), base::Closure()));
 }
 
 namespace internals {
 
-bool CreatePlatformShortcuts(
-    const base::FilePath& app_data_path,
-    std::unique_ptr<ShortcutInfo> shortcut_info,
-    const ShortcutLocations& creation_locations,
-    ShortcutCreationReason creation_reason) {
+bool CreatePlatformShortcuts(const base::FilePath& app_data_path,
+                             const ShortcutInfo& shortcut_info,
+                             const ShortcutLocations& creation_locations,
+                             ShortcutCreationReason creation_reason) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::FILE);
   if (AppShimsDisabledForTest())
     return true;
 
-  WebAppShortcutCreator shortcut_creator(app_data_path, shortcut_info.get());
+  WebAppShortcutCreator shortcut_creator(app_data_path, &shortcut_info);
   return shortcut_creator.CreateShortcuts(creation_reason, creation_locations);
 }
 
 void DeletePlatformShortcuts(const base::FilePath& app_data_path,
-                             std::unique_ptr<ShortcutInfo> shortcut_info) {
+                             const ShortcutInfo& shortcut_info) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::FILE);
-  WebAppShortcutCreator shortcut_creator(app_data_path, shortcut_info.get());
+  WebAppShortcutCreator shortcut_creator(app_data_path, &shortcut_info);
   shortcut_creator.DeleteShortcuts();
 }
 
 void UpdatePlatformShortcuts(const base::FilePath& app_data_path,
                              const base::string16& old_app_title,
-                             std::unique_ptr<ShortcutInfo> shortcut_info) {
-  UpdatePlatformShortcutsInternal(app_data_path, old_app_title, *shortcut_info);
+                             const ShortcutInfo& shortcut_info) {
+  UpdatePlatformShortcutsInternal(app_data_path, old_app_title, shortcut_info);
 }
 
 void DeleteAllShortcutsForProfile(const base::FilePath& profile_path) {
diff --git a/chrome/browser/web_applications/web_app_mac_unittest.mm b/chrome/browser/web_applications/web_app_mac_unittest.mm
index 7001f54..148470b 100644
--- a/chrome/browser/web_applications/web_app_mac_unittest.mm
+++ b/chrome/browser/web_applications/web_app_mac_unittest.mm
@@ -25,6 +25,7 @@
 #import "chrome/common/mac/app_mode_common.h"
 #include "chrome/grit/theme_resources.h"
 #include "components/version_info/version_info.h"
+#include "content/public/test/test_browser_thread_bundle.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #import "testing/gtest_mac.h"
@@ -87,6 +88,9 @@
     shim_path_ = destination_dir_.Append(shim_base_name_);
   }
 
+  // Needed by DCHECK_CURRENTLY_ON in ShortcutInfo destructor.
+  content::TestBrowserThreadBundle thread_bundle_;
+
   base::ScopedTempDir temp_app_data_dir_;
   base::ScopedTempDir temp_destination_dir_;
   base::FilePath app_data_dir_;
diff --git a/chrome/browser/web_applications/web_app_win.cc b/chrome/browser/web_applications/web_app_win.cc
index 467217f..6b08168 100644
--- a/chrome/browser/web_applications/web_app_win.cc
+++ b/chrome/browser/web_applications/web_app_win.cc
@@ -336,17 +336,16 @@
   }
 }
 
-void CreateIconAndSetRelaunchDetails(
-    const base::FilePath& web_app_path,
-    const base::FilePath& icon_file,
-    std::unique_ptr<web_app::ShortcutInfo> shortcut_info,
-    HWND hwnd) {
+void CreateIconAndSetRelaunchDetails(const base::FilePath& web_app_path,
+                                     const base::FilePath& icon_file,
+                                     const web_app::ShortcutInfo& shortcut_info,
+                                     HWND hwnd) {
   DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
 
   base::CommandLine command_line =
-      shell_integration::CommandLineArgsForLauncher(
-          shortcut_info->url, shortcut_info->extension_id,
-          shortcut_info->profile_path);
+      shell_integration::CommandLineArgsForLauncher(shortcut_info.url,
+                                                    shortcut_info.extension_id,
+                                                    shortcut_info.profile_path);
 
   base::FilePath chrome_exe;
   if (!PathService::Get(base::FILE_EXE, &chrome_exe)) {
@@ -355,13 +354,13 @@
   }
   command_line.SetProgram(chrome_exe);
   ui::win::SetRelaunchDetailsForWindow(command_line.GetCommandLineString(),
-                                       shortcut_info->title, hwnd);
+                                       shortcut_info.title, hwnd);
 
   if (!base::PathExists(web_app_path) && !base::CreateDirectory(web_app_path))
     return;
 
   ui::win::SetAppIconForWindow(icon_file, 0, hwnd);
-  web_app::internals::CheckAndSaveIcon(icon_file, shortcut_info->favicon, true);
+  web_app::internals::CheckAndSaveIcon(icon_file, shortcut_info.favicon, true);
 }
 
 void OnShortcutInfoLoadedForSetRelaunchDetails(
@@ -376,9 +375,13 @@
       shortcut_info->url);
   base::FilePath icon_file =
       web_app::internals::GetIconFilePath(web_app_path, shortcut_info->title);
-  content::BrowserThread::PostBlockingPoolTask(
-      FROM_HERE, base::Bind(&CreateIconAndSetRelaunchDetails, web_app_path,
-                            icon_file, base::Passed(&shortcut_info), hwnd));
+  const web_app::ShortcutInfo& shortcut_info_ref = *shortcut_info;
+  content::BrowserThread::PostBlockingPoolTaskAndReply(
+      FROM_HERE,
+      base::Bind(&CreateIconAndSetRelaunchDetails, web_app_path, icon_file,
+                 base::ConstRef(shortcut_info_ref), hwnd),
+      base::Bind(&web_app::internals::DeleteShortcutInfoOnUIThread,
+                 base::Passed(&shortcut_info), base::Closure()));
 }
 
 }  // namespace
@@ -442,11 +445,10 @@
   return true;
 }
 
-bool CreatePlatformShortcuts(
-    const base::FilePath& web_app_path,
-    std::unique_ptr<ShortcutInfo> shortcut_info,
-    const ShortcutLocations& creation_locations,
-    ShortcutCreationReason creation_reason) {
+bool CreatePlatformShortcuts(const base::FilePath& web_app_path,
+                             const ShortcutInfo& shortcut_info,
+                             const ShortcutLocations& creation_locations,
+                             ShortcutCreationReason creation_reason) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::FILE);
 
   // Nothing to do on Windows for hidden apps.
@@ -470,12 +472,12 @@
   if (shortcut_paths.empty())
     return false;
 
-  if (!CreateShortcutsInPaths(web_app_path, *shortcut_info, shortcut_paths,
+  if (!CreateShortcutsInPaths(web_app_path, shortcut_info, shortcut_paths,
                               creation_reason, NULL))
     return false;
 
   if (pin_to_taskbar) {
-    base::FilePath file_name = GetSanitizedFileName(shortcut_info->title);
+    base::FilePath file_name = GetSanitizedFileName(shortcut_info.title);
     // Use the web app path shortcut for pinning to avoid having unique numbers
     // in the application name.
     base::FilePath shortcut_to_pin = web_app_path.Append(file_name).
@@ -489,29 +491,29 @@
 
 void UpdatePlatformShortcuts(const base::FilePath& web_app_path,
                              const base::string16& old_app_title,
-                             std::unique_ptr<ShortcutInfo> shortcut_info) {
+                             const ShortcutInfo& shortcut_info) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::FILE);
 
   // Generates file name to use with persisted ico and shortcut file.
   base::FilePath file_name =
-      web_app::internals::GetSanitizedFileName(shortcut_info->title);
+      web_app::internals::GetSanitizedFileName(shortcut_info.title);
 
-  if (old_app_title != shortcut_info->title) {
+  if (old_app_title != shortcut_info.title) {
     // The app's title has changed. Delete all existing app shortcuts and
     // recreate them in any locations they already existed (but do not add them
     // to locations where they do not currently exist).
     bool was_pinned_to_taskbar;
     std::vector<base::FilePath> shortcut_paths;
     GetShortcutLocationsAndDeleteShortcuts(
-        web_app_path, shortcut_info->profile_path, old_app_title,
+        web_app_path, shortcut_info.profile_path, old_app_title,
         &was_pinned_to_taskbar, &shortcut_paths);
-    CreateShortcutsInPaths(web_app_path, *shortcut_info, shortcut_paths,
+    CreateShortcutsInPaths(web_app_path, shortcut_info, shortcut_paths,
                            SHORTCUT_CREATION_BY_USER, NULL);
     // If the shortcut was pinned to the taskbar,
     // GetShortcutLocationsAndDeleteShortcuts will have deleted it. In that
     // case, re-pin it.
     if (was_pinned_to_taskbar && base::win::CanPinShortcutToTaskbar()) {
-      base::FilePath file_name = GetSanitizedFileName(shortcut_info->title);
+      base::FilePath file_name = GetSanitizedFileName(shortcut_info.title);
       // Use the web app path shortcut for pinning to avoid having unique
       // numbers in the application name.
       base::FilePath shortcut_to_pin = web_app_path.Append(file_name).
@@ -521,16 +523,15 @@
   }
 
   // Update the icon if necessary.
-  base::FilePath icon_file =
-      GetIconFilePath(web_app_path, shortcut_info->title);
-  CheckAndSaveIcon(icon_file, shortcut_info->favicon, true);
+  base::FilePath icon_file = GetIconFilePath(web_app_path, shortcut_info.title);
+  CheckAndSaveIcon(icon_file, shortcut_info.favicon, true);
 }
 
 void DeletePlatformShortcuts(const base::FilePath& web_app_path,
-                             std::unique_ptr<ShortcutInfo> shortcut_info) {
+                             const ShortcutInfo& shortcut_info) {
   GetShortcutLocationsAndDeleteShortcuts(web_app_path,
-                                         shortcut_info->profile_path,
-                                         shortcut_info->title, NULL, NULL);
+                                         shortcut_info.profile_path,
+                                         shortcut_info.title, NULL, NULL);
 
   // If there are no more shortcuts in the Chrome Apps subdirectory, remove it.
   base::FilePath chrome_apps_dir;
diff --git a/chrome/renderer/resources/OWNERS b/chrome/renderer/resources/OWNERS
index b5db47d..8999077 100644
--- a/chrome/renderer/resources/OWNERS
+++ b/chrome/renderer/resources/OWNERS
@@ -1,8 +1,3 @@
-achuith@chromium.org
-bauerb@chromium.org
-dbeam@chromium.org
-estade@chromium.org
-pam@chromium.org
-xiyuan@chromium.org
+file://ui/webui/OWNERS
 
 per-file offline.js=edwardjung@chromium.org
diff --git a/components/about_ui/OWNERS b/components/about_ui/OWNERS
index 74f407e..c4cf1964 100644
--- a/components/about_ui/OWNERS
+++ b/components/about_ui/OWNERS
@@ -1,6 +1 @@
-achuith@chromium.org
-bauerb@chromium.org
-dbeam@chromium.org
-estade@chromium.org
-pam@chromium.org
-xiyuan@chromium.org
+file://ui/webui/OWNERS
diff --git a/components/flags_ui/OWNERS b/components/flags_ui/OWNERS
index 1190db0..61dccf7 100644
--- a/components/flags_ui/OWNERS
+++ b/components/flags_ui/OWNERS
@@ -1,7 +1,3 @@
-achuith@chromium.org
+file://ui/webui/OWNERS
+
 asvitkine@chromium.org
-bauerb@chromium.org
-dbeam@chromium.org
-estade@chromium.org
-pam@chromium.org
-xiyuan@chromium.org
diff --git a/components/version_ui/OWNERS b/components/version_ui/OWNERS
index 74f407e..c4cf1964 100644
--- a/components/version_ui/OWNERS
+++ b/components/version_ui/OWNERS
@@ -1,6 +1 @@
-achuith@chromium.org
-bauerb@chromium.org
-dbeam@chromium.org
-estade@chromium.org
-pam@chromium.org
-xiyuan@chromium.org
+file://ui/webui/OWNERS
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index 6ab9d7f..92eada8 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -593,6 +593,8 @@
     "download/mhtml_generation_manager.h",
     "download/parallel_download_job.cc",
     "download/parallel_download_job.h",
+    "download/parallel_download_utils.cc",
+    "download/parallel_download_utils.h",
     "download/rate_estimator.cc",
     "download/rate_estimator.h",
     "download/save_file.cc",
diff --git a/content/browser/download/download_item_impl.cc b/content/browser/download/download_item_impl.cc
index d3d3d48..95e5aac 100644
--- a/content/browser/download/download_item_impl.cc
+++ b/content/browser/download/download_item_impl.cc
@@ -45,6 +45,7 @@
 #include "content/browser/download/download_net_log_parameters.h"
 #include "content/browser/download/download_request_handle.h"
 #include "content/browser/download/download_stats.h"
+#include "content/browser/download/parallel_download_utils.h"
 #include "content/browser/renderer_host/render_view_host_impl.h"
 #include "content/browser/web_contents/web_contents_impl.h"
 #include "content/public/browser/browser_context.h"
@@ -1953,7 +1954,13 @@
       new DownloadUrlParameters(GetURL(),
                                 storage_partition->GetURLRequestContext()));
   download_params->set_file_path(GetFullPath());
-  download_params->set_offset(GetReceivedBytes());
+  if (received_slices_.size() > 0) {
+    ReceivedSlice next_slice = FindNextSliceToDownload(received_slices_);
+    download_params->set_offset(next_slice.offset);
+    download_params->set_length(next_slice.received_bytes);
+  } else {
+    download_params->set_offset(GetReceivedBytes());
+  }
   download_params->set_last_modified(GetLastModifiedTime());
   download_params->set_etag(GetETag());
   download_params->set_hash_of_partial_file(hash_);
diff --git a/content/browser/download/parallel_download_utils.cc b/content/browser/download/parallel_download_utils.cc
new file mode 100644
index 0000000..5846e1eb
--- /dev/null
+++ b/content/browser/download/parallel_download_utils.cc
@@ -0,0 +1,41 @@
+// 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/download/parallel_download_utils.h"
+
+#include "content/public/browser/download_save_info.h"
+
+namespace content {
+
+DownloadItem::ReceivedSlice FindNextSliceToDownload(
+    const std::vector<DownloadItem::ReceivedSlice>& received_slices) {
+  if (received_slices.empty())
+    return DownloadItem::ReceivedSlice(0, DownloadSaveInfo::kLengthFullContent);
+
+  std::vector<DownloadItem::ReceivedSlice>::const_iterator iter =
+      received_slices.begin();
+  DCHECK_GE(iter->offset, 0);
+  if (iter->offset != 0)
+    return DownloadItem::ReceivedSlice(0, iter->offset);
+
+  int64_t offset = 0;
+  int64_t remaining_bytes = DownloadSaveInfo::kLengthFullContent;
+  while (iter != received_slices.end()) {
+    offset = iter->offset + iter->received_bytes;
+    std::vector<DownloadItem::ReceivedSlice>::const_iterator next =
+        std::next(iter);
+    if (next == received_slices.end())
+      break;
+
+    DCHECK_GE(next->offset, offset);
+    if (next->offset > offset) {
+      remaining_bytes = next->offset - offset;
+      break;
+    }
+    iter = next;
+  }
+  return DownloadItem::ReceivedSlice(offset, remaining_bytes);
+}
+
+}  // namespace content
diff --git a/content/browser/download/parallel_download_utils.h b/content/browser/download/parallel_download_utils.h
new file mode 100644
index 0000000..62d732e
--- /dev/null
+++ b/content/browser/download/parallel_download_utils.h
@@ -0,0 +1,23 @@
+// 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_DOWNLOAD_PARALLEL_DOWNLOAD_UTILS_H_
+#define CONTENT_BROWSER_DOWNLOAD_PARALLEL_DOWNLOAD_UTILS_H_
+
+#include <vector>
+
+#include "content/common/content_export.h"
+#include "content/public/browser/download_item.h"
+
+namespace content {
+
+// Find the first gap in an array of received slices and return it as the next
+// slice to download. If not found, return a slice that is to the end of all
+// slices. |received_slices| must be ordered by offsets.
+CONTENT_EXPORT DownloadItem::ReceivedSlice FindNextSliceToDownload(
+    const std::vector<DownloadItem::ReceivedSlice>& received_slices);
+
+}  //  namespace content
+
+#endif  // CONTENT_BROWSER_DOWNLOAD_PARALLEL_DOWNLOAD_UTILS_H_
diff --git a/content/browser/download/parallel_download_utils_unittest.cc b/content/browser/download/parallel_download_utils_unittest.cc
new file mode 100644
index 0000000..abab942
--- /dev/null
+++ b/content/browser/download/parallel_download_utils_unittest.cc
@@ -0,0 +1,42 @@
+// 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/download/parallel_download_utils.h"
+
+#include "content/public/browser/download_save_info.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace content {
+
+TEST(ParallelDownloadUtilsTest, FindNextSliceToDownload) {
+  std::vector<DownloadItem::ReceivedSlice> slices;
+  DownloadItem::ReceivedSlice slice = FindNextSliceToDownload(slices);
+  EXPECT_EQ(0, slice.offset);
+  EXPECT_EQ(DownloadSaveInfo::kLengthFullContent, slice.received_bytes);
+
+  slices.emplace_back(0, 500);
+  slice = FindNextSliceToDownload(slices);
+  EXPECT_EQ(500, slice.offset);
+  EXPECT_EQ(DownloadSaveInfo::kLengthFullContent, slice.received_bytes);
+
+  // Create a gap between slices.
+  slices.emplace_back(1000, 500);
+  slice = FindNextSliceToDownload(slices);
+  EXPECT_EQ(500, slice.offset);
+  EXPECT_EQ(500, slice.received_bytes);
+
+  // Fill the gap.
+  slices.emplace(slices.begin() + 1, slice);
+  slice = FindNextSliceToDownload(slices);
+  EXPECT_EQ(1500, slice.offset);
+  EXPECT_EQ(DownloadSaveInfo::kLengthFullContent, slice.received_bytes);
+
+  // Create a new gap at the beginning.
+  slices.erase(slices.begin());
+  slice = FindNextSliceToDownload(slices);
+  EXPECT_EQ(0, slice.offset);
+  EXPECT_EQ(500, slice.received_bytes);
+}
+
+}  // namespace content
diff --git a/content/common/origin_trials/OWNERS b/content/common/origin_trials/OWNERS
index e70c4f6d..e6e2190 100644
--- a/content/common/origin_trials/OWNERS
+++ b/content/common/origin_trials/OWNERS
@@ -3,6 +3,7 @@
 #   //content/renderer/origin_trials/
 #   //tools/origin_trials/
 
+chasej@chromium.org
 iclelland@chromium.org
 mek@chromium.org
 
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index e02d1a1..c6f5c6f 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -1052,6 +1052,7 @@
     "../browser/download/download_manager_impl_unittest.cc",
     "../browser/download/download_request_core_unittest.cc",
     "../browser/download/parallel_download_job_unittest.cc",
+    "../browser/download/parallel_download_utils_unittest.cc",
     "../browser/download/rate_estimator_unittest.cc",
     "../browser/download/save_package_unittest.cc",
     "../browser/fileapi/copy_or_move_file_validator_unittest.cc",
diff --git a/content/test/data/accessibility/readme.txt b/content/test/data/accessibility/readme.md
similarity index 96%
rename from content/test/data/accessibility/readme.txt
rename to content/test/data/accessibility/readme.md
index 4d89e1a..893dc47 100644
--- a/content/test/data/accessibility/readme.txt
+++ b/content/test/data/accessibility/readme.md
@@ -1,4 +1,4 @@
-DumpAccessibilityTreeTest and DumpAccessibilityEventsTest Notes:
+# DumpAccessibilityTreeTest and DumpAccessibilityEventsTest Notes:
 
 Both sets of tests use a similar format for files.
 
@@ -28,6 +28,7 @@
 out/Debug/content_browsertests --gtest_filter="DumpAccessibility*"
 
 Files used:
+
 * foo.html -- a file to be tested
 * foo-expected-android.txt -- expected Android AccessibilityNodeInfo output
 * foo-expected-auralinux.txt -- expected Linux ATK output
@@ -36,6 +37,7 @@
 * foo-expected-win.txt -- expected Win IAccessible/IAccessible2 output
 
 Format for expected files:
+
 * Blank lines and lines beginning with # are ignored
 * Skipped files: if first line of file begins with #<skip then the
   test passes. This can be used to indicate desired output with a link
@@ -43,6 +45,7 @@
 * Use 2 plus signs for indent to show hierarchy
 
 Filters:
+
 * By default only some attributes of nodes in the accessibility tree, or
   events fired (when running DumpAccessibilityEvents) are output.
   This is to keep the tests robust and not prone to failure when unrelated
@@ -51,11 +54,13 @@
   They can appear anywhere but typically they're in an HTML comment block,
   and must be one per line.
 * Filters are platform-specific:
+```
     @WIN-
     @MAC-
     @BLINK-
     @ANDROID-
     @AURALINUX-
+```
 * To dump all attributes while writing or debugging a test, add this filter:
     @WIN-ALLOW:*
   (and similarly for other platforms).
@@ -64,16 +69,18 @@
   ALLOW filter means to include the attribute, and a DENY filter means to
   exclude it. Filters can contain simple wildcards ('*') only, they're not
   regular expressions. Examples:
+```
   - @WIN-ALLOW:name* - this will output the name attribute on Windows
   - @WIN-ALLOW:name='Foo' - this will only output the name attribute if it
       exactly matches 'Foo'.
   - @WIN-DENY:name='X* - this will skip outputting any name that begins with
       the letter X.
+```
 * By default empty attributes are skipped. To output the value an attribute
   even if it's empty, use @WIN-ALLOW-EMPTY:name, for example, and similarly
   for other platforms.
 
-Advanced:
+## Advanced:
 
 Normally the system waits for the document to finish loading before dumping
 the accessibility tree.
@@ -98,7 +105,7 @@
 use /cross-site/HOSTNAME/ in the url, for example:
   <iframe src="cross-site/1.com/accessibility/html/frame.html"></iframe>
 
-Generating expectations and rebaselining:
+## Generating expectations and rebaselining:
 
 If you want to populate the expectation file directly rather than typing it
 or copying-and-pasting it, first make sure the file exists (it can be empty),
@@ -113,7 +120,7 @@
 a great way to rebaseline a bunch of tests after making a change. Please
 manually check the diff, of course!
 
-Adding a new test:
+## Adding a new test:
 
 If you are adding a new test file remember to add a corresponding test case in
 content/browser/accessibility/dump_accessibility_events_browsertest.cc
diff --git a/docs/accessibility.md b/docs/accessibility.md
index b12e9089..eef1344f 100644
--- a/docs/accessibility.md
+++ b/docs/accessibility.md
@@ -1,6 +1,7 @@
 # Accessibility
 
 * [Accessibility Overview](accessibility/overview.md)
+* [Accessibility Tests](accessibility/tests.md)
 
 ## Chrome OS
 
diff --git a/docs/accessibility/chromevox_on_desktop_linux.md b/docs/accessibility/chromevox_on_desktop_linux.md
index fc6850c..3dc1285 100644
--- a/docs/accessibility/chromevox_on_desktop_linux.md
+++ b/docs/accessibility/chromevox_on_desktop_linux.md
@@ -15,7 +15,9 @@
 
 Create a GN configuration with "chromeos" as the target OS, for example:
 
-```> gn args out/ChromeOSRelease```
+```
+gn args out/cros
+```
 
 ...in editor, add this line:
 
@@ -27,82 +29,85 @@
 
 Note: Only ```target_os = "chromeos"``` is required, the others are recommended
 for a good experience but you can configure Chrome however you like otherwise.
-Note that Native Client is required, so do not put enable_nacl = false in
+Note that Native Client is required, so do not put `enable_nacl = false` in
 your file anywhere!
 
 Now build Chrome as usual, e.g.:
 
-```ninja -C out/cros chrome```
+```
+ninja -C out/cros chrome
+```
 
 And run it as usual to see a mostly-complete Chrome OS desktop inside
 of a window:
 
-```out/cros/chrome```
+```
+out/cros/chrome
+```
 
 By default you'll be logged in as the default user. If you want to
 simulate the login manager too, run it like this:
 
-```out/cros/chrome --login-manager```
+```
+out/cros/chrome --login-manager
+```
 
-You can run any of the above under it’s own X session (avoiding any
-window manager key combo conflicts) by doing something like
+You can run any of the above under it’s own X session (avoiding any window
+manager key combo conflicts) by doing something like
 
-```startx out/cros/chrome```
+```
+startx out/cros/chrome
+```
 
 ## Speech
 
-If you want speech, you just need to copy the speech synthesis data
-files to /usr/share like it would be on a Chrome OS device:
+If you want speech, you just need to copy the speech synthesis data files to
+/usr/share like it would be on a Chrome OS device:
 
 ```
-git clone https://chromium.googlesource.com/chromiumos/platform/assets
-sudo cp assets /usr/share/chromeos-assets
+sudo git clone https://chromium.googlesource.com/chromiumos/platform/assets /usr/share/chromeos-assets
 ```
 
-Next, move to that directory and unzip the NaCl executables. You only need
-to do the one for your host architecture:
+Change the permissions:
 
 ```
-cd /usr/share/chromeos-assets/speech_synthesis/patts
-unzip tts_service_x86-64.nexe.zip
+sudo find /usr/share/chromeos-assets -type f -exec chmod 644 {} \;
+sudo find /usr/share/chromeos-assets -type d -exec chmod 755 {} \;
 ```
 
-Finally, fix the permissions:
+Next, unzip the NaCl executables. You only need to do the one for your host
+architecture:
 
 ```
-sudo chmod oug+r -R /usr/share/chromeos-assets
+PATTS_DIR=/usr/share/chromeos-assets/speech_synthesis/patts
+sudo unzip $PATTS_DIR/tts_service_x86-64.nexe.zip -d $PATTS_DIR 
 ```
 
-**Be sure to check permissions of /usr/share/chromeos-assets, some
-  users report they need to chmod or chown too, it really depends
-  on your system.**
+**Be sure to check permissions of /usr/share/chromeos-assets, some users report
+they need to chmod or chown too, it really depends on your system.**
 
-After you do that, just run "chrome" as above
-(e.g. out/cros/chrome) and press Ctrl+Alt+Z, and you should hear it
-speak! If not, check the logs.
+After you do that, just run "chrome" as above (e.g. out/cros/chrome) and press
+Ctrl+Alt+Z, and you should hear it speak! If not, check the logs.
 
 ## Braille
 
-ChromeVox uses extension APIs to deliver braille to Brltty through
-libbrlapi and uses Liblouis to perform translation and
-backtranslation.
+ChromeVox uses extension APIs to deliver braille to Brltty through libbrlapi
+and uses Liblouis to perform translation and backtranslation.
 
-Once built, Chrome and ChromeVox will use your machine’s running
-Brltty daemon to display braille if ChromeVox is running. Simply
-ensure you have a display connected before running Chrome and that
-Brltty is running.
+Once built, Chrome and ChromeVox will use your machine’s running Brltty
+daemon to display braille if ChromeVox is running. Simply ensure you have a
+display connected before running Chrome and that Brltty is running.
 
-Testing against the latest releases of Brltty (e.g. 5.4 at time of
-writing) is encouraged.
+Testing against the latest releases of Brltty (e.g. 5.4 at time of writing) is
+encouraged.
 
 For more general information, see [ChromeVox](chromevox.md)
 
 # Using ChromeVox
 
-ChromeVox keyboard shortcuts use Search. On Linux that's usually your
-Windows key. If some shortcuts don't work, you may need to remove
-Gnome keyboard shortcut bindings, or use "startx", as suggested above,
-or remap it.
+ChromeVox keyboard shortcuts use Search. On Linux that's usually your Windows
+key. If some shortcuts don't work, you may need to remove Gnome keyboard
+shortcut bindings, or use "startx", as suggested above, or remap it.
 
 * Search+Space: Click
 * Search+Left/Right: navigate linearly
diff --git a/docs/accessibility/patts.md b/docs/accessibility/patts.md
index c51b911..7d67ee8 100644
--- a/docs/accessibility/patts.md
+++ b/docs/accessibility/patts.md
@@ -81,3 +81,32 @@
   TEST=Write what you tested here
 repo upload .
 ```
+
+## Ebuild
+
+Note that sometimes you'll have to update the ebuild file that
+takes the patts data files and installs them, unzipping the .nexe
+files in the process.
+
+For example, you'll need to edit the ebuild if you add or remove
+a language code, or if you add or remove a file that needs to be
+installed as part of the extension.
+
+To update the ebuild, edit this file:
+
+```
+/third_party/chromiumos-overlay/chromeos-base/common-assets/common-assets-9999.ebuild
+```
+
+If you need to land changes to both common-assets and chromiumos-assets,
+upload the changes separately and then make them depend on one another
+using this syntax in the changelog:
+
+```
+CQ-DEPEND=CL:12345
+```
+
+Note that you can (and often should) have two changes depend on one another
+so they'll land atomically.
+
+
diff --git a/docs/accessibility/tests.md b/docs/accessibility/tests.md
new file mode 100644
index 0000000..f1a7420
--- /dev/null
+++ b/docs/accessibility/tests.md
@@ -0,0 +1,123 @@
+# Accessibility
+
+Here's a quick overview of all of the locations in the codebase where
+you'll find accessibility tests, and a brief overview of the purpose of
+all of them.
+
+## Layout Tests
+
+This is the primary place where we test accessibility code in Blink. This
+code should have no platform-specific code. Use this to test anything
+where there's any interesting web platform logic, or where you need to be
+able to query things synchronously from the renderer thread to test them.
+
+Don't add tests for trivial features like ARIA attributes that we just
+expose directly to the next layer up. In those cases the Blink tests are
+trivial and it's more valuable to test these features at a higher level
+where we can ensure they actually work.
+
+Note that many of these tests are inherited from WebKit and the coding style
+has evolved a lot since then. Look for more recent tests as a guide if writing
+a new one.
+
+Test files:
+```
+third_party/WebKit/LayoutTests/accessibility
+```
+
+Source code to AccessibilityController and WebAXObjectProxy:
+```
+content/shell/test_runner
+```
+
+To run all accessibility LayoutTests:
+```
+ninja -C out/release blink_tests
+third_party/WebKit/Tools/Scripts/run-webkit-tests --build-directory=out --target=release accessibility/
+```
+
+To run just one test by itself without the script:
+```
+ninja -C out/release blink_tests
+out/release/content_shell --run-layout-test third_party/WebKit/LayoutTests/accessibility/name-calc-inputs.html
+```
+
+## DumpAccessibilityTree tests
+
+This is the best way to write both cross-platform and platform-specific tests
+using only an input HTML file, some magic strings to describe what attributes
+you're interested in, and one or more expectation files to enable checking
+whether the resulting accessibility tree is correct or not. In particular,
+almost no test code is required.
+
+[More documentation on DumpAccessibilityTree](../../content/test/data/accessibility/readme.md)
+
+Test files:
+```
+content/test/data/accessibility
+```
+
+Test runner:
+```
+content/browser/accessibility/dump_accessibility_tree_browsertest.cc
+```
+
+To run all tests:
+```
+ninja -C out/release content_browsertests
+out/release/content_browsertests --gtest_filter="DumpAccessibilityTree*"
+```
+
+## Other content_browsertests
+
+There are many other tests in content/ that relate to accessibility.
+All of these tests work by launching a full multi-process browser shell,
+loading a web page in a renderer, then accessing the resulting accessibility
+tree from the browser process, and running some test from there.
+
+To run all tests:
+```
+ninja -C out/release content_browsertests
+out/release/content_browsertests --gtest_filter="*ccessib*"
+```
+
+## Accessibility unittests
+
+This tests the core accessibility code that's shared by both web and non-web
+accessibility infrastructure.
+
+Code location:
+```
+ui/accessibility
+```
+
+To run all tests:
+```
+ninja -C out/release accessibility_unittests
+out/release/accessibility_unittests
+```
+
+## ChromeVox tests
+
+You must build with ```target_os = "chromeos"``` in your GN args.
+
+To run all tests:
+```
+ninja -C out/release chromevox_tests
+out/release/chromevox_tests --test-launcher-jobs=10
+```
+
+## Other locations of accessibility tests:
+
+Even this isn't a complete list. The tests described above cover more
+than 90% of the accessibility tests, and the remainder are scattered
+throughout the codebase. Here are a few other locations to check:
+
+```
+chrome/android/javatests/src/org/chromium/chrome/browser/accessibility
+chrome/browser/accessibility
+chrome/browser/chromeos/accessibility/
+ui/chromeos
+ui/views/accessibility
+```
+
diff --git a/ios/chrome/browser/payments/payment_request.mm b/ios/chrome/browser/payments/payment_request.mm
index 5ff29f57..68e0830 100644
--- a/ios/chrome/browser/payments/payment_request.mm
+++ b/ios/chrome/browser/payments/payment_request.mm
@@ -82,6 +82,11 @@
   std::vector<autofill::CreditCard*> credit_cards =
       personal_data_manager_->GetCreditCardsToSuggest();
 
+  // TODO(crbug.com/602666): Update the following logic to allow basic card
+  // payment. https://w3c.github.io/webpayments-methods-card/
+  // new PaymentRequest([{supportedMethods: ['basic-card'],
+  //                      data: {supportedNetworks:['visa']}]}], ...);
+
   for (const auto* credit_card : credit_cards) {
     std::string spec_card_type =
         autofill::data_util::GetPaymentRequestData(credit_card->type())
diff --git a/ios/chrome/browser/payments/payment_request_coordinator.h b/ios/chrome/browser/payments/payment_request_coordinator.h
index 5491986c..733be165 100644
--- a/ios/chrome/browser/payments/payment_request_coordinator.h
+++ b/ios/chrome/browser/payments/payment_request_coordinator.h
@@ -90,7 +90,10 @@
 // Displays an error message. Invokes |callback| when the message is dismissed.
 - (void)displayErrorWithCallback:(ProceduralBlock)callback;
 
-// Called when a credit card has been successfully unmasked.
+// Called when a credit card has been successfully unmasked. Note that |card|
+// may be different from what's returned by the selected_credit_card() method of
+// |paymentRequest|, because CVC unmasking process may update the credit card
+// number and expiration date.
 - (void)fullCardRequestDidSucceedWithCard:(const autofill::CreditCard&)card
                                       CVC:(const base::string16&)cvc;
 
diff --git a/ios/chrome/browser/payments/payment_request_coordinator.mm b/ios/chrome/browser/payments/payment_request_coordinator.mm
index e198fd9..6db03c9 100644
--- a/ios/chrome/browser/payments/payment_request_coordinator.mm
+++ b/ios/chrome/browser/payments/payment_request_coordinator.mm
@@ -31,6 +31,11 @@
 #error "This file requires ARC support."
 #endif
 
+namespace {
+using ::payment_request_util::GetBasicCardResponseFromAutofillCreditCard;
+using ::payment_request_util::GetPaymentAddressFromAutofillProfile;
+}  // namespace
+
 // The unmask prompt UI for Payment Request.
 class PRCardUnmaskPromptViewBridge
     : public autofill::CardUnmaskPromptViewBridge {
@@ -185,23 +190,58 @@
 - (void)fullCardRequestDidSucceedWithCard:(const autofill::CreditCard&)card
                                       CVC:(const base::string16&)cvc {
   web::PaymentResponse paymentResponse;
-  paymentResponse.details.cardholder_name =
-      card.GetRawInfo(autofill::CREDIT_CARD_NAME_FULL);
-  paymentResponse.details.card_number =
-      card.GetRawInfo(autofill::CREDIT_CARD_NUMBER);
-  paymentResponse.details.expiry_month =
-      card.GetRawInfo(autofill::CREDIT_CARD_EXP_MONTH);
-  paymentResponse.details.expiry_year =
-      card.GetRawInfo(autofill::CREDIT_CARD_EXP_4_DIGIT_YEAR);
-  paymentResponse.details.card_security_code = cvc;
-  if (!card.billing_address_id().empty()) {
-    autofill::AutofillProfile* address =
-        autofill::PersonalDataManager::GetProfileFromProfilesByGUID(
-            card.billing_address_id(), _paymentRequest->billing_profiles());
-    if (address) {
-      paymentResponse.details.billing_address =
-          payment_request_util::GetPaymentAddressFromAutofillProfile(address);
-    }
+
+  paymentResponse.method_name =
+      base::ASCIIToUTF16(autofill::data_util::GetPaymentRequestData(card.type())
+                             .basic_card_payment_type);
+
+  paymentResponse.details =
+      GetBasicCardResponseFromAutofillCreditCard(*_paymentRequest, card, cvc);
+
+  if (_paymentRequest->payment_options().request_shipping) {
+    autofill::AutofillProfile* shippingAddress =
+        _paymentRequest->selected_shipping_profile();
+    // TODO(crbug.com/602666): User should get here only if they have selected
+    // a shipping address.
+    DCHECK(shippingAddress);
+    paymentResponse.shipping_address =
+        GetPaymentAddressFromAutofillProfile(*shippingAddress);
+
+    web::PaymentShippingOption* shippingOption =
+        _paymentRequest->selected_shipping_option();
+    DCHECK(shippingOption);
+    paymentResponse.shipping_option = shippingOption->id;
+  }
+
+  if (_paymentRequest->payment_options().request_payer_name) {
+    autofill::AutofillProfile* contactInfo =
+        _paymentRequest->selected_contact_profile();
+    // TODO(crbug.com/602666): User should get here only if they have selected
+    // a contact info.
+    DCHECK(contactInfo);
+    paymentResponse.payer_name =
+        contactInfo->GetInfo(autofill::AutofillType(autofill::NAME_FULL),
+                             GetApplicationContext()->GetApplicationLocale());
+  }
+
+  if (_paymentRequest->payment_options().request_payer_email) {
+    autofill::AutofillProfile* contactInfo =
+        _paymentRequest->selected_contact_profile();
+    // TODO(crbug.com/602666): User should get here only if they have selected
+    // a contact info.
+    DCHECK(contactInfo);
+    paymentResponse.payer_email =
+        contactInfo->GetRawInfo(autofill::EMAIL_ADDRESS);
+  }
+
+  if (_paymentRequest->payment_options().request_payer_phone) {
+    autofill::AutofillProfile* contactInfo =
+        _paymentRequest->selected_contact_profile();
+    // TODO(crbug.com/602666): User should get here only if they have selected
+    // a contact info.
+    DCHECK(contactInfo);
+    paymentResponse.payer_phone =
+        contactInfo->GetRawInfo(autofill::PHONE_HOME_WHOLE_NUMBER);
   }
 
   _viewController.view.userInteractionEnabled = NO;
@@ -355,10 +395,9 @@
                    didSelectShippingAddress:
                        (autofill::AutofillProfile*)shippingAddress {
   _pendingShippingAddress = shippingAddress;
-
+  DCHECK(shippingAddress);
   web::PaymentAddress address =
-      payment_request_util::GetPaymentAddressFromAutofillProfile(
-          shippingAddress);
+      GetPaymentAddressFromAutofillProfile(*shippingAddress);
   [_delegate paymentRequestCoordinator:self didSelectShippingAddress:address];
 }
 
diff --git a/ios/chrome/browser/payments/payment_request_coordinator_unittest.mm b/ios/chrome/browser/payments/payment_request_coordinator_unittest.mm
index 37e912ed..34d95131 100644
--- a/ios/chrome/browser/payments/payment_request_coordinator_unittest.mm
+++ b/ios/chrome/browser/payments/payment_request_coordinator_unittest.mm
@@ -178,12 +178,11 @@
          EXPECT_EQ(base::ASCIIToUTF16("John Mitchell Doe"),
                    shippingAddress.recipient);
          EXPECT_EQ(base::ASCIIToUTF16("Fox"), shippingAddress.organization);
-         ASSERT_EQ(3U, shippingAddress.address_line.size());
+         ASSERT_EQ(2U, shippingAddress.address_line.size());
          EXPECT_EQ(base::ASCIIToUTF16("123 Zoo St"),
                    shippingAddress.address_line[0]);
          EXPECT_EQ(base::ASCIIToUTF16("unit 5"),
                    shippingAddress.address_line[1]);
-         EXPECT_EQ(base::ASCIIToUTF16(""), shippingAddress.address_line[2]);
          EXPECT_EQ(base::ASCIIToUTF16("12345678910"), shippingAddress.phone);
          EXPECT_EQ(base::ASCIIToUTF16("Hollywood"), shippingAddress.city);
          EXPECT_EQ(base::ASCIIToUTF16("CA"), shippingAddress.region);
diff --git a/ios/chrome/browser/payments/payment_request_util.h b/ios/chrome/browser/payments/payment_request_util.h
index 7491016..f247e9a 100644
--- a/ios/chrome/browser/payments/payment_request_util.h
+++ b/ios/chrome/browser/payments/payment_request_util.h
@@ -7,63 +7,81 @@
 
 #import <Foundation/Foundation.h>
 
-#include "ios/web/public/payments/payment_request.h"
+#include "base/strings/string16.h"
 
 namespace autofill {
 class AutofillProfile;
+class CreditCard;
 }  // namespace autofill
 
+namespace web {
+class BasicCardResponse;
+class PaymentAddress;
+}  // namespace web
+
 class PaymentRequest;
 
 namespace payment_request_util {
 
 // Helper function to get the name label from an autofill profile. Returns nil
 // if the name field is empty.
-NSString* GetNameLabelFromAutofillProfile(autofill::AutofillProfile* profile);
+NSString* GetNameLabelFromAutofillProfile(
+    const autofill::AutofillProfile& profile);
 
 // Helper function to get the address label from an autofill profile. Returns
 // nil if the address field is empty.
 NSString* GetAddressLabelFromAutofillProfile(
-    autofill::AutofillProfile* profile);
+    const autofill::AutofillProfile& profile);
 
 // Helper function to get the phone number label from an autofill profile.
 // Returns nil if the phone number field is empty.
 NSString* GetPhoneNumberLabelFromAutofillProfile(
-    autofill::AutofillProfile* profile);
+    const autofill::AutofillProfile& profile);
 
 // Helper function to get the email label from an autofill profile. Returns nil
 // if the email field is empty.
-NSString* GetEmailLabelFromAutofillProfile(autofill::AutofillProfile* profile);
+NSString* GetEmailLabelFromAutofillProfile(
+    const autofill::AutofillProfile& profile);
 
 // Helper function to get an instance of web::PaymentAddress from an autofill
 // profile.
 web::PaymentAddress GetPaymentAddressFromAutofillProfile(
-    autofill::AutofillProfile* profile);
+    const autofill::AutofillProfile& profile);
+
+// Helper function to get an instance of web::BasicCardResponse from an autofill
+// credit card.
+web::BasicCardResponse GetBasicCardResponseFromAutofillCreditCard(
+    const PaymentRequest& payment_request,
+    const autofill::CreditCard& card,
+    const base::string16& cvc);
 
 // Returns the title for the shipping section of the payment summary view given
 // the shipping type specified in |payment_request|.
-NSString* GetShippingSectionTitle(PaymentRequest* payment_request);
+NSString* GetShippingSectionTitle(const PaymentRequest& payment_request);
 
 // Returns the title for the shipping address selection view given the shipping
 // type specified in |payment_request|.
-NSString* GetShippingAddressSelectorTitle(PaymentRequest* payment_request);
+NSString* GetShippingAddressSelectorTitle(
+    const PaymentRequest& payment_request);
 
 // Returns the informational message to be displayed in the shipping address
 // selection view given the shipping type specified in |payment_request|.
-NSString* GetShippingAddressSelectorInfoMessage(PaymentRequest* paymentRequest);
+NSString* GetShippingAddressSelectorInfoMessage(
+    const PaymentRequest& payment_request);
 
 // Returns the error message to be displayed in the shipping address selection
 // view given the shipping type specified in |payment_request|.
 NSString* GetShippingAddressSelectorErrorMessage(
-    PaymentRequest* paymentRequest);
+    const PaymentRequest& payment_request);
 
 // Returns the title for the shipping option selection view given the shipping
 // type specified in |payment_request|.
-NSString* GetShippingOptionSelectorTitle(PaymentRequest* payment_request);
+NSString* GetShippingOptionSelectorTitle(const PaymentRequest& payment_request);
 
 // Returns the error message to be displayed in the shipping option selection
 // view given the shipping type specified in |payment_request|.
-NSString* GetShippingOptionSelectorErrorMessage(PaymentRequest* paymentRequest);
+NSString* GetShippingOptionSelectorErrorMessage(
+    const PaymentRequest& payment_request);
 
 }  // namespace payment_request_util
 
diff --git a/ios/chrome/browser/payments/payment_request_util.mm b/ios/chrome/browser/payments/payment_request_util.mm
index 9e8b0be..ceb9ef7c 100644
--- a/ios/chrome/browser/payments/payment_request_util.mm
+++ b/ios/chrome/browser/payments/payment_request_util.mm
@@ -5,13 +5,17 @@
 #import "ios/chrome/browser/payments/payment_request_util.h"
 
 #include "base/strings/string16.h"
+#include "base/strings/string_split.h"
 #include "base/strings/sys_string_conversions.h"
 #include "base/strings/utf_string_conversions.h"
 #include "components/autofill/core/browser/autofill_profile.h"
+#include "components/autofill/core/browser/credit_card.h"
 #include "components/autofill/core/browser/field_types.h"
+#include "components/autofill/core/browser/personal_data_manager.h"
 #include "components/strings/grit/components_strings.h"
 #include "ios/chrome/browser/application_context.h"
 #include "ios/chrome/browser/payments/payment_request.h"
+#include "ios/web/public/payments/payment_request.h"
 #include "ui/base/l10n/l10n_util.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
@@ -20,14 +24,15 @@
 
 namespace payment_request_util {
 
-NSString* GetNameLabelFromAutofillProfile(autofill::AutofillProfile* profile) {
+NSString* GetNameLabelFromAutofillProfile(
+    const autofill::AutofillProfile& profile) {
   return base::SysUTF16ToNSString(
-      profile->GetInfo(autofill::AutofillType(autofill::NAME_FULL),
-                       GetApplicationContext()->GetApplicationLocale()));
+      profile.GetInfo(autofill::AutofillType(autofill::NAME_FULL),
+                      GetApplicationContext()->GetApplicationLocale()));
 }
 
 NSString* GetAddressLabelFromAutofillProfile(
-    autofill::AutofillProfile* profile) {
+    const autofill::AutofillProfile& profile) {
   // Name and country are not included in the shipping address label.
   std::vector<autofill::ServerFieldType> label_fields;
   label_fields.push_back(autofill::COMPANY_NAME);
@@ -39,52 +44,78 @@
   label_fields.push_back(autofill::ADDRESS_HOME_ZIP);
   label_fields.push_back(autofill::ADDRESS_HOME_SORTING_CODE);
 
-  base::string16 label = profile->ConstructInferredLabel(
+  base::string16 label = profile.ConstructInferredLabel(
       label_fields, label_fields.size(),
       GetApplicationContext()->GetApplicationLocale());
   return !label.empty() ? base::SysUTF16ToNSString(label) : nil;
 }
 
 NSString* GetPhoneNumberLabelFromAutofillProfile(
-    autofill::AutofillProfile* profile) {
-  base::string16 label = profile->GetRawInfo(autofill::PHONE_HOME_WHOLE_NUMBER);
+    const autofill::AutofillProfile& profile) {
+  base::string16 label = profile.GetRawInfo(autofill::PHONE_HOME_WHOLE_NUMBER);
   return !label.empty() ? base::SysUTF16ToNSString(label) : nil;
 }
 
-NSString* GetEmailLabelFromAutofillProfile(autofill::AutofillProfile* profile) {
-  base::string16 label = profile->GetRawInfo(autofill::EMAIL_ADDRESS);
+NSString* GetEmailLabelFromAutofillProfile(
+    const autofill::AutofillProfile& profile) {
+  base::string16 label = profile.GetRawInfo(autofill::EMAIL_ADDRESS);
   return !label.empty() ? base::SysUTF16ToNSString(label) : nil;
 }
 
 web::PaymentAddress GetPaymentAddressFromAutofillProfile(
-    autofill::AutofillProfile* profile) {
+    const autofill::AutofillProfile& profile) {
   web::PaymentAddress address;
-  address.country = profile->GetRawInfo(autofill::ADDRESS_HOME_COUNTRY);
-  address.address_line.push_back(
-      profile->GetRawInfo(autofill::ADDRESS_HOME_LINE1));
-  address.address_line.push_back(
-      profile->GetRawInfo(autofill::ADDRESS_HOME_LINE2));
-  address.address_line.push_back(
-      profile->GetRawInfo(autofill::ADDRESS_HOME_LINE3));
-  address.region = profile->GetRawInfo(autofill::ADDRESS_HOME_STATE);
-  address.city = profile->GetRawInfo(autofill::ADDRESS_HOME_CITY);
+  address.country = profile.GetRawInfo(autofill::ADDRESS_HOME_COUNTRY);
+  address.address_line = base::SplitString(
+      profile.GetInfo(
+          autofill::AutofillType(autofill::ADDRESS_HOME_STREET_ADDRESS),
+          GetApplicationContext()->GetApplicationLocale()),
+      base::ASCIIToUTF16("\n"), base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
+  address.region = profile.GetRawInfo(autofill::ADDRESS_HOME_STATE);
+  address.city = profile.GetRawInfo(autofill::ADDRESS_HOME_CITY);
   address.dependent_locality =
-      profile->GetRawInfo(autofill::ADDRESS_HOME_DEPENDENT_LOCALITY);
-  address.postal_code = profile->GetRawInfo(autofill::ADDRESS_HOME_ZIP);
+      profile.GetRawInfo(autofill::ADDRESS_HOME_DEPENDENT_LOCALITY);
+  address.postal_code = profile.GetRawInfo(autofill::ADDRESS_HOME_ZIP);
   address.sorting_code =
-      profile->GetRawInfo(autofill::ADDRESS_HOME_SORTING_CODE);
-  address.language_code = base::UTF8ToUTF16(profile->language_code());
-  address.organization = profile->GetRawInfo(autofill::COMPANY_NAME);
+      profile.GetRawInfo(autofill::ADDRESS_HOME_SORTING_CODE);
+  address.language_code = base::UTF8ToUTF16(profile.language_code());
+  address.organization = profile.GetRawInfo(autofill::COMPANY_NAME);
   address.recipient =
-      profile->GetInfo(autofill::AutofillType(autofill::NAME_FULL),
-                       GetApplicationContext()->GetApplicationLocale());
-  address.phone = profile->GetRawInfo(autofill::PHONE_HOME_WHOLE_NUMBER);
+      profile.GetInfo(autofill::AutofillType(autofill::NAME_FULL),
+                      GetApplicationContext()->GetApplicationLocale());
+  address.phone = profile.GetRawInfo(autofill::PHONE_HOME_WHOLE_NUMBER);
 
   return address;
 }
 
-NSString* GetShippingSectionTitle(PaymentRequest* payment_request) {
-  switch (payment_request->payment_options().shipping_type) {
+web::BasicCardResponse GetBasicCardResponseFromAutofillCreditCard(
+    const PaymentRequest& payment_request,
+    const autofill::CreditCard& card,
+    const base::string16& cvc) {
+  web::BasicCardResponse response;
+  response.cardholder_name = card.GetRawInfo(autofill::CREDIT_CARD_NAME_FULL);
+  response.card_number = card.GetRawInfo(autofill::CREDIT_CARD_NUMBER);
+  response.expiry_month = card.GetRawInfo(autofill::CREDIT_CARD_EXP_MONTH);
+  response.expiry_year =
+      card.GetRawInfo(autofill::CREDIT_CARD_EXP_4_DIGIT_YEAR);
+  response.card_security_code = cvc;
+
+  // TODO(crbug.com/602666): Ensure we reach here only if the card has a billing
+  // address. Then add DCHECK(!card->billing_address_id().empty()).
+  if (!card.billing_address_id().empty()) {
+    const autofill::AutofillProfile* billing_address =
+        autofill::PersonalDataManager::GetProfileFromProfilesByGUID(
+            card.billing_address_id(), payment_request.billing_profiles());
+    DCHECK(billing_address);
+    response.billing_address =
+        GetPaymentAddressFromAutofillProfile(*billing_address);
+  }
+
+  return response;
+}
+
+NSString* GetShippingSectionTitle(const PaymentRequest& payment_request) {
+  switch (payment_request.payment_options().shipping_type) {
     case web::PaymentShippingType::SHIPPING:
       return l10n_util::GetNSString(IDS_PAYMENTS_SHIPPING_SUMMARY_LABEL);
     case web::PaymentShippingType::DELIVERY:
@@ -97,8 +128,9 @@
   }
 }
 
-NSString* GetShippingAddressSelectorTitle(PaymentRequest* payment_request) {
-  switch (payment_request->payment_options().shipping_type) {
+NSString* GetShippingAddressSelectorTitle(
+    const PaymentRequest& payment_request) {
+  switch (payment_request.payment_options().shipping_type) {
     case web::PaymentShippingType::SHIPPING:
       return l10n_util::GetNSString(IDS_PAYMENTS_SHIPPING_ADDRESS_LABEL);
     case web::PaymentShippingType::DELIVERY:
@@ -112,8 +144,8 @@
 }
 
 NSString* GetShippingAddressSelectorInfoMessage(
-    PaymentRequest* payment_request) {
-  switch (payment_request->payment_options().shipping_type) {
+    const PaymentRequest& payment_request) {
+  switch (payment_request.payment_options().shipping_type) {
     case web::PaymentShippingType::SHIPPING:
       return l10n_util::GetNSString(
           IDS_PAYMENTS_SELECT_SHIPPING_ADDRESS_FOR_SHIPPING_METHODS);
@@ -130,11 +162,11 @@
 }
 
 NSString* GetShippingAddressSelectorErrorMessage(
-    PaymentRequest* payment_request) {
-  if (!payment_request->payment_details().error.empty())
-    return base::SysUTF16ToNSString(payment_request->payment_details().error);
+    const PaymentRequest& payment_request) {
+  if (!payment_request.payment_details().error.empty())
+    return base::SysUTF16ToNSString(payment_request.payment_details().error);
 
-  switch (payment_request->payment_options().shipping_type) {
+  switch (payment_request.payment_options().shipping_type) {
     case web::PaymentShippingType::SHIPPING:
       return l10n_util::GetNSString(IDS_PAYMENTS_UNSUPPORTED_SHIPPING_ADDRESS);
     case web::PaymentShippingType::DELIVERY:
@@ -147,8 +179,9 @@
   }
 }
 
-NSString* GetShippingOptionSelectorTitle(PaymentRequest* payment_request) {
-  switch (payment_request->payment_options().shipping_type) {
+NSString* GetShippingOptionSelectorTitle(
+    const PaymentRequest& payment_request) {
+  switch (payment_request.payment_options().shipping_type) {
     case web::PaymentShippingType::SHIPPING:
       return l10n_util::GetNSString(IDS_PAYMENTS_SHIPPING_OPTION_LABEL);
     case web::PaymentShippingType::DELIVERY:
@@ -162,11 +195,11 @@
 }
 
 NSString* GetShippingOptionSelectorErrorMessage(
-    PaymentRequest* payment_request) {
-  if (!payment_request->payment_details().error.empty())
-    return base::SysUTF16ToNSString(payment_request->payment_details().error);
+    const PaymentRequest& payment_request) {
+  if (!payment_request.payment_details().error.empty())
+    return base::SysUTF16ToNSString(payment_request.payment_details().error);
 
-  switch (payment_request->payment_options().shipping_type) {
+  switch (payment_request.payment_options().shipping_type) {
     case web::PaymentShippingType::SHIPPING:
       return l10n_util::GetNSString(IDS_PAYMENTS_UNSUPPORTED_SHIPPING_OPTION);
     case web::PaymentShippingType::DELIVERY:
diff --git a/ios/chrome/browser/payments/payment_request_view_controller.mm b/ios/chrome/browser/payments/payment_request_view_controller.mm
index 4a86a5db..f643121 100644
--- a/ios/chrome/browser/payments/payment_request_view_controller.mm
+++ b/ios/chrome/browser/payments/payment_request_view_controller.mm
@@ -47,6 +47,10 @@
 using ::payment_request_util::GetAddressLabelFromAutofillProfile;
 using ::payment_request_util::GetPhoneNumberLabelFromAutofillProfile;
 using ::payment_request_util::GetEmailLabelFromAutofillProfile;
+using ::payment_request_util::GetShippingSectionTitle;
+using ::payment_request_util::GetShippingAddressSelectorTitle;
+using ::payment_request_util::GetShippingOptionSelectorTitle;
+
 }  // namespace
 
 NSString* const kPaymentRequestCollectionViewID =
@@ -224,8 +228,7 @@
 
   CollectionViewTextItem* shippingTitle =
       [[CollectionViewTextItem alloc] initWithType:ItemTypeShippingTitle];
-  shippingTitle.text =
-      payment_request_util::GetShippingSectionTitle(_paymentRequest);
+  shippingTitle.text = GetShippingSectionTitle(*_paymentRequest);
   [model setHeader:shippingTitle
       forSectionWithIdentifier:SectionIdentifierShipping];
 
@@ -245,8 +248,7 @@
     CollectionViewDetailItem* addAddressItem = [[CollectionViewDetailItem alloc]
         initWithType:ItemTypeAddShippingAddress];
     shippingAddressItem = addAddressItem;
-    addAddressItem.text =
-        payment_request_util::GetShippingAddressSelectorTitle(_paymentRequest);
+    addAddressItem.text = GetShippingAddressSelectorTitle(*_paymentRequest);
     addAddressItem.detailText = [l10n_util::GetNSString(IDS_ADD)
         uppercaseStringWithLocale:[NSLocale currentLocale]];
     addAddressItem.accessibilityTraits |= UIAccessibilityTraitButton;
@@ -272,7 +274,7 @@
             initWithType:ItemTypeSelectShippingOption];
     shippingOptionItem = selectShippingOptionItem;
     selectShippingOptionItem.text =
-        payment_request_util::GetShippingOptionSelectorTitle(_paymentRequest);
+        GetShippingOptionSelectorTitle(*_paymentRequest);
     selectShippingOptionItem.accessoryType =
         MDCCollectionViewCellAccessoryDisclosureIndicator;
     selectShippingOptionItem.accessibilityTraits |= UIAccessibilityTraitButton;
@@ -416,9 +418,10 @@
 
 - (void)fillShippingAddressItem:(AutofillProfileItem*)item
             withAutofillProfile:(autofill::AutofillProfile*)profile {
-  item.name = GetNameLabelFromAutofillProfile(profile);
-  item.address = GetAddressLabelFromAutofillProfile(profile);
-  item.phoneNumber = GetPhoneNumberLabelFromAutofillProfile(profile);
+  DCHECK(profile);
+  item.name = GetNameLabelFromAutofillProfile(*profile);
+  item.address = GetAddressLabelFromAutofillProfile(*profile);
+  item.phoneNumber = GetPhoneNumberLabelFromAutofillProfile(*profile);
 }
 
 - (void)fillShippingOptionItem:(CollectionViewTextItem*)item
@@ -443,9 +446,10 @@
 
 - (void)fillContactInfoItem:(AutofillProfileItem*)item
         withAutofillProfile:(autofill::AutofillProfile*)profile {
-  item.name = GetNameLabelFromAutofillProfile(profile);
-  item.phoneNumber = GetPhoneNumberLabelFromAutofillProfile(profile);
-  item.email = GetEmailLabelFromAutofillProfile(profile);
+  DCHECK(profile);
+  item.name = GetNameLabelFromAutofillProfile(*profile);
+  item.phoneNumber = GetPhoneNumberLabelFromAutofillProfile(*profile);
+  item.email = GetEmailLabelFromAutofillProfile(*profile);
 }
 
 #pragma mark UICollectionViewDataSource
diff --git a/ios/chrome/browser/payments/resources/payment_request_manager.js b/ios/chrome/browser/payments/resources/payment_request_manager.js
index 88ceba38..2cfbfd774 100644
--- a/ios/chrome/browser/payments/resources/payment_request_manager.js
+++ b/ios/chrome/browser/payments/resources/payment_request_manager.js
@@ -142,8 +142,14 @@
  * A simple object representation of |window.PaymentResponse| meant for
  * communication to the app side.
  * @typedef {{
+ *   paymentRequestId: string,
  *   methodName: string,
- *   details: Object
+ *   details: Object,
+ *   shippingAddress: (!window.PaymentAddress|undefined),
+ *   shippingOption: (string|undefined),
+ *   payerName: (string|undefined),
+ *   payerEmail: (string|undefined),
+ *   payerPhone: (string|undefined)
  * }}
  */
 var SerializedPaymentResponse;
@@ -196,8 +202,21 @@
    */
   __gCrWeb['paymentRequestManager'].parsePaymentResponseData = function(
       paymentResponseData) {
-    return new window.PaymentResponse(
+    var response = new window.PaymentResponse(
+        paymentResponseData['paymentRequestID'],
         paymentResponseData['methodName'], paymentResponseData['details']);
+    if (paymentResponseData['shippingAddress'])
+      response.shippingAddress = paymentResponseData['shippingAddress'];
+    if (paymentResponseData['shippingOption'])
+      response.shippingOption = paymentResponseData['shippingOption'];
+    if (paymentResponseData['payerName'])
+      response.payerName = paymentResponseData['payerName'];
+    if (paymentResponseData['payerEmail'])
+      response.payerEmail = paymentResponseData['payerEmail'];
+    if (paymentResponseData['payerPhone'])
+      response.payerPhone = paymentResponseData['payerPhone'];
+
+    return response;
   };
 
   /**
@@ -645,7 +664,6 @@
  */
 window.PaymentItem;
 
-// TODO(crbug.com/602666): Convert this to a class with readonly properties.
 /**
  * @typedef {{
  *   country: string,
@@ -677,24 +695,73 @@
  * A response to a request to make a payment. Represents the choices made by the
  * user and provided to the web page through the resolve function of the Promise
  * returned by PaymentRequest.show().
- * @param {string} methodName The payment method identifier for the payment
- *     method that the user selected to fulfil the transaction.
- * @param {Object} details An object that provides a payment method specific
- *     message used by the merchant to process the transaction and determine
- *     successful fund transfer.
+ * https://w3c.github.io/browser-payment-api/#paymentresponse-interface
+ * @param {string} methodName
+ * @param {Object} details
  * @constructor
  * @private
  */
-window.PaymentResponse = function(methodName, details) {
+window.PaymentResponse = function(paymentRequestID, methodName, details) {
   /**
+   * The same paymentRequestID present in the original window.PaymentRequest.
+   * @type {string}
+   */
+  this.paymentRequestID = paymentRequestID;
+
+  /**
+   * The payment method identifier for the payment method that the user selected
+   * to fulfil the transaction.
    * @type {string}
    */
   this.methodName = methodName;
 
   /**
+   * An object that provides a payment method specific message used by the
+   * merchant to process the transaction and determine successful fund transfer.
+   * the payment request.
    * @type {Object}
    */
   this.details = details;
+
+  /**
+   * If the requestShipping flag was set to true in the window.PaymentOptions
+   * passed to the window.PaymentRequest constructor, this will be the full and
+   * final shipping address chosen by the user.
+   * @type {?window.PaymentAddress}
+   */
+   this.shippingAddress = null;
+
+  /**
+   * If the requestShipping flag was set to true in the window.PaymentOptions
+   * passed to the window.PaymentRequest constructor, this will be the id
+   * attribute of the selected shipping option.
+   * @type {?string}
+   */
+   this.shippingOption = null;
+
+  /**
+   * If the requestPayerName flag was set to true in the window.PaymentOptions
+   * passed to the window.PaymentRequest constructor, this will be the name
+   * provided by the user.
+   * @type {?string}
+   */
+   this.payerName = null;
+
+  /**
+   * If the requestPayerEmail flag was set to true in the window.PaymentOptions
+   * passed to the window.PaymentRequest constructor, this will be the email
+   * address chosen by the user.
+   * @type {?string}
+   */
+   this.payerEmail = null;
+
+  /**
+   * If the requestPayerPhone flag was set to true in the window.PaymentOptions
+   * passed to the window.PaymentRequest constructor, this will be the phone
+   * number chosen by the user.
+   * @type {?string}
+   */
+   this.payerPhone = null;
 };
 
 /**
diff --git a/ios/chrome/browser/payments/shipping_address_selection_coordinator.mm b/ios/chrome/browser/payments/shipping_address_selection_coordinator.mm
index c1e983e7b..91ec224 100644
--- a/ios/chrome/browser/payments/shipping_address_selection_coordinator.mm
+++ b/ios/chrome/browser/payments/shipping_address_selection_coordinator.mm
@@ -13,6 +13,10 @@
 #error "This file requires ARC support."
 #endif
 
+namespace {
+using ::payment_request_util::GetShippingAddressSelectorErrorMessage;
+}  // namespace
+
 @interface ShippingAddressSelectionCoordinator ()
 
 @property(nonatomic, strong)
@@ -56,9 +60,9 @@
   _viewController.view.userInteractionEnabled = YES;
 
   [_viewController setPending:NO];
-  [_viewController setErrorMessage:payment_request_util::
-                                       GetShippingAddressSelectorErrorMessage(
-                                           _paymentRequest)];
+  DCHECK(_paymentRequest);
+  [_viewController
+      setErrorMessage:GetShippingAddressSelectorErrorMessage(*_paymentRequest)];
   [_viewController loadModel];
   [[_viewController collectionView] reloadData];
 }
diff --git a/ios/chrome/browser/payments/shipping_address_selection_view_controller.mm b/ios/chrome/browser/payments/shipping_address_selection_view_controller.mm
index 30f0a0a..745b193b 100644
--- a/ios/chrome/browser/payments/shipping_address_selection_view_controller.mm
+++ b/ios/chrome/browser/payments/shipping_address_selection_view_controller.mm
@@ -34,13 +34,12 @@
 using ::payment_request_util::GetNameLabelFromAutofillProfile;
 using ::payment_request_util::GetAddressLabelFromAutofillProfile;
 using ::payment_request_util::GetPhoneNumberLabelFromAutofillProfile;
-}  // namespace
+using ::payment_request_util::GetShippingAddressSelectorTitle;
+using ::payment_request_util::GetShippingAddressSelectorInfoMessage;
 
 NSString* const kShippingAddressSelectionCollectionViewID =
     @"kShippingAddressSelectionCollectionViewID";
 
-namespace {
-
 const CGFloat kSeparatorEdgeInset = 14;
 
 typedef NS_ENUM(NSInteger, SectionIdentifier) {
@@ -78,8 +77,7 @@
 - (instancetype)initWithPaymentRequest:(PaymentRequest*)paymentRequest {
   DCHECK(paymentRequest);
   if ((self = [super initWithStyle:CollectionViewControllerStyleAppBar])) {
-    self.title =
-        payment_request_util::GetShippingAddressSelectorTitle(paymentRequest);
+    self.title = GetShippingAddressSelectorTitle(*paymentRequest);
 
     // Set up leading (return) button.
     UIBarButtonItem* returnButton =
@@ -121,20 +119,19 @@
     messageItem.text = _errorMessage;
     messageItem.image = NativeImage(IDR_IOS_PAYMENTS_WARNING);
   } else {
-    messageItem.text =
-        payment_request_util::GetShippingAddressSelectorInfoMessage(
-            _paymentRequest);
+    messageItem.text = GetShippingAddressSelectorInfoMessage(*_paymentRequest);
   }
   [model addItem:messageItem
       toSectionWithIdentifier:SectionIdentifierShippingAddress];
 
   for (auto* shippingAddress : _paymentRequest->shipping_profiles()) {
+    DCHECK(shippingAddress);
     AutofillProfileItem* item =
         [[AutofillProfileItem alloc] initWithType:ItemTypeShippingAddress];
     item.accessibilityTraits |= UIAccessibilityTraitButton;
-    item.name = GetNameLabelFromAutofillProfile(shippingAddress);
-    item.address = GetAddressLabelFromAutofillProfile(shippingAddress);
-    item.phoneNumber = GetPhoneNumberLabelFromAutofillProfile(shippingAddress);
+    item.name = GetNameLabelFromAutofillProfile(*shippingAddress);
+    item.address = GetAddressLabelFromAutofillProfile(*shippingAddress);
+    item.phoneNumber = GetPhoneNumberLabelFromAutofillProfile(*shippingAddress);
     if (_paymentRequest->selected_shipping_profile() == shippingAddress) {
       item.accessoryType = MDCCollectionViewCellAccessoryCheckmark;
       _selectedItem = item;
diff --git a/ios/chrome/browser/payments/shipping_option_selection_coordinator.mm b/ios/chrome/browser/payments/shipping_option_selection_coordinator.mm
index ba8691f3..35b0f530 100644
--- a/ios/chrome/browser/payments/shipping_option_selection_coordinator.mm
+++ b/ios/chrome/browser/payments/shipping_option_selection_coordinator.mm
@@ -11,6 +11,10 @@
 #error "This file requires ARC support."
 #endif
 
+namespace {
+using ::payment_request_util::GetShippingOptionSelectorErrorMessage;
+}  // namespace
+
 @interface ShippingOptionSelectionCoordinator ()
 
 @property(nonatomic, strong)
@@ -54,9 +58,9 @@
   _viewController.view.userInteractionEnabled = YES;
 
   [_viewController setPending:NO];
-  [_viewController setErrorMessage:payment_request_util::
-                                       GetShippingOptionSelectorErrorMessage(
-                                           _paymentRequest)];
+  DCHECK(_paymentRequest);
+  [_viewController
+      setErrorMessage:GetShippingOptionSelectorErrorMessage(*_paymentRequest)];
   [_viewController loadModel];
   [[_viewController collectionView] reloadData];
 }
diff --git a/ios/chrome/browser/payments/shipping_option_selection_view_controller.mm b/ios/chrome/browser/payments/shipping_option_selection_view_controller.mm
index 3cc381dd..7ec0c53 100644
--- a/ios/chrome/browser/payments/shipping_option_selection_view_controller.mm
+++ b/ios/chrome/browser/payments/shipping_option_selection_view_controller.mm
@@ -30,11 +30,12 @@
 #error "This file requires ARC support."
 #endif
 
+namespace {
+using ::payment_request_util::GetShippingOptionSelectorTitle;
+
 NSString* const kShippingOptionSelectionCollectionViewID =
     @"kShippingOptionSelectionCollectionViewID";
 
-namespace {
-
 const CGFloat kSeparatorEdgeInset = 14;
 
 typedef NS_ENUM(NSInteger, SectionIdentifier) {
@@ -71,8 +72,7 @@
 - (instancetype)initWithPaymentRequest:(PaymentRequest*)paymentRequest {
   DCHECK(paymentRequest);
   if ((self = [super initWithStyle:CollectionViewControllerStyleAppBar])) {
-    self.title =
-        payment_request_util::GetShippingOptionSelectorTitle(paymentRequest);
+    self.title = GetShippingOptionSelectorTitle(*paymentRequest);
 
     // Set up leading (return) button.
     UIBarButtonItem* returnButton =
@@ -117,7 +117,7 @@
         toSectionWithIdentifier:SectionIdentifierShippingOption];
   }
 
-  for (const auto& shippingOption : _paymentRequest->shipping_options()) {
+  for (const auto* shippingOption : _paymentRequest->shipping_options()) {
     CollectionViewTextItem* item =
         [[CollectionViewTextItem alloc] initWithType:ItemTypeShippingOption];
     item.text = base::SysUTF16ToNSString(shippingOption->label);
diff --git a/ios/chrome/browser/tabs/tab_model_unittest.mm b/ios/chrome/browser/tabs/tab_model_unittest.mm
index 2f4bb27..6ec04bc6 100644
--- a/ios/chrome/browser/tabs/tab_model_unittest.mm
+++ b/ios/chrome/browser/tabs/tab_model_unittest.mm
@@ -24,6 +24,7 @@
 #include "ios/chrome/test/ios_chrome_scoped_testing_chrome_browser_state_manager.h"
 #import "ios/web/navigation/crw_session_controller.h"
 #import "ios/web/navigation/navigation_manager_impl.h"
+#import "ios/web/public/crw_session_storage.h"
 #import "ios/web/public/navigation_manager.h"
 #include "ios/web/public/referrer.h"
 #import "ios/web/public/serializable_user_data_manager.h"
@@ -151,7 +152,7 @@
     SessionWindowIOS* window = [[SessionWindowIOS alloc] init];
     for (int i = 0; i < entries; i++) {
       CRWSessionStorage* session_storage =
-          CreateWebState()->BuildSessionStorage();
+          [[[CRWSessionStorage alloc] init] autorelease];
       [window addSerializedSessionStorage:session_storage];
     }
     if (entries)
diff --git a/ios/web/navigation/serializable_user_data_manager_impl.mm b/ios/web/navigation/serializable_user_data_manager_impl.mm
index 7d1f310..09657b84 100644
--- a/ios/web/navigation/serializable_user_data_manager_impl.mm
+++ b/ios/web/navigation/serializable_user_data_manager_impl.mm
@@ -5,6 +5,7 @@
 #import "ios/web/navigation/serializable_user_data_manager_impl.h"
 
 #import "base/mac/foundation_util.h"
+#include "base/memory/ptr_util.h"
 #import "ios/web/public/web_state/web_state.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
@@ -13,7 +14,7 @@
 
 namespace web {
 namespace {
-// The key under which SerializableUserDataMangerWrapper are stored in the
+// The key under which SerializableUserDataManagerWrapper are stored in the
 // WebState's user data.
 const void* const kSerializableUserDataManagerKey =
     &kSerializableUserDataManagerKey;
@@ -22,9 +23,9 @@
 
 // Wrapper class used to associate SerializableUserDataManagerImpls with its
 // associated WebState.
-class SerializableUserDataManagerWrapper : base::SupportsUserData::Data {
+class SerializableUserDataManagerWrapper : public base::SupportsUserData::Data {
  public:
-  // Returns the SerializableUserDataMangerWrapper associated with |web_state|,
+  // Returns the SerializableUserDataManagerWrapper associated with |web_state|,
   // creating one if necessary.
   static SerializableUserDataManagerWrapper* FromWebState(
       web::WebState* web_state) {
@@ -32,30 +33,28 @@
     SerializableUserDataManagerWrapper* wrapper =
         static_cast<SerializableUserDataManagerWrapper*>(
             web_state->GetUserData(kSerializableUserDataManagerKey));
-    if (!wrapper)
-      wrapper = new SerializableUserDataManagerWrapper(web_state);
-    return wrapper;
+    if (wrapper)
+      return wrapper;
+
+    web_state->SetUserData(
+        kSerializableUserDataManagerKey,
+        base::MakeUnique<SerializableUserDataManagerWrapper>());
+    return static_cast<SerializableUserDataManagerWrapper*>(
+        web_state->GetUserData(kSerializableUserDataManagerKey));
   }
 
   // Returns the manager owned by this wrapper.
   SerializableUserDataManagerImpl* manager() { return &manager_; }
 
  private:
-  // The SerializableUserDataMangerWrapper owned by this object.
+  // The SerializableUserDataManagerWrapper owned by this object.
   SerializableUserDataManagerImpl manager_;
-
-  // Private constructor.  The created object will be added to |web_state|'s
-  // user data.
-  SerializableUserDataManagerWrapper(web::WebState* web_state) {
-    DCHECK(web_state);
-    web_state->SetUserData(kSerializableUserDataManagerKey, this);
-  }
 };
 }  // namespace
 
 // static
 std::unique_ptr<SerializableUserData> SerializableUserData::Create() {
-  return std::unique_ptr<SerializableUserData>(new SerializableUserDataImpl());
+  return base::MakeUnique<SerializableUserDataImpl>();
 }
 
 SerializableUserDataImpl::SerializableUserDataImpl()
@@ -118,16 +117,16 @@
 
 std::unique_ptr<SerializableUserData>
 SerializableUserDataManagerImpl::CreateSerializableUserData() const {
-  return std::unique_ptr<SerializableUserData>(
-      new SerializableUserDataImpl(data_));
+  return base::MakeUnique<SerializableUserDataImpl>(data_);
 }
 
 void SerializableUserDataManagerImpl::AddSerializableUserData(
     SerializableUserData* data) {
-  DCHECK(data);
-  SerializableUserDataImpl* data_impl =
-      static_cast<SerializableUserDataImpl*>(data);
-  data_.reset([data_impl->data() mutableCopy]);
+  if (data) {
+    SerializableUserDataImpl* data_impl =
+        static_cast<SerializableUserDataImpl*>(data);
+    data_.reset([data_impl->data() mutableCopy]);
+  }
 }
 
 }  // namespace web
diff --git a/ios/web/payments/payment_request.cc b/ios/web/payments/payment_request.cc
index 5d56b31..a92285f 100644
--- a/ios/web/payments/payment_request.cc
+++ b/ios/web/payments/payment_request.cc
@@ -29,15 +29,12 @@
 static const char kCardCardSecurityCode[] = "cardSecurityCode";
 static const char kCardExpiryMonth[] = "expiryMonth";
 static const char kCardExpiryYear[] = "expiryYear";
-static const char kMethodData[] = "methodData";
 static const char kMethodDataData[] = "data";
-static const char kMethodName[] = "methodName";
-static const char kPaymentCurrencyAmountCurrency[] = "currency";
-static const char kPaymentCurrencyAmountCurrencySystem[] = "currencySystem";
 static const char kPaymentCurrencyAmountCurrencySystemISO4217[] =
     "urn:iso:std:iso:4217";
+static const char kPaymentCurrencyAmountCurrencySystem[] = "currencySystem";
+static const char kPaymentCurrencyAmountCurrency[] = "currency";
 static const char kPaymentCurrencyAmountValue[] = "value";
-static const char kPaymentDetails[] = "details";
 static const char kPaymentDetailsDisplayItems[] = "displayItems";
 static const char kPaymentDetailsError[] = "error";
 static const char kPaymentDetailsShippingOptions[] = "shippingOptions";
@@ -45,14 +42,24 @@
 static const char kPaymentItemAmount[] = "amount";
 static const char kPaymentItemLabel[] = "label";
 static const char kPaymentItemPending[] = "pending";
-static const char kPaymentOptions[] = "options";
 static const char kPaymentOptionsRequestPayerEmail[] = "requestPayerEmail";
 static const char kPaymentOptionsRequestPayerName[] = "requestPayerName";
 static const char kPaymentOptionsRequestPayerPhone[] = "requestPayerPhone";
 static const char kPaymentOptionsRequestShipping[] = "requestShipping";
-static const char kPaymentOptionsShippingType[] = "shippingType";
 static const char kPaymentOptionsShippingTypeDelivery[] = "delivery";
 static const char kPaymentOptionsShippingTypePickup[] = "pickup";
+static const char kPaymentOptionsShippingType[] = "shippingType";
+static const char kPaymentRequestDetails[] = "details";
+static const char kPaymentRequestId[] = "paymentRequestID";
+static const char kPaymentRequestMethodData[] = "methodData";
+static const char kPaymentRequestOptions[] = "options";
+static const char kPaymentResponseDetails[] = "details";
+static const char kPaymentResponseMethodName[] = "methodName";
+static const char kPaymentResponsePayerEmail[] = "payerEmail";
+static const char kPaymentResponsePayerName[] = "payerName";
+static const char kPaymentResponsePayerPhone[] = "payerPhone";
+static const char kPaymentResponseShippingAddress[] = "shippingAddress";
+static const char kPaymentResponseShippingOption[] = "shippingOption";
 static const char kPaymentShippingOptionAmount[] = "amount";
 static const char kPaymentShippingOptionId[] = "id";
 static const char kPaymentShippingOptionLabel[] = "label";
@@ -94,7 +101,8 @@
   if (!this->address_line.empty()) {
     std::unique_ptr<base::ListValue> address_line(new base::ListValue);
     for (base::string16 address_line_string : this->address_line) {
-      address_line->AppendString(address_line_string);
+      if (!address_line_string.empty())
+        address_line->AppendString(address_line_string);
     }
     result->Set(kAddressAddressLine, std::move(address_line));
   }
@@ -395,7 +403,8 @@
 PaymentRequest::~PaymentRequest() = default;
 
 bool PaymentRequest::operator==(const PaymentRequest& other) const {
-  return this->shipping_address == other.shipping_address &&
+  return this->payment_request_id == other.payment_request_id &&
+         this->shipping_address == other.shipping_address &&
          this->shipping_option == other.shipping_option &&
          this->method_data == other.method_data &&
          this->details == other.details && this->options == other.options;
@@ -411,7 +420,7 @@
   // Parse the payment method data.
   const base::ListValue* method_data_list = nullptr;
   // At least one method is required.
-  if (!value.GetList(kMethodData, &method_data_list) ||
+  if (!value.GetList(kPaymentRequestMethodData, &method_data_list) ||
       method_data_list->GetSize() == 0) {
     return false;
   }
@@ -428,7 +437,7 @@
 
   // Parse the payment details.
   const base::DictionaryValue* payment_details_dict = nullptr;
-  if (!value.GetDictionary(kPaymentDetails, &payment_details_dict) ||
+  if (!value.GetDictionary(kPaymentRequestDetails, &payment_details_dict) ||
       !this->details.FromDictionaryValue(*payment_details_dict)) {
     return false;
   }
@@ -436,7 +445,7 @@
   // Parse the payment options.
   const base::DictionaryValue* payment_options = nullptr;
   // Options field is optional.
-  if (value.GetDictionary(kPaymentOptions, &payment_options))
+  if (value.GetDictionary(kPaymentRequestOptions, &payment_options))
     if (!this->options.FromDictionaryValue(*payment_options))
       return false;
 
@@ -466,15 +475,15 @@
 
   if (!this->cardholder_name.empty())
     result->SetString(kCardCardholderName, this->cardholder_name);
-  if (!this->card_number.empty())
-    result->SetString(kCardCardNumber, this->card_number);
+  result->SetString(kCardCardNumber, this->card_number);
   if (!this->expiry_month.empty())
     result->SetString(kCardExpiryMonth, this->expiry_month);
   if (!this->expiry_year.empty())
     result->SetString(kCardExpiryYear, this->expiry_year);
   if (!this->card_security_code.empty())
     result->SetString(kCardCardSecurityCode, this->card_security_code);
-  result->Set(kCardBillingAddress, this->billing_address.ToDictionaryValue());
+  if (!this->billing_address.ToDictionaryValue()->empty())
+    result->Set(kCardBillingAddress, this->billing_address.ToDictionaryValue());
 
   return result;
 }
@@ -484,8 +493,14 @@
 PaymentResponse::~PaymentResponse() = default;
 
 bool PaymentResponse::operator==(const PaymentResponse& other) const {
-  return this->method_name == other.method_name &&
-         this->details == other.details;
+  return this->payment_request_id == other.payment_request_id &&
+         this->method_name == other.method_name &&
+         this->details == other.details &&
+         this->shipping_address == other.shipping_address &&
+         this->shipping_option == other.shipping_option &&
+         this->payer_name == other.payer_name &&
+         this->payer_email == other.payer_email &&
+         this->payer_phone == other.payer_phone;
 }
 
 bool PaymentResponse::operator!=(const PaymentResponse& other) const {
@@ -496,9 +511,21 @@
     const {
   std::unique_ptr<base::DictionaryValue> result(new base::DictionaryValue());
 
-  if (!this->method_name.empty())
-    result->SetString(kMethodName, this->method_name);
-  result->Set(kPaymentDetails, this->details.ToDictionaryValue());
+  result->SetString(kPaymentRequestId, this->payment_request_id);
+  result->SetString(kPaymentResponseMethodName, this->method_name);
+  result->Set(kPaymentResponseDetails, this->details.ToDictionaryValue());
+  if (!this->shipping_address.ToDictionaryValue()->empty()) {
+    result->Set(kPaymentResponseShippingAddress,
+                this->shipping_address.ToDictionaryValue());
+  }
+  if (!this->shipping_option.empty())
+    result->SetString(kPaymentResponseShippingOption, this->shipping_option);
+  if (!this->payer_name.empty())
+    result->SetString(kPaymentResponsePayerName, this->payer_name);
+  if (!this->payer_email.empty())
+    result->SetString(kPaymentResponsePayerEmail, this->payer_email);
+  if (!this->payer_phone.empty())
+    result->SetString(kPaymentResponsePayerPhone, this->payer_phone);
 
   return result;
 }
diff --git a/ios/web/payments/payment_request_unittest.cc b/ios/web/payments/payment_request_unittest.cc
index 529c8590..d4f1a0f 100644
--- a/ios/web/payments/payment_request_unittest.cc
+++ b/ios/web/payments/payment_request_unittest.cc
@@ -293,13 +293,15 @@
 
 // PaymentResponse serialization tests.
 
-// Tests that serializing a default PaymentResponse yields an empty dictionary.
+// Tests that serializing a default PaymentResponse yields the expected result.
 TEST(PaymentRequestTest, EmptyResponseDictionary) {
   base::DictionaryValue expected_value;
+
   std::unique_ptr<base::DictionaryValue> details(new base::DictionaryValue);
-  std::unique_ptr<base::DictionaryValue> address(new base::DictionaryValue);
-  details->Set("billingAddress", std::move(address));
+  details->SetString("cardNumber", "");
   expected_value.Set("details", std::move(details));
+  expected_value.SetString("paymentRequestID", "");
+  expected_value.SetString("methodName", "");
 
   PaymentResponse payment_response;
   EXPECT_TRUE(
@@ -310,24 +312,45 @@
 // result.
 TEST(PaymentRequestTest, PopulatedResponseDictionary) {
   base::DictionaryValue expected_value;
+
   std::unique_ptr<base::DictionaryValue> details(new base::DictionaryValue);
-  std::unique_ptr<base::DictionaryValue> address(new base::DictionaryValue);
-  details->Set("billingAddress", std::move(address));
+  details->SetString("cardNumber", "1111-1111-1111-1111");
+  details->SetString("cardholderName", "Jon Doe");
+  details->SetString("expiryMonth", "02");
+  details->SetString("expiryYear", "2090");
+  details->SetString("cardSecurityCode", "111");
+  std::unique_ptr<base::DictionaryValue> billing_address(
+      new base::DictionaryValue);
+  billing_address->SetString("postalCode", "90210");
+  details->Set("billingAddress", std::move(billing_address));
   expected_value.Set("details", std::move(details));
-
+  expected_value.SetString("paymentRequestID", "12345");
   expected_value.SetString("methodName", "American Express");
-  PaymentResponse payment_response;
-  payment_response.method_name = base::ASCIIToUTF16("American Express");
-  EXPECT_TRUE(
-      expected_value.Equals(payment_response.ToDictionaryValue().get()));
+  std::unique_ptr<base::DictionaryValue> shipping_address(
+      new base::DictionaryValue);
+  shipping_address->SetString("postalCode", "94115");
+  expected_value.Set("shippingAddress", std::move(shipping_address));
+  expected_value.SetString("shippingOption", "666");
+  expected_value.SetString("payerName", "Jane Doe");
+  expected_value.SetString("payerEmail", "jane@example.com");
+  expected_value.SetString("payerPhone", "1234-567-890");
 
-  details.reset(new base::DictionaryValue);
-  address.reset(new base::DictionaryValue);
-  address->SetString("postalCode", "90210");
-  details->Set("billingAddress", std::move(address));
-  expected_value.Set("details", std::move(details));
+  PaymentResponse payment_response;
+  payment_response.payment_request_id = base::ASCIIToUTF16("12345");
+  payment_response.method_name = base::ASCIIToUTF16("American Express");
+  payment_response.details.card_number =
+      base::ASCIIToUTF16("1111-1111-1111-1111");
+  payment_response.details.cardholder_name = base::ASCIIToUTF16("Jon Doe");
+  payment_response.details.expiry_month = base::ASCIIToUTF16("02");
+  payment_response.details.expiry_year = base::ASCIIToUTF16("2090");
+  payment_response.details.card_security_code = base::ASCIIToUTF16("111");
   payment_response.details.billing_address.postal_code =
       base::ASCIIToUTF16("90210");
+  payment_response.shipping_address.postal_code = base::ASCIIToUTF16("94115");
+  payment_response.shipping_option = base::ASCIIToUTF16("666");
+  payment_response.payer_name = base::ASCIIToUTF16("Jane Doe");
+  payment_response.payer_email = base::ASCIIToUTF16("jane@example.com");
+  payment_response.payer_phone = base::ASCIIToUTF16("1234-567-890");
   EXPECT_TRUE(
       expected_value.Equals(payment_response.ToDictionaryValue().get()));
 }
diff --git a/ios/web/public/payments/payment_request.h b/ios/web/public/payments/payment_request.h
index a12c289..dea1b52 100644
--- a/ios/web/public/payments/payment_request.h
+++ b/ios/web/public/payments/payment_request.h
@@ -311,6 +311,10 @@
   // if the required values are present.
   bool FromDictionaryValue(const base::DictionaryValue& value);
 
+  // The unique ID for this PaymentRequest. If it is not provided during
+  // construction, one is generated.
+  base::string16 payment_request_id;
+
   // Properties set in order to communicate user choices back to the page.
   PaymentAddress shipping_address;
   base::string16 shipping_option;
@@ -371,6 +375,9 @@
   // Populates |value| with the properties of this PaymentResponse.
   std::unique_ptr<base::DictionaryValue> ToDictionaryValue() const;
 
+  // The same paymentRequestID present in the original PaymentRequest.
+  base::string16 payment_request_id;
+
   // The payment method identifier for the payment method that the user selected
   // to fulfil the transaction.
   base::string16 method_name;
@@ -378,6 +385,31 @@
   // A credit card response object used by the merchant to process the
   // transaction and determine successful fund transfer.
   BasicCardResponse details;
+
+  // If request_shipping was set to true in the PaymentOptions passed to the
+  // PaymentRequest constructor, this will be the full and final shipping
+  // address chosen by the user.
+  PaymentAddress shipping_address;
+
+  // If the request_shipping flag was set to true in the PaymentOptions passed
+  // to the PaymentRequest constructor, this will be the id attribute of the
+  // selected shipping option.
+  base::string16 shipping_option;
+
+  // If the request_payer_name flag was set to true in the PaymentOptions passed
+  // to the PaymentRequest constructor, this will be the name provided by the
+  // user.
+  base::string16 payer_name;
+
+  // If the request_payer_email flag was set to true in the PaymentOptions
+  // passed to the PaymentRequest constructor, this will be the email address
+  // chosen by the user.
+  base::string16 payer_email;
+
+  // If the request_payer_phone flag was set to true in the PaymentOptions
+  // passed to the PaymentRequest constructor, this will be the phone number
+  // chosen by the user.
+  base::string16 payer_phone;
 };
 
 }  // namespace web
diff --git a/third_party/WebKit/LayoutTests/inspector/tracing/timeline-misc/timeline-aggregated-details.html b/third_party/WebKit/LayoutTests/inspector/tracing/timeline-misc/timeline-aggregated-details.html
index 08f86b1..fdf683f1 100644
--- a/third_party/WebKit/LayoutTests/inspector/tracing/timeline-misc/timeline-aggregated-details.html
+++ b/third_party/WebKit/LayoutTests/inspector/tracing/timeline-misc/timeline-aggregated-details.html
@@ -609,26 +609,32 @@
     var groupByEnum = Timeline.AggregatedTimelineTreeView.GroupBy;
     for (var grouping in groupByEnum) {
         var groupingValue = groupByEnum[grouping];
-        testEventTree(Timeline.TimelinePanel.ViewMode.CallTree, groupingValue);
-        testEventTree(Timeline.TimelinePanel.ViewMode.BottomUp, groupingValue);
+        testEventTree("CallTree", groupingValue);
+        testEventTree("BottomUp", groupingValue);
     }
 
-    testEventTree(Timeline.TimelinePanel.ViewMode.EventLog);
+    testEventTree("EventLog");
     InspectorTest.completeTest();
 
     function testEventTree(type, grouping)
     {
         InspectorTest.addResult("");
-        timeline._tabbedPane.selectTab(type, true);
-        var callTree = timeline._currentView._treeView;
+        var tree;
+        if (timeline._tabbedPane) {
+            timeline._tabbedPane.selectTab(type, true);
+            tree = timeline._currentView._treeView;
+        } else {
+            timeline._currentView._detailsView._tabbedPane.selectTab(type, true);
+            tree = timeline._currentView._detailsView._tabbedPane.visibleView;
+        }
         if (grouping) {
             InspectorTest.addResult(type + "  Group by: " + grouping);
-            callTree._groupByCombobox.select(callTree._groupByCombobox.options().find(x => x.value === grouping));
-            callTree._onGroupByChanged();
+            tree._groupByCombobox.select(tree._groupByCombobox.options().find(x => x.value === grouping));
+            tree._onGroupByChanged();
         } else {
             InspectorTest.addResult(type);
         }
-        var rootNode = callTree._dataGrid.rootNode();
+        var rootNode = tree._dataGrid.rootNode();
         for (var node of rootNode.children)
             printEventTree(1, node._profileNode, node._treeView);
     }
diff --git a/third_party/WebKit/LayoutTests/inspector/tracing/timeline-misc/timeline-tree-search.html b/third_party/WebKit/LayoutTests/inspector/tracing/timeline-misc/timeline-tree-search.html
index 926bdf8..0437824 100644
--- a/third_party/WebKit/LayoutTests/inspector/tracing/timeline-misc/timeline-tree-search.html
+++ b/third_party/WebKit/LayoutTests/inspector/tracing/timeline-misc/timeline-tree-search.html
@@ -50,11 +50,11 @@
         {"name": "Program", "ts": 2590000, "ph": "E", "tid": mainThread, "pid": pid, "cat":"disabled-by-default-devtools.timeline", "args": {}}
     ];
 
+    Runtime.experiments.enableForTest("timelineMultipleMainViews");
     var model = InspectorTest.createPerformanceModelWithEvents(testData);
-    var panel = UI.panels.timeline;
+    var panel = new Timeline.TimelinePanel();
     panel._setModel(model);
-    var viewModes = Timeline.TimelinePanel.ViewMode;
-    for (var mode of [viewModes.BottomUp, viewModes.CallTree, viewModes.EventLog]) {
+    for (var mode of ["BottomUp", "CallTree", "EventLog"]) {
         InspectorTest.addResult("");
         InspectorTest.addResult(`Testing mode: ${mode}`);
         panel._tabbedPane.selectTab(mode);
diff --git a/third_party/WebKit/LayoutTests/transitions/transition-property-layer-collision.html b/third_party/WebKit/LayoutTests/transitions/transition-property-layer-collision.html
new file mode 100644
index 0000000..85d918f
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/transitions/transition-property-layer-collision.html
@@ -0,0 +1,40 @@
+<script src="../resources/testharness.js"></script>
+<script src="../resources/testharnessreport.js"></script>
+<body></body>
+<script>
+function createTarget() {
+  var target = document.createElement('div');
+  document.body.appendChild(target);
+  return target;
+}
+
+test(() => {
+  var target = createTarget();
+  target.style.left = '100px';
+  assert_equals(getComputedStyle(target).left, '100px');
+  target.style.transition = 'left 1s -0.5s, left 1s -0.25s linear';
+  target.style.left = '200px';
+  assert_equals(getComputedStyle(target).left, '125px');
+  target.remove();
+}, 'The last timing properties for a transition property should be used');
+
+test(() => {
+  var target = createTarget();
+  target.style.left = '100px';
+  assert_equals(getComputedStyle(target).left, '100px');
+  target.style.transition = 'left 1s -0.5s, all 1s -0.25s linear';
+  target.style.left = '200px';
+  assert_equals(getComputedStyle(target).left, '125px');
+  target.remove();
+}, 'The last timing properties for a transition property should be used including shorthand references to the property');
+
+test(() => {
+  var target = createTarget();
+  target.style.left = '100px';
+  assert_equals(getComputedStyle(target).left, '100px');
+  target.style.transition = 'left 1s -0.5s, left 0s';
+  target.style.left = '200px';
+  assert_equals(getComputedStyle(target).left, '200px');
+  target.remove();
+}, 'The last timing properties for a transition property should be used even if they cause the transition to not start');
+</script>
\ No newline at end of file
diff --git a/third_party/WebKit/Source/core/animation/css/CSSAnimationUpdate.h b/third_party/WebKit/Source/core/animation/css/CSSAnimationUpdate.h
index 8b2de64..f4e7980 100644
--- a/third_party/WebKit/Source/core/animation/css/CSSAnimationUpdate.h
+++ b/third_party/WebKit/Source/core/animation/css/CSSAnimationUpdate.h
@@ -177,6 +177,7 @@
     newTransition.effect = &effect;
     m_newTransitions.set(id, newTransition);
   }
+  void unstartTransition(CSSPropertyID id) { m_newTransitions.remove(id); }
   bool isCancelledTransition(CSSPropertyID id) const {
     return m_cancelledTransitions.contains(id);
   }
diff --git a/third_party/WebKit/Source/core/animation/css/CSSAnimations.cpp b/third_party/WebKit/Source/core/animation/css/CSSAnimations.cpp
index 9a3a752b..8c0894d 100644
--- a/third_party/WebKit/Source/core/animation/css/CSSAnimations.cpp
+++ b/third_party/WebKit/Source/core/animation/css/CSSAnimations.cpp
@@ -677,8 +677,15 @@
   // last one since we iterate over them in order.
 
   Timing timing = transitionData.convertToTiming(transitionIndex);
-  if (timing.startDelay + timing.iterationDuration <= 0)
+  if (timing.startDelay + timing.iterationDuration <= 0) {
+    // We may have started a transition in a prior CSSTransitionData update,
+    // this CSSTransitionData update needs to override them.
+    // TODO(alancutter): Just iterate over the CSSTransitionDatas in reverse and
+    // skip any properties that have already been visited so we don't need to
+    // "undo" work like this.
+    update.unstartTransition(id);
     return;
+  }
 
   AnimatableValue* reversingAdjustedStartValue = from.get();
   double reversingShorteningFactor = 1;
diff --git a/third_party/WebKit/Source/core/editing/Editor.cpp b/third_party/WebKit/Source/core/editing/Editor.cpp
index 43bc426..3ce9536 100644
--- a/third_party/WebKit/Source/core/editing/Editor.cpp
+++ b/third_party/WebKit/Source/core/editing/Editor.cpp
@@ -1753,6 +1753,10 @@
                            InputEvent::InputType::InsertReplacementText);
 }
 
+TypingCommand* Editor::lastTypingCommandIfStillOpenForTyping() const {
+  return TypingCommand::lastTypingCommandIfStillOpenForTyping(&frame());
+}
+
 DEFINE_TRACE(Editor) {
   visitor->trace(m_frame);
   visitor->trace(m_lastEditCommand);
diff --git a/third_party/WebKit/Source/core/editing/Editor.h b/third_party/WebKit/Source/core/editing/Editor.h
index eb7a17e..3e05912 100644
--- a/third_party/WebKit/Source/core/editing/Editor.h
+++ b/third_party/WebKit/Source/core/editing/Editor.h
@@ -55,6 +55,7 @@
 class SpellChecker;
 class StylePropertySet;
 class TextEvent;
+class TypingCommand;
 class UndoStack;
 class UndoStep;
 
@@ -79,6 +80,7 @@
   EditorClient& client() const;
 
   CompositeEditCommand* lastEditCommand() { return m_lastEditCommand.get(); }
+  TypingCommand* lastTypingCommandIfStillOpenForTyping() const;
 
   void handleKeyboardEvent(KeyboardEvent*);
   bool handleTextEvent(TextEvent*);
diff --git a/third_party/WebKit/Source/core/editing/commands/TypingCommand.h b/third_party/WebKit/Source/core/editing/commands/TypingCommand.h
index 6d6f5a8d..7a52aa8 100644
--- a/third_party/WebKit/Source/core/editing/commands/TypingCommand.h
+++ b/third_party/WebKit/Source/core/editing/commands/TypingCommand.h
@@ -82,6 +82,8 @@
   static bool insertParagraphSeparatorInQuotedContent(Document&);
   static void closeTyping(LocalFrame*);
 
+  static TypingCommand* lastTypingCommandIfStillOpenForTyping(LocalFrame*);
+
   void insertText(const String& text, bool selectInsertedText, EditingState*);
   void insertTextRunWithoutNewlines(const String& text,
                                     bool selectInsertedText,
@@ -135,8 +137,6 @@
   bool isOpenForMoreTyping() const { return m_openForMoreTyping; }
   void closeTyping() { m_openForMoreTyping = false; }
 
-  static TypingCommand* lastTypingCommandIfStillOpenForTyping(LocalFrame*);
-
   void doApply(EditingState*) override;
   InputEvent::InputType inputType() const override;
   bool isTypingCommand() const override;
diff --git a/third_party/WebKit/Source/core/frame/FrameHost.cpp b/third_party/WebKit/Source/core/frame/FrameHost.cpp
index de575c8..a1047400 100644
--- a/third_party/WebKit/Source/core/frame/FrameHost.cpp
+++ b/third_party/WebKit/Source/core/frame/FrameHost.cpp
@@ -52,7 +52,6 @@
 FrameHost::FrameHost(Page& page)
     : m_page(&page),
       m_browserControls(BrowserControls::create(*this)),
-      m_pageScaleConstraintsSet(PageScaleConstraintsSet::create()),
       m_visualViewport(VisualViewport::create(*this)),
       m_overscrollController(
           OverscrollController::create(*m_visualViewport,
@@ -99,11 +98,11 @@
 }
 
 PageScaleConstraintsSet& FrameHost::pageScaleConstraintsSet() {
-  return *m_pageScaleConstraintsSet;
+  return page().pageScaleConstraintsSet();
 }
 
 const PageScaleConstraintsSet& FrameHost::pageScaleConstraintsSet() const {
-  return *m_pageScaleConstraintsSet;
+  return page().pageScaleConstraintsSet();
 }
 
 EventHandlerRegistry& FrameHost::eventHandlerRegistry() {
diff --git a/third_party/WebKit/Source/core/frame/FrameHost.h b/third_party/WebKit/Source/core/frame/FrameHost.h
index 9b7939db..9b3bc06 100644
--- a/third_party/WebKit/Source/core/frame/FrameHost.h
+++ b/third_party/WebKit/Source/core/frame/FrameHost.h
@@ -114,7 +114,6 @@
 
   const Member<Page> m_page;
   const Member<BrowserControls> m_browserControls;
-  const std::unique_ptr<PageScaleConstraintsSet> m_pageScaleConstraintsSet;
   const Member<VisualViewport> m_visualViewport;
   const Member<OverscrollController> m_overscrollController;
   const Member<EventHandlerRegistry> m_eventHandlerRegistry;
diff --git a/third_party/WebKit/Source/core/html/TextControlElementTest.cpp b/third_party/WebKit/Source/core/html/TextControlElementTest.cpp
index 22d78a4..a502e30 100644
--- a/third_party/WebKit/Source/core/html/TextControlElementTest.cpp
+++ b/third_party/WebKit/Source/core/html/TextControlElementTest.cpp
@@ -27,9 +27,6 @@
   TextControlElement& textControl() const { return *m_textControl; }
   HTMLInputElement& input() const { return *m_input; }
 
-  int layoutCount() const { return page().frameView().layoutCount(); }
-  void forceLayoutFlag();
-
  private:
   std::unique_ptr<DummyPageHolder> m_dummyPageHolder;
 
@@ -52,15 +49,6 @@
   m_input = toHTMLInputElement(m_document->getElementById("input"));
 }
 
-void TextControlElementTest::forceLayoutFlag() {
-  FrameView& frameView = page().frameView();
-  IntRect frameRect = frameView.frameRect();
-  frameRect.setWidth(frameRect.width() + 1);
-  frameRect.setHeight(frameRect.height() + 1);
-  page().frameView().setFrameRect(frameRect);
-  document().updateStyleAndLayoutIgnorePendingStylesheets();
-}
-
 TEST_F(TextControlElementTest, SetSelectionRange) {
   EXPECT_EQ(0u, textControl().selectionStart());
   EXPECT_EQ(0u, textControl().selectionEnd());
@@ -78,24 +66,14 @@
   input().focus();
   input().setValue("Hello, input form.");
   input().setSelectionRange(1, 1);
-  FrameSelection& frameSelection = document().frame()->selection();
-  forceLayoutFlag();
-  LayoutRect oldCaretRect(frameSelection.absoluteCaretBounds());
-  EXPECT_FALSE(oldCaretRect.isEmpty());
-  int startLayoutCount = layoutCount();
-  input().setSelectionRange(1, 1);
-  EXPECT_EQ(startLayoutCount, layoutCount());
-  LayoutRect newCaretRect(frameSelection.absoluteCaretBounds());
-  EXPECT_EQ(oldCaretRect, newCaretRect);
 
-  forceLayoutFlag();
-  oldCaretRect = LayoutRect(frameSelection.absoluteCaretBounds());
-  EXPECT_FALSE(oldCaretRect.isEmpty());
-  startLayoutCount = layoutCount();
+  // Force layout if document().updateStyleAndLayoutIgnorePendingStylesheets()
+  // is called.
+  document().body()->appendChild(document().createTextNode("foo"));
+  const int startLayoutCount = page().frameView().layoutCount();
+  EXPECT_TRUE(document().needsLayoutTreeUpdate());
   input().setSelectionRange(2, 2);
-  EXPECT_EQ(startLayoutCount, layoutCount());
-  newCaretRect = LayoutRect(frameSelection.absoluteCaretBounds());
-  EXPECT_NE(oldCaretRect, newCaretRect);
+  EXPECT_EQ(startLayoutCount, page().frameView().layoutCount());
 }
 
 TEST_F(TextControlElementTest, IndexForPosition) {
diff --git a/third_party/WebKit/Source/core/loader/BUILD.gn b/third_party/WebKit/Source/core/loader/BUILD.gn
index 1f9be6cf..bde7e116 100644
--- a/third_party/WebKit/Source/core/loader/BUILD.gn
+++ b/third_party/WebKit/Source/core/loader/BUILD.gn
@@ -49,6 +49,8 @@
     "TextTrackLoader.h",
     "ThreadableLoader.cpp",
     "ThreadableLoaderClient.h",
+    "ThreadableLoadingContext.cpp",
+    "ThreadableLoadingContext.h",
     "WorkerThreadableLoader.cpp",
     "WorkerThreadableLoader.h",
     "WorkletScriptLoader.cpp",
diff --git a/third_party/WebKit/Source/core/loader/DocumentThreadableLoader.cpp b/third_party/WebKit/Source/core/loader/DocumentThreadableLoader.cpp
index defc959..59f46ef 100644
--- a/third_party/WebKit/Source/core/loader/DocumentThreadableLoader.cpp
+++ b/third_party/WebKit/Source/core/loader/DocumentThreadableLoader.cpp
@@ -42,6 +42,7 @@
 #include "core/loader/DocumentThreadableLoaderClient.h"
 #include "core/loader/FrameLoader.h"
 #include "core/loader/ThreadableLoaderClient.h"
+#include "core/loader/ThreadableLoadingContext.h"
 #include "core/loader/private/CrossOriginPreflightResultCache.h"
 #include "core/page/ChromeClient.h"
 #include "core/page/Page.h"
@@ -138,28 +139,30 @@
     ThreadableLoaderClient& client,
     const ThreadableLoaderOptions& options,
     const ResourceLoaderOptions& resourceLoaderOptions) {
-  (new DocumentThreadableLoader(document, &client, LoadSynchronously, options,
+  (new DocumentThreadableLoader(*ThreadableLoadingContext::create(document),
+                                &client, LoadSynchronously, options,
                                 resourceLoaderOptions))
       ->start(request);
 }
 
 DocumentThreadableLoader* DocumentThreadableLoader::create(
-    Document& document,
+    ThreadableLoadingContext& loadingContext,
     ThreadableLoaderClient* client,
     const ThreadableLoaderOptions& options,
     const ResourceLoaderOptions& resourceLoaderOptions) {
-  return new DocumentThreadableLoader(document, client, LoadAsynchronously,
-                                      options, resourceLoaderOptions);
+  return new DocumentThreadableLoader(loadingContext, client,
+                                      LoadAsynchronously, options,
+                                      resourceLoaderOptions);
 }
 
 DocumentThreadableLoader::DocumentThreadableLoader(
-    Document& document,
+    ThreadableLoadingContext& loadingContext,
     ThreadableLoaderClient* client,
     BlockingBehavior blockingBehavior,
     const ThreadableLoaderOptions& options,
     const ResourceLoaderOptions& resourceLoaderOptions)
     : m_client(client),
-      m_document(&document),
+      m_loadingContext(&loadingContext),
       m_options(options),
       m_resourceLoaderOptions(resourceLoaderOptions),
       m_forceDoNotAllowStoredCredentials(false),
@@ -168,7 +171,7 @@
       m_isUsingDataConsumerHandle(false),
       m_async(blockingBehavior == LoadAsynchronously),
       m_requestContext(WebURLRequest::RequestContextUnspecified),
-      m_timeoutTimer(TaskRunnerHelper::get(TaskType::Networking, &document),
+      m_timeoutTimer(m_loadingContext->getTaskRunner(TaskType::Networking),
                      this,
                      &DocumentThreadableLoader::didTimeout),
       m_requestStartedSeconds(0.0),
@@ -192,7 +195,7 @@
   if (!m_sameOriginRequest &&
       m_options.crossOriginRequestPolicy == DenyCrossOriginRequests) {
     InspectorInstrumentation::
-        documentThreadableLoaderFailedToStartLoadingForClient(m_document,
+        documentThreadableLoaderFailedToStartLoadingForClient(document(),
                                                               m_client);
     ThreadableLoaderClient* client = m_client;
     clear();
@@ -222,8 +225,8 @@
   //   initiated fetch.
   // - Some non-script initiated fetches such as WorkerScriptLoader also use
   //   ThreadableLoader, but they are guaranteed to use GET method.
-  if (request.httpMethod() != HTTPNames::GET) {
-    if (Page* page = m_document->page())
+  if (request.httpMethod() != HTTPNames::GET && document()) {
+    if (Page* page = document()->page())
       page->chromeClient().didObserveNonGetFetchFromScript();
   }
 
@@ -268,7 +271,7 @@
       request.getServiceWorkerMode() == WebURLRequest::ServiceWorkerMode::All &&
       SchemeRegistry::shouldTreatURLSchemeAsAllowingServiceWorkers(
           request.url().protocol()) &&
-      m_document->fetcher()->isControlledByServiceWorker()) {
+      m_loadingContext->getResourceFetcher()->isControlledByServiceWorker()) {
     if (newRequest.fetchRequestMode() == WebURLRequest::FetchRequestModeCORS ||
         newRequest.fetchRequestMode() ==
             WebURLRequest::FetchRequestModeCORSWithForcedPreflight) {
@@ -326,7 +329,7 @@
   if (!SchemeRegistry::shouldTreatURLSchemeAsCORSEnabled(
           request.url().protocol())) {
     InspectorInstrumentation::
-        documentThreadableLoaderFailedToStartLoadingForClient(m_document,
+        documentThreadableLoaderFailedToStartLoadingForClient(document(),
                                                               m_client);
     dispatchDidFailAccessControlCheck(ResourceError(
         errorDomainBlinkInternal, 0, request.url().getString(),
@@ -337,7 +340,7 @@
 
   // Non-secure origins may not make "external requests":
   // https://mikewest.github.io/cors-rfc1918/#integration-fetch
-  if (!document().isSecureContext() && request.isExternalRequest()) {
+  if (!m_loadingContext->isSecureContext() && request.isExternalRequest()) {
     dispatchDidFailAccessControlCheck(
         ResourceError(errorDomainBlinkInternal, 0, request.url().getString(),
                       "Requests to internal network resources are not allowed "
@@ -387,7 +390,7 @@
 
     bool shouldForcePreflight =
         request.isExternalRequest() ||
-        InspectorInstrumentation::shouldForceCORSPreflight(m_document);
+        InspectorInstrumentation::shouldForceCORSPreflight(document());
     bool canSkipPreflight =
         CrossOriginPreflightResultCache::shared().canSkipPreflight(
             getSecurityOrigin()->toString(), crossOriginRequest.url(),
@@ -552,10 +555,12 @@
 
   --m_corsRedirectLimit;
 
-  InspectorInstrumentation::didReceiveCORSRedirectResponse(
-      document().frame(), resource->identifier(),
-      document().frame()->loader().documentLoader(), redirectResponse,
-      resource);
+  if (document() && document()->frame()) {
+    InspectorInstrumentation::didReceiveCORSRedirectResponse(
+        document()->frame(), resource->identifier(),
+        document()->frame()->loader().documentLoader(), redirectResponse,
+        resource);
+  }
 
   String accessControlErrorDescription;
 
@@ -767,10 +772,7 @@
 void DocumentThreadableLoader::reportResponseReceived(
     unsigned long identifier,
     const ResourceResponse& response) {
-  LocalFrame* frame = document().frame();
-  // We are seeing crashes caused by nullptr (crbug.com/578849). But the frame
-  // must be set here. TODO(horo): Find the root cause of the unset frame.
-  DCHECK(frame);
+  LocalFrame* frame = document() ? document()->frame() : nullptr;
   if (!frame)
     return;
   TRACE_EVENT1(
@@ -796,7 +798,7 @@
 
   if (response.wasFetchedViaServiceWorker()) {
     if (response.wasFetchedViaForeignFetch())
-      UseCounter::count(m_document, UseCounter::ForeignFetchInterception);
+      m_loadingContext->recordUseCount(UseCounter::ForeignFetchInterception);
     if (response.wasFallbackRequiredByServiceWorker()) {
       // At this point we must have m_fallbackRequestForServiceWorker. (For
       // SharedWorker the request won't be CORS or CORS-with-preflight,
@@ -1002,17 +1004,18 @@
     newRequest.setOriginRestriction(FetchRequest::NoOriginRestriction);
   DCHECK(!resource());
 
+  ResourceFetcher* fetcher = m_loadingContext->getResourceFetcher();
   if (request.requestContext() == WebURLRequest::RequestContextVideo ||
       request.requestContext() == WebURLRequest::RequestContextAudio)
-    setResource(RawResource::fetchMedia(newRequest, document().fetcher()));
+    setResource(RawResource::fetchMedia(newRequest, fetcher));
   else if (request.requestContext() == WebURLRequest::RequestContextManifest)
-    setResource(RawResource::fetchManifest(newRequest, document().fetcher()));
+    setResource(RawResource::fetchManifest(newRequest, fetcher));
   else
-    setResource(RawResource::fetch(newRequest, document().fetcher()));
+    setResource(RawResource::fetch(newRequest, fetcher));
 
   if (!resource()) {
     InspectorInstrumentation::
-        documentThreadableLoaderFailedToStartLoadingForClient(m_document,
+        documentThreadableLoaderFailedToStartLoadingForClient(document(),
                                                               m_client);
     ThreadableLoaderClient* client = m_client;
     clear();
@@ -1030,10 +1033,10 @@
   if (resource()->isLoading()) {
     unsigned long identifier = resource()->identifier();
     InspectorInstrumentation::documentThreadableLoaderStartedLoadingForClient(
-        m_document, identifier, m_client);
+        document(), identifier, m_client);
   } else {
     InspectorInstrumentation::
-        documentThreadableLoaderFailedToStartLoadingForClient(m_document,
+        documentThreadableLoaderFailedToStartLoadingForClient(document(),
                                                               m_client);
   }
 }
@@ -1045,8 +1048,8 @@
                             resourceLoaderOptions);
   if (m_options.crossOriginRequestPolicy == AllowCrossOriginRequests)
     fetchRequest.setOriginRestriction(FetchRequest::NoOriginRestriction);
-  Resource* resource =
-      RawResource::fetchSynchronously(fetchRequest, document().fetcher());
+  Resource* resource = RawResource::fetchSynchronously(
+      fetchRequest, m_loadingContext->getResourceFetcher());
   ResourceResponse response =
       resource ? resource->response() : ResourceResponse();
   unsigned long identifier = resource
@@ -1055,7 +1058,7 @@
   ResourceError error = resource ? resource->resourceError() : ResourceError();
 
   InspectorInstrumentation::documentThreadableLoaderStartedLoadingForClient(
-      m_document, identifier, m_client);
+      document(), identifier, m_client);
   ThreadableLoaderClient* client = m_client;
 
   if (!resource) {
@@ -1141,17 +1144,17 @@
 
 const SecurityOrigin* DocumentThreadableLoader::getSecurityOrigin() const {
   return m_securityOrigin ? m_securityOrigin.get()
-                          : document().getSecurityOrigin();
+                          : m_loadingContext->getSecurityOrigin();
 }
 
-Document& DocumentThreadableLoader::document() const {
-  DCHECK(m_document);
-  return *m_document;
+Document* DocumentThreadableLoader::document() const {
+  DCHECK(m_loadingContext);
+  return m_loadingContext->getLoadingDocument();
 }
 
 DEFINE_TRACE(DocumentThreadableLoader) {
   visitor->trace(m_resource);
-  visitor->trace(m_document);
+  visitor->trace(m_loadingContext);
   ThreadableLoader::trace(visitor);
   RawResourceClient::trace(visitor);
 }
diff --git a/third_party/WebKit/Source/core/loader/DocumentThreadableLoader.h b/third_party/WebKit/Source/core/loader/DocumentThreadableLoader.h
index 7303b3e..03f776e0 100644
--- a/third_party/WebKit/Source/core/loader/DocumentThreadableLoader.h
+++ b/third_party/WebKit/Source/core/loader/DocumentThreadableLoader.h
@@ -52,6 +52,7 @@
 class ResourceRequest;
 class SecurityOrigin;
 class ThreadableLoaderClient;
+class ThreadableLoadingContext;
 
 class CORE_EXPORT DocumentThreadableLoader final : public ThreadableLoader,
                                                    private RawResourceClient {
@@ -63,7 +64,7 @@
                                         ThreadableLoaderClient&,
                                         const ThreadableLoaderOptions&,
                                         const ResourceLoaderOptions&);
-  static DocumentThreadableLoader* create(Document&,
+  static DocumentThreadableLoader* create(ThreadableLoadingContext&,
                                           ThreadableLoaderClient*,
                                           const ThreadableLoaderOptions&,
                                           const ResourceLoaderOptions&);
@@ -81,7 +82,7 @@
  private:
   enum BlockingBehavior { LoadSynchronously, LoadAsynchronously };
 
-  DocumentThreadableLoader(Document&,
+  DocumentThreadableLoader(ThreadableLoadingContext&,
                            ThreadableLoaderClient*,
                            BlockingBehavior,
                            const ThreadableLoaderOptions&,
@@ -182,10 +183,12 @@
   // End of ResourceOwner re-implementation, see above.
 
   const SecurityOrigin* getSecurityOrigin() const;
-  Document& document() const;
+
+  // TODO(kinuko): Remove dependency to document.
+  Document* document() const;
 
   ThreadableLoaderClient* m_client;
-  Member<Document> m_document;
+  Member<ThreadableLoadingContext> m_loadingContext;
 
   const ThreadableLoaderOptions m_options;
   // Some items may be overridden by m_forceDoNotAllowStoredCredentials and
diff --git a/third_party/WebKit/Source/core/loader/ThreadableLoader.cpp b/third_party/WebKit/Source/core/loader/ThreadableLoader.cpp
index 11cedc5..63cefa8 100644
--- a/third_party/WebKit/Source/core/loader/ThreadableLoader.cpp
+++ b/third_party/WebKit/Source/core/loader/ThreadableLoader.cpp
@@ -33,6 +33,7 @@
 #include "core/dom/Document.h"
 #include "core/dom/ExecutionContext.h"
 #include "core/loader/DocumentThreadableLoader.h"
+#include "core/loader/ThreadableLoadingContext.h"
 #include "core/loader/WorkerThreadableLoader.h"
 #include "core/workers/WorkerGlobalScope.h"
 
@@ -50,8 +51,9 @@
                                           options, resourceLoaderOptions);
   }
 
-  return DocumentThreadableLoader::create(toDocument(context), client, options,
-                                          resourceLoaderOptions);
+  return DocumentThreadableLoader::create(
+      *ThreadableLoadingContext::create(*toDocument(&context)), client, options,
+      resourceLoaderOptions);
 }
 
 void ThreadableLoader::loadResourceSynchronously(
diff --git a/third_party/WebKit/Source/core/loader/ThreadableLoaderTest.cpp b/third_party/WebKit/Source/core/loader/ThreadableLoaderTest.cpp
index 240f0b2..3866a96 100644
--- a/third_party/WebKit/Source/core/loader/ThreadableLoaderTest.cpp
+++ b/third_party/WebKit/Source/core/loader/ThreadableLoaderTest.cpp
@@ -4,8 +4,10 @@
 
 #include "core/loader/ThreadableLoader.h"
 
+#include <memory>
 #include "core/loader/DocumentThreadableLoader.h"
 #include "core/loader/ThreadableLoaderClient.h"
+#include "core/loader/ThreadableLoadingContext.h"
 #include "core/loader/WorkerThreadableLoader.h"
 #include "core/testing/DummyPageHolder.h"
 #include "core/workers/WorkerLoaderProxy.h"
@@ -32,7 +34,6 @@
 #include "wtf/Functional.h"
 #include "wtf/PtrUtil.h"
 #include "wtf/RefPtr.h"
-#include <memory>
 
 namespace blink {
 
@@ -130,8 +131,9 @@
     ThreadableLoaderOptions options;
     options.crossOriginRequestPolicy = crossOriginRequestPolicy;
     ResourceLoaderOptions resourceLoaderOptions;
-    m_loader = DocumentThreadableLoader::create(document(), client, options,
-                                                resourceLoaderOptions);
+    m_loader = DocumentThreadableLoader::create(
+        *ThreadableLoadingContext::create(document()), client, options,
+        resourceLoaderOptions);
   }
 
   void startLoader(const ResourceRequest& request) override {
@@ -241,6 +243,7 @@
         ParentFrameTaskRunners::create(&m_dummyPageHolder->frame());
     m_workerThread = WTF::wrapUnique(
         new WorkerThreadForTest(this, *m_mockWorkerReportingProxy));
+    m_loadingContext = ThreadableLoadingContext::create(document());
 
     expectWorkerLifetimeReportingCalls();
     m_workerThread->startWithSourceCode(m_securityOrigin.get(),
@@ -341,7 +344,9 @@
     m_workerThread->postTask(location, std::move(task));
   }
 
-  ExecutionContext* getLoaderExecutionContext() override { return &document(); }
+  ThreadableLoadingContext* getThreadableLoadingContext() override {
+    return m_loadingContext.get();
+  }
 
   RefPtr<SecurityOrigin> m_securityOrigin;
   std::unique_ptr<MockWorkerReportingProxy> m_mockWorkerReportingProxy;
@@ -353,6 +358,8 @@
   Checkpoint m_checkpoint;
   // |m_loader| must be touched only from the worker thread only.
   CrossThreadPersistent<ThreadableLoader> m_loader;
+
+  Persistent<ThreadableLoadingContext> m_loadingContext;
 };
 
 class ThreadableLoaderTest
diff --git a/third_party/WebKit/Source/core/loader/ThreadableLoadingContext.cpp b/third_party/WebKit/Source/core/loader/ThreadableLoadingContext.cpp
new file mode 100644
index 0000000..eb48586
--- /dev/null
+++ b/third_party/WebKit/Source/core/loader/ThreadableLoadingContext.cpp
@@ -0,0 +1,76 @@
+// 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/loader/ThreadableLoadingContext.h"
+
+#include "core/dom/Document.h"
+#include "core/dom/TaskRunnerHelper.h"
+#include "platform/loader/fetch/ResourceFetcher.h"
+
+namespace blink {
+
+class DocumentThreadableLoadingContext final : public ThreadableLoadingContext {
+ public:
+  explicit DocumentThreadableLoadingContext(Document& document)
+      : m_document(&document) {}
+
+  ~DocumentThreadableLoadingContext() override = default;
+
+  bool isContextThread() const override {
+    return m_document->isContextThread();
+  }
+
+  ResourceFetcher* getResourceFetcher() override {
+    DCHECK(isContextThread());
+    return m_document->fetcher();
+  }
+
+  SecurityOrigin* getSecurityOrigin() override {
+    DCHECK(isContextThread());
+    return m_document->getSecurityOrigin();
+  }
+
+  bool isSecureContext() const override {
+    DCHECK(isContextThread());
+    return m_document->isSecureContext();
+  }
+
+  KURL firstPartyForCookies() const override {
+    DCHECK(isContextThread());
+    return m_document->firstPartyForCookies();
+  }
+
+  String userAgent() const override {
+    DCHECK(isContextThread());
+    return m_document->userAgent();
+  }
+
+  Document* getLoadingDocument() override {
+    DCHECK(isContextThread());
+    return m_document.get();
+  }
+
+  RefPtr<WebTaskRunner> getTaskRunner(TaskType type) override {
+    return TaskRunnerHelper::get(type, m_document.get());
+  }
+
+  void recordUseCount(UseCounter::Feature feature) override {
+    UseCounter::count(m_document.get(), feature);
+  }
+
+  DEFINE_INLINE_VIRTUAL_TRACE() {
+    visitor->trace(m_document);
+    ThreadableLoadingContext::trace(visitor);
+  }
+
+ private:
+  Member<Document> m_document;
+};
+
+ThreadableLoadingContext* ThreadableLoadingContext::create(Document& document) {
+  // For now this is the only default implementation.
+  return new DocumentThreadableLoadingContext(document);
+}
+
+}  // namespace blink
diff --git a/third_party/WebKit/Source/core/loader/ThreadableLoadingContext.h b/third_party/WebKit/Source/core/loader/ThreadableLoadingContext.h
new file mode 100644
index 0000000..eee4ddb
--- /dev/null
+++ b/third_party/WebKit/Source/core/loader/ThreadableLoadingContext.h
@@ -0,0 +1,54 @@
+// 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 ThreadableLoadingContext_h
+#define ThreadableLoadingContext_h
+
+#include "core/CoreExport.h"
+#include "core/dom/TaskRunnerHelper.h"
+#include "core/frame/UseCounter.h"
+#include "platform/heap/GarbageCollected.h"
+#include "platform/weborigin/KURL.h"
+#include "wtf/Forward.h"
+#include "wtf/Noncopyable.h"
+
+namespace blink {
+
+class Document;
+class ResourceFetcher;
+class SecurityOrigin;
+class WebTaskRunner;
+
+// An abstract interface for top-level loading context.
+// This should be accessed only from the thread where the loading
+// context is bound to (e.g. on the main thread).
+class CORE_EXPORT ThreadableLoadingContext
+    : public GarbageCollected<ThreadableLoadingContext> {
+  WTF_MAKE_NONCOPYABLE(ThreadableLoadingContext);
+
+ public:
+  static ThreadableLoadingContext* create(Document&);
+
+  ThreadableLoadingContext() = default;
+  virtual ~ThreadableLoadingContext() = default;
+
+  virtual bool isContextThread() const = 0;
+
+  virtual ResourceFetcher* getResourceFetcher() = 0;
+  virtual SecurityOrigin* getSecurityOrigin() = 0;
+  virtual bool isSecureContext() const = 0;
+  virtual KURL firstPartyForCookies() const = 0;
+  virtual String userAgent() const = 0;
+  virtual RefPtr<WebTaskRunner> getTaskRunner(TaskType) = 0;
+  virtual void recordUseCount(UseCounter::Feature) = 0;
+
+  // TODO(kinuko): Try getting rid of dependency to Document.
+  virtual Document* getLoadingDocument() { return nullptr; }
+
+  DEFINE_INLINE_VIRTUAL_TRACE() {}
+};
+
+}  // namespace blink
+
+#endif  // ThreadableLoadingContext_h
diff --git a/third_party/WebKit/Source/core/loader/WorkerThreadableLoader.cpp b/third_party/WebKit/Source/core/loader/WorkerThreadableLoader.cpp
index c3ed20de..18ea76e 100644
--- a/third_party/WebKit/Source/core/loader/WorkerThreadableLoader.cpp
+++ b/third_party/WebKit/Source/core/loader/WorkerThreadableLoader.cpp
@@ -30,8 +30,9 @@
 
 #include "core/loader/WorkerThreadableLoader.h"
 
-#include "core/dom/Document.h"
+#include <memory>
 #include "core/loader/DocumentThreadableLoader.h"
+#include "core/loader/ThreadableLoadingContext.h"
 #include "core/timing/WorkerGlobalScopePerformance.h"
 #include "core/workers/WorkerGlobalScope.h"
 #include "core/workers/WorkerLoaderProxy.h"
@@ -45,7 +46,6 @@
 #include "platform/weborigin/SecurityPolicy.h"
 #include "wtf/Functional.h"
 #include "wtf/debug/Alias.h"
-#include <memory>
 
 namespace blink {
 
@@ -436,8 +436,9 @@
   DCHECK(isMainThread());
   TaskForwarder* forwarder;
   RefPtr<WorkerLoaderProxy> loaderProxy = passLoaderProxy;
-  ExecutionContext* loaderContext = loaderProxy->getLoaderExecutionContext();
-  if (!loaderContext)
+  ThreadableLoadingContext* loadingContext =
+      loaderProxy->getThreadableLoadingContext();
+  if (!loadingContext)
     return;
   if (eventWithTasks)
     forwarder = new SyncTaskForwarder(std::move(eventWithTasks));
@@ -458,8 +459,8 @@
       crossThreadBind(&WorkerThreadableLoader::didStart,
                       wrapCrossThreadPersistent(workerLoader),
                       wrapCrossThreadPersistent(mainThreadLoaderHolder)));
-  mainThreadLoaderHolder->start(*toDocument(loaderContext), std::move(request),
-                                options, resourceLoaderOptions);
+  mainThreadLoaderHolder->start(*loadingContext, std::move(request), options,
+                                resourceLoaderOptions);
 }
 
 WorkerThreadableLoader::MainThreadLoaderHolder::~MainThreadLoaderHolder() {
@@ -660,15 +661,15 @@
 }
 
 void WorkerThreadableLoader::MainThreadLoaderHolder::start(
-    Document& document,
+    ThreadableLoadingContext& loadingContext,
     std::unique_ptr<CrossThreadResourceRequestData> request,
     const ThreadableLoaderOptions& options,
     const ResourceLoaderOptions& originalResourceLoaderOptions) {
   DCHECK(isMainThread());
   ResourceLoaderOptions resourceLoaderOptions = originalResourceLoaderOptions;
   resourceLoaderOptions.requestInitiatorContext = WorkerContext;
-  m_mainThreadLoader = DocumentThreadableLoader::create(document, this, options,
-                                                        resourceLoaderOptions);
+  m_mainThreadLoader = DocumentThreadableLoader::create(
+      loadingContext, this, options, resourceLoaderOptions);
   m_mainThreadLoader->start(ResourceRequest(request.get()));
 }
 
diff --git a/third_party/WebKit/Source/core/loader/WorkerThreadableLoader.h b/third_party/WebKit/Source/core/loader/WorkerThreadableLoader.h
index 6eabba2..40b169dd 100644
--- a/third_party/WebKit/Source/core/loader/WorkerThreadableLoader.h
+++ b/third_party/WebKit/Source/core/loader/WorkerThreadableLoader.h
@@ -47,6 +47,7 @@
 
 namespace blink {
 
+class ThreadableLoadingContext;
 class ResourceError;
 class ResourceRequest;
 class ResourceResponse;
@@ -172,7 +173,7 @@
 
    private:
     MainThreadLoaderHolder(TaskForwarder*, WorkerThreadLifecycleContext*);
-    void start(Document&,
+    void start(ThreadableLoadingContext&,
                std::unique_ptr<CrossThreadResourceRequestData>,
                const ThreadableLoaderOptions&,
                const ResourceLoaderOptions&);
diff --git a/third_party/WebKit/Source/core/page/Page.cpp b/third_party/WebKit/Source/core/page/Page.cpp
index 8f4ead3..1f6910a 100644
--- a/third_party/WebKit/Source/core/page/Page.cpp
+++ b/third_party/WebKit/Source/core/page/Page.cpp
@@ -34,6 +34,7 @@
 #include "core/frame/FrameConsole.h"
 #include "core/frame/FrameHost.h"
 #include "core/frame/FrameView.h"
+#include "core/frame/PageScaleConstraintsSet.h"
 #include "core/frame/RemoteFrame.h"
 #include "core/frame/RemoteFrameView.h"
 #include "core/frame/Settings.h"
@@ -99,6 +100,7 @@
       m_focusController(FocusController::create(this)),
       m_contextMenuController(
           ContextMenuController::create(this, pageClients.contextMenuClient)),
+      m_pageScaleConstraintsSet(PageScaleConstraintsSet::create()),
       m_pointerLockController(PointerLockController::create(this)),
       m_mainFrame(nullptr),
       m_editorClient(pageClients.editorClient),
@@ -150,6 +152,14 @@
   return m_scrollingCoordinator.get();
 }
 
+PageScaleConstraintsSet& Page::pageScaleConstraintsSet() {
+  return *m_pageScaleConstraintsSet;
+}
+
+const PageScaleConstraintsSet& Page::pageScaleConstraintsSet() const {
+  return *m_pageScaleConstraintsSet;
+}
+
 ClientRectList* Page::nonFastScrollableRects(const LocalFrame* frame) {
   DisableCompositingQueryAsserts disabler;
   if (ScrollingCoordinator* scrollingCoordinator =
diff --git a/third_party/WebKit/Source/core/page/Page.h b/third_party/WebKit/Source/core/page/Page.h
index 32b7bcfa..8fbd28d 100644
--- a/third_party/WebKit/Source/core/page/Page.h
+++ b/third_party/WebKit/Source/core/page/Page.h
@@ -58,6 +58,7 @@
 class FocusController;
 class Frame;
 class FrameHost;
+class PageScaleConstraintsSet;
 class PluginData;
 class PointerLockController;
 class ScopedPageSuspender;
@@ -180,6 +181,9 @@
   Deprecation& deprecation() { return m_deprecation; }
   HostsUsingFeatures& hostsUsingFeatures() { return m_hostsUsingFeatures; }
 
+  PageScaleConstraintsSet& pageScaleConstraintsSet();
+  const PageScaleConstraintsSet& pageScaleConstraintsSet() const;
+
   void setTabKeyCyclesThroughElements(bool b) {
     m_tabKeyCyclesThroughElements = b;
   }
@@ -258,6 +262,7 @@
   const Member<DragController> m_dragController;
   const Member<FocusController> m_focusController;
   const Member<ContextMenuController> m_contextMenuController;
+  const std::unique_ptr<PageScaleConstraintsSet> m_pageScaleConstraintsSet;
   const Member<PointerLockController> m_pointerLockController;
   Member<ScrollingCoordinator> m_scrollingCoordinator;
 
diff --git a/third_party/WebKit/Source/core/workers/ThreadedMessagingProxyBase.cpp b/third_party/WebKit/Source/core/workers/ThreadedMessagingProxyBase.cpp
index 62dd73a..f5cf18a 100644
--- a/third_party/WebKit/Source/core/workers/ThreadedMessagingProxyBase.cpp
+++ b/third_party/WebKit/Source/core/workers/ThreadedMessagingProxyBase.cpp
@@ -6,9 +6,9 @@
 
 #include "bindings/core/v8/SourceLocation.h"
 #include "core/dom/Document.h"
-#include "core/dom/ExecutionContextTask.h"
 #include "core/frame/Deprecation.h"
 #include "core/loader/DocumentLoader.h"
+#include "core/loader/ThreadableLoadingContext.h"
 #include "core/workers/WorkerInspectorProxy.h"
 #include "core/workers/WorkerThreadStartupData.h"
 #include "wtf/CurrentTime.h"
@@ -77,9 +77,14 @@
       ->postTask(BLINK_FROM_HERE, std::move(task));
 }
 
-ExecutionContext* ThreadedMessagingProxyBase::getLoaderExecutionContext() {
+ThreadableLoadingContext*
+ThreadedMessagingProxyBase::getThreadableLoadingContext() {
   DCHECK(isParentContextThread());
-  return getExecutionContext();
+  if (!m_loadingContext) {
+    m_loadingContext =
+        ThreadableLoadingContext::create(*toDocument(m_executionContext));
+  }
+  return m_loadingContext;
 }
 
 void ThreadedMessagingProxyBase::countFeature(UseCounter::Feature feature) {
diff --git a/third_party/WebKit/Source/core/workers/ThreadedMessagingProxyBase.h b/third_party/WebKit/Source/core/workers/ThreadedMessagingProxyBase.h
index 978f8059..89aaabb4d 100644
--- a/third_party/WebKit/Source/core/workers/ThreadedMessagingProxyBase.h
+++ b/third_party/WebKit/Source/core/workers/ThreadedMessagingProxyBase.h
@@ -85,7 +85,7 @@
   void postTaskToWorkerGlobalScope(
       const WebTraceLocation&,
       std::unique_ptr<WTF::CrossThreadClosure>) override;
-  ExecutionContext* getLoaderExecutionContext() override;
+  ThreadableLoadingContext* getThreadableLoadingContext() override;
 
  private:
   friend class InProcessWorkerMessagingProxyForTest;
@@ -94,6 +94,7 @@
   void parentObjectDestroyedInternal();
 
   Persistent<ExecutionContext> m_executionContext;
+  Persistent<ThreadableLoadingContext> m_loadingContext;
   Persistent<WorkerInspectorProxy> m_workerInspectorProxy;
   // Accessed cross-thread when worker thread posts tasks to the parent.
   CrossThreadPersistent<ParentFrameTaskRunners> m_parentFrameTaskRunners;
diff --git a/third_party/WebKit/Source/core/workers/WorkerLoaderProxy.cpp b/third_party/WebKit/Source/core/workers/WorkerLoaderProxy.cpp
index 01e0c417..5220a03 100644
--- a/third_party/WebKit/Source/core/workers/WorkerLoaderProxy.cpp
+++ b/third_party/WebKit/Source/core/workers/WorkerLoaderProxy.cpp
@@ -4,7 +4,7 @@
 
 #include "core/workers/WorkerLoaderProxy.h"
 
-#include "core/dom/ExecutionContext.h"
+#include "core/loader/ThreadableLoadingContext.h"
 
 namespace blink {
 
@@ -44,13 +44,14 @@
   m_loaderProxyProvider->postTaskToWorkerGlobalScope(location, std::move(task));
 }
 
-ExecutionContext* WorkerLoaderProxy::getLoaderExecutionContext() {
+ThreadableLoadingContext* WorkerLoaderProxy::getThreadableLoadingContext() {
   DCHECK(isMainThread());
   // Note: No locking needed for the access from the main thread.
   if (!m_loaderProxyProvider)
     return nullptr;
-  DCHECK(m_loaderProxyProvider->getLoaderExecutionContext()->isContextThread());
-  return m_loaderProxyProvider->getLoaderExecutionContext();
+  DCHECK(
+      m_loaderProxyProvider->getThreadableLoadingContext()->isContextThread());
+  return m_loaderProxyProvider->getThreadableLoadingContext();
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/workers/WorkerLoaderProxy.h b/third_party/WebKit/Source/core/workers/WorkerLoaderProxy.h
index c2235f02..323ff01d 100644
--- a/third_party/WebKit/Source/core/workers/WorkerLoaderProxy.h
+++ b/third_party/WebKit/Source/core/workers/WorkerLoaderProxy.h
@@ -41,7 +41,7 @@
 
 namespace blink {
 
-class ExecutionContext;
+class ThreadableLoadingContext;
 
 // The WorkerLoaderProxy is a proxy to the loader context. Normally, the
 // document on the main thread provides loading services for the subordinate
@@ -77,8 +77,8 @@
       std::unique_ptr<WTF::CrossThreadClosure>) = 0;
 
   // It is guaranteed that this gets accessed only on the thread where
-  // the ExecutionContext is bound.
-  virtual ExecutionContext* getLoaderExecutionContext() = 0;
+  // the loading context is bound.
+  virtual ThreadableLoadingContext* getThreadableLoadingContext() = 0;
 };
 
 class CORE_EXPORT WorkerLoaderProxy final
@@ -101,8 +101,8 @@
 
   // This may return nullptr.
   // This must be called from the main thread (== the thread of the
-  // loader execution context).
-  ExecutionContext* getLoaderExecutionContext();
+  // loading context).
+  ThreadableLoadingContext* getThreadableLoadingContext();
 
   // Notification from the provider that it can no longer be accessed. An
   // implementation of WorkerLoaderProxyProvider is required to call
diff --git a/third_party/WebKit/Source/core/workers/WorkerThreadTestHelper.h b/third_party/WebKit/Source/core/workers/WorkerThreadTestHelper.h
index 765aa09..6fa6600 100644
--- a/third_party/WebKit/Source/core/workers/WorkerThreadTestHelper.h
+++ b/third_party/WebKit/Source/core/workers/WorkerThreadTestHelper.h
@@ -52,7 +52,7 @@
     NOTIMPLEMENTED();
   }
 
-  ExecutionContext* getLoaderExecutionContext() override {
+  ThreadableLoadingContext* getThreadableLoadingContext() override {
     NOTIMPLEMENTED();
     return nullptr;
   }
diff --git a/third_party/WebKit/Source/devtools/front_end/main/Main.js b/third_party/WebKit/Source/devtools/front_end/main/Main.js
index 5b211ff..f638ac8 100644
--- a/third_party/WebKit/Source/devtools/front_end/main/Main.js
+++ b/third_party/WebKit/Source/devtools/front_end/main/Main.js
@@ -115,7 +115,7 @@
     Runtime.experiments.register('sourceDiff', 'Source diff');
     Runtime.experiments.register('terminalInDrawer', 'Terminal in drawer', true);
     Runtime.experiments.register('timelineInvalidationTracking', 'Timeline invalidation tracking', true);
-    Runtime.experiments.register('timelineMultipleMainViews', 'Timeline with multiple main views');
+    Runtime.experiments.register('timelineMultipleMainViews', 'Tabbed views on Performance panel');
     Runtime.experiments.register('timelineTracingJSProfile', 'Timeline tracing based JS profiler', true);
     Runtime.experiments.register('timelineV8RuntimeCallStats', 'V8 Runtime Call Stats on Timeline', true);
     Runtime.experiments.register('timelinePerFrameTrack', 'Show track per frame on Timeline', true);
@@ -135,7 +135,7 @@
         Runtime.experiments.enableForTest('releaseNote');
     }
 
-    Runtime.experiments.setDefaultExperiments(['persistenceValidation', 'timelineMultipleMainViews']);
+    Runtime.experiments.setDefaultExperiments(['persistenceValidation']);
   }
 
   /**
diff --git a/third_party/WebKit/Source/devtools/front_end/timeline/TimelineDetailsView.js b/third_party/WebKit/Source/devtools/front_end/timeline/TimelineDetailsView.js
index 615df98..bb3dc5e5 100644
--- a/third_party/WebKit/Source/devtools/front_end/timeline/TimelineDetailsView.js
+++ b/third_party/WebKit/Source/devtools/front_end/timeline/TimelineDetailsView.js
@@ -42,8 +42,8 @@
       this._rangeDetailViews.set(tabIds.CallTree, callTreeView);
 
       const eventsView = new Timeline.EventsTimelineTreeView(filters, delegate);
-      this._appendTab(tabIds.Events, Common.UIString('Event Log'), eventsView);
-      this._rangeDetailViews.set(tabIds.Events, eventsView);
+      this._appendTab(tabIds.EventLog, Common.UIString('Event Log'), eventsView);
+      this._rangeDetailViews.set(tabIds.EventLog, eventsView);
     }
 
     this._tabbedPane.addEventListener(UI.TabbedPane.Events.TabSelected, this._tabSelected, this);
@@ -237,7 +237,7 @@
  */
 Timeline.TimelineDetailsView.Tab = {
   Details: 'Details',
-  Events: 'Events',
+  EventLog: 'EventLog',
   CallTree: 'CallTree',
   BottomUp: 'BottomUp',
   PaintProfiler: 'PaintProfiler',
diff --git a/third_party/WebKit/Source/modules/websockets/DocumentWebSocketChannel.cpp b/third_party/WebKit/Source/modules/websockets/DocumentWebSocketChannel.cpp
index 743f06d..089e790 100644
--- a/third_party/WebKit/Source/modules/websockets/DocumentWebSocketChannel.cpp
+++ b/third_party/WebKit/Source/modules/websockets/DocumentWebSocketChannel.cpp
@@ -32,7 +32,6 @@
 
 #include <memory>
 #include "core/dom/DOMArrayBuffer.h"
-#include "core/dom/Document.h"
 #include "core/dom/ExecutionContext.h"
 #include "core/fileapi/FileReaderLoader.h"
 #include "core/fileapi/FileReaderLoaderClient.h"
@@ -42,6 +41,7 @@
 #include "core/inspector/InspectorInstrumentation.h"
 #include "core/loader/FrameLoader.h"
 #include "core/loader/MixedContentChecker.h"
+#include "core/loader/ThreadableLoadingContext.h"
 #include "modules/websockets/InspectorWebSocketEvents.h"
 #include "modules/websockets/WebSocketChannelClient.h"
 #include "modules/websockets/WebSocketFrame.h"
@@ -108,6 +108,7 @@
     : m_channel(channel),
       m_loader(
           FileReaderLoader::create(FileReaderLoader::ReadAsArrayBuffer, this)) {
+  // TODO(kinuko): Remove dependency to document.
   m_loader->start(channel->document(), std::move(blobDataHandle));
 }
 
@@ -129,14 +130,14 @@
 }
 
 DocumentWebSocketChannel::DocumentWebSocketChannel(
-    Document* document,
+    ThreadableLoadingContext* loadingContext,
     WebSocketChannelClient* client,
     std::unique_ptr<SourceLocation> location,
     WebSocketHandle* handle)
     : m_handle(WTF::wrapUnique(handle ? handle : new WebSocketHandleImpl())),
       m_client(client),
       m_identifier(createUniqueIdentifier()),
-      m_document(document),
+      m_loadingContext(loadingContext),
       m_sendingQuota(0),
       m_receivedDataSizeForFlowControl(
           receivedDataSizeForFlowControlHighWaterMark * 2),  // initial quota
@@ -153,22 +154,24 @@
   if (!m_handle)
     return false;
 
-  if (document()->frame()) {
-    if (MixedContentChecker::shouldBlockWebSocket(document()->frame(), url))
-      return false;
-  }
-  if (MixedContentChecker::isMixedContent(document()->getSecurityOrigin(),
-                                          url)) {
-    String message =
-        "Connecting to a non-secure WebSocket server from a secure origin is "
-        "deprecated.";
-    document()->addConsoleMessage(
-        ConsoleMessage::create(JSMessageSource, WarningMessageLevel, message));
-  }
+  if (document()) {
+    if (document()->frame()) {
+      if (MixedContentChecker::shouldBlockWebSocket(document()->frame(), url))
+        return false;
+    }
+    if (MixedContentChecker::isMixedContent(document()->getSecurityOrigin(),
+                                            url)) {
+      String message =
+          "Connecting to a non-secure WebSocket server from a secure origin is "
+          "deprecated.";
+      document()->addConsoleMessage(ConsoleMessage::create(
+          JSMessageSource, WarningMessageLevel, message));
+    }
 
-  if (document()->frame()) {
-    connection_handle_for_scheduler_ =
-        document()->frame()->frameScheduler()->onActiveConnectionCreated();
+    if (document()->frame()) {
+      connection_handle_for_scheduler_ =
+          document()->frame()->frameScheduler()->onActiveConnectionCreated();
+    }
   }
 
   m_url = url;
@@ -181,7 +184,9 @@
     protocol.split(", ", true, protocols);
   }
 
-  if (document()->frame() &&
+  // TODO(kinuko): document() should return nullptr if we don't
+  // have valid document/frame that returns non-empty interface provider.
+  if (document() && document()->frame() &&
       document()->frame()->interfaceProvider() !=
           InterfaceProvider::getEmptyInterfaceProvider()) {
     // Initialize the WebSocketHandle with the frame's InterfaceProvider to
@@ -192,9 +197,9 @@
   } else {
     m_handle->initialize(Platform::current()->interfaceProvider());
   }
-  m_handle->connect(url, protocols, document()->getSecurityOrigin(),
-                    document()->firstPartyForCookies(), document()->userAgent(),
-                    this);
+  m_handle->connect(url, protocols, m_loadingContext->getSecurityOrigin(),
+                    m_loadingContext->firstPartyForCookies(),
+                    m_loadingContext->userAgent(), this);
 
   flowControlIfNecessary();
   TRACE_EVENT_INSTANT1("devtools.timeline", "WebSocketCreate",
@@ -296,12 +301,14 @@
 
   connection_handle_for_scheduler_.reset();
 
-  InspectorInstrumentation::didReceiveWebSocketFrameError(document(),
-                                                          m_identifier, reason);
-  const String message = "WebSocket connection to '" + m_url.elidedString() +
-                         "' failed: " + reason;
-  document()->addConsoleMessage(ConsoleMessage::create(
-      JSMessageSource, level, message, std::move(location)));
+  if (document()) {
+    InspectorInstrumentation::didReceiveWebSocketFrameError(
+        document(), m_identifier, reason);
+    const String message = "WebSocket connection to '" + m_url.elidedString() +
+                           "' failed: " + reason;
+    document()->addConsoleMessage(ConsoleMessage::create(
+        JSMessageSource, level, message, std::move(location)));
+  }
 
   if (m_client)
     m_client->didError();
@@ -457,8 +464,12 @@
   // client->didClose may delete this object.
 }
 
+ThreadableLoadingContext* DocumentWebSocketChannel::loadingContext() {
+  return m_loadingContext;
+}
+
 Document* DocumentWebSocketChannel::document() {
-  return m_document;
+  return m_loadingContext->getLoadingDocument();
 }
 
 void DocumentWebSocketChannel::didConnect(WebSocketHandle* handle,
@@ -483,11 +494,14 @@
   DCHECK(m_handle);
   DCHECK_EQ(handle, m_handle.get());
 
-  TRACE_EVENT_INSTANT1("devtools.timeline", "WebSocketSendHandshakeRequest",
-                       TRACE_EVENT_SCOPE_THREAD, "data",
-                       InspectorWebSocketEvent::data(document(), m_identifier));
-  InspectorInstrumentation::willSendWebSocketHandshakeRequest(
-      document(), m_identifier, request.get());
+  if (document()) {
+    TRACE_EVENT_INSTANT1(
+        "devtools.timeline", "WebSocketSendHandshakeRequest",
+        TRACE_EVENT_SCOPE_THREAD, "data",
+        InspectorWebSocketEvent::data(document(), m_identifier));
+    InspectorInstrumentation::willSendWebSocketHandshakeRequest(
+        document(), m_identifier, request.get());
+  }
   m_handshakeRequest = request;
 }
 
@@ -499,11 +513,14 @@
   DCHECK(m_handle);
   DCHECK_EQ(handle, m_handle.get());
 
-  TRACE_EVENT_INSTANT1("devtools.timeline", "WebSocketReceiveHandshakeResponse",
-                       TRACE_EVENT_SCOPE_THREAD, "data",
-                       InspectorWebSocketEvent::data(document(), m_identifier));
-  InspectorInstrumentation::didReceiveWebSocketHandshakeResponse(
-      document(), m_identifier, m_handshakeRequest.get(), response);
+  if (document()) {
+    TRACE_EVENT_INSTANT1(
+        "devtools.timeline", "WebSocketReceiveHandshakeResponse",
+        TRACE_EVENT_SCOPE_THREAD, "data",
+        InspectorWebSocketEvent::data(document(), m_identifier));
+    InspectorInstrumentation::didReceiveWebSocketHandshakeResponse(
+        document(), m_identifier, m_handshakeRequest.get(), response);
+  }
   m_handshakeRequest.clear();
 }
 
@@ -566,9 +583,11 @@
                                       : WebSocketFrame::OpCodeBinary;
   WebSocketFrame frame(opcode, m_receivingMessageData.data(),
                        m_receivingMessageData.size(), WebSocketFrame::Final);
-  InspectorInstrumentation::didReceiveWebSocketFrame(
-      document(), m_identifier, frame.opCode, frame.masked, frame.payload,
-      frame.payloadLength);
+  if (document()) {
+    InspectorInstrumentation::didReceiveWebSocketFrame(
+        document(), m_identifier, frame.opCode, frame.masked, frame.payload,
+        frame.payloadLength);
+  }
   if (m_receivingMessageTypeIsText) {
     String message = m_receivingMessageData.isEmpty()
                          ? emptyString
@@ -603,7 +622,7 @@
 
   m_handle.reset();
 
-  if (m_identifier) {
+  if (m_identifier && document()) {
     TRACE_EVENT_INSTANT1(
         "devtools.timeline", "WebSocketDestroy", TRACE_EVENT_SCOPE_THREAD,
         "data", InspectorWebSocketEvent::data(document(), m_identifier));
@@ -666,7 +685,7 @@
   visitor->trace(m_blobLoader);
   visitor->trace(m_messages);
   visitor->trace(m_client);
-  visitor->trace(m_document);
+  visitor->trace(m_loadingContext);
   WebSocketChannel::trace(visitor);
 }
 
diff --git a/third_party/WebKit/Source/modules/websockets/DocumentWebSocketChannel.h b/third_party/WebKit/Source/modules/websockets/DocumentWebSocketChannel.h
index 90eb1f2..319c14a 100644
--- a/third_party/WebKit/Source/modules/websockets/DocumentWebSocketChannel.h
+++ b/third_party/WebKit/Source/modules/websockets/DocumentWebSocketChannel.h
@@ -36,6 +36,7 @@
 #include "bindings/core/v8/SourceLocation.h"
 #include "core/fileapi/Blob.h"
 #include "core/fileapi/FileError.h"
+#include "core/loader/ThreadableLoadingContext.h"
 #include "modules/ModulesExport.h"
 #include "modules/websockets/WebSocketChannel.h"
 #include "modules/websockets/WebSocketHandle.h"
@@ -52,7 +53,7 @@
 
 namespace blink {
 
-class Document;
+class ThreadableLoadingContext;
 class WebSocketHandshakeRequest;
 
 // This class is a WebSocketChannel subclass that works with a Document in a
@@ -71,8 +72,17 @@
       WebSocketChannelClient* client,
       std::unique_ptr<SourceLocation> location,
       WebSocketHandle* handle = 0) {
-    return new DocumentWebSocketChannel(document, client, std::move(location),
-                                        handle);
+    DCHECK(document);
+    return create(ThreadableLoadingContext::create(*document), client,
+                  std::move(location), handle);
+  }
+  static DocumentWebSocketChannel* create(
+      ThreadableLoadingContext* loadingContext,
+      WebSocketChannelClient* client,
+      std::unique_ptr<SourceLocation> location,
+      WebSocketHandle* handle = 0) {
+    return new DocumentWebSocketChannel(loadingContext, client,
+                                        std::move(location), handle);
   }
   ~DocumentWebSocketChannel() override;
 
@@ -113,7 +123,7 @@
     Vector<char> data;
   };
 
-  DocumentWebSocketChannel(Document*,
+  DocumentWebSocketChannel(ThreadableLoadingContext*,
                            WebSocketChannelClient*,
                            std::unique_ptr<SourceLocation>,
                            WebSocketHandle*);
@@ -128,6 +138,10 @@
   }
   void abortAsyncOperations();
   void handleDidClose(bool wasClean, unsigned short code, const String& reason);
+  ThreadableLoadingContext* loadingContext();
+
+  // This may return nullptr.
+  // TODO(kinuko): Remove dependency to document.
   Document* document();
 
   // WebSocketHandleClient functions.
@@ -168,7 +182,7 @@
   Member<BlobLoader> m_blobLoader;
   HeapDeque<Member<Message>> m_messages;
   Vector<char> m_receivingMessageData;
-  Member<Document> m_document;
+  Member<ThreadableLoadingContext> m_loadingContext;
 
   bool m_receivingMessageTypeIsText;
   uint64_t m_sendingQuota;
diff --git a/third_party/WebKit/Source/modules/websockets/WebSocketChannel.h b/third_party/WebKit/Source/modules/websockets/WebSocketChannel.h
index 5d6dafc..bcf56de 100644
--- a/third_party/WebKit/Source/modules/websockets/WebSocketChannel.h
+++ b/third_party/WebKit/Source/modules/websockets/WebSocketChannel.h
@@ -43,7 +43,6 @@
 
 class BlobDataHandle;
 class DOMArrayBuffer;
-class ExecutionContext;
 class KURL;
 class WebSocketChannelClient;
 
diff --git a/third_party/WebKit/Source/modules/websockets/WorkerWebSocketChannel.cpp b/third_party/WebKit/Source/modules/websockets/WorkerWebSocketChannel.cpp
index 79cf91f7..8e70bb7 100644
--- a/third_party/WebKit/Source/modules/websockets/WorkerWebSocketChannel.cpp
+++ b/third_party/WebKit/Source/modules/websockets/WorkerWebSocketChannel.cpp
@@ -32,9 +32,9 @@
 
 #include <memory>
 #include "core/dom/DOMArrayBuffer.h"
-#include "core/dom/Document.h"
 #include "core/dom/ExecutionContext.h"
 #include "core/fileapi/Blob.h"
+#include "core/loader/ThreadableLoadingContext.h"
 #include "core/workers/WorkerGlobalScope.h"
 #include "core/workers/WorkerLoaderProxy.h"
 #include "core/workers/WorkerThread.h"
@@ -175,13 +175,12 @@
 }
 
 bool Peer::initialize(std::unique_ptr<SourceLocation> location,
-                      ExecutionContext* context) {
+                      ThreadableLoadingContext* loadingContext) {
   DCHECK(isMainThread());
   if (wasContextDestroyedBeforeObserverCreation())
     return false;
-  Document* document = toDocument(context);
-  m_mainWebSocketChannel =
-      DocumentWebSocketChannel::create(document, this, std::move(location));
+  m_mainWebSocketChannel = DocumentWebSocketChannel::create(
+      loadingContext, this, std::move(location));
   return true;
 }
 
@@ -371,11 +370,12 @@
     WebSocketChannelSyncHelper* syncHelper) {
   DCHECK(isMainThread());
   DCHECK(!m_peer);
-  ExecutionContext* loaderContext = loaderProxy->getLoaderExecutionContext();
-  if (!loaderContext)
+  ThreadableLoadingContext* loadingContext =
+      loaderProxy->getThreadableLoadingContext();
+  if (!loadingContext)
     return;
   Peer* peer = new Peer(this, m_loaderProxy, workerThreadLifecycleContext);
-  if (peer->initialize(std::move(location), loaderContext)) {
+  if (peer->initialize(std::move(location), loadingContext)) {
     m_peer = peer;
     syncHelper->setConnectRequestResult(m_peer->connect(url, protocol));
   }
diff --git a/third_party/WebKit/Source/modules/websockets/WorkerWebSocketChannel.h b/third_party/WebKit/Source/modules/websockets/WorkerWebSocketChannel.h
index 883b12d6..55340a1 100644
--- a/third_party/WebKit/Source/modules/websockets/WorkerWebSocketChannel.h
+++ b/third_party/WebKit/Source/modules/websockets/WorkerWebSocketChannel.h
@@ -49,7 +49,7 @@
 
 class BlobDataHandle;
 class KURL;
-class ExecutionContext;
+class ThreadableLoadingContext;
 class WebSocketChannelSyncHelper;
 class WorkerGlobalScope;
 class WorkerLoaderProxy;
@@ -100,7 +100,7 @@
     ~Peer() override;
 
     // SourceLocation parameter may be shown when the connection fails.
-    bool initialize(std::unique_ptr<SourceLocation>, ExecutionContext*);
+    bool initialize(std::unique_ptr<SourceLocation>, ThreadableLoadingContext*);
 
     bool connect(const KURL&, const String& protocol);
     void sendTextAsCharVector(std::unique_ptr<Vector<char>>);
diff --git a/third_party/WebKit/Source/web/WebAssociatedURLLoaderImpl.cpp b/third_party/WebKit/Source/web/WebAssociatedURLLoaderImpl.cpp
index fdc51d5..7c7e5adc 100644
--- a/third_party/WebKit/Source/web/WebAssociatedURLLoaderImpl.cpp
+++ b/third_party/WebKit/Source/web/WebAssociatedURLLoaderImpl.cpp
@@ -30,10 +30,13 @@
 
 #include "web/WebAssociatedURLLoaderImpl.h"
 
+#include <limits.h>
+#include <memory>
 #include "core/dom/ContextLifecycleObserver.h"
 #include "core/dom/TaskRunnerHelper.h"
 #include "core/loader/DocumentThreadableLoader.h"
 #include "core/loader/DocumentThreadableLoaderClient.h"
+#include "core/loader/ThreadableLoadingContext.h"
 #include "platform/Timer.h"
 #include "platform/exported/WrappedResourceRequest.h"
 #include "platform/exported/WrappedResourceResponse.h"
@@ -51,8 +54,6 @@
 #include "wtf/HashSet.h"
 #include "wtf/PtrUtil.h"
 #include "wtf/text/WTFString.h"
-#include <limits.h>
-#include <memory>
 
 namespace blink {
 
@@ -421,7 +422,8 @@
     Document* document = toDocument(m_observer->lifecycleContext());
     DCHECK(document);
     m_loader = DocumentThreadableLoader::create(
-        *document, m_clientAdapter.get(), options, resourceLoaderOptions);
+        *ThreadableLoadingContext::create(*document), m_clientAdapter.get(),
+        options, resourceLoaderOptions);
     m_loader->start(webcoreRequest);
   }
 
diff --git a/third_party/WebKit/Source/web/WebEmbeddedWorkerImpl.cpp b/third_party/WebKit/Source/web/WebEmbeddedWorkerImpl.cpp
index e8ebbf6..3135c41 100644
--- a/third_party/WebKit/Source/web/WebEmbeddedWorkerImpl.cpp
+++ b/third_party/WebKit/Source/web/WebEmbeddedWorkerImpl.cpp
@@ -30,6 +30,7 @@
 
 #include "web/WebEmbeddedWorkerImpl.h"
 
+#include <memory>
 #include "bindings/core/v8/SourceLocation.h"
 #include "core/dom/Document.h"
 #include "core/dom/ExecutionContextTask.h"
@@ -38,6 +39,7 @@
 #include "core/inspector/ConsoleMessage.h"
 #include "core/inspector/InspectorInstrumentation.h"
 #include "core/loader/FrameLoadRequest.h"
+#include "core/loader/ThreadableLoadingContext.h"
 #include "core/workers/ParentFrameTaskRunners.h"
 #include "core/workers/WorkerClients.h"
 #include "core/workers/WorkerGlobalScope.h"
@@ -73,7 +75,6 @@
 #include "web/WorkerContentSettingsClient.h"
 #include "wtf/Functional.h"
 #include "wtf/PtrUtil.h"
-#include <memory>
 
 namespace blink {
 
@@ -275,8 +276,12 @@
   m_workerThread->postTask(location, std::move(task));
 }
 
-ExecutionContext* WebEmbeddedWorkerImpl::getLoaderExecutionContext() {
-  return m_mainFrame->frame()->document();
+ThreadableLoadingContext* WebEmbeddedWorkerImpl::getThreadableLoadingContext() {
+  if (!m_loadingContext) {
+    m_loadingContext =
+        ThreadableLoadingContext::create(*m_mainFrame->frame()->document());
+  }
+  return m_loadingContext;
 }
 
 void WebEmbeddedWorkerImpl::prepareShadowPageForLoader() {
diff --git a/third_party/WebKit/Source/web/WebEmbeddedWorkerImpl.h b/third_party/WebKit/Source/web/WebEmbeddedWorkerImpl.h
index 78856bc..d98cd69 100644
--- a/third_party/WebKit/Source/web/WebEmbeddedWorkerImpl.h
+++ b/third_party/WebKit/Source/web/WebEmbeddedWorkerImpl.h
@@ -43,6 +43,7 @@
 
 namespace blink {
 
+class ThreadableLoadingContext;
 class ParentFrameTaskRunners;
 class ServiceWorkerGlobalScopeProxy;
 class WebLocalFrameImpl;
@@ -106,7 +107,7 @@
   void postTaskToWorkerGlobalScope(
       const WebTraceLocation&,
       std::unique_ptr<WTF::CrossThreadClosure>) override;
-  ExecutionContext* getLoaderExecutionContext() override;
+  ThreadableLoadingContext* getThreadableLoadingContext() override;
 
   WebEmbeddedWorkerStartData m_workerStartData;
 
@@ -139,6 +140,7 @@
   WebView* m_webView;
 
   Persistent<WebLocalFrameImpl> m_mainFrame;
+  Persistent<ThreadableLoadingContext> m_loadingContext;
 
   bool m_loadingShadowPage;
   bool m_askedToTerminate;
diff --git a/third_party/WebKit/Source/web/WebSharedWorkerImpl.cpp b/third_party/WebKit/Source/web/WebSharedWorkerImpl.cpp
index bbeff519..bc785668 100644
--- a/third_party/WebKit/Source/web/WebSharedWorkerImpl.cpp
+++ b/third_party/WebKit/Source/web/WebSharedWorkerImpl.cpp
@@ -30,12 +30,14 @@
 
 #include "web/WebSharedWorkerImpl.h"
 
+#include <memory>
 #include "core/dom/Document.h"
 #include "core/events/MessageEvent.h"
 #include "core/inspector/ConsoleMessage.h"
 #include "core/inspector/InspectorInstrumentation.h"
 #include "core/loader/FrameLoadRequest.h"
 #include "core/loader/FrameLoader.h"
+#include "core/loader/ThreadableLoadingContext.h"
 #include "core/workers/ParentFrameTaskRunners.h"
 #include "core/workers/SharedWorkerGlobalScope.h"
 #include "core/workers/SharedWorkerThread.h"
@@ -70,7 +72,6 @@
 #include "web/WorkerContentSettingsClient.h"
 #include "wtf/Functional.h"
 #include "wtf/PtrUtil.h"
-#include <memory>
 
 namespace blink {
 
@@ -276,9 +277,12 @@
   m_workerThread->postTask(location, std::move(task));
 }
 
-ExecutionContext* WebSharedWorkerImpl::getLoaderExecutionContext() {
-  DCHECK(isMainThread());
-  return m_loadingDocument.get();
+ThreadableLoadingContext* WebSharedWorkerImpl::getThreadableLoadingContext() {
+  if (!m_loadingContext) {
+    m_loadingContext =
+        ThreadableLoadingContext::create(*toDocument(m_loadingDocument.get()));
+  }
+  return m_loadingContext;
 }
 
 void WebSharedWorkerImpl::connect(WebMessagePortChannel* webChannel) {
diff --git a/third_party/WebKit/Source/web/WebSharedWorkerImpl.h b/third_party/WebKit/Source/web/WebSharedWorkerImpl.h
index 1a37ac931..7454b51 100644
--- a/third_party/WebKit/Source/web/WebSharedWorkerImpl.h
+++ b/third_party/WebKit/Source/web/WebSharedWorkerImpl.h
@@ -137,11 +137,12 @@
   void postTaskToWorkerGlobalScope(
       const WebTraceLocation&,
       std::unique_ptr<WTF::CrossThreadClosure>) override;
-  ExecutionContext* getLoaderExecutionContext() override;
+  ThreadableLoadingContext* getThreadableLoadingContext() override;
 
   // 'shadow page' - created to proxy loading requests from the worker.
   // Will be accessed by worker thread when posting tasks.
   Persistent<ExecutionContext> m_loadingDocument;
+  Persistent<ThreadableLoadingContext> m_loadingContext;
   WebView* m_webView;
   Persistent<WebLocalFrameImpl> m_mainFrame;
   bool m_askedToTerminate;
diff --git a/ui/file_manager/externs/platform.js b/ui/file_manager/externs/platform.js
index 693489c..2dfe238a 100644
--- a/ui/file_manager/externs/platform.js
+++ b/ui/file_manager/externs/platform.js
@@ -43,12 +43,3 @@
  * @see http://dev.w3.org/html5/spec-author-view/video.html#mediaerror
  */
 MediaError.MEDIA_ERR_SRC_NOT_SUPPORTED = 4;
-
-/**
- * Animate method.
- * @param {!Array<!Object>} frames
- * @param {number} duration
- * @see http://www.w3.org/TR/web-animations/#widl-Animatable-animate-
- *     AnimationPlayer-object-effect--double-or-AnimationTimingInput--timing
- */
-HTMLElement.prototype.animate = function(frames, duration) {};
diff --git a/ui/file_manager/file_manager/foreground/js/compiled_resources.gyp b/ui/file_manager/file_manager/foreground/js/compiled_resources.gyp
index 5f81d5a..37260ea 100644
--- a/ui/file_manager/file_manager/foreground/js/compiled_resources.gyp
+++ b/ui/file_manager/file_manager/foreground/js/compiled_resources.gyp
@@ -170,6 +170,7 @@
           '<(EXTERNS_DIR)/command_line_private.js',
           '<(EXTERNS_DIR)/file_manager_private.js',
           '<(EXTERNS_DIR)/metrics_private.js',
+          '<(EXTERNS_DIR)/web_animations.js',
           '../../../../../third_party/analytics/externs.js',
           '../../../externs/background_window.js',
           '../../../externs/background_window_common.js',
diff --git a/ui/file_manager/gallery/js/compiled_resources.gyp b/ui/file_manager/gallery/js/compiled_resources.gyp
index 5a5c3fe..9136a67 100644
--- a/ui/file_manager/gallery/js/compiled_resources.gyp
+++ b/ui/file_manager/gallery/js/compiled_resources.gyp
@@ -130,6 +130,7 @@
           './image_editor/image_view.js',
           './image_editor/commands.js',
           './image_editor/image_editor.js',
+          './image_editor/image_editor_prompt.js',
           './image_editor/image_transform.js',
           './image_editor/image_adjust.js',
           './image_editor/image_resize.js',
@@ -152,6 +153,7 @@
           '<(EXTERNS_DIR)/chrome_send.js',
           '<(EXTERNS_DIR)/file_manager_private.js',
           '<(EXTERNS_DIR)/metrics_private.js',
+          '<(EXTERNS_DIR)/web_animations.js',
           '../../../../third_party/analytics/externs.js',
           '../../../../third_party/closure_compiler/externs/polymer-1.0.js',
           '../../externs/app_window_common.js',
diff --git a/ui/file_manager/gallery/js/compiled_resources2.gyp b/ui/file_manager/gallery/js/compiled_resources2.gyp
index ec96d1b..d285967 100644
--- a/ui/file_manager/gallery/js/compiled_resources2.gyp
+++ b/ui/file_manager/gallery/js/compiled_resources2.gyp
@@ -14,6 +14,7 @@
         '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:assert',
         '<(EXTERNS_GYP):chrome_extensions',
         'gallery_constants',
+        'image_editor/compiled_resources2.gyp:image_editor_prompt',
       ],
       'includes': ['../../compile_js2.gypi'],
     },
diff --git a/ui/file_manager/gallery/js/gallery.js b/ui/file_manager/gallery/js/gallery.js
index cd07d9a3..e145c1fd 100644
--- a/ui/file_manager/gallery/js/gallery.js
+++ b/ui/file_manager/gallery/js/gallery.js
@@ -108,7 +108,7 @@
 
   var buttonSpacer = queryRequiredElement('.button-spacer', this.topToolbar_);
 
-  this.prompt_ = new ImageEditor.Prompt(this.container_, strf);
+  this.prompt_ = new ImageEditorPrompt(this.container_, strf);
 
   this.errorBanner_ = new ErrorBanner(this.container_);
 
diff --git a/ui/file_manager/gallery/js/gallery_scripts.js b/ui/file_manager/gallery/js/gallery_scripts.js
index d168069..dcfab34 100644
--- a/ui/file_manager/gallery/js/gallery_scripts.js
+++ b/ui/file_manager/gallery/js/gallery_scripts.js
@@ -68,6 +68,7 @@
 // <include src="image_editor/image_loader.js">
 // <include src="image_editor/image_view.js">
 // <include src="image_editor/commands.js">
+// <include src="image_editor/image_editor_prompt.js">
 // <include src="image_editor/image_editor.js">
 // <include src="image_editor/image_transform.js">
 // <include src="image_editor/image_adjust.js">
diff --git a/ui/file_manager/gallery/js/image_editor/commands.js b/ui/file_manager/gallery/js/image_editor/commands.js
index 866c319..c424db5 100644
--- a/ui/file_manager/gallery/js/image_editor/commands.js
+++ b/ui/file_manager/gallery/js/image_editor/commands.js
@@ -48,7 +48,7 @@
  * Once the UI is attached the results of image manipulations are displayed.
  *
  * @param {!ImageView} imageView The ImageView object to display the results.
- * @param {!ImageEditor.Prompt} prompt Prompt to use with this CommandQueue.
+ * @param {!ImageEditorPrompt} prompt Prompt to use with this CommandQueue.
  * @param {!FilesToast} toast Toast.
  * @param {function()} updateUndoRedo Function to update undo and redo buttons
  *     state.
diff --git a/ui/file_manager/gallery/js/image_editor/compiled_resources2.gyp b/ui/file_manager/gallery/js/image_editor/compiled_resources2.gyp
index 8e8d8ab..477b9dc 100644
--- a/ui/file_manager/gallery/js/image_editor/compiled_resources2.gyp
+++ b/ui/file_manager/gallery/js/image_editor/compiled_resources2.gyp
@@ -4,18 +4,18 @@
 # TODO(oka): Compile all the targets.
 {
   'targets': [
-#    {
-#      'target_name': 'commands',
-#      'dependencies': [
-#        'filter',
-#        'image_view',
-#        'viewport',
-#        'image_util',
-#        '../../../file_manager/foreground/elements/compiled_resources2.gyp:files_toast',
-#        'image_editor',
-#      ],
-#      'includes': ['../../../compile_js2.gypi'],
-#    },
+    {
+      'target_name': 'commands',
+      'dependencies': [
+        'filter',
+        'image_editor_prompt',
+        'image_util',
+        'image_view',
+        'viewport',
+        '../../../file_manager/foreground/elements/compiled_resources2.gyp:files_toast',
+      ],
+      'includes': ['../../../compile_js2.gypi'],
+    },
     {
       'target_name': 'exif_encoder',
       'dependencies': [
@@ -63,6 +63,13 @@
 #      'includes': ['../../../compile_js2.gypi'],
 #    },
     {
+      'target_name': 'image_editor_prompt',
+      'dependencies': [
+        '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:assert',
+      ],
+      'includes': ['../../../compile_js2.gypi'],
+    },
+    {
       'target_name': 'image_encoder',
       'dependencies': [
         '../../../file_manager/foreground/js/metadata/compiled_resources2.gyp:metadata_item',
diff --git a/ui/file_manager/gallery/js/image_editor/image_editor.js b/ui/file_manager/gallery/js/image_editor/image_editor.js
index 30da9fe3..0adb1b3 100644
--- a/ui/file_manager/gallery/js/image_editor/image_editor.js
+++ b/ui/file_manager/gallery/js/image_editor/image_editor.js
@@ -8,7 +8,7 @@
  *
  * @param {!Viewport} viewport The viewport.
  * @param {!ImageView} imageView The ImageView containing the images to edit.
- * @param {!ImageEditor.Prompt} prompt Prompt instance.
+ * @param {!ImageEditorPrompt} prompt Prompt instance.
  * @param {!Object} DOMContainers Various DOM containers required for the
  *     editor.
  * @param {!Array<!ImageEditor.Mode>} modes Available editor modes.
@@ -350,7 +350,7 @@
 ImageEditor.prototype.getViewport = function() { return this.viewport_; };
 
 /**
- * @return {!ImageEditor.Prompt} Prompt instance.
+ * @return {!ImageEditorPrompt} Prompt instance.
  */
 ImageEditor.prototype.getPrompt = function() { return this.prompt_; };
 
@@ -1463,151 +1463,3 @@
       input.focus();
   }
 };
-
-/** A prompt panel for the editor.
- *
- * @param {!HTMLElement} container Container element.
- * @param {function(string, ...string)} displayStringFunction A formatting
- *     function.
- * @constructor
- * @struct
- */
-ImageEditor.Prompt = function(container, displayStringFunction) {
-  this.container_ = container;
-  this.displayStringFunction_ = displayStringFunction;
-
-  /**
-   * @type {HTMLDivElement}
-   * @private
-   */
-  this.wrapper_ = null;
-
-  /**
-   * @type {HTMLDivElement}
-   * @private
-   */
-  this.prompt_ = null;
-
-  /**
-   * @type {number}
-   * @private
-   */
-  this.timer_ = 0;
-};
-
-/**
- * Reset the prompt.
- */
-ImageEditor.Prompt.prototype.reset = function() {
-  this.cancelTimer();
-  if (this.wrapper_) {
-    this.container_.removeChild(this.wrapper_);
-    this.wrapper_ = null;
-    this.prompt_ = null;
-  }
-};
-
-/**
- * Cancel the delayed action.
- */
-ImageEditor.Prompt.prototype.cancelTimer = function() {
-  if (this.timer_) {
-    clearTimeout(this.timer_);
-    this.timer_ = 0;
-  }
-};
-
-/**
- * Schedule the delayed action.
- * @param {function()} callback Callback.
- * @param {number} timeout Timeout.
- */
-ImageEditor.Prompt.prototype.setTimer = function(callback, timeout) {
-  this.cancelTimer();
-  var self = this;
-  this.timer_ = setTimeout(function() {
-    self.timer_ = 0;
-    callback();
-  }, timeout);
-};
-
-/**
- * Show the prompt.
- *
- * @param {string} text The prompt text.
- * @param {number=} opt_timeout Timeout in ms.
- * @param {...Object} var_args varArgs for the formatting function.
- */
-ImageEditor.Prompt.prototype.show = function(text, opt_timeout, var_args) {
-  var args = [text].concat(Array.prototype.slice.call(arguments, 2));
-  var message = this.displayStringFunction_.apply(null, args);
-  this.showStringAt('center', message, opt_timeout);
-};
-
-/**
- * Show the position at the specific position.
- *
- * @param {string} pos The 'pos' attribute value.
- * @param {string} text The prompt text.
- * @param {number} timeout Timeout in ms.
- * @param {...Object} var_args varArgs for the formatting function.
- */
-ImageEditor.Prompt.prototype.showAt = function(
-    pos, text, timeout, var_args) {
-  var args = [text].concat(Array.prototype.slice.call(arguments, 3));
-  var message = this.displayStringFunction_.apply(null, args);
-  this.showStringAt(pos, message, timeout);
-};
-
-/**
- * Show the string in the prompt
- *
- * @param {string} pos The 'pos' attribute value.
- * @param {string} text The prompt text.
- * @param {number=} opt_timeout Timeout in ms.
- */
-ImageEditor.Prompt.prototype.showStringAt = function(pos, text, opt_timeout) {
-  this.reset();
-  if (!text)
-    return;
-
-  var document = this.container_.ownerDocument;
-  this.wrapper_ = assertInstanceof(document.createElement('div'),
-      HTMLDivElement);
-  this.wrapper_.className = 'prompt-wrapper';
-  this.wrapper_.setAttribute('pos', pos);
-  this.container_.appendChild(this.wrapper_);
-
-  this.prompt_ = assertInstanceof(document.createElement('div'),
-      HTMLDivElement);
-  this.prompt_.className = 'prompt';
-
-  // Create an extra wrapper which opacity can be manipulated separately.
-  var tool = document.createElement('div');
-  tool.className = 'dimmable';
-  this.wrapper_.appendChild(tool);
-  tool.appendChild(this.prompt_);
-
-  this.prompt_.textContent = text;
-
-  var close = document.createElement('div');
-  close.className = 'close';
-  close.addEventListener('click', this.hide.bind(this));
-  this.prompt_.appendChild(close);
-
-  setTimeout(
-      this.prompt_.setAttribute.bind(this.prompt_, 'state', 'fadein'), 0);
-
-  if (opt_timeout)
-    this.setTimer(this.hide.bind(this), opt_timeout);
-};
-
-/**
- * Hide the prompt.
- */
-ImageEditor.Prompt.prototype.hide = function() {
-  if (!this.prompt_) return;
-  this.prompt_.setAttribute('state', 'fadeout');
-  // Allow some time for the animation to play out.
-  this.setTimer(this.reset.bind(this), 500);
-};
diff --git a/ui/file_manager/gallery/js/image_editor/image_editor_prompt.js b/ui/file_manager/gallery/js/image_editor/image_editor_prompt.js
new file mode 100644
index 0000000..fdf28be
--- /dev/null
+++ b/ui/file_manager/gallery/js/image_editor/image_editor_prompt.js
@@ -0,0 +1,152 @@
+// 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.
+
+/**
+ * A prompt panel for the editor.
+ *
+ * @param {!HTMLElement} container Container element.
+ * @param {function(string, ...string)} displayStringFunction A formatting
+ *     function.
+ * @constructor
+ * @struct
+ */
+function ImageEditorPrompt(container, displayStringFunction) {
+  this.container_ = container;
+  this.displayStringFunction_ = displayStringFunction;
+
+  /**
+   * @type {HTMLDivElement}
+   * @private
+   */
+  this.wrapper_ = null;
+
+  /**
+   * @type {HTMLDivElement}
+   * @private
+   */
+  this.prompt_ = null;
+
+  /**
+   * @type {number}
+   * @private
+   */
+  this.timer_ = 0;
+};
+
+/**
+ * Reset the prompt.
+ */
+ImageEditorPrompt.prototype.reset = function() {
+  this.cancelTimer();
+  if (this.wrapper_) {
+    this.container_.removeChild(this.wrapper_);
+    this.wrapper_ = null;
+    this.prompt_ = null;
+  }
+};
+
+/**
+ * Cancel the delayed action.
+ */
+ImageEditorPrompt.prototype.cancelTimer = function() {
+  if (this.timer_) {
+    clearTimeout(this.timer_);
+    this.timer_ = 0;
+  }
+};
+
+/**
+ * Schedule the delayed action.
+ * @param {function()} callback Callback.
+ * @param {number} timeout Timeout.
+ */
+ImageEditorPrompt.prototype.setTimer = function(callback, timeout) {
+  this.cancelTimer();
+  var self = this;
+  this.timer_ = setTimeout(function() {
+    self.timer_ = 0;
+    callback();
+  }, timeout);
+};
+
+/**
+ * Show the prompt.
+ *
+ * @param {string} text The prompt text.
+ * @param {number=} opt_timeout Timeout in ms.
+ * @param {...Object} var_args varArgs for the formatting function.
+ */
+ImageEditorPrompt.prototype.show = function(text, opt_timeout, var_args) {
+  var args = [text].concat(Array.prototype.slice.call(arguments, 2));
+  var message = this.displayStringFunction_.apply(null, args);
+  this.showStringAt('center', message, opt_timeout);
+};
+
+/**
+ * Show the position at the specific position.
+ *
+ * @param {string} pos The 'pos' attribute value.
+ * @param {string} text The prompt text.
+ * @param {number} timeout Timeout in ms.
+ * @param {...Object} var_args varArgs for the formatting function.
+ */
+ImageEditorPrompt.prototype.showAt = function(pos, text, timeout, var_args) {
+  var args = [text].concat(Array.prototype.slice.call(arguments, 3));
+  var message = this.displayStringFunction_.apply(null, args);
+  this.showStringAt(pos, message, timeout);
+};
+
+/**
+ * Show the string in the prompt
+ *
+ * @param {string} pos The 'pos' attribute value.
+ * @param {string} text The prompt text.
+ * @param {number=} opt_timeout Timeout in ms.
+ */
+ImageEditorPrompt.prototype.showStringAt = function(pos, text, opt_timeout) {
+  this.reset();
+  if (!text)
+    return;
+
+  var document = this.container_.ownerDocument;
+  this.wrapper_ =
+      assertInstanceof(document.createElement('div'), HTMLDivElement);
+  this.wrapper_.className = 'prompt-wrapper';
+  this.wrapper_.setAttribute('pos', pos);
+  this.container_.appendChild(this.wrapper_);
+
+  this.prompt_ =
+      assertInstanceof(document.createElement('div'), HTMLDivElement);
+  this.prompt_.className = 'prompt';
+
+  // Create an extra wrapper which opacity can be manipulated separately.
+  var tool = document.createElement('div');
+  tool.className = 'dimmable';
+  this.wrapper_.appendChild(tool);
+  tool.appendChild(this.prompt_);
+
+  this.prompt_.textContent = text;
+
+  var close = document.createElement('div');
+  close.className = 'close';
+  close.addEventListener('click', this.hide.bind(this));
+  this.prompt_.appendChild(close);
+
+  setTimeout(
+      this.prompt_.setAttribute.bind(this.prompt_, 'state', 'fadein'), 0);
+
+  if (opt_timeout)
+    this.setTimer(this.hide.bind(this), opt_timeout);
+};
+
+/**
+ * Hide the prompt.
+ */
+ImageEditorPrompt.prototype.hide = function() {
+  if (!this.prompt_)
+    return;
+  this.prompt_.setAttribute('state', 'fadeout');
+  // Allow some time for the animation to play out.
+  this.setTimer(this.reset.bind(this), 500);
+};
diff --git a/ui/file_manager/gallery/js/slide_mode.js b/ui/file_manager/gallery/js/slide_mode.js
index ef11a6a9..25d93a4 100644
--- a/ui/file_manager/gallery/js/slide_mode.js
+++ b/ui/file_manager/gallery/js/slide_mode.js
@@ -10,7 +10,7 @@
  * @param {!HTMLElement} content Content container element.
  * @param {!HTMLElement} topToolbar Top toolbar element.
  * @param {!HTMLElement} bottomToolbar Toolbar element.
- * @param {!ImageEditor.Prompt} prompt Prompt.
+ * @param {!ImageEditorPrompt} prompt Prompt.
  * @param {!ErrorBanner} errorBanner Error banner.
  * @param {!cr.ui.ArrayDataModel} dataModel Data model.
  * @param {!cr.ui.ListSelectionModel} selectionModel Selection model.
@@ -65,7 +65,7 @@
   this.bottomToolbar_ = bottomToolbar;
 
   /**
-   * @type {!ImageEditor.Prompt}
+   * @type {!ImageEditorPrompt}
    * @private
    * @const
    */